VirtualBox

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

Last change on this file since 21945 was 21929, checked in by vboxsync, 15 years ago

Console::loadStateFileExec: Call SSMR3SkipToEndOfUnit to indicate that we don't care about the saved data (shared folder stuff).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 243.0 KB
Line 
1/* $Id: ConsoleImpl.cpp 21929 2009-08-02 17:21:25Z 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), 0, &ssm);
959 if (VBOX_SUCCESS (vrc))
960 {
961 uint32_t version = 0;
962 vrc = SSMR3Seek (ssm, sSSMConsoleUnit, 0 /* iInstance */, &version);
963 if (SSM_VERSION_MAJOR(version) == SSM_VERSION_MAJOR(sSSMConsoleVer))
964 {
965 if (VBOX_SUCCESS (vrc))
966 vrc = loadStateFileExecInternal (ssm, version);
967 else if (vrc == VERR_SSM_UNIT_NOT_FOUND)
968 vrc = VINF_SUCCESS;
969 }
970 else
971 vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
972
973 SSMR3Close (ssm);
974 }
975
976 if (VBOX_FAILURE (vrc))
977 rc = setError (VBOX_E_FILE_ERROR,
978 tr ("The saved state file '%ls' is invalid (%Rrc). "
979 "Discard the saved state and try again"),
980 savedStateFile.raw(), vrc);
981
982 mSavedStateDataLoaded = true;
983
984 return rc;
985}
986
987/**
988 * Callback handler to save various console data to the state file,
989 * called when the user saves the VM state.
990 *
991 * @param pvUser pointer to Console
992 *
993 * @note Locks the Console object for reading.
994 */
995//static
996DECLCALLBACK(void)
997Console::saveStateFileExec (PSSMHANDLE pSSM, void *pvUser)
998{
999 LogFlowFunc (("\n"));
1000
1001 Console *that = static_cast <Console *> (pvUser);
1002 AssertReturnVoid (that);
1003
1004 AutoCaller autoCaller(that);
1005 AssertComRCReturnVoid (autoCaller.rc());
1006
1007 AutoReadLock alock(that);
1008
1009 int vrc = SSMR3PutU32 (pSSM, (uint32_t)that->mSharedFolders.size());
1010 AssertRC (vrc);
1011
1012 for (SharedFolderMap::const_iterator it = that->mSharedFolders.begin();
1013 it != that->mSharedFolders.end();
1014 ++ it)
1015 {
1016 ComObjPtr<SharedFolder> folder = (*it).second;
1017 // don't lock the folder because methods we access are const
1018
1019 Utf8Str name = folder->name();
1020 vrc = SSMR3PutU32 (pSSM, (uint32_t)name.length() + 1 /* term. 0 */);
1021 AssertRC (vrc);
1022 vrc = SSMR3PutStrZ (pSSM, name);
1023 AssertRC (vrc);
1024
1025 Utf8Str hostPath = folder->hostPath();
1026 vrc = SSMR3PutU32 (pSSM, (uint32_t)hostPath.length() + 1 /* term. 0 */);
1027 AssertRC (vrc);
1028 vrc = SSMR3PutStrZ (pSSM, hostPath);
1029 AssertRC (vrc);
1030
1031 vrc = SSMR3PutBool (pSSM, !!folder->writable());
1032 AssertRC (vrc);
1033 }
1034
1035 return;
1036}
1037
1038/**
1039 * Callback handler to load various console data from the state file.
1040 * Called when the VM is being restored from the saved state.
1041 *
1042 * @param pvUser pointer to Console
1043 * @param u32Version Console unit version.
1044 * Should match sSSMConsoleVer.
1045 *
1046 * @note Should locks the Console object for writing, if necessary.
1047 */
1048//static
1049DECLCALLBACK(int)
1050Console::loadStateFileExec (PSSMHANDLE pSSM, void *pvUser, uint32_t u32Version)
1051{
1052 LogFlowFunc (("\n"));
1053
1054 if (SSM_VERSION_MAJOR_CHANGED(u32Version, sSSMConsoleVer))
1055 return VERR_VERSION_MISMATCH;
1056
1057 Console *that = static_cast <Console *> (pvUser);
1058 AssertReturn(that, VERR_INVALID_PARAMETER);
1059
1060 /* Currently, nothing to do when we've been called from VMR3Load. */
1061 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))
1849 {
1850 int vrc = RTDirCreateFullPath (dir, 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, PDMDEVATT_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, PDMDEVATT_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, NULL, PDMDEVATT_FLAGS_NOT_HOT_PLUG);
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, &pBase, PDMDEVATT_FLAGS_NOT_HOT_PLUG);
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)
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#ifdef VBOX_DYNAMIC_NET_ATTACH
3292 /* Get the current network attachment type */
3293 HRESULT rc;
3294 NetworkAttachmentType_T eAttachmentType;
3295 Bstr eHostInterface;
3296 Bstr eInternalNetwork;
3297
3298 rc = aNetworkAdapter->COMGETTER (AttachmentType) (&eAttachmentType);
3299 ComAssertComRCRetRC (rc);
3300 rc = aNetworkAdapter->COMGETTER (HostInterface) (eHostInterface.asOutParam());
3301 ComAssertComRCRetRC (rc);
3302 rc = aNetworkAdapter->COMGETTER (InternalNetwork) (eInternalNetwork.asOutParam());
3303 ComAssertComRCRetRC (rc);
3304#endif /* VBOX_DYNAMIC_NET_ATTACH */
3305
3306 /* Get the properties we need from the adapter */
3307 BOOL fCableConnected;
3308#ifdef VBOX_DYNAMIC_NET_ATTACH
3309 rc = aNetworkAdapter->COMGETTER(CableConnected) (&fCableConnected);
3310#else /* !VBOX_DYNAMIC_NET_ATTACH */
3311 HRESULT rc = aNetworkAdapter->COMGETTER(CableConnected) (&fCableConnected);
3312#endif /* !VBOX_DYNAMIC_NET_ATTACH */
3313 AssertComRC(rc);
3314 if (SUCCEEDED(rc))
3315 {
3316 ULONG ulInstance;
3317 rc = aNetworkAdapter->COMGETTER(Slot) (&ulInstance);
3318 AssertComRC (rc);
3319 if (SUCCEEDED(rc))
3320 {
3321 /*
3322 * Find the pcnet instance, get the config interface and update
3323 * the link state.
3324 */
3325 PPDMIBASE pBase;
3326 const char *pszAdapterName = "pcnet";
3327#ifdef VBOX_WITH_E1000
3328 /*
3329 * Perhaps it would be much wiser to wrap both 'pcnet' and 'e1000'
3330 * into generic 'net' device.
3331 */
3332 NetworkAdapterType_T adapterType;
3333 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3334 AssertComRC(rc);
3335 if (adapterType == NetworkAdapterType_I82540EM ||
3336 adapterType == NetworkAdapterType_I82543GC ||
3337 adapterType == NetworkAdapterType_I82545EM)
3338 pszAdapterName = "e1000";
3339#endif
3340 int vrc = PDMR3QueryDeviceLun (mpVM, pszAdapterName,
3341 (unsigned) ulInstance, 0, &pBase);
3342 ComAssertRC (vrc);
3343 if (VBOX_SUCCESS (vrc))
3344 {
3345 Assert(pBase);
3346 PPDMINETWORKCONFIG pINetCfg = (PPDMINETWORKCONFIG) pBase->
3347 pfnQueryInterface(pBase, PDMINTERFACE_NETWORK_CONFIG);
3348 if (pINetCfg)
3349 {
3350 Log (("Console::onNetworkAdapterChange: setting link state to %d\n",
3351 fCableConnected));
3352 vrc = pINetCfg->pfnSetLinkState (pINetCfg,
3353 fCableConnected ? PDMNETWORKLINKSTATE_UP
3354 : PDMNETWORKLINKSTATE_DOWN);
3355 ComAssertRC (vrc);
3356 }
3357 }
3358
3359#ifdef VBOX_DYNAMIC_NET_ATTACH
3360 if (VBOX_SUCCESS (vrc))
3361 {
3362 VMSTATE enmVMState = VMR3GetState (mpVM);
3363 if ( (enmVMState == VMSTATE_RUNNING)
3364 || (enmVMState == VMSTATE_SUSPENDED))
3365 {
3366 bool changeAdaptor = false;
3367
3368 if ( !( (eAttachmentType == NetworkAttachmentType_Null)
3369 && (meAttachmentType[ulInstance] == NetworkAttachmentType_Null)))
3370 changeAdaptor = true;
3371
3372 /** @todo pritesh: Need to check for mNATNetwork as well here
3373 * when NAT is shifted to use IntNet, till then just compare
3374 * if the current and next attachment types are not same
3375 */
3376 if ( !( (eAttachmentType == NetworkAttachmentType_NAT)
3377 && (meAttachmentType[ulInstance] == NetworkAttachmentType_NAT)))
3378 changeAdaptor = true;
3379
3380 if ( !( (eAttachmentType == NetworkAttachmentType_Bridged)
3381 && (meAttachmentType[ulInstance] == NetworkAttachmentType_Bridged)
3382 && (mHostInterface[ulInstance] == eHostInterface)))
3383 changeAdaptor = true;
3384
3385 if ( !( (eAttachmentType == NetworkAttachmentType_HostOnly)
3386 && (meAttachmentType[ulInstance] == NetworkAttachmentType_HostOnly)
3387 && (mHostInterface[ulInstance] == eHostInterface)))
3388 changeAdaptor = true;
3389
3390 if ( !( (eAttachmentType == NetworkAttachmentType_Internal)
3391 && (meAttachmentType[ulInstance] == NetworkAttachmentType_Internal)
3392 && (mInternalNetwork[ulInstance] == eInternalNetwork)))
3393 changeAdaptor = true;
3394
3395 if (changeAdaptor)
3396 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3397 }
3398 }
3399#endif /* VBOX_DYNAMIC_NET_ATTACH */
3400
3401 if (VBOX_FAILURE (vrc))
3402 rc = E_FAIL;
3403 }
3404 }
3405
3406 /* notify console callbacks on success */
3407 if (SUCCEEDED(rc))
3408 {
3409 CallbackList::iterator it = mCallbacks.begin();
3410 while (it != mCallbacks.end())
3411 (*it++)->OnNetworkAdapterChange (aNetworkAdapter);
3412 }
3413
3414 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3415 return rc;
3416}
3417
3418
3419#ifdef VBOX_DYNAMIC_NET_ATTACH
3420/**
3421 * Process a network adaptor change.
3422 *
3423 * @returns COM status code.
3424 *
3425 * @param pszDevice The PDM device name.
3426 * @param uInstance The PDM device instance.
3427 * @param uLun The PDM LUN number of the drive.
3428 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3429 *
3430 * @note Locks this object for writing.
3431 */
3432HRESULT Console::doNetworkAdapterChange (const char *pszDevice,
3433 unsigned uInstance,
3434 unsigned uLun,
3435 INetworkAdapter *aNetworkAdapter)
3436{
3437 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3438 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3439
3440 AutoCaller autoCaller(this);
3441 AssertComRCReturnRC(autoCaller.rc());
3442
3443 /* We will need to release the write lock before calling EMT */
3444 AutoWriteLock alock(this);
3445
3446 /* protect mpVM */
3447 AutoVMCaller autoVMCaller (this);
3448 CheckComRCReturnRC(autoVMCaller.rc());
3449
3450 /*
3451 * Call worker in EMT, that's faster and safer than doing everything
3452 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3453 * here to make requests from under the lock in order to serialize them.
3454 */
3455 PVMREQ pReq;
3456 int vrc = VMR3ReqCall (mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */,
3457 (PFNRT) Console::changeNetworkAttachment, 5,
3458 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3459
3460 /* leave the lock before waiting for a result (EMT will call us back!) */
3461 alock.leave();
3462
3463 if (vrc == VERR_TIMEOUT || VBOX_SUCCESS (vrc))
3464 {
3465 vrc = VMR3ReqWait (pReq, RT_INDEFINITE_WAIT);
3466 AssertRC (vrc);
3467 if (VBOX_SUCCESS (vrc))
3468 vrc = pReq->iStatus;
3469 }
3470 VMR3ReqFree (pReq);
3471
3472 if (VBOX_SUCCESS (vrc))
3473 {
3474 LogFlowThisFunc(("Returns S_OK\n"));
3475 return S_OK;
3476 }
3477
3478 return setError (E_FAIL,
3479 tr ("Could not change the network adaptor attachement type (%Rrc)"), vrc);
3480}
3481
3482
3483/**
3484 * Performs the Network Adaptor change in EMT.
3485 *
3486 * @returns VBox status code.
3487 *
3488 * @param pThis Pointer to the Console object.
3489 * @param pszDevice The PDM device name.
3490 * @param uInstance The PDM device instance.
3491 * @param uLun The PDM LUN number of the drive.
3492 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3493 *
3494 * @thread EMT
3495 * @note Locks the Console object for writing.
3496 */
3497DECLCALLBACK(int) Console::changeNetworkAttachment (Console *pThis,
3498 const char *pszDevice,
3499 unsigned uInstance,
3500 unsigned uLun,
3501 INetworkAdapter *aNetworkAdapter)
3502{
3503 LogFlowFunc (("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3504 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3505
3506 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3507
3508 AssertMsg ( (!strcmp (pszDevice, "pcnet") && uLun == 0 && uInstance < SchemaDefs::NetworkAdapterCount)
3509 || (!strcmp (pszDevice, "e1000") && uLun == 0 && uInstance < SchemaDefs::NetworkAdapterCount),
3510 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3511 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3512
3513 AutoCaller autoCaller(pThis);
3514 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);
3515
3516 /* protect mpVM */
3517 AutoVMCaller autoVMCaller (pThis);
3518 CheckComRCReturnRC(autoVMCaller.rc());
3519
3520 PVM pVM = pThis->mpVM;
3521
3522 /*
3523 * Suspend the VM first.
3524 *
3525 * The VM must not be running since it might have pending I/O to
3526 * the drive which is being changed.
3527 */
3528 bool fResume;
3529 VMSTATE enmVMState = VMR3GetState (pVM);
3530 switch (enmVMState)
3531 {
3532 case VMSTATE_RESETTING:
3533 case VMSTATE_RUNNING:
3534 {
3535 LogFlowFunc (("Suspending the VM...\n"));
3536 /* disable the callback to prevent Console-level state change */
3537 pThis->mVMStateChangeCallbackDisabled = true;
3538 int rc = VMR3Suspend (pVM);
3539 pThis->mVMStateChangeCallbackDisabled = false;
3540 AssertRCReturn (rc, rc);
3541 fResume = true;
3542 break;
3543 }
3544
3545 case VMSTATE_SUSPENDED:
3546 case VMSTATE_CREATED:
3547 case VMSTATE_OFF:
3548 fResume = false;
3549 break;
3550
3551 default:
3552 AssertMsgFailedReturn (("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3553 }
3554
3555 int rc = VINF_SUCCESS;
3556 int rcRet = VINF_SUCCESS;
3557
3558 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3559 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3560 PCFGMNODE pInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
3561 AssertRelease (pInst);
3562
3563 /** @todo pritesh: Need to store the previous network configuration
3564 * and restore it if configNetwork fails, currently not sure if the
3565 * previous atachment will also cleanly reattach with the later one
3566 * failing to attach.
3567 */
3568 rcRet = configNetwork(pThis, pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
3569
3570 /*
3571 * Resume the VM if necessary.
3572 */
3573 if (fResume)
3574 {
3575 LogFlowFunc (("Resuming the VM...\n"));
3576 /* disable the callback to prevent Console-level state change */
3577 pThis->mVMStateChangeCallbackDisabled = true;
3578 rc = VMR3Resume (pVM);
3579 pThis->mVMStateChangeCallbackDisabled = false;
3580 AssertRC (rc);
3581 if (VBOX_FAILURE (rc))
3582 {
3583 /* too bad, we failed. try to sync the console state with the VMM state */
3584 vmstateChangeCallback (pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3585 }
3586 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3587 // error (if any) will be hidden from the caller. For proper reporting
3588 // of such multiple errors to the caller we need to enhance the
3589 // IVirtualBoxError interface. For now, give the first error the higher
3590 // priority.
3591 if (VBOX_SUCCESS (rcRet))
3592 rcRet = rc;
3593 }
3594
3595 LogFlowFunc (("Returning %Rrc\n", rcRet));
3596 return rcRet;
3597}
3598#endif /* VBOX_DYNAMIC_NET_ATTACH */
3599
3600
3601/**
3602 * Called by IInternalSessionControl::OnSerialPortChange().
3603 *
3604 * @note Locks this object for writing.
3605 */
3606HRESULT Console::onSerialPortChange (ISerialPort *aSerialPort)
3607{
3608 LogFlowThisFunc(("\n"));
3609
3610 AutoCaller autoCaller(this);
3611 AssertComRCReturnRC(autoCaller.rc());
3612
3613 AutoWriteLock alock(this);
3614
3615 /* Don't do anything if the VM isn't running */
3616 if (!mpVM)
3617 return S_OK;
3618
3619 HRESULT rc = S_OK;
3620
3621 /* protect mpVM */
3622 AutoVMCaller autoVMCaller (this);
3623 CheckComRCReturnRC(autoVMCaller.rc());
3624
3625 /* nothing to do so far */
3626
3627 /* notify console callbacks on success */
3628 if (SUCCEEDED(rc))
3629 {
3630 CallbackList::iterator it = mCallbacks.begin();
3631 while (it != mCallbacks.end())
3632 (*it++)->OnSerialPortChange (aSerialPort);
3633 }
3634
3635 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3636 return rc;
3637}
3638
3639/**
3640 * Called by IInternalSessionControl::OnParallelPortChange().
3641 *
3642 * @note Locks this object for writing.
3643 */
3644HRESULT Console::onParallelPortChange (IParallelPort *aParallelPort)
3645{
3646 LogFlowThisFunc(("\n"));
3647
3648 AutoCaller autoCaller(this);
3649 AssertComRCReturnRC(autoCaller.rc());
3650
3651 AutoWriteLock alock(this);
3652
3653 /* Don't do anything if the VM isn't running */
3654 if (!mpVM)
3655 return S_OK;
3656
3657 HRESULT rc = S_OK;
3658
3659 /* protect mpVM */
3660 AutoVMCaller autoVMCaller (this);
3661 CheckComRCReturnRC(autoVMCaller.rc());
3662
3663 /* nothing to do so far */
3664
3665 /* notify console callbacks on success */
3666 if (SUCCEEDED(rc))
3667 {
3668 CallbackList::iterator it = mCallbacks.begin();
3669 while (it != mCallbacks.end())
3670 (*it++)->OnParallelPortChange (aParallelPort);
3671 }
3672
3673 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3674 return rc;
3675}
3676
3677/**
3678 * Called by IInternalSessionControl::OnStorageControllerChange().
3679 *
3680 * @note Locks this object for writing.
3681 */
3682HRESULT Console::onStorageControllerChange ()
3683{
3684 LogFlowThisFunc(("\n"));
3685
3686 AutoCaller autoCaller(this);
3687 AssertComRCReturnRC(autoCaller.rc());
3688
3689 AutoWriteLock alock(this);
3690
3691 /* Don't do anything if the VM isn't running */
3692 if (!mpVM)
3693 return S_OK;
3694
3695 HRESULT rc = S_OK;
3696
3697 /* protect mpVM */
3698 AutoVMCaller autoVMCaller (this);
3699 CheckComRCReturnRC(autoVMCaller.rc());
3700
3701 /* nothing to do so far */
3702
3703 /* notify console callbacks on success */
3704 if (SUCCEEDED(rc))
3705 {
3706 CallbackList::iterator it = mCallbacks.begin();
3707 while (it != mCallbacks.end())
3708 (*it++)->OnStorageControllerChange ();
3709 }
3710
3711 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3712 return rc;
3713}
3714
3715/**
3716 * Called by IInternalSessionControl::OnVRDPServerChange().
3717 *
3718 * @note Locks this object for writing.
3719 */
3720HRESULT Console::onVRDPServerChange()
3721{
3722 AutoCaller autoCaller(this);
3723 AssertComRCReturnRC(autoCaller.rc());
3724
3725 AutoWriteLock alock(this);
3726
3727 HRESULT rc = S_OK;
3728
3729 if (mVRDPServer && mMachineState == MachineState_Running)
3730 {
3731 BOOL vrdpEnabled = FALSE;
3732
3733 rc = mVRDPServer->COMGETTER(Enabled) (&vrdpEnabled);
3734 ComAssertComRCRetRC (rc);
3735
3736 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
3737 alock.leave();
3738
3739 if (vrdpEnabled)
3740 {
3741 // If there was no VRDP server started the 'stop' will do nothing.
3742 // However if a server was started and this notification was called,
3743 // we have to restart the server.
3744 mConsoleVRDPServer->Stop ();
3745
3746 if (VBOX_FAILURE(mConsoleVRDPServer->Launch ()))
3747 {
3748 rc = E_FAIL;
3749 }
3750 else
3751 {
3752 mConsoleVRDPServer->EnableConnections ();
3753 }
3754 }
3755 else
3756 {
3757 mConsoleVRDPServer->Stop ();
3758 }
3759
3760 alock.enter();
3761 }
3762
3763 /* notify console callbacks on success */
3764 if (SUCCEEDED(rc))
3765 {
3766 CallbackList::iterator it = mCallbacks.begin();
3767 while (it != mCallbacks.end())
3768 (*it++)->OnVRDPServerChange();
3769 }
3770
3771 return rc;
3772}
3773
3774/**
3775 * Called by IInternalSessionControl::OnUSBControllerChange().
3776 *
3777 * @note Locks this object for writing.
3778 */
3779HRESULT Console::onUSBControllerChange()
3780{
3781 LogFlowThisFunc(("\n"));
3782
3783 AutoCaller autoCaller(this);
3784 AssertComRCReturnRC(autoCaller.rc());
3785
3786 AutoWriteLock alock(this);
3787
3788 /* Ignore if no VM is running yet. */
3789 if (!mpVM)
3790 return S_OK;
3791
3792 HRESULT rc = S_OK;
3793
3794/// @todo (dmik)
3795// check for the Enabled state and disable virtual USB controller??
3796// Anyway, if we want to query the machine's USB Controller we need to cache
3797// it to mUSBController in #init() (as it is done with mDVDDrive).
3798//
3799// bird: While the VM supports hot-plugging, I doubt any guest can handle it at this time... :-)
3800//
3801// /* protect mpVM */
3802// AutoVMCaller autoVMCaller (this);
3803// CheckComRCReturnRC(autoVMCaller.rc());
3804
3805 /* notify console callbacks on success */
3806 if (SUCCEEDED(rc))
3807 {
3808 CallbackList::iterator it = mCallbacks.begin();
3809 while (it != mCallbacks.end())
3810 (*it++)->OnUSBControllerChange();
3811 }
3812
3813 return rc;
3814}
3815
3816/**
3817 * Called by IInternalSessionControl::OnSharedFolderChange().
3818 *
3819 * @note Locks this object for writing.
3820 */
3821HRESULT Console::onSharedFolderChange (BOOL aGlobal)
3822{
3823 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
3824
3825 AutoCaller autoCaller(this);
3826 AssertComRCReturnRC(autoCaller.rc());
3827
3828 AutoWriteLock alock(this);
3829
3830 HRESULT rc = fetchSharedFolders (aGlobal);
3831
3832 /* notify console callbacks on success */
3833 if (SUCCEEDED(rc))
3834 {
3835 CallbackList::iterator it = mCallbacks.begin();
3836 while (it != mCallbacks.end())
3837 (*it++)->OnSharedFolderChange (aGlobal ? (Scope_T) Scope_Global
3838 : (Scope_T) Scope_Machine);
3839 }
3840
3841 return rc;
3842}
3843
3844/**
3845 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
3846 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
3847 * returns TRUE for a given remote USB device.
3848 *
3849 * @return S_OK if the device was attached to the VM.
3850 * @return failure if not attached.
3851 *
3852 * @param aDevice
3853 * The device in question.
3854 * @param aMaskedIfs
3855 * The interfaces to hide from the guest.
3856 *
3857 * @note Locks this object for writing.
3858 */
3859HRESULT Console::onUSBDeviceAttach (IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
3860{
3861#ifdef VBOX_WITH_USB
3862 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
3863
3864 AutoCaller autoCaller(this);
3865 ComAssertComRCRetRC (autoCaller.rc());
3866
3867 AutoWriteLock alock(this);
3868
3869 /* protect mpVM (we don't need error info, since it's a callback) */
3870 AutoVMCallerQuiet autoVMCaller (this);
3871 if (FAILED (autoVMCaller.rc()))
3872 {
3873 /* The VM may be no more operational when this message arrives
3874 * (e.g. it may be Saving or Stopping or just PoweredOff) --
3875 * autoVMCaller.rc() will return a failure in this case. */
3876 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
3877 mMachineState));
3878 return autoVMCaller.rc();
3879 }
3880
3881 if (aError != NULL)
3882 {
3883 /* notify callbacks about the error */
3884 onUSBDeviceStateChange (aDevice, true /* aAttached */, aError);
3885 return S_OK;
3886 }
3887
3888 /* Don't proceed unless there's at least one USB hub. */
3889 if (!PDMR3USBHasHub (mpVM))
3890 {
3891 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
3892 return E_FAIL;
3893 }
3894
3895 HRESULT rc = attachUSBDevice (aDevice, aMaskedIfs);
3896 if (FAILED (rc))
3897 {
3898 /* take the current error info */
3899 com::ErrorInfoKeeper eik;
3900 /* the error must be a VirtualBoxErrorInfo instance */
3901 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
3902 Assert (!error.isNull());
3903 if (!error.isNull())
3904 {
3905 /* notify callbacks about the error */
3906 onUSBDeviceStateChange (aDevice, true /* aAttached */, error);
3907 }
3908 }
3909
3910 return rc;
3911
3912#else /* !VBOX_WITH_USB */
3913 return E_FAIL;
3914#endif /* !VBOX_WITH_USB */
3915}
3916
3917/**
3918 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
3919 * processRemoteUSBDevices().
3920 *
3921 * @note Locks this object for writing.
3922 */
3923HRESULT Console::onUSBDeviceDetach (IN_BSTR aId,
3924 IVirtualBoxErrorInfo *aError)
3925{
3926#ifdef VBOX_WITH_USB
3927 Guid Uuid (aId);
3928 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
3929
3930 AutoCaller autoCaller(this);
3931 AssertComRCReturnRC(autoCaller.rc());
3932
3933 AutoWriteLock alock(this);
3934
3935 /* Find the device. */
3936 ComObjPtr<OUSBDevice> device;
3937 USBDeviceList::iterator it = mUSBDevices.begin();
3938 while (it != mUSBDevices.end())
3939 {
3940 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
3941 if ((*it)->id() == Uuid)
3942 {
3943 device = *it;
3944 break;
3945 }
3946 ++ it;
3947 }
3948
3949
3950 if (device.isNull())
3951 {
3952 LogFlowThisFunc(("USB device not found.\n"));
3953
3954 /* The VM may be no more operational when this message arrives
3955 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
3956 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
3957 * failure in this case. */
3958
3959 AutoVMCallerQuiet autoVMCaller (this);
3960 if (FAILED (autoVMCaller.rc()))
3961 {
3962 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
3963 mMachineState));
3964 return autoVMCaller.rc();
3965 }
3966
3967 /* the device must be in the list otherwise */
3968 AssertFailedReturn (E_FAIL);
3969 }
3970
3971 if (aError != NULL)
3972 {
3973 /* notify callback about an error */
3974 onUSBDeviceStateChange (device, false /* aAttached */, aError);
3975 return S_OK;
3976 }
3977
3978 HRESULT rc = detachUSBDevice (it);
3979
3980 if (FAILED (rc))
3981 {
3982 /* take the current error info */
3983 com::ErrorInfoKeeper eik;
3984 /* the error must be a VirtualBoxErrorInfo instance */
3985 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
3986 Assert (!error.isNull());
3987 if (!error.isNull())
3988 {
3989 /* notify callbacks about the error */
3990 onUSBDeviceStateChange (device, false /* aAttached */, error);
3991 }
3992 }
3993
3994 return rc;
3995
3996#else /* !VBOX_WITH_USB */
3997 return E_FAIL;
3998#endif /* !VBOX_WITH_USB */
3999}
4000
4001/**
4002 * @note Temporarily locks this object for writing.
4003 */
4004HRESULT Console::getGuestProperty (IN_BSTR aName, BSTR *aValue,
4005 ULONG64 *aTimestamp, BSTR *aFlags)
4006{
4007#if !defined (VBOX_WITH_GUEST_PROPS)
4008 ReturnComNotImplemented();
4009#else
4010 if (!VALID_PTR (aName))
4011 return E_INVALIDARG;
4012 if (!VALID_PTR (aValue))
4013 return E_POINTER;
4014 if ((aTimestamp != NULL) && !VALID_PTR (aTimestamp))
4015 return E_POINTER;
4016 if ((aFlags != NULL) && !VALID_PTR (aFlags))
4017 return E_POINTER;
4018
4019 AutoCaller autoCaller(this);
4020 AssertComRCReturnRC(autoCaller.rc());
4021
4022 /* protect mpVM (if not NULL) */
4023 AutoVMCallerWeak autoVMCaller (this);
4024 CheckComRCReturnRC(autoVMCaller.rc());
4025
4026 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4027 * autoVMCaller, so there is no need to hold a lock of this */
4028
4029 HRESULT rc = E_UNEXPECTED;
4030 using namespace guestProp;
4031
4032 try
4033 {
4034 VBOXHGCMSVCPARM parm[4];
4035 Utf8Str Utf8Name = aName;
4036 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4037
4038 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4039 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4040 /* The + 1 is the null terminator */
4041 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4042 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4043 parm[1].u.pointer.addr = pszBuffer;
4044 parm[1].u.pointer.size = sizeof(pszBuffer);
4045 int vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", GET_PROP_HOST,
4046 4, &parm[0]);
4047 /* The returned string should never be able to be greater than our buffer */
4048 AssertLogRel (vrc != VERR_BUFFER_OVERFLOW);
4049 AssertLogRel (RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4050 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4051 {
4052 rc = S_OK;
4053 if (vrc != VERR_NOT_FOUND)
4054 {
4055 Utf8Str strBuffer(pszBuffer);
4056 strBuffer.cloneTo(aValue);
4057
4058 *aTimestamp = parm[2].u.uint64;
4059
4060 size_t iFlags = strBuffer.length() + 1;
4061 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4062 }
4063 else
4064 aValue = NULL;
4065 }
4066 else
4067 rc = setError (E_UNEXPECTED,
4068 tr ("The service call failed with the error %Rrc"), vrc);
4069 }
4070 catch(std::bad_alloc &e)
4071 {
4072 rc = E_OUTOFMEMORY;
4073 }
4074 return rc;
4075#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
4076}
4077
4078/**
4079 * @note Temporarily locks this object for writing.
4080 */
4081HRESULT Console::setGuestProperty (IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4082{
4083#if !defined (VBOX_WITH_GUEST_PROPS)
4084 ReturnComNotImplemented();
4085#else
4086 if (!VALID_PTR (aName))
4087 return E_INVALIDARG;
4088 if ((aValue != NULL) && !VALID_PTR (aValue))
4089 return E_INVALIDARG;
4090 if ((aFlags != NULL) && !VALID_PTR (aFlags))
4091 return E_INVALIDARG;
4092
4093 AutoCaller autoCaller(this);
4094 AssertComRCReturnRC(autoCaller.rc());
4095
4096 /* protect mpVM (if not NULL) */
4097 AutoVMCallerWeak autoVMCaller (this);
4098 CheckComRCReturnRC(autoVMCaller.rc());
4099
4100 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4101 * autoVMCaller, so there is no need to hold a lock of this */
4102
4103 HRESULT rc = E_UNEXPECTED;
4104 using namespace guestProp;
4105
4106 VBOXHGCMSVCPARM parm[3];
4107 Utf8Str Utf8Name = aName;
4108 int vrc = VINF_SUCCESS;
4109
4110 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4111 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4112 /* The + 1 is the null terminator */
4113 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4114 Utf8Str Utf8Value = aValue;
4115 if (aValue != NULL)
4116 {
4117 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4118 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4119 /* The + 1 is the null terminator */
4120 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4121 }
4122 Utf8Str Utf8Flags = aFlags;
4123 if (aFlags != NULL)
4124 {
4125 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4126 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4127 /* The + 1 is the null terminator */
4128 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4129 }
4130 if ((aValue != NULL) && (aFlags != NULL))
4131 vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", SET_PROP_HOST,
4132 3, &parm[0]);
4133 else if (aValue != NULL)
4134 vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4135 2, &parm[0]);
4136 else
4137 vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", DEL_PROP_HOST,
4138 1, &parm[0]);
4139 if (RT_SUCCESS(vrc))
4140 rc = S_OK;
4141 else
4142 rc = setError (E_UNEXPECTED,
4143 tr ("The service call failed with the error %Rrc"), vrc);
4144 return rc;
4145#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
4146}
4147
4148
4149/**
4150 * @note Temporarily locks this object for writing.
4151 */
4152HRESULT Console::enumerateGuestProperties (IN_BSTR aPatterns,
4153 ComSafeArrayOut(BSTR, aNames),
4154 ComSafeArrayOut(BSTR, aValues),
4155 ComSafeArrayOut(ULONG64, aTimestamps),
4156 ComSafeArrayOut(BSTR, aFlags))
4157{
4158#if !defined (VBOX_WITH_GUEST_PROPS)
4159 ReturnComNotImplemented();
4160#else
4161 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
4162 return E_POINTER;
4163 if (ComSafeArrayOutIsNull(aNames))
4164 return E_POINTER;
4165 if (ComSafeArrayOutIsNull(aValues))
4166 return E_POINTER;
4167 if (ComSafeArrayOutIsNull(aTimestamps))
4168 return E_POINTER;
4169 if (ComSafeArrayOutIsNull(aFlags))
4170 return E_POINTER;
4171
4172 AutoCaller autoCaller(this);
4173 AssertComRCReturnRC(autoCaller.rc());
4174
4175 /* protect mpVM (if not NULL) */
4176 AutoVMCallerWeak autoVMCaller (this);
4177 CheckComRCReturnRC(autoVMCaller.rc());
4178
4179 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4180 * autoVMCaller, so there is no need to hold a lock of this */
4181
4182 return doEnumerateGuestProperties (aPatterns, ComSafeArrayOutArg(aNames),
4183 ComSafeArrayOutArg(aValues),
4184 ComSafeArrayOutArg(aTimestamps),
4185 ComSafeArrayOutArg(aFlags));
4186#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
4187}
4188
4189/**
4190 * Gets called by Session::UpdateMachineState()
4191 * (IInternalSessionControl::updateMachineState()).
4192 *
4193 * Must be called only in certain cases (see the implementation).
4194 *
4195 * @note Locks this object for writing.
4196 */
4197HRESULT Console::updateMachineState (MachineState_T aMachineState)
4198{
4199 AutoCaller autoCaller(this);
4200 AssertComRCReturnRC(autoCaller.rc());
4201
4202 AutoWriteLock alock(this);
4203
4204 AssertReturn(mMachineState == MachineState_Saving ||
4205 mMachineState == MachineState_Discarding,
4206 E_FAIL);
4207
4208 return setMachineStateLocally (aMachineState);
4209}
4210
4211/**
4212 * @note Locks this object for writing.
4213 */
4214void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4215 uint32_t xHot, uint32_t yHot,
4216 uint32_t width, uint32_t height,
4217 void *pShape)
4218{
4219#if 0
4220 LogFlowThisFuncEnter();
4221 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, "
4222 "height=%d, shape=%p\n",
4223 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4224#endif
4225
4226 AutoCaller autoCaller(this);
4227 AssertComRCReturnVoid (autoCaller.rc());
4228
4229 /* We need a write lock because we alter the cached callback data */
4230 AutoWriteLock alock(this);
4231
4232 /* Save the callback arguments */
4233 mCallbackData.mpsc.visible = fVisible;
4234 mCallbackData.mpsc.alpha = fAlpha;
4235 mCallbackData.mpsc.xHot = xHot;
4236 mCallbackData.mpsc.yHot = yHot;
4237 mCallbackData.mpsc.width = width;
4238 mCallbackData.mpsc.height = height;
4239
4240 /* start with not valid */
4241 bool wasValid = mCallbackData.mpsc.valid;
4242 mCallbackData.mpsc.valid = false;
4243
4244 if (pShape != NULL)
4245 {
4246 size_t cb = (width + 7) / 8 * height; /* size of the AND mask */
4247 cb = ((cb + 3) & ~3) + width * 4 * height; /* + gap + size of the XOR mask */
4248 /* try to reuse the old shape buffer if the size is the same */
4249 if (!wasValid)
4250 mCallbackData.mpsc.shape = NULL;
4251 else
4252 if (mCallbackData.mpsc.shape != NULL && mCallbackData.mpsc.shapeSize != cb)
4253 {
4254 RTMemFree (mCallbackData.mpsc.shape);
4255 mCallbackData.mpsc.shape = NULL;
4256 }
4257 if (mCallbackData.mpsc.shape == NULL)
4258 {
4259 mCallbackData.mpsc.shape = (BYTE *) RTMemAllocZ (cb);
4260 AssertReturnVoid (mCallbackData.mpsc.shape);
4261 }
4262 mCallbackData.mpsc.shapeSize = cb;
4263 memcpy (mCallbackData.mpsc.shape, pShape, cb);
4264 }
4265 else
4266 {
4267 if (wasValid && mCallbackData.mpsc.shape != NULL)
4268 RTMemFree (mCallbackData.mpsc.shape);
4269 mCallbackData.mpsc.shape = NULL;
4270 mCallbackData.mpsc.shapeSize = 0;
4271 }
4272
4273 mCallbackData.mpsc.valid = true;
4274
4275 CallbackList::iterator it = mCallbacks.begin();
4276 while (it != mCallbacks.end())
4277 (*it++)->OnMousePointerShapeChange (fVisible, fAlpha, xHot, yHot,
4278 width, height, (BYTE *) pShape);
4279
4280#if 0
4281 LogFlowThisFuncLeave();
4282#endif
4283}
4284
4285/**
4286 * @note Locks this object for writing.
4287 */
4288void Console::onMouseCapabilityChange (BOOL supportsAbsolute, BOOL needsHostCursor)
4289{
4290 LogFlowThisFunc(("supportsAbsolute=%d needsHostCursor=%d\n",
4291 supportsAbsolute, needsHostCursor));
4292
4293 AutoCaller autoCaller(this);
4294 AssertComRCReturnVoid (autoCaller.rc());
4295
4296 /* We need a write lock because we alter the cached callback data */
4297 AutoWriteLock alock(this);
4298
4299 /* save the callback arguments */
4300 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
4301 mCallbackData.mcc.needsHostCursor = needsHostCursor;
4302 mCallbackData.mcc.valid = true;
4303
4304 CallbackList::iterator it = mCallbacks.begin();
4305 while (it != mCallbacks.end())
4306 {
4307 Log2(("Console::onMouseCapabilityChange: calling %p\n", (void*)*it));
4308 (*it++)->OnMouseCapabilityChange (supportsAbsolute, needsHostCursor);
4309 }
4310}
4311
4312/**
4313 * @note Locks this object for reading.
4314 */
4315void Console::onStateChange (MachineState_T machineState)
4316{
4317 AutoCaller autoCaller(this);
4318 AssertComRCReturnVoid (autoCaller.rc());
4319
4320 AutoReadLock alock(this);
4321
4322 CallbackList::iterator it = mCallbacks.begin();
4323 while (it != mCallbacks.end())
4324 (*it++)->OnStateChange (machineState);
4325}
4326
4327/**
4328 * @note Locks this object for reading.
4329 */
4330void Console::onAdditionsStateChange()
4331{
4332 AutoCaller autoCaller(this);
4333 AssertComRCReturnVoid (autoCaller.rc());
4334
4335 AutoReadLock alock(this);
4336
4337 CallbackList::iterator it = mCallbacks.begin();
4338 while (it != mCallbacks.end())
4339 (*it++)->OnAdditionsStateChange();
4340}
4341
4342/**
4343 * @note Locks this object for reading.
4344 */
4345void Console::onAdditionsOutdated()
4346{
4347 AutoCaller autoCaller(this);
4348 AssertComRCReturnVoid (autoCaller.rc());
4349
4350 AutoReadLock alock(this);
4351
4352 /** @todo Use the On-Screen Display feature to report the fact.
4353 * The user should be told to install additions that are
4354 * provided with the current VBox build:
4355 * VBOX_VERSION_MAJOR.VBOX_VERSION_MINOR.VBOX_VERSION_BUILD
4356 */
4357}
4358
4359/**
4360 * @note Locks this object for writing.
4361 */
4362void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
4363{
4364 AutoCaller autoCaller(this);
4365 AssertComRCReturnVoid (autoCaller.rc());
4366
4367 /* We need a write lock because we alter the cached callback data */
4368 AutoWriteLock alock(this);
4369
4370 /* save the callback arguments */
4371 mCallbackData.klc.numLock = fNumLock;
4372 mCallbackData.klc.capsLock = fCapsLock;
4373 mCallbackData.klc.scrollLock = fScrollLock;
4374 mCallbackData.klc.valid = true;
4375
4376 CallbackList::iterator it = mCallbacks.begin();
4377 while (it != mCallbacks.end())
4378 (*it++)->OnKeyboardLedsChange(fNumLock, fCapsLock, fScrollLock);
4379}
4380
4381/**
4382 * @note Locks this object for reading.
4383 */
4384void Console::onUSBDeviceStateChange (IUSBDevice *aDevice, bool aAttached,
4385 IVirtualBoxErrorInfo *aError)
4386{
4387 AutoCaller autoCaller(this);
4388 AssertComRCReturnVoid (autoCaller.rc());
4389
4390 AutoReadLock alock(this);
4391
4392 CallbackList::iterator it = mCallbacks.begin();
4393 while (it != mCallbacks.end())
4394 (*it++)->OnUSBDeviceStateChange (aDevice, aAttached, aError);
4395}
4396
4397/**
4398 * @note Locks this object for reading.
4399 */
4400void Console::onRuntimeError (BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
4401{
4402 AutoCaller autoCaller(this);
4403 AssertComRCReturnVoid (autoCaller.rc());
4404
4405 AutoReadLock alock(this);
4406
4407 CallbackList::iterator it = mCallbacks.begin();
4408 while (it != mCallbacks.end())
4409 (*it++)->OnRuntimeError (aFatal, aErrorID, aMessage);
4410}
4411
4412/**
4413 * @note Locks this object for reading.
4414 */
4415HRESULT Console::onShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
4416{
4417 AssertReturn(aCanShow, E_POINTER);
4418 AssertReturn(aWinId, E_POINTER);
4419
4420 *aCanShow = FALSE;
4421 *aWinId = 0;
4422
4423 AutoCaller autoCaller(this);
4424 AssertComRCReturnRC(autoCaller.rc());
4425
4426 AutoReadLock alock(this);
4427
4428 HRESULT rc = S_OK;
4429 CallbackList::iterator it = mCallbacks.begin();
4430
4431 if (aCheck)
4432 {
4433 while (it != mCallbacks.end())
4434 {
4435 BOOL canShow = FALSE;
4436 rc = (*it++)->OnCanShowWindow (&canShow);
4437 AssertComRC (rc);
4438 if (FAILED (rc) || !canShow)
4439 return rc;
4440 }
4441 *aCanShow = TRUE;
4442 }
4443 else
4444 {
4445 while (it != mCallbacks.end())
4446 {
4447 ULONG64 winId = 0;
4448 rc = (*it++)->OnShowWindow (&winId);
4449 AssertComRC (rc);
4450 if (FAILED (rc))
4451 return rc;
4452 /* only one callback may return non-null winId */
4453 Assert (*aWinId == 0 || winId == 0);
4454 if (*aWinId == 0)
4455 *aWinId = winId;
4456 }
4457 }
4458
4459 return S_OK;
4460}
4461
4462// private methods
4463////////////////////////////////////////////////////////////////////////////////
4464
4465/**
4466 * Increases the usage counter of the mpVM pointer. Guarantees that
4467 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
4468 * is called.
4469 *
4470 * If this method returns a failure, the caller is not allowed to use mpVM
4471 * and may return the failed result code to the upper level. This method sets
4472 * the extended error info on failure if \a aQuiet is false.
4473 *
4474 * Setting \a aQuiet to true is useful for methods that don't want to return
4475 * the failed result code to the caller when this method fails (e.g. need to
4476 * silently check for the mpVM availability).
4477 *
4478 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
4479 * returned instead of asserting. Having it false is intended as a sanity check
4480 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
4481 *
4482 * @param aQuiet true to suppress setting error info
4483 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
4484 * (otherwise this method will assert if mpVM is NULL)
4485 *
4486 * @note Locks this object for writing.
4487 */
4488HRESULT Console::addVMCaller (bool aQuiet /* = false */,
4489 bool aAllowNullVM /* = false */)
4490{
4491 AutoCaller autoCaller (this);
4492 AssertComRCReturnRC(autoCaller.rc());
4493
4494 AutoWriteLock alock(this);
4495
4496 if (mVMDestroying)
4497 {
4498 /* powerDown() is waiting for all callers to finish */
4499 return aQuiet ? E_ACCESSDENIED : setError (E_ACCESSDENIED,
4500 tr ("Virtual machine is being powered down"));
4501 }
4502
4503 if (mpVM == NULL)
4504 {
4505 Assert (aAllowNullVM == true);
4506
4507 /* The machine is not powered up */
4508 return aQuiet ? E_ACCESSDENIED : setError (E_ACCESSDENIED,
4509 tr ("Virtual machine is not powered up"));
4510 }
4511
4512 ++ mVMCallers;
4513
4514 return S_OK;
4515}
4516
4517/**
4518 * Decreases the usage counter of the mpVM pointer. Must always complete
4519 * the addVMCaller() call after the mpVM pointer is no more necessary.
4520 *
4521 * @note Locks this object for writing.
4522 */
4523void Console::releaseVMCaller()
4524{
4525 AutoCaller autoCaller (this);
4526 AssertComRCReturnVoid (autoCaller.rc());
4527
4528 AutoWriteLock alock(this);
4529
4530 AssertReturnVoid (mpVM != NULL);
4531
4532 Assert (mVMCallers > 0);
4533 -- mVMCallers;
4534
4535 if (mVMCallers == 0 && mVMDestroying)
4536 {
4537 /* inform powerDown() there are no more callers */
4538 RTSemEventSignal (mVMZeroCallersSem);
4539 }
4540}
4541
4542/**
4543 * Initialize the release logging facility. In case something
4544 * goes wrong, there will be no release logging. Maybe in the future
4545 * we can add some logic to use different file names in this case.
4546 * Note that the logic must be in sync with Machine::DeleteSettings().
4547 */
4548HRESULT Console::consoleInitReleaseLog (const ComPtr<IMachine> aMachine)
4549{
4550 HRESULT hrc = S_OK;
4551
4552 Bstr logFolder;
4553 hrc = aMachine->COMGETTER(LogFolder) (logFolder.asOutParam());
4554 CheckComRCReturnRC(hrc);
4555
4556 Utf8Str logDir = logFolder;
4557
4558 /* make sure the Logs folder exists */
4559 Assert(logDir.length());
4560 if (!RTDirExists (logDir))
4561 RTDirCreateFullPath (logDir, 0777);
4562
4563 Utf8Str logFile = Utf8StrFmt ("%s%cVBox.log",
4564 logDir.raw(), RTPATH_DELIMITER);
4565 Utf8Str pngFile = Utf8StrFmt ("%s%cVBox.png",
4566 logDir.raw(), RTPATH_DELIMITER);
4567
4568 /*
4569 * Age the old log files
4570 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
4571 * Overwrite target files in case they exist.
4572 */
4573 ComPtr<IVirtualBox> virtualBox;
4574 aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
4575 ComPtr<ISystemProperties> systemProperties;
4576 virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4577 ULONG uLogHistoryCount = 3;
4578 systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4579 ComPtr<IHost> host;
4580 virtualBox->COMGETTER(Host)(host.asOutParam());
4581 ULONG uHostRamMb = 0, uHostRamAvailMb = 0;
4582 host->COMGETTER(MemorySize)(&uHostRamMb);
4583 host->COMGETTER(MemoryAvailable)(&uHostRamAvailMb);
4584 if (uLogHistoryCount)
4585 {
4586 for (int i = uLogHistoryCount-1; i >= 0; i--)
4587 {
4588 Utf8Str *files[] = { &logFile, &pngFile };
4589 Utf8Str oldName, newName;
4590
4591 for (unsigned int j = 0; j < RT_ELEMENTS (files); ++ j)
4592 {
4593 if (i > 0)
4594 oldName = Utf8StrFmt ("%s.%d", files [j]->raw(), i);
4595 else
4596 oldName = *files [j];
4597 newName = Utf8StrFmt ("%s.%d", files [j]->raw(), i + 1);
4598 /* If the old file doesn't exist, delete the new file (if it
4599 * exists) to provide correct rotation even if the sequence is
4600 * broken */
4601 if ( RTFileRename (oldName, newName, RTFILEMOVE_FLAGS_REPLACE)
4602 == VERR_FILE_NOT_FOUND)
4603 RTFileDelete (newName);
4604 }
4605 }
4606 }
4607
4608 PRTLOGGER loggerRelease;
4609 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
4610 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
4611#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4612 fFlags |= RTLOGFLAGS_USECRLF;
4613#endif
4614 char szError[RTPATH_MAX + 128] = "";
4615 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
4616 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
4617 RTLOGDEST_FILE, szError, sizeof(szError), logFile.raw());
4618 if (RT_SUCCESS(vrc))
4619 {
4620 /* some introductory information */
4621 RTTIMESPEC timeSpec;
4622 char szTmp[256];
4623 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
4624 RTLogRelLogger(loggerRelease, 0, ~0U,
4625 "VirtualBox %s r%d %s (%s %s) release log\n"
4626 "Log opened %s\n",
4627 VBOX_VERSION_STRING, VBoxSVNRev (), VBOX_BUILD_TARGET,
4628 __DATE__, __TIME__, szTmp);
4629
4630 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
4631 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4632 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
4633 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
4634 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4635 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
4636 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
4637 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4638 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
4639 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
4640 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4641 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
4642 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
4643 uHostRamMb, uHostRamAvailMb);
4644 /* the package type is interesting for Linux distributions */
4645 char szExecName[RTPATH_MAX];
4646 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
4647 RTLogRelLogger(loggerRelease, 0, ~0U,
4648 "Executable: %s\n"
4649 "Process ID: %u\n"
4650 "Package type: %s"
4651#ifdef VBOX_OSE
4652 " (OSE)"
4653#endif
4654 "\n",
4655 pszExecName ? pszExecName : "unknown",
4656 RTProcSelf(),
4657 VBOX_PACKAGE_STRING);
4658
4659 /* register this logger as the release logger */
4660 RTLogRelSetDefaultInstance(loggerRelease);
4661 hrc = S_OK;
4662 }
4663 else
4664 hrc = setError (E_FAIL,
4665 tr ("Failed to open release log (%s, %Rrc)"), szError, vrc);
4666
4667 return hrc;
4668}
4669
4670/**
4671 * Common worker for PowerUp and PowerUpPaused.
4672 *
4673 * @returns COM status code.
4674 *
4675 * @param aProgress Where to return the progress object.
4676 * @param aPaused true if PowerUpPaused called.
4677 *
4678 * @todo move down to powerDown();
4679 */
4680HRESULT Console::powerUp (IProgress **aProgress, bool aPaused)
4681{
4682 if (aProgress == NULL)
4683 return E_POINTER;
4684
4685 LogFlowThisFuncEnter();
4686 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
4687
4688 AutoCaller autoCaller(this);
4689 CheckComRCReturnRC(autoCaller.rc());
4690
4691 AutoWriteLock alock(this);
4692
4693 if (Global::IsOnlineOrTransient (mMachineState))
4694 return setError(VBOX_E_INVALID_VM_STATE,
4695 tr ("Virtual machine is already running or busy "
4696 "(machine state: %d)"), mMachineState);
4697
4698 HRESULT rc = S_OK;
4699
4700 /* the network cards will undergo a quick consistency check */
4701 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
4702 {
4703 ComPtr<INetworkAdapter> adapter;
4704 mMachine->GetNetworkAdapter (slot, adapter.asOutParam());
4705 BOOL enabled = FALSE;
4706 adapter->COMGETTER(Enabled) (&enabled);
4707 if (!enabled)
4708 continue;
4709
4710 NetworkAttachmentType_T netattach;
4711 adapter->COMGETTER(AttachmentType)(&netattach);
4712 switch (netattach)
4713 {
4714 case NetworkAttachmentType_Bridged:
4715 {
4716#ifdef RT_OS_WINDOWS
4717 /* a valid host interface must have been set */
4718 Bstr hostif;
4719 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
4720 if (!hostif)
4721 {
4722 return setError (VBOX_E_HOST_ERROR,
4723 tr ("VM cannot start because host interface networking "
4724 "requires a host interface name to be set"));
4725 }
4726 ComPtr<IVirtualBox> virtualBox;
4727 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
4728 ComPtr<IHost> host;
4729 virtualBox->COMGETTER(Host)(host.asOutParam());
4730 ComPtr<IHostNetworkInterface> hostInterface;
4731 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
4732 {
4733 return setError (VBOX_E_HOST_ERROR,
4734 tr ("VM cannot start because the host interface '%ls' "
4735 "does not exist"),
4736 hostif.raw());
4737 }
4738#endif /* RT_OS_WINDOWS */
4739 break;
4740 }
4741 default:
4742 break;
4743 }
4744 }
4745
4746 /* Read console data stored in the saved state file (if not yet done) */
4747 rc = loadDataFromSavedState();
4748 CheckComRCReturnRC(rc);
4749
4750 /* Check all types of shared folders and compose a single list */
4751 SharedFolderDataMap sharedFolders;
4752 {
4753 /* first, insert global folders */
4754 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
4755 it != mGlobalSharedFolders.end(); ++ it)
4756 sharedFolders [it->first] = it->second;
4757
4758 /* second, insert machine folders */
4759 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
4760 it != mMachineSharedFolders.end(); ++ it)
4761 sharedFolders [it->first] = it->second;
4762
4763 /* third, insert console folders */
4764 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
4765 it != mSharedFolders.end(); ++ it)
4766 sharedFolders [it->first] = SharedFolderData(it->second->hostPath(), it->second->writable());
4767 }
4768
4769 Bstr savedStateFile;
4770
4771 /*
4772 * Saved VMs will have to prove that their saved states seem kosher.
4773 */
4774 if (mMachineState == MachineState_Saved)
4775 {
4776 rc = mMachine->COMGETTER(StateFilePath) (savedStateFile.asOutParam());
4777 CheckComRCReturnRC(rc);
4778 ComAssertRet (!!savedStateFile, E_FAIL);
4779 int vrc = SSMR3ValidateFile (Utf8Str (savedStateFile), false /* fChecksumIt */);
4780 if (VBOX_FAILURE (vrc))
4781 return setError (VBOX_E_FILE_ERROR,
4782 tr ("VM cannot start because the saved state file '%ls' is invalid (%Rrc). "
4783 "Discard the saved state prior to starting the VM"),
4784 savedStateFile.raw(), vrc);
4785 }
4786
4787 /* create a progress object to track progress of this operation */
4788 ComObjPtr<Progress> powerupProgress;
4789 powerupProgress.createObject();
4790 Bstr progressDesc;
4791 if (mMachineState == MachineState_Saved)
4792 progressDesc = tr ("Restoring virtual machine");
4793 else
4794 progressDesc = tr ("Starting virtual machine");
4795 rc = powerupProgress->init (static_cast <IConsole *> (this),
4796 progressDesc, FALSE /* aCancelable */);
4797 CheckComRCReturnRC(rc);
4798
4799 /* setup task object and thread to carry out the operation
4800 * asynchronously */
4801
4802 std::auto_ptr <VMPowerUpTask> task (new VMPowerUpTask (this, powerupProgress));
4803 ComAssertComRCRetRC (task->rc());
4804
4805 task->mSetVMErrorCallback = setVMErrorCallback;
4806 task->mConfigConstructor = configConstructor;
4807 task->mSharedFolders = sharedFolders;
4808 task->mStartPaused = aPaused;
4809 if (mMachineState == MachineState_Saved)
4810 task->mSavedStateFile = savedStateFile;
4811
4812 /* Reset differencing hard disks for which autoReset is true */
4813 {
4814 com::SafeIfaceArray<IHardDiskAttachment> atts;
4815 rc = mMachine->
4816 COMGETTER(HardDiskAttachments) (ComSafeArrayAsOutParam (atts));
4817 CheckComRCReturnRC(rc);
4818
4819 for (size_t i = 0; i < atts.size(); ++ i)
4820 {
4821 ComPtr<IHardDisk> hardDisk;
4822 rc = atts [i]->COMGETTER(HardDisk) (hardDisk.asOutParam());
4823 CheckComRCReturnRC(rc);
4824
4825 /* save for later use on the powerup thread */
4826 task->hardDisks.push_back (hardDisk);
4827
4828 /* needs autoreset? */
4829 BOOL autoReset = FALSE;
4830 rc = hardDisk->COMGETTER(AutoReset)(&autoReset);
4831 CheckComRCReturnRC(rc);
4832
4833 if (autoReset)
4834 {
4835 ComPtr<IProgress> resetProgress;
4836 rc = hardDisk->Reset (resetProgress.asOutParam());
4837 CheckComRCReturnRC(rc);
4838
4839 /* save for later use on the powerup thread */
4840 task->hardDiskProgresses.push_back (resetProgress);
4841 }
4842 }
4843 }
4844
4845 rc = consoleInitReleaseLog (mMachine);
4846 CheckComRCReturnRC(rc);
4847
4848 /* pass the progress object to the caller if requested */
4849 if (aProgress)
4850 {
4851 if (task->hardDiskProgresses.size() == 0)
4852 {
4853 /* there are no other operations to track, return the powerup
4854 * progress only */
4855 powerupProgress.queryInterfaceTo(aProgress);
4856 }
4857 else
4858 {
4859 /* create a combined progress object */
4860 ComObjPtr<CombinedProgress> progress;
4861 progress.createObject();
4862 VMPowerUpTask::ProgressList progresses (task->hardDiskProgresses);
4863 progresses.push_back (ComPtr<IProgress> (powerupProgress));
4864 rc = progress->init (static_cast <IConsole *> (this),
4865 progressDesc, progresses.begin(),
4866 progresses.end());
4867 AssertComRCReturnRC(rc);
4868 progress.queryInterfaceTo(aProgress);
4869 }
4870 }
4871
4872 int vrc = RTThreadCreate (NULL, Console::powerUpThread, (void *) task.get(),
4873 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
4874
4875 ComAssertMsgRCRet (vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
4876 E_FAIL);
4877
4878 /* task is now owned by powerUpThread(), so release it */
4879 task.release();
4880
4881 /* finally, set the state: no right to fail in this method afterwards
4882 * since we've already started the thread and it is now responsible for
4883 * any error reporting and appropriate state change! */
4884
4885 if (mMachineState == MachineState_Saved)
4886 setMachineState (MachineState_Restoring);
4887 else
4888 setMachineState (MachineState_Starting);
4889
4890 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
4891 LogFlowThisFuncLeave();
4892 return S_OK;
4893}
4894
4895/**
4896 * Internal power off worker routine.
4897 *
4898 * This method may be called only at certain places with the following meaning
4899 * as shown below:
4900 *
4901 * - if the machine state is either Running or Paused, a normal
4902 * Console-initiated powerdown takes place (e.g. PowerDown());
4903 * - if the machine state is Saving, saveStateThread() has successfully done its
4904 * job;
4905 * - if the machine state is Starting or Restoring, powerUpThread() has failed
4906 * to start/load the VM;
4907 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
4908 * as a result of the powerDown() call).
4909 *
4910 * Calling it in situations other than the above will cause unexpected behavior.
4911 *
4912 * Note that this method should be the only one that destroys mpVM and sets it
4913 * to NULL.
4914 *
4915 * @param aProgress Progress object to run (may be NULL).
4916 *
4917 * @note Locks this object for writing.
4918 *
4919 * @note Never call this method from a thread that called addVMCaller() or
4920 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
4921 * release(). Otherwise it will deadlock.
4922 */
4923HRESULT Console::powerDown (Progress *aProgress /*= NULL*/)
4924{
4925 LogFlowThisFuncEnter();
4926
4927 AutoCaller autoCaller(this);
4928 AssertComRCReturnRC(autoCaller.rc());
4929
4930 AutoWriteLock alock(this);
4931
4932 /* Total # of steps for the progress object. Must correspond to the
4933 * number of "advance percent count" comments in this method! */
4934 enum { StepCount = 7 };
4935 /* current step */
4936 ULONG step = 0;
4937
4938 HRESULT rc = S_OK;
4939 int vrc = VINF_SUCCESS;
4940
4941 /* sanity */
4942 Assert (mVMDestroying == false);
4943
4944 Assert (mpVM != NULL);
4945
4946 AssertMsg (mMachineState == MachineState_Running ||
4947 mMachineState == MachineState_Paused ||
4948 mMachineState == MachineState_Stuck ||
4949 mMachineState == MachineState_Saving ||
4950 mMachineState == MachineState_Starting ||
4951 mMachineState == MachineState_Restoring ||
4952 mMachineState == MachineState_Stopping,
4953 ("Invalid machine state: %d\n", mMachineState));
4954
4955 LogRel (("Console::powerDown(): A request to power off the VM has been "
4956 "issued (mMachineState=%d, InUninit=%d)\n",
4957 mMachineState, autoCaller.state() == InUninit));
4958
4959 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
4960 * VM has already powered itself off in vmstateChangeCallback() and is just
4961 * notifying Console about that. In case of Starting or Restoring,
4962 * powerUpThread() is calling us on failure, so the VM is already off at
4963 * that point. */
4964 if (!mVMPoweredOff &&
4965 (mMachineState == MachineState_Starting ||
4966 mMachineState == MachineState_Restoring))
4967 mVMPoweredOff = true;
4968
4969 /* go to Stopping state if not already there. Note that we don't go from
4970 * Saving/Restoring to Stopping because vmstateChangeCallback() needs it to
4971 * set the state to Saved on VMSTATE_TERMINATED. In terms of protecting from
4972 * inappropriate operations while leaving the lock below, Saving or
4973 * Restoring should be fine too */
4974 if (mMachineState != MachineState_Saving &&
4975 mMachineState != MachineState_Restoring &&
4976 mMachineState != MachineState_Stopping)
4977 setMachineState (MachineState_Stopping);
4978
4979 /* ----------------------------------------------------------------------
4980 * DONE with necessary state changes, perform the power down actions (it's
4981 * safe to leave the object lock now if needed)
4982 * ---------------------------------------------------------------------- */
4983
4984 /* Stop the VRDP server to prevent new clients connection while VM is being
4985 * powered off. */
4986 if (mConsoleVRDPServer)
4987 {
4988 LogFlowThisFunc(("Stopping VRDP server...\n"));
4989
4990 /* Leave the lock since EMT will call us back as addVMCaller()
4991 * in updateDisplayData(). */
4992 alock.leave();
4993
4994 mConsoleVRDPServer->Stop();
4995
4996 alock.enter();
4997 }
4998
4999 /* advance percent count */
5000 if (aProgress)
5001 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5002
5003#ifdef VBOX_WITH_HGCM
5004
5005# ifdef VBOX_WITH_GUEST_PROPS
5006
5007 /* Save all guest property store entries to the machine XML file */
5008 com::SafeArray<BSTR> namesOut;
5009 com::SafeArray<BSTR> valuesOut;
5010 com::SafeArray<ULONG64> timestampsOut;
5011 com::SafeArray<BSTR> flagsOut;
5012 Bstr pattern("");
5013 if (pattern.isNull())
5014 rc = E_OUTOFMEMORY;
5015 else
5016 rc = doEnumerateGuestProperties (Bstr (""), ComSafeArrayAsOutParam (namesOut),
5017 ComSafeArrayAsOutParam (valuesOut),
5018 ComSafeArrayAsOutParam (timestampsOut),
5019 ComSafeArrayAsOutParam (flagsOut));
5020 if (SUCCEEDED(rc))
5021 {
5022 try
5023 {
5024 std::vector <BSTR> names;
5025 std::vector <BSTR> values;
5026 std::vector <ULONG64> timestamps;
5027 std::vector <BSTR> flags;
5028 for (unsigned i = 0; i < namesOut.size(); ++i)
5029 {
5030 uint32_t fFlags;
5031 guestProp::validateFlags (Utf8Str(flagsOut[i]).raw(), &fFlags);
5032 if ( !( fFlags & guestProp::TRANSIENT)
5033 || (mMachineState == MachineState_Saving)
5034 )
5035 {
5036 names.push_back(namesOut[i]);
5037 values.push_back(valuesOut[i]);
5038 timestamps.push_back(timestampsOut[i]);
5039 flags.push_back(flagsOut[i]);
5040 }
5041 }
5042 com::SafeArray<BSTR> namesIn (names);
5043 com::SafeArray<BSTR> valuesIn (values);
5044 com::SafeArray<ULONG64> timestampsIn (timestamps);
5045 com::SafeArray<BSTR> flagsIn (flags);
5046 if ( namesIn.isNull()
5047 || valuesIn.isNull()
5048 || timestampsIn.isNull()
5049 || flagsIn.isNull()
5050 )
5051 throw std::bad_alloc();
5052 /* PushGuestProperties() calls DiscardSettings(), which calls us back */
5053 alock.leave();
5054 mControl->PushGuestProperties (ComSafeArrayAsInParam (namesIn),
5055 ComSafeArrayAsInParam (valuesIn),
5056 ComSafeArrayAsInParam (timestampsIn),
5057 ComSafeArrayAsInParam (flagsIn));
5058 alock.enter();
5059 }
5060 catch (std::bad_alloc)
5061 {
5062 rc = E_OUTOFMEMORY;
5063 }
5064 }
5065
5066 /* advance percent count */
5067 if (aProgress)
5068 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5069
5070# endif /* VBOX_WITH_GUEST_PROPS defined */
5071
5072 /* Shutdown HGCM services before stopping the guest, because they might
5073 * need a cleanup. */
5074 if (mVMMDev)
5075 {
5076 LogFlowThisFunc(("Shutdown HGCM...\n"));
5077
5078 /* Leave the lock since EMT will call us back as addVMCaller() */
5079 alock.leave();
5080
5081 mVMMDev->hgcmShutdown ();
5082
5083 alock.enter();
5084 }
5085
5086 /* advance percent count */
5087 if (aProgress)
5088 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5089
5090#endif /* VBOX_WITH_HGCM */
5091
5092 /* ----------------------------------------------------------------------
5093 * Now, wait for all mpVM callers to finish their work if there are still
5094 * some on other threads. NO methods that need mpVM (or initiate other calls
5095 * that need it) may be called after this point
5096 * ---------------------------------------------------------------------- */
5097
5098 if (mVMCallers > 0)
5099 {
5100 /* go to the destroying state to prevent from adding new callers */
5101 mVMDestroying = true;
5102
5103 /* lazy creation */
5104 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5105 RTSemEventCreate (&mVMZeroCallersSem);
5106
5107 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5108 mVMCallers));
5109
5110 alock.leave();
5111
5112 RTSemEventWait (mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5113
5114 alock.enter();
5115 }
5116
5117 /* advance percent count */
5118 if (aProgress)
5119 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5120
5121 vrc = VINF_SUCCESS;
5122
5123 /* Power off the VM if not already done that */
5124 if (!mVMPoweredOff)
5125 {
5126 LogFlowThisFunc(("Powering off the VM...\n"));
5127
5128 /* Leave the lock since EMT will call us back on VMR3PowerOff() */
5129 alock.leave();
5130
5131 vrc = VMR3PowerOff (mpVM);
5132
5133 /* Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5134 * VM-(guest-)initiated power off happened in parallel a ms before this
5135 * call. So far, we let this error pop up on the user's side. */
5136
5137 alock.enter();
5138
5139 }
5140 else
5141 {
5142 /* reset the flag for further re-use */
5143 mVMPoweredOff = false;
5144 }
5145
5146 /* advance percent count */
5147 if (aProgress)
5148 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5149
5150 LogFlowThisFunc(("Ready for VM destruction.\n"));
5151
5152 /* If we are called from Console::uninit(), then try to destroy the VM even
5153 * on failure (this will most likely fail too, but what to do?..) */
5154 if (VBOX_SUCCESS (vrc) || autoCaller.state() == InUninit)
5155 {
5156 /* If the machine has an USB comtroller, release all USB devices
5157 * (symmetric to the code in captureUSBDevices()) */
5158 bool fHasUSBController = false;
5159 {
5160 PPDMIBASE pBase;
5161 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);
5162 if (VBOX_SUCCESS (vrc))
5163 {
5164 fHasUSBController = true;
5165 detachAllUSBDevices (false /* aDone */);
5166 }
5167 }
5168
5169 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5170 * this point). We leave the lock before calling VMR3Destroy() because
5171 * it will result into calling destructors of drivers associated with
5172 * Console children which may in turn try to lock Console (e.g. by
5173 * instantiating SafeVMPtr to access mpVM). It's safe here because
5174 * mVMDestroying is set which should prevent any activity. */
5175
5176 /* Set mpVM to NULL early just in case if some old code is not using
5177 * addVMCaller()/releaseVMCaller(). */
5178 PVM pVM = mpVM;
5179 mpVM = NULL;
5180
5181 LogFlowThisFunc(("Destroying the VM...\n"));
5182
5183 alock.leave();
5184
5185 vrc = VMR3Destroy (pVM);
5186
5187 /* take the lock again */
5188 alock.enter();
5189
5190 /* advance percent count */
5191 if (aProgress)
5192 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5193
5194 if (VBOX_SUCCESS (vrc))
5195 {
5196 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5197 mMachineState));
5198 /* Note: the Console-level machine state change happens on the
5199 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5200 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5201 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5202 * occurred yet. This is okay, because mMachineState is already
5203 * Stopping in this case, so any other attempt to call PowerDown()
5204 * will be rejected. */
5205 }
5206 else
5207 {
5208 /* bad bad bad, but what to do? */
5209 mpVM = pVM;
5210 rc = setError (VBOX_E_VM_ERROR,
5211 tr ("Could not destroy the machine. (Error: %Rrc)"), vrc);
5212 }
5213
5214 /* Complete the detaching of the USB devices. */
5215 if (fHasUSBController)
5216 detachAllUSBDevices (true /* aDone */);
5217
5218 /* advance percent count */
5219 if (aProgress)
5220 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5221 }
5222 else
5223 {
5224 rc = setError (VBOX_E_VM_ERROR,
5225 tr ("Could not power off the machine. (Error: %Rrc)"), vrc);
5226 }
5227
5228 /* Finished with destruction. Note that if something impossible happened and
5229 * we've failed to destroy the VM, mVMDestroying will remain true and
5230 * mMachineState will be something like Stopping, so most Console methods
5231 * will return an error to the caller. */
5232 if (mpVM == NULL)
5233 mVMDestroying = false;
5234
5235 if (SUCCEEDED(rc))
5236 {
5237 /* uninit dynamically allocated members of mCallbackData */
5238 if (mCallbackData.mpsc.valid)
5239 {
5240 if (mCallbackData.mpsc.shape != NULL)
5241 RTMemFree (mCallbackData.mpsc.shape);
5242 }
5243 memset (&mCallbackData, 0, sizeof (mCallbackData));
5244 }
5245
5246 /* complete the progress */
5247 if (aProgress)
5248 aProgress->notifyComplete (rc);
5249
5250 LogFlowThisFuncLeave();
5251 return rc;
5252}
5253
5254/**
5255 * @note Locks this object for writing.
5256 */
5257HRESULT Console::setMachineState (MachineState_T aMachineState,
5258 bool aUpdateServer /* = true */)
5259{
5260 AutoCaller autoCaller(this);
5261 AssertComRCReturnRC(autoCaller.rc());
5262
5263 AutoWriteLock alock(this);
5264
5265 HRESULT rc = S_OK;
5266
5267 if (mMachineState != aMachineState)
5268 {
5269 LogFlowThisFunc(("machineState=%d\n", aMachineState));
5270 mMachineState = aMachineState;
5271
5272 /// @todo (dmik)
5273 // possibly, we need to redo onStateChange() using the dedicated
5274 // Event thread, like it is done in VirtualBox. This will make it
5275 // much safer (no deadlocks possible if someone tries to use the
5276 // console from the callback), however, listeners will lose the
5277 // ability to synchronously react to state changes (is it really
5278 // necessary??)
5279 LogFlowThisFunc(("Doing onStateChange()...\n"));
5280 onStateChange (aMachineState);
5281 LogFlowThisFunc(("Done onStateChange()\n"));
5282
5283 if (aUpdateServer)
5284 {
5285 /* Server notification MUST be done from under the lock; otherwise
5286 * the machine state here and on the server might go out of sync
5287 * whihc can lead to various unexpected results (like the machine
5288 * state being >= MachineState_Running on the server, while the
5289 * session state is already SessionState_Closed at the same time
5290 * there).
5291 *
5292 * Cross-lock conditions should be carefully watched out: calling
5293 * UpdateState we will require Machine and SessionMachine locks
5294 * (remember that here we're holding the Console lock here, and also
5295 * all locks that have been entered by the thread before calling
5296 * this method).
5297 */
5298 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5299 rc = mControl->UpdateState (aMachineState);
5300 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5301 }
5302 }
5303
5304 return rc;
5305}
5306
5307/**
5308 * Searches for a shared folder with the given logical name
5309 * in the collection of shared folders.
5310 *
5311 * @param aName logical name of the shared folder
5312 * @param aSharedFolder where to return the found object
5313 * @param aSetError whether to set the error info if the folder is
5314 * not found
5315 * @return
5316 * S_OK when found or E_INVALIDARG when not found
5317 *
5318 * @note The caller must lock this object for writing.
5319 */
5320HRESULT Console::findSharedFolder (CBSTR aName,
5321 ComObjPtr<SharedFolder> &aSharedFolder,
5322 bool aSetError /* = false */)
5323{
5324 /* sanity check */
5325 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5326
5327 SharedFolderMap::const_iterator it = mSharedFolders.find (aName);
5328 if (it != mSharedFolders.end())
5329 {
5330 aSharedFolder = it->second;
5331 return S_OK;
5332 }
5333
5334 if (aSetError)
5335 setError (VBOX_E_FILE_ERROR,
5336 tr ("Could not find a shared folder named '%ls'."), aName);
5337
5338 return VBOX_E_FILE_ERROR;
5339}
5340
5341/**
5342 * Fetches the list of global or machine shared folders from the server.
5343 *
5344 * @param aGlobal true to fetch global folders.
5345 *
5346 * @note The caller must lock this object for writing.
5347 */
5348HRESULT Console::fetchSharedFolders (BOOL aGlobal)
5349{
5350 /* sanity check */
5351 AssertReturn(AutoCaller (this).state() == InInit ||
5352 isWriteLockOnCurrentThread(), E_FAIL);
5353
5354 /* protect mpVM (if not NULL) */
5355 AutoVMCallerQuietWeak autoVMCaller (this);
5356
5357 HRESULT rc = S_OK;
5358
5359 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5360
5361 if (aGlobal)
5362 {
5363 /// @todo grab & process global folders when they are done
5364 }
5365 else
5366 {
5367 SharedFolderDataMap oldFolders;
5368 if (online)
5369 oldFolders = mMachineSharedFolders;
5370
5371 mMachineSharedFolders.clear();
5372
5373 SafeIfaceArray<ISharedFolder> folders;
5374 rc = mMachine->COMGETTER(SharedFolders) (ComSafeArrayAsOutParam(folders));
5375 AssertComRCReturnRC(rc);
5376
5377 for (size_t i = 0; i < folders.size(); ++i)
5378 {
5379 ComPtr<ISharedFolder> folder = folders[i];
5380
5381 Bstr name;
5382 Bstr hostPath;
5383 BOOL writable;
5384
5385 rc = folder->COMGETTER(Name) (name.asOutParam());
5386 CheckComRCBreakRC (rc);
5387 rc = folder->COMGETTER(HostPath) (hostPath.asOutParam());
5388 CheckComRCBreakRC (rc);
5389 rc = folder->COMGETTER(Writable) (&writable);
5390
5391 mMachineSharedFolders.insert (std::make_pair (name, SharedFolderData (hostPath, writable)));
5392
5393 /* send changes to HGCM if the VM is running */
5394 /// @todo report errors as runtime warnings through VMSetError
5395 if (online)
5396 {
5397 SharedFolderDataMap::iterator it = oldFolders.find (name);
5398 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5399 {
5400 /* a new machine folder is added or
5401 * the existing machine folder is changed */
5402 if (mSharedFolders.find (name) != mSharedFolders.end())
5403 ; /* the console folder exists, nothing to do */
5404 else
5405 {
5406 /* remove the old machine folder (when changed)
5407 * or the global folder if any (when new) */
5408 if (it != oldFolders.end() ||
5409 mGlobalSharedFolders.find (name) !=
5410 mGlobalSharedFolders.end())
5411 rc = removeSharedFolder (name);
5412 /* create the new machine folder */
5413 rc = createSharedFolder (name, SharedFolderData (hostPath, writable));
5414 }
5415 }
5416 /* forget the processed (or identical) folder */
5417 if (it != oldFolders.end())
5418 oldFolders.erase (it);
5419
5420 rc = S_OK;
5421 }
5422 }
5423
5424 AssertComRCReturnRC(rc);
5425
5426 /* process outdated (removed) folders */
5427 /// @todo report errors as runtime warnings through VMSetError
5428 if (online)
5429 {
5430 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5431 it != oldFolders.end(); ++ it)
5432 {
5433 if (mSharedFolders.find (it->first) != mSharedFolders.end())
5434 ; /* the console folder exists, nothing to do */
5435 else
5436 {
5437 /* remove the outdated machine folder */
5438 rc = removeSharedFolder (it->first);
5439 /* create the global folder if there is any */
5440 SharedFolderDataMap::const_iterator git =
5441 mGlobalSharedFolders.find (it->first);
5442 if (git != mGlobalSharedFolders.end())
5443 rc = createSharedFolder (git->first, git->second);
5444 }
5445 }
5446
5447 rc = S_OK;
5448 }
5449 }
5450
5451 return rc;
5452}
5453
5454/**
5455 * Searches for a shared folder with the given name in the list of machine
5456 * shared folders and then in the list of the global shared folders.
5457 *
5458 * @param aName Name of the folder to search for.
5459 * @param aIt Where to store the pointer to the found folder.
5460 * @return @c true if the folder was found and @c false otherwise.
5461 *
5462 * @note The caller must lock this object for reading.
5463 */
5464bool Console::findOtherSharedFolder (IN_BSTR aName,
5465 SharedFolderDataMap::const_iterator &aIt)
5466{
5467 /* sanity check */
5468 AssertReturn(isWriteLockOnCurrentThread(), false);
5469
5470 /* first, search machine folders */
5471 aIt = mMachineSharedFolders.find (aName);
5472 if (aIt != mMachineSharedFolders.end())
5473 return true;
5474
5475 /* second, search machine folders */
5476 aIt = mGlobalSharedFolders.find (aName);
5477 if (aIt != mGlobalSharedFolders.end())
5478 return true;
5479
5480 return false;
5481}
5482
5483/**
5484 * Calls the HGCM service to add a shared folder definition.
5485 *
5486 * @param aName Shared folder name.
5487 * @param aHostPath Shared folder path.
5488 *
5489 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5490 * @note Doesn't lock anything.
5491 */
5492HRESULT Console::createSharedFolder (CBSTR aName, SharedFolderData aData)
5493{
5494 ComAssertRet (aName && *aName, E_FAIL);
5495 ComAssertRet (aData.mHostPath, E_FAIL);
5496
5497 /* sanity checks */
5498 AssertReturn(mpVM, E_FAIL);
5499 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5500
5501 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
5502 SHFLSTRING *pFolderName, *pMapName;
5503 size_t cbString;
5504
5505 Log (("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
5506
5507 cbString = (RTUtf16Len (aData.mHostPath) + 1) * sizeof (RTUTF16);
5508 if (cbString >= UINT16_MAX)
5509 return setError (E_INVALIDARG, tr ("The name is too long"));
5510 pFolderName = (SHFLSTRING *) RTMemAllocZ (sizeof (SHFLSTRING) + cbString);
5511 Assert (pFolderName);
5512 memcpy (pFolderName->String.ucs2, aData.mHostPath, cbString);
5513
5514 pFolderName->u16Size = (uint16_t)cbString;
5515 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5516
5517 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
5518 parms[0].u.pointer.addr = pFolderName;
5519 parms[0].u.pointer.size = sizeof (SHFLSTRING) + (uint16_t)cbString;
5520
5521 cbString = (RTUtf16Len (aName) + 1) * sizeof (RTUTF16);
5522 if (cbString >= UINT16_MAX)
5523 {
5524 RTMemFree (pFolderName);
5525 return setError (E_INVALIDARG, tr ("The host path is too long"));
5526 }
5527 pMapName = (SHFLSTRING *) RTMemAllocZ (sizeof(SHFLSTRING) + cbString);
5528 Assert (pMapName);
5529 memcpy (pMapName->String.ucs2, aName, cbString);
5530
5531 pMapName->u16Size = (uint16_t)cbString;
5532 pMapName->u16Length = (uint16_t)cbString - sizeof (RTUTF16);
5533
5534 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
5535 parms[1].u.pointer.addr = pMapName;
5536 parms[1].u.pointer.size = sizeof (SHFLSTRING) + (uint16_t)cbString;
5537
5538 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
5539 parms[2].u.uint32 = aData.mWritable;
5540
5541 int vrc = mVMMDev->hgcmHostCall ("VBoxSharedFolders",
5542 SHFL_FN_ADD_MAPPING,
5543 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
5544 RTMemFree (pFolderName);
5545 RTMemFree (pMapName);
5546
5547 if (VBOX_FAILURE (vrc))
5548 return setError (E_FAIL,
5549 tr ("Could not create a shared folder '%ls' "
5550 "mapped to '%ls' (%Rrc)"),
5551 aName, aData.mHostPath.raw(), vrc);
5552
5553 return S_OK;
5554}
5555
5556/**
5557 * Calls the HGCM service to remove the shared folder definition.
5558 *
5559 * @param aName Shared folder name.
5560 *
5561 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5562 * @note Doesn't lock anything.
5563 */
5564HRESULT Console::removeSharedFolder (CBSTR aName)
5565{
5566 ComAssertRet (aName && *aName, E_FAIL);
5567
5568 /* sanity checks */
5569 AssertReturn(mpVM, E_FAIL);
5570 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5571
5572 VBOXHGCMSVCPARM parms;
5573 SHFLSTRING *pMapName;
5574 size_t cbString;
5575
5576 Log (("Removing shared folder '%ls'\n", aName));
5577
5578 cbString = (RTUtf16Len (aName) + 1) * sizeof (RTUTF16);
5579 if (cbString >= UINT16_MAX)
5580 return setError (E_INVALIDARG, tr ("The name is too long"));
5581 pMapName = (SHFLSTRING *) RTMemAllocZ (sizeof (SHFLSTRING) + cbString);
5582 Assert (pMapName);
5583 memcpy (pMapName->String.ucs2, aName, cbString);
5584
5585 pMapName->u16Size = (uint16_t)cbString;
5586 pMapName->u16Length = (uint16_t)cbString - sizeof (RTUTF16);
5587
5588 parms.type = VBOX_HGCM_SVC_PARM_PTR;
5589 parms.u.pointer.addr = pMapName;
5590 parms.u.pointer.size = sizeof (SHFLSTRING) + (uint16_t)cbString;
5591
5592 int vrc = mVMMDev->hgcmHostCall ("VBoxSharedFolders",
5593 SHFL_FN_REMOVE_MAPPING,
5594 1, &parms);
5595 RTMemFree(pMapName);
5596 if (VBOX_FAILURE (vrc))
5597 return setError (E_FAIL,
5598 tr ("Could not remove the shared folder '%ls' (%Rrc)"),
5599 aName, vrc);
5600
5601 return S_OK;
5602}
5603
5604/**
5605 * VM state callback function. Called by the VMM
5606 * using its state machine states.
5607 *
5608 * Primarily used to handle VM initiated power off, suspend and state saving,
5609 * but also for doing termination completed work (VMSTATE_TERMINATE).
5610 *
5611 * In general this function is called in the context of the EMT.
5612 *
5613 * @param aVM The VM handle.
5614 * @param aState The new state.
5615 * @param aOldState The old state.
5616 * @param aUser The user argument (pointer to the Console object).
5617 *
5618 * @note Locks the Console object for writing.
5619 */
5620DECLCALLBACK(void)
5621Console::vmstateChangeCallback (PVM aVM, VMSTATE aState, VMSTATE aOldState,
5622 void *aUser)
5623{
5624 LogFlowFunc (("Changing state from %d to %d (aVM=%p)\n",
5625 aOldState, aState, aVM));
5626
5627 Console *that = static_cast <Console *> (aUser);
5628 AssertReturnVoid (that);
5629
5630 AutoCaller autoCaller(that);
5631
5632 /* Note that we must let this method proceed even if Console::uninit() has
5633 * been already called. In such case this VMSTATE change is a result of:
5634 * 1) powerDown() called from uninit() itself, or
5635 * 2) VM-(guest-)initiated power off. */
5636 AssertReturnVoid (autoCaller.isOk() ||
5637 autoCaller.state() == InUninit);
5638
5639 switch (aState)
5640 {
5641 /*
5642 * The VM has terminated
5643 */
5644 case VMSTATE_OFF:
5645 {
5646 AutoWriteLock alock(that);
5647
5648 if (that->mVMStateChangeCallbackDisabled)
5649 break;
5650
5651 /* Do we still think that it is running? It may happen if this is a
5652 * VM-(guest-)initiated shutdown/poweroff.
5653 */
5654 if (that->mMachineState != MachineState_Stopping &&
5655 that->mMachineState != MachineState_Saving &&
5656 that->mMachineState != MachineState_Restoring)
5657 {
5658 LogFlowFunc (("VM has powered itself off but Console still "
5659 "thinks it is running. Notifying.\n"));
5660
5661 /* prevent powerDown() from calling VMR3PowerOff() again */
5662 Assert (that->mVMPoweredOff == false);
5663 that->mVMPoweredOff = true;
5664
5665 /* we are stopping now */
5666 that->setMachineState (MachineState_Stopping);
5667
5668 /* Setup task object and thread to carry out the operation
5669 * asynchronously (if we call powerDown() right here but there
5670 * is one or more mpVM callers (added with addVMCaller()) we'll
5671 * deadlock).
5672 */
5673 std::auto_ptr <VMProgressTask> task (
5674 new VMProgressTask (that, NULL /* aProgress */,
5675 true /* aUsesVMPtr */));
5676
5677 /* If creating a task is falied, this can currently mean one of
5678 * two: either Console::uninit() has been called just a ms
5679 * before (so a powerDown() call is already on the way), or
5680 * powerDown() itself is being already executed. Just do
5681 * nothing.
5682 */
5683 if (!task->isOk())
5684 {
5685 LogFlowFunc (("Console is already being uninitialized.\n"));
5686 break;
5687 }
5688
5689 int vrc = RTThreadCreate (NULL, Console::powerDownThread,
5690 (void *) task.get(), 0,
5691 RTTHREADTYPE_MAIN_WORKER, 0,
5692 "VMPowerDown");
5693
5694 AssertMsgRCBreak (vrc,
5695 ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
5696
5697 /* task is now owned by powerDownThread(), so release it */
5698 task.release();
5699 }
5700 break;
5701 }
5702
5703 /* The VM has been completely destroyed.
5704 *
5705 * Note: This state change can happen at two points:
5706 * 1) At the end of VMR3Destroy() if it was not called from EMT.
5707 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
5708 * called by EMT.
5709 */
5710 case VMSTATE_TERMINATED:
5711 {
5712 AutoWriteLock alock(that);
5713
5714 if (that->mVMStateChangeCallbackDisabled)
5715 break;
5716
5717 /* Terminate host interface networking. If aVM is NULL, we've been
5718 * manually called from powerUpThread() either before calling
5719 * VMR3Create() or after VMR3Create() failed, so no need to touch
5720 * networking.
5721 */
5722 if (aVM)
5723 that->powerDownHostInterfaces();
5724
5725 /* From now on the machine is officially powered down or remains in
5726 * the Saved state.
5727 */
5728 switch (that->mMachineState)
5729 {
5730 default:
5731 AssertFailed();
5732 /* fall through */
5733 case MachineState_Stopping:
5734 /* successfully powered down */
5735 that->setMachineState (MachineState_PoweredOff);
5736 break;
5737 case MachineState_Saving:
5738 /* successfully saved (note that the machine is already in
5739 * the Saved state on the server due to EndSavingState()
5740 * called from saveStateThread(), so only change the local
5741 * state) */
5742 that->setMachineStateLocally (MachineState_Saved);
5743 break;
5744 case MachineState_Starting:
5745 /* failed to start, but be patient: set back to PoweredOff
5746 * (for similarity with the below) */
5747 that->setMachineState (MachineState_PoweredOff);
5748 break;
5749 case MachineState_Restoring:
5750 /* failed to load the saved state file, but be patient: set
5751 * back to Saved (to preserve the saved state file) */
5752 that->setMachineState (MachineState_Saved);
5753 break;
5754 }
5755
5756 break;
5757 }
5758
5759 case VMSTATE_SUSPENDED:
5760 {
5761 if (aOldState == VMSTATE_RUNNING)
5762 {
5763 AutoWriteLock alock(that);
5764
5765 if (that->mVMStateChangeCallbackDisabled)
5766 break;
5767
5768 /* Change the machine state from Running to Paused */
5769 Assert (that->mMachineState == MachineState_Running);
5770 that->setMachineState (MachineState_Paused);
5771 }
5772
5773 break;
5774 }
5775
5776 case VMSTATE_RUNNING:
5777 {
5778 if (aOldState == VMSTATE_CREATED ||
5779 aOldState == VMSTATE_SUSPENDED)
5780 {
5781 AutoWriteLock alock(that);
5782
5783 if (that->mVMStateChangeCallbackDisabled)
5784 break;
5785
5786 /* Change the machine state from Starting, Restoring or Paused
5787 * to Running */
5788 Assert ( ( ( that->mMachineState == MachineState_Starting
5789 || that->mMachineState == MachineState_Paused)
5790 && aOldState == VMSTATE_CREATED)
5791 || ( ( that->mMachineState == MachineState_Restoring
5792 || that->mMachineState == MachineState_Paused)
5793 && aOldState == VMSTATE_SUSPENDED));
5794
5795 that->setMachineState (MachineState_Running);
5796 }
5797
5798 break;
5799 }
5800
5801 case VMSTATE_GURU_MEDITATION:
5802 {
5803 AutoWriteLock alock(that);
5804
5805 if (that->mVMStateChangeCallbackDisabled)
5806 break;
5807
5808 /* Guru respects only running VMs */
5809 Assert (Global::IsOnline (that->mMachineState));
5810
5811 that->setMachineState (MachineState_Stuck);
5812
5813 break;
5814 }
5815
5816 default: /* shut up gcc */
5817 break;
5818 }
5819}
5820
5821#ifdef VBOX_WITH_USB
5822
5823/**
5824 * Sends a request to VMM to attach the given host device.
5825 * After this method succeeds, the attached device will appear in the
5826 * mUSBDevices collection.
5827 *
5828 * @param aHostDevice device to attach
5829 *
5830 * @note Synchronously calls EMT.
5831 * @note Must be called from under this object's lock.
5832 */
5833HRESULT Console::attachUSBDevice (IUSBDevice *aHostDevice, ULONG aMaskedIfs)
5834{
5835 AssertReturn(aHostDevice, E_FAIL);
5836 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5837
5838 /* still want a lock object because we need to leave it */
5839 AutoWriteLock alock(this);
5840
5841 HRESULT hrc;
5842
5843 /*
5844 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
5845 * method in EMT (using usbAttachCallback()).
5846 */
5847 Bstr BstrAddress;
5848 hrc = aHostDevice->COMGETTER (Address) (BstrAddress.asOutParam());
5849 ComAssertComRCRetRC (hrc);
5850
5851 Utf8Str Address (BstrAddress);
5852
5853 Bstr id;
5854 hrc = aHostDevice->COMGETTER (Id) (id.asOutParam());
5855 ComAssertComRCRetRC (hrc);
5856 Guid uuid(id);
5857
5858 BOOL fRemote = FALSE;
5859 hrc = aHostDevice->COMGETTER (Remote) (&fRemote);
5860 ComAssertComRCRetRC (hrc);
5861
5862 /* protect mpVM */
5863 AutoVMCaller autoVMCaller (this);
5864 CheckComRCReturnRC(autoVMCaller.rc());
5865
5866 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
5867 Address.raw(), uuid.ptr()));
5868
5869 /* leave the lock before a VMR3* call (EMT will call us back)! */
5870 alock.leave();
5871
5872/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
5873 PVMREQ pReq = NULL;
5874 int vrc = VMR3ReqCall (mpVM, VMCPUID_ANY, &pReq, RT_INDEFINITE_WAIT,
5875 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
5876 if (VBOX_SUCCESS (vrc))
5877 vrc = pReq->iStatus;
5878 VMR3ReqFree (pReq);
5879
5880 /* restore the lock */
5881 alock.enter();
5882
5883 /* hrc is S_OK here */
5884
5885 if (VBOX_FAILURE (vrc))
5886 {
5887 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
5888 Address.raw(), uuid.ptr(), vrc));
5889
5890 switch (vrc)
5891 {
5892 case VERR_VUSB_NO_PORTS:
5893 hrc = setError (E_FAIL,
5894 tr ("Failed to attach the USB device. (No available ports on the USB controller)."));
5895 break;
5896 case VERR_VUSB_USBFS_PERMISSION:
5897 hrc = setError (E_FAIL,
5898 tr ("Not permitted to open the USB device, check usbfs options"));
5899 break;
5900 default:
5901 hrc = setError (E_FAIL,
5902 tr ("Failed to create a proxy device for the USB device. (Error: %Rrc)"), vrc);
5903 break;
5904 }
5905 }
5906
5907 return hrc;
5908}
5909
5910/**
5911 * USB device attach callback used by AttachUSBDevice().
5912 * Note that AttachUSBDevice() doesn't return until this callback is executed,
5913 * so we don't use AutoCaller and don't care about reference counters of
5914 * interface pointers passed in.
5915 *
5916 * @thread EMT
5917 * @note Locks the console object for writing.
5918 */
5919//static
5920DECLCALLBACK(int)
5921Console::usbAttachCallback (Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
5922{
5923 LogFlowFuncEnter();
5924 LogFlowFunc (("that={%p}\n", that));
5925
5926 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
5927
5928 void *pvRemoteBackend = NULL;
5929 if (aRemote)
5930 {
5931 RemoteUSBDevice *pRemoteUSBDevice = static_cast <RemoteUSBDevice *> (aHostDevice);
5932 Guid guid (*aUuid);
5933
5934 pvRemoteBackend = that->consoleVRDPServer ()->USBBackendRequestPointer (pRemoteUSBDevice->clientId (), &guid);
5935 if (!pvRemoteBackend)
5936 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
5937 }
5938
5939 USHORT portVersion = 1;
5940 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
5941 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
5942 Assert(portVersion == 1 || portVersion == 2);
5943
5944 int vrc = PDMR3USBCreateProxyDevice (that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
5945 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
5946 if (VBOX_SUCCESS (vrc))
5947 {
5948 /* Create a OUSBDevice and add it to the device list */
5949 ComObjPtr<OUSBDevice> device;
5950 device.createObject();
5951 HRESULT hrc = device->init (aHostDevice);
5952 AssertComRC (hrc);
5953
5954 AutoWriteLock alock(that);
5955 that->mUSBDevices.push_back (device);
5956 LogFlowFunc (("Attached device {%RTuuid}\n", device->id().raw()));
5957
5958 /* notify callbacks */
5959 that->onUSBDeviceStateChange (device, true /* aAttached */, NULL);
5960 }
5961
5962 LogFlowFunc (("vrc=%Rrc\n", vrc));
5963 LogFlowFuncLeave();
5964 return vrc;
5965}
5966
5967/**
5968 * Sends a request to VMM to detach the given host device. After this method
5969 * succeeds, the detached device will disappear from the mUSBDevices
5970 * collection.
5971 *
5972 * @param aIt Iterator pointing to the device to detach.
5973 *
5974 * @note Synchronously calls EMT.
5975 * @note Must be called from under this object's lock.
5976 */
5977HRESULT Console::detachUSBDevice (USBDeviceList::iterator &aIt)
5978{
5979 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5980
5981 /* still want a lock object because we need to leave it */
5982 AutoWriteLock alock(this);
5983
5984 /* protect mpVM */
5985 AutoVMCaller autoVMCaller (this);
5986 CheckComRCReturnRC(autoVMCaller.rc());
5987
5988 /* if the device is attached, then there must at least one USB hub. */
5989 AssertReturn(PDMR3USBHasHub (mpVM), E_FAIL);
5990
5991 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
5992 (*aIt)->id().raw()));
5993
5994 /* leave the lock before a VMR3* call (EMT will call us back)! */
5995 alock.leave();
5996
5997 PVMREQ pReq;
5998/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
5999 int vrc = VMR3ReqCall (mpVM, VMCPUID_ANY, &pReq, RT_INDEFINITE_WAIT,
6000 (PFNRT) usbDetachCallback, 4,
6001 this, &aIt, (*aIt)->id().raw());
6002 if (VBOX_SUCCESS (vrc))
6003 vrc = pReq->iStatus;
6004 VMR3ReqFree (pReq);
6005
6006 ComAssertRCRet (vrc, E_FAIL);
6007
6008 return S_OK;
6009}
6010
6011/**
6012 * USB device detach callback used by DetachUSBDevice().
6013 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6014 * so we don't use AutoCaller and don't care about reference counters of
6015 * interface pointers passed in.
6016 *
6017 * @thread EMT
6018 * @note Locks the console object for writing.
6019 */
6020//static
6021DECLCALLBACK(int)
6022Console::usbDetachCallback (Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6023{
6024 LogFlowFuncEnter();
6025 LogFlowFunc (("that={%p}\n", that));
6026
6027 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6028 ComObjPtr<OUSBDevice> device = **aIt;
6029
6030 /*
6031 * If that was a remote device, release the backend pointer.
6032 * The pointer was requested in usbAttachCallback.
6033 */
6034 BOOL fRemote = FALSE;
6035
6036 HRESULT hrc2 = (**aIt)->COMGETTER (Remote) (&fRemote);
6037 ComAssertComRC (hrc2);
6038
6039 if (fRemote)
6040 {
6041 Guid guid (*aUuid);
6042 that->consoleVRDPServer ()->USBBackendReleasePointer (&guid);
6043 }
6044
6045 int vrc = PDMR3USBDetachDevice (that->mpVM, aUuid);
6046
6047 if (VBOX_SUCCESS (vrc))
6048 {
6049 AutoWriteLock alock(that);
6050
6051 /* Remove the device from the collection */
6052 that->mUSBDevices.erase (*aIt);
6053 LogFlowFunc (("Detached device {%RTuuid}\n", device->id().raw()));
6054
6055 /* notify callbacks */
6056 that->onUSBDeviceStateChange (device, false /* aAttached */, NULL);
6057 }
6058
6059 LogFlowFunc (("vrc=%Rrc\n", vrc));
6060 LogFlowFuncLeave();
6061 return vrc;
6062}
6063
6064#endif /* VBOX_WITH_USB */
6065
6066
6067/**
6068 * Helper function to handle host interface device creation and attachment.
6069 *
6070 * @param networkAdapter the network adapter which attachment should be reset
6071 * @return COM status code
6072 *
6073 * @note The caller must lock this object for writing.
6074 */
6075HRESULT Console::attachToBridgedInterface(INetworkAdapter *networkAdapter)
6076{
6077#if !defined(RT_OS_LINUX) || defined(VBOX_WITH_NETFLT)
6078 /*
6079 * Nothing to do here.
6080 *
6081 * Note, the reason for this method in the first place a memory / fork
6082 * bug on linux. All this code belongs in DrvTAP and similar places.
6083 */
6084 NOREF(networkAdapter);
6085 return S_OK;
6086
6087#else /* RT_OS_LINUX && !VBOX_WITH_NETFLT */
6088
6089 LogFlowThisFunc(("\n"));
6090 /* sanity check */
6091 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6092
6093# ifdef VBOX_STRICT
6094 /* paranoia */
6095 NetworkAttachmentType_T attachment;
6096 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6097 Assert(attachment == NetworkAttachmentType_Bridged);
6098# endif /* VBOX_STRICT */
6099
6100 HRESULT rc = S_OK;
6101
6102 ULONG slot = 0;
6103 rc = networkAdapter->COMGETTER(Slot)(&slot);
6104 AssertComRC(rc);
6105
6106 /*
6107 * Allocate a host interface device
6108 */
6109 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6110 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6111 if (VBOX_SUCCESS(rcVBox))
6112 {
6113 /*
6114 * Set/obtain the tap interface.
6115 */
6116 struct ifreq IfReq;
6117 memset(&IfReq, 0, sizeof(IfReq));
6118 /* The name of the TAP interface we are using */
6119 Bstr tapDeviceName;
6120 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6121 if (FAILED(rc))
6122 tapDeviceName.setNull(); /* Is this necessary? */
6123 if (tapDeviceName.isEmpty())
6124 {
6125 LogRel(("No TAP device name was supplied.\n"));
6126 rc = setError(E_FAIL, tr ("No TAP device name was supplied for the host networking interface"));
6127 }
6128
6129 if (SUCCEEDED(rc))
6130 {
6131 /* If we are using a static TAP device then try to open it. */
6132 Utf8Str str(tapDeviceName);
6133 if (str.length() <= sizeof(IfReq.ifr_name))
6134 strcpy(IfReq.ifr_name, str.raw());
6135 else
6136 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6137 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6138 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6139 if (rcVBox != 0)
6140 {
6141 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6142 rc = setError(E_FAIL, tr ("Failed to open the host network interface %ls"),
6143 tapDeviceName.raw());
6144 }
6145 }
6146 if (SUCCEEDED(rc))
6147 {
6148 /*
6149 * Make it pollable.
6150 */
6151 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6152 {
6153 Log(("attachToBridgedInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6154 /*
6155 * Here is the right place to communicate the TAP file descriptor and
6156 * the host interface name to the server if/when it becomes really
6157 * necessary.
6158 */
6159 maTAPDeviceName[slot] = tapDeviceName;
6160 rcVBox = VINF_SUCCESS;
6161 }
6162 else
6163 {
6164 int iErr = errno;
6165
6166 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6167 rcVBox = VERR_HOSTIF_BLOCKING;
6168 rc = setError(E_FAIL, tr ("could not set up the host networking device for non blocking access: %s"),
6169 strerror(errno));
6170 }
6171 }
6172 }
6173 else
6174 {
6175 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6176 switch (rcVBox)
6177 {
6178 case VERR_ACCESS_DENIED:
6179 /* will be handled by our caller */
6180 rc = rcVBox;
6181 break;
6182 default:
6183 rc = setError(E_FAIL, tr ("Could not set up the host networking device: %Rrc"), rcVBox);
6184 break;
6185 }
6186 }
6187 /* in case of failure, cleanup. */
6188 if (VBOX_FAILURE(rcVBox) && SUCCEEDED(rc))
6189 {
6190 LogRel(("General failure attaching to host interface\n"));
6191 rc = setError(E_FAIL, tr ("General failure attaching to host interface"));
6192 }
6193 LogFlowThisFunc(("rc=%d\n", rc));
6194 return rc;
6195#endif /* RT_OS_LINUX */
6196}
6197
6198/**
6199 * Helper function to handle detachment from a host interface
6200 *
6201 * @param networkAdapter the network adapter which attachment should be reset
6202 * @return COM status code
6203 *
6204 * @note The caller must lock this object for writing.
6205 */
6206HRESULT Console::detachFromBridgedInterface(INetworkAdapter *networkAdapter)
6207{
6208#if !defined(RT_OS_LINUX) || defined(VBOX_WITH_NETFLT)
6209 /*
6210 * Nothing to do here.
6211 */
6212 NOREF(networkAdapter);
6213 return S_OK;
6214
6215#else /* RT_OS_LINUX */
6216
6217 /* sanity check */
6218 LogFlowThisFunc(("\n"));
6219 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6220
6221 HRESULT rc = S_OK;
6222# ifdef VBOX_STRICT
6223 /* paranoia */
6224 NetworkAttachmentType_T attachment;
6225 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6226 Assert(attachment == NetworkAttachmentType_Bridged);
6227# endif /* VBOX_STRICT */
6228
6229 ULONG slot = 0;
6230 rc = networkAdapter->COMGETTER(Slot)(&slot);
6231 AssertComRC(rc);
6232
6233 /* is there an open TAP device? */
6234 if (maTapFD[slot] != NIL_RTFILE)
6235 {
6236 /*
6237 * Close the file handle.
6238 */
6239 Bstr tapDeviceName, tapTerminateApplication;
6240 bool isStatic = true;
6241 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6242 if (FAILED(rc) || tapDeviceName.isEmpty())
6243 {
6244 /* If the name is empty, this is a dynamic TAP device, so close it now,
6245 so that the termination script can remove the interface. Otherwise we still
6246 need the FD to pass to the termination script. */
6247 isStatic = false;
6248 int rcVBox = RTFileClose(maTapFD[slot]);
6249 AssertRC(rcVBox);
6250 maTapFD[slot] = NIL_RTFILE;
6251 }
6252 if (isStatic)
6253 {
6254 /* If we are using a static TAP device, we close it now, after having called the
6255 termination script. */
6256 int rcVBox = RTFileClose(maTapFD[slot]);
6257 AssertRC(rcVBox);
6258 }
6259 /* the TAP device name and handle are no longer valid */
6260 maTapFD[slot] = NIL_RTFILE;
6261 maTAPDeviceName[slot] = "";
6262 }
6263 LogFlowThisFunc(("returning %d\n", rc));
6264 return rc;
6265#endif /* RT_OS_LINUX */
6266}
6267
6268
6269/**
6270 * Called at power down to terminate host interface networking.
6271 *
6272 * @note The caller must lock this object for writing.
6273 */
6274HRESULT Console::powerDownHostInterfaces()
6275{
6276 LogFlowThisFunc(("\n"));
6277
6278 /* sanity check */
6279 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6280
6281 /*
6282 * host interface termination handling
6283 */
6284 HRESULT rc;
6285 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6286 {
6287 ComPtr<INetworkAdapter> networkAdapter;
6288 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6289 CheckComRCBreakRC (rc);
6290
6291 BOOL enabled = FALSE;
6292 networkAdapter->COMGETTER(Enabled) (&enabled);
6293 if (!enabled)
6294 continue;
6295
6296 NetworkAttachmentType_T attachment;
6297 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6298 if (attachment == NetworkAttachmentType_Bridged)
6299 {
6300 HRESULT rc2 = detachFromBridgedInterface(networkAdapter);
6301 if (FAILED(rc2) && SUCCEEDED(rc))
6302 rc = rc2;
6303 }
6304 }
6305
6306 return rc;
6307}
6308
6309
6310/**
6311 * Process callback handler for VMR3Load and VMR3Save.
6312 *
6313 * @param pVM The VM handle.
6314 * @param uPercent Completetion precentage (0-100).
6315 * @param pvUser Pointer to the VMProgressTask structure.
6316 * @return VINF_SUCCESS.
6317 */
6318/*static*/ DECLCALLBACK (int)
6319Console::stateProgressCallback (PVM pVM, unsigned uPercent, void *pvUser)
6320{
6321 VMProgressTask *task = static_cast <VMProgressTask *> (pvUser);
6322 AssertReturn(task, VERR_INVALID_PARAMETER);
6323
6324 /* update the progress object */
6325 if (task->mProgress)
6326 task->mProgress->setCurrentOperationProgress(uPercent);
6327
6328 return VINF_SUCCESS;
6329}
6330
6331/**
6332 * VM error callback function. Called by the various VM components.
6333 *
6334 * @param pVM VM handle. Can be NULL if an error occurred before
6335 * successfully creating a VM.
6336 * @param pvUser Pointer to the VMProgressTask structure.
6337 * @param rc VBox status code.
6338 * @param pszFormat Printf-like error message.
6339 * @param args Various number of arguments for the error message.
6340 *
6341 * @thread EMT, VMPowerUp...
6342 *
6343 * @note The VMProgressTask structure modified by this callback is not thread
6344 * safe.
6345 */
6346/* static */ DECLCALLBACK (void)
6347Console::setVMErrorCallback (PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6348 const char *pszFormat, va_list args)
6349{
6350 VMProgressTask *task = static_cast <VMProgressTask *> (pvUser);
6351 AssertReturnVoid (task);
6352
6353 /* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */
6354 va_list va2;
6355 va_copy (va2, args); /* Have to make a copy here or GCC will break. */
6356
6357 /* append to the existing error message if any */
6358 if (task->mErrorMsg.length())
6359 task->mErrorMsg = Utf8StrFmt ("%s.\n%N (%Rrc)", task->mErrorMsg.raw(),
6360 pszFormat, &va2, rc, rc);
6361 else
6362 task->mErrorMsg = Utf8StrFmt ("%N (%Rrc)",
6363 pszFormat, &va2, rc, rc);
6364
6365 va_end (va2);
6366}
6367
6368/**
6369 * VM runtime error callback function.
6370 * See VMSetRuntimeError for the detailed description of parameters.
6371 *
6372 * @param pVM The VM handle.
6373 * @param pvUser The user argument.
6374 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6375 * @param pszErrorId Error ID string.
6376 * @param pszFormat Error message format string.
6377 * @param va Error message arguments.
6378 * @thread EMT.
6379 */
6380/* static */ DECLCALLBACK(void)
6381Console::setVMRuntimeErrorCallback (PVM pVM, void *pvUser, uint32_t fFlags,
6382 const char *pszErrorId,
6383 const char *pszFormat, va_list va)
6384{
6385 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6386 LogFlowFuncEnter();
6387
6388 Console *that = static_cast <Console *> (pvUser);
6389 AssertReturnVoid (that);
6390
6391 Utf8Str message = Utf8StrFmtVA (pszFormat, va);
6392
6393 LogRel (("Console: VM runtime error: fatal=%RTbool, "
6394 "errorID=%s message=\"%s\"\n",
6395 fFatal, pszErrorId, message.raw()));
6396
6397 that->onRuntimeError (BOOL (fFatal), Bstr (pszErrorId), Bstr (message));
6398
6399 LogFlowFuncLeave();
6400}
6401
6402/**
6403 * Captures USB devices that match filters of the VM.
6404 * Called at VM startup.
6405 *
6406 * @param pVM The VM handle.
6407 *
6408 * @note The caller must lock this object for writing.
6409 */
6410HRESULT Console::captureUSBDevices (PVM pVM)
6411{
6412 LogFlowThisFunc(("\n"));
6413
6414 /* sanity check */
6415 ComAssertRet (isWriteLockOnCurrentThread(), E_FAIL);
6416
6417 /* If the machine has an USB controller, ask the USB proxy service to
6418 * capture devices */
6419 PPDMIBASE pBase;
6420 int vrc = PDMR3QueryLun (pVM, "usb-ohci", 0, 0, &pBase);
6421 if (VBOX_SUCCESS (vrc))
6422 {
6423 /* leave the lock before calling Host in VBoxSVC since Host may call
6424 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6425 * produce an inter-process dead-lock otherwise. */
6426 AutoWriteLock alock(this);
6427 alock.leave();
6428
6429 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6430 ComAssertComRCRetRC (hrc);
6431 }
6432 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6433 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6434 vrc = VINF_SUCCESS;
6435 else
6436 AssertRC (vrc);
6437
6438 return VBOX_SUCCESS (vrc) ? S_OK : E_FAIL;
6439}
6440
6441
6442/**
6443 * Detach all USB device which are attached to the VM for the
6444 * purpose of clean up and such like.
6445 *
6446 * @note The caller must lock this object for writing.
6447 */
6448void Console::detachAllUSBDevices (bool aDone)
6449{
6450 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6451
6452 /* sanity check */
6453 AssertReturnVoid (isWriteLockOnCurrentThread());
6454
6455 mUSBDevices.clear();
6456
6457 /* leave the lock before calling Host in VBoxSVC since Host may call
6458 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6459 * produce an inter-process dead-lock otherwise. */
6460 AutoWriteLock alock(this);
6461 alock.leave();
6462
6463 mControl->DetachAllUSBDevices (aDone);
6464}
6465
6466/**
6467 * @note Locks this object for writing.
6468 */
6469void Console::processRemoteUSBDevices (uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6470{
6471 LogFlowThisFuncEnter();
6472 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
6473
6474 AutoCaller autoCaller(this);
6475 if (!autoCaller.isOk())
6476 {
6477 /* Console has been already uninitialized, deny request */
6478 AssertMsgFailed (("Temporary assertion to prove that it happens, "
6479 "please report to dmik\n"));
6480 LogFlowThisFunc(("Console is already uninitialized\n"));
6481 LogFlowThisFuncLeave();
6482 return;
6483 }
6484
6485 AutoWriteLock alock(this);
6486
6487 /*
6488 * Mark all existing remote USB devices as dirty.
6489 */
6490 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6491 while (it != mRemoteUSBDevices.end())
6492 {
6493 (*it)->dirty (true);
6494 ++ it;
6495 }
6496
6497 /*
6498 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
6499 */
6500 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
6501 VRDPUSBDEVICEDESC *e = pDevList;
6502
6503 /* The cbDevList condition must be checked first, because the function can
6504 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
6505 */
6506 while (cbDevList >= 2 && e->oNext)
6507 {
6508 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
6509 e->idVendor, e->idProduct,
6510 e->oProduct? (char *)e + e->oProduct: ""));
6511
6512 bool fNewDevice = true;
6513
6514 it = mRemoteUSBDevices.begin();
6515 while (it != mRemoteUSBDevices.end())
6516 {
6517 if ((*it)->devId () == e->id
6518 && (*it)->clientId () == u32ClientId)
6519 {
6520 /* The device is already in the list. */
6521 (*it)->dirty (false);
6522 fNewDevice = false;
6523 break;
6524 }
6525
6526 ++ it;
6527 }
6528
6529 if (fNewDevice)
6530 {
6531 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
6532 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""
6533 ));
6534
6535 /* Create the device object and add the new device to list. */
6536 ComObjPtr<RemoteUSBDevice> device;
6537 device.createObject();
6538 device->init (u32ClientId, e);
6539
6540 mRemoteUSBDevices.push_back (device);
6541
6542 /* Check if the device is ok for current USB filters. */
6543 BOOL fMatched = FALSE;
6544 ULONG fMaskedIfs = 0;
6545
6546 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
6547
6548 AssertComRC (hrc);
6549
6550 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
6551
6552 if (fMatched)
6553 {
6554 hrc = onUSBDeviceAttach (device, NULL, fMaskedIfs);
6555
6556 /// @todo (r=dmik) warning reporting subsystem
6557
6558 if (hrc == S_OK)
6559 {
6560 LogFlowThisFunc(("Device attached\n"));
6561 device->captured (true);
6562 }
6563 }
6564 }
6565
6566 if (cbDevList < e->oNext)
6567 {
6568 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
6569 cbDevList, e->oNext));
6570 break;
6571 }
6572
6573 cbDevList -= e->oNext;
6574
6575 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
6576 }
6577
6578 /*
6579 * Remove dirty devices, that is those which are not reported by the server anymore.
6580 */
6581 for (;;)
6582 {
6583 ComObjPtr<RemoteUSBDevice> device;
6584
6585 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6586 while (it != mRemoteUSBDevices.end())
6587 {
6588 if ((*it)->dirty ())
6589 {
6590 device = *it;
6591 break;
6592 }
6593
6594 ++ it;
6595 }
6596
6597 if (!device)
6598 {
6599 break;
6600 }
6601
6602 USHORT vendorId = 0;
6603 device->COMGETTER(VendorId) (&vendorId);
6604
6605 USHORT productId = 0;
6606 device->COMGETTER(ProductId) (&productId);
6607
6608 Bstr product;
6609 device->COMGETTER(Product) (product.asOutParam());
6610
6611 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
6612 vendorId, productId, product.raw ()
6613 ));
6614
6615 /* Detach the device from VM. */
6616 if (device->captured ())
6617 {
6618 Bstr uuid;
6619 device->COMGETTER (Id) (uuid.asOutParam());
6620 onUSBDeviceDetach (uuid, NULL);
6621 }
6622
6623 /* And remove it from the list. */
6624 mRemoteUSBDevices.erase (it);
6625 }
6626
6627 LogFlowThisFuncLeave();
6628}
6629
6630/**
6631 * Thread function which starts the VM (also from saved state) and
6632 * track progress.
6633 *
6634 * @param Thread The thread id.
6635 * @param pvUser Pointer to a VMPowerUpTask structure.
6636 * @return VINF_SUCCESS (ignored).
6637 *
6638 * @note Locks the Console object for writing.
6639 */
6640/*static*/
6641DECLCALLBACK (int) Console::powerUpThread (RTTHREAD Thread, void *pvUser)
6642{
6643 LogFlowFuncEnter();
6644
6645 std::auto_ptr <VMPowerUpTask> task (static_cast <VMPowerUpTask *> (pvUser));
6646 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
6647
6648 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
6649 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
6650
6651#if defined(RT_OS_WINDOWS)
6652 {
6653 /* initialize COM */
6654 HRESULT hrc = CoInitializeEx (NULL,
6655 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
6656 COINIT_SPEED_OVER_MEMORY);
6657 LogFlowFunc (("CoInitializeEx()=%08X\n", hrc));
6658 }
6659#endif
6660
6661 HRESULT rc = S_OK;
6662 int vrc = VINF_SUCCESS;
6663
6664 /* Set up a build identifier so that it can be seen from core dumps what
6665 * exact build was used to produce the core. */
6666 static char saBuildID[40];
6667 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%d %s%s%s%s",
6668 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, VBoxSVNRev (), "BU", "IL", "DI", "D");
6669
6670 ComObjPtr<Console> console = task->mConsole;
6671
6672 /* Note: no need to use addCaller() because VMPowerUpTask does that */
6673
6674 /* The lock is also used as a signal from the task initiator (which
6675 * releases it only after RTThreadCreate()) that we can start the job */
6676 AutoWriteLock alock(console);
6677
6678 /* sanity */
6679 Assert (console->mpVM == NULL);
6680
6681 try
6682 {
6683 /* wait for auto reset ops to complete so that we can successfully lock
6684 * the attached hard disks by calling LockMedia() below */
6685 for (VMPowerUpTask::ProgressList::const_iterator
6686 it = task->hardDiskProgresses.begin();
6687 it != task->hardDiskProgresses.end(); ++ it)
6688 {
6689 HRESULT rc2 = (*it)->WaitForCompletion (-1);
6690 AssertComRC (rc2);
6691 }
6692
6693 /* lock attached media. This method will also check their
6694 * accessibility. Note that the media will be unlocked automatically
6695 * by SessionMachine::setMachineState() when the VM is powered down. */
6696 rc = console->mControl->LockMedia();
6697 CheckComRCThrowRC (rc);
6698
6699#ifdef VBOX_WITH_VRDP
6700
6701 /* Create the VRDP server. In case of headless operation, this will
6702 * also create the framebuffer, required at VM creation.
6703 */
6704 ConsoleVRDPServer *server = console->consoleVRDPServer();
6705 Assert (server);
6706
6707 /// @todo (dmik)
6708 // does VRDP server call Console from the other thread?
6709 // Not sure, so leave the lock just in case
6710 alock.leave();
6711 vrc = server->Launch();
6712 alock.enter();
6713
6714 if (VBOX_FAILURE (vrc))
6715 {
6716 Utf8Str errMsg;
6717 switch (vrc)
6718 {
6719 case VERR_NET_ADDRESS_IN_USE:
6720 {
6721 ULONG port = 0;
6722 console->mVRDPServer->COMGETTER(Port) (&port);
6723 errMsg = Utf8StrFmt (tr ("VRDP server port %d is already in use"),
6724 port);
6725 break;
6726 }
6727 case VERR_FILE_NOT_FOUND:
6728 {
6729 errMsg = Utf8StrFmt (tr ("Could not load the VRDP library"));
6730 break;
6731 }
6732 default:
6733 errMsg = Utf8StrFmt (tr ("Failed to launch VRDP server (%Rrc)"),
6734 vrc);
6735 }
6736 LogRel (("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
6737 vrc, errMsg.raw()));
6738 throw setError (E_FAIL, errMsg);
6739 }
6740
6741#endif /* VBOX_WITH_VRDP */
6742
6743 ComPtr<IMachine> pMachine = console->machine();
6744 ULONG cCpus = 1;
6745 pMachine->COMGETTER(CPUCount)(&cCpus);
6746
6747 /*
6748 * Create the VM
6749 */
6750 PVM pVM;
6751 /*
6752 * leave the lock since EMT will call Console. It's safe because
6753 * mMachineState is either Starting or Restoring state here.
6754 */
6755 alock.leave();
6756
6757 vrc = VMR3Create (cCpus, task->mSetVMErrorCallback, task.get(),
6758 task->mConfigConstructor, static_cast <Console *> (console),
6759 &pVM);
6760
6761 alock.enter();
6762
6763#ifdef VBOX_WITH_VRDP
6764 /* Enable client connections to the server. */
6765 console->consoleVRDPServer()->EnableConnections ();
6766#endif /* VBOX_WITH_VRDP */
6767
6768 if (VBOX_SUCCESS (vrc))
6769 {
6770 do
6771 {
6772 /*
6773 * Register our load/save state file handlers
6774 */
6775 vrc = SSMR3RegisterExternal (pVM,
6776 sSSMConsoleUnit, 0 /* iInstance */, sSSMConsoleVer,
6777 0 /* cbGuess */,
6778 NULL, saveStateFileExec, NULL, NULL, loadStateFileExec, NULL,
6779 static_cast <Console *> (console));
6780 AssertRC (vrc);
6781 if (VBOX_FAILURE (vrc))
6782 break;
6783
6784 vrc = static_cast <Console *>(console)->getDisplay()->registerSSM(pVM);
6785 AssertRC (vrc);
6786 if (VBOX_FAILURE (vrc))
6787 break;
6788
6789 /*
6790 * Synchronize debugger settings
6791 */
6792 MachineDebugger *machineDebugger = console->getMachineDebugger();
6793 if (machineDebugger)
6794 {
6795 machineDebugger->flushQueuedSettings();
6796 }
6797
6798 /*
6799 * Shared Folders
6800 */
6801 if (console->getVMMDev()->isShFlActive())
6802 {
6803 /// @todo (dmik)
6804 // does the code below call Console from the other thread?
6805 // Not sure, so leave the lock just in case
6806 alock.leave();
6807
6808 for (SharedFolderDataMap::const_iterator
6809 it = task->mSharedFolders.begin();
6810 it != task->mSharedFolders.end();
6811 ++ it)
6812 {
6813 rc = console->createSharedFolder ((*it).first, (*it).second);
6814 CheckComRCBreakRC (rc);
6815 }
6816
6817 /* enter the lock again */
6818 alock.enter();
6819
6820 CheckComRCBreakRC (rc);
6821 }
6822
6823 /*
6824 * Capture USB devices.
6825 */
6826 rc = console->captureUSBDevices (pVM);
6827 CheckComRCBreakRC (rc);
6828
6829 /* leave the lock before a lengthy operation */
6830 alock.leave();
6831
6832 /* Load saved state? */
6833 if (task->mSavedStateFile.length())
6834 {
6835 LogFlowFunc (("Restoring saved state from '%s'...\n",
6836 task->mSavedStateFile.raw()));
6837
6838 vrc = VMR3Load (pVM, task->mSavedStateFile,
6839 Console::stateProgressCallback,
6840 static_cast <VMProgressTask *> (task.get()));
6841
6842 if (VBOX_SUCCESS (vrc))
6843 {
6844 if (task->mStartPaused)
6845 /* done */
6846 console->setMachineState (MachineState_Paused);
6847 else
6848 {
6849 /* Start/Resume the VM execution */
6850 vrc = VMR3Resume (pVM);
6851 AssertRC (vrc);
6852 }
6853 }
6854
6855 /* Power off in case we failed loading or resuming the VM */
6856 if (VBOX_FAILURE (vrc))
6857 {
6858 int vrc2 = VMR3PowerOff (pVM);
6859 AssertRC (vrc2);
6860 }
6861 }
6862 else if (task->mStartPaused)
6863 /* done */
6864 console->setMachineState (MachineState_Paused);
6865 else
6866 {
6867 /* Power on the VM (i.e. start executing) */
6868 vrc = VMR3PowerOn(pVM);
6869 AssertRC (vrc);
6870 }
6871
6872 /* enter the lock again */
6873 alock.enter();
6874 }
6875 while (0);
6876
6877 /* On failure, destroy the VM */
6878 if (FAILED (rc) || VBOX_FAILURE (vrc))
6879 {
6880 /* preserve existing error info */
6881 ErrorInfoKeeper eik;
6882
6883 /* powerDown() will call VMR3Destroy() and do all necessary
6884 * cleanup (VRDP, USB devices) */
6885 HRESULT rc2 = console->powerDown();
6886 AssertComRC (rc2);
6887 }
6888 else
6889 {
6890 /*
6891 * Deregister the VMSetError callback. This is necessary as the
6892 * pfnVMAtError() function passed to VMR3Create() is supposed to
6893 * be sticky but our error callback isn't.
6894 */
6895 alock.leave();
6896 VMR3AtErrorDeregister(pVM, task->mSetVMErrorCallback, task.get());
6897 /** @todo register another VMSetError callback? */
6898 alock.enter();
6899 }
6900 }
6901 else
6902 {
6903 /*
6904 * If VMR3Create() failed it has released the VM memory.
6905 */
6906 console->mpVM = NULL;
6907 }
6908
6909 if (SUCCEEDED(rc) && VBOX_FAILURE (vrc))
6910 {
6911 /* If VMR3Create() or one of the other calls in this function fail,
6912 * an appropriate error message has been set in task->mErrorMsg.
6913 * However since that happens via a callback, the rc status code in
6914 * this function is not updated.
6915 */
6916 if (!task->mErrorMsg.length())
6917 {
6918 /* If the error message is not set but we've got a failure,
6919 * convert the VBox status code into a meaningfulerror message.
6920 * This becomes unused once all the sources of errors set the
6921 * appropriate error message themselves.
6922 */
6923 AssertMsgFailed (("Missing error message during powerup for "
6924 "status code %Rrc\n", vrc));
6925 task->mErrorMsg = Utf8StrFmt (
6926 tr ("Failed to start VM execution (%Rrc)"), vrc);
6927 }
6928
6929 /* Set the error message as the COM error.
6930 * Progress::notifyComplete() will pick it up later. */
6931 throw setError (E_FAIL, task->mErrorMsg);
6932 }
6933 }
6934 catch (HRESULT aRC) { rc = aRC; }
6935
6936 if (console->mMachineState == MachineState_Starting ||
6937 console->mMachineState == MachineState_Restoring)
6938 {
6939 /* We are still in the Starting/Restoring state. This means one of:
6940 *
6941 * 1) we failed before VMR3Create() was called;
6942 * 2) VMR3Create() failed.
6943 *
6944 * In both cases, there is no need to call powerDown(), but we still
6945 * need to go back to the PoweredOff/Saved state. Reuse
6946 * vmstateChangeCallback() for that purpose.
6947 */
6948
6949 /* preserve existing error info */
6950 ErrorInfoKeeper eik;
6951
6952 Assert (console->mpVM == NULL);
6953 vmstateChangeCallback (NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
6954 console);
6955 }
6956
6957 /*
6958 * Evaluate the final result. Note that the appropriate mMachineState value
6959 * is already set by vmstateChangeCallback() in all cases.
6960 */
6961
6962 /* leave the lock, don't need it any more */
6963 alock.leave();
6964
6965 if (SUCCEEDED(rc))
6966 {
6967 /* Notify the progress object of the success */
6968 task->mProgress->notifyComplete (S_OK);
6969 }
6970 else
6971 {
6972 /* The progress object will fetch the current error info */
6973 task->mProgress->notifyComplete (rc);
6974
6975 LogRel (("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
6976 }
6977
6978#if defined(RT_OS_WINDOWS)
6979 /* uninitialize COM */
6980 CoUninitialize();
6981#endif
6982
6983 LogFlowFuncLeave();
6984
6985 return VINF_SUCCESS;
6986}
6987
6988
6989/**
6990 * Reconfigures a VDI.
6991 *
6992 * @param pVM The VM handle.
6993 * @param lInstance The instance of the controller.
6994 * @param enmController The type of the controller.
6995 * @param hda The harddisk attachment.
6996 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
6997 * @return VBox status code.
6998 */
6999static DECLCALLBACK(int) reconfigureHardDisks(PVM pVM, ULONG lInstance,
7000 StorageControllerType_T enmController,
7001 IHardDiskAttachment *hda,
7002 HRESULT *phrc)
7003{
7004 LogFlowFunc (("pVM=%p hda=%p phrc=%p\n", pVM, hda, phrc));
7005
7006 int rc;
7007 HRESULT hrc;
7008 Bstr bstr;
7009 *phrc = S_OK;
7010#define RC_CHECK() do { if (VBOX_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7011#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7012
7013 /*
7014 * Figure out which IDE device this is.
7015 */
7016 ComPtr<IHardDisk> hardDisk;
7017 hrc = hda->COMGETTER(HardDisk)(hardDisk.asOutParam()); H();
7018 LONG lDev;
7019 hrc = hda->COMGETTER(Device)(&lDev); H();
7020 LONG lPort;
7021 hrc = hda->COMGETTER(Port)(&lPort); H();
7022
7023 int iLUN;
7024 const char *pcszDevice = NULL;
7025 bool fSCSI = false;
7026
7027 switch (enmController)
7028 {
7029 case StorageControllerType_PIIX3:
7030 case StorageControllerType_PIIX4:
7031 case StorageControllerType_ICH6:
7032 {
7033 if (lPort >= 2 || lPort < 0)
7034 {
7035 AssertMsgFailed(("invalid controller channel number: %d\n", lPort));
7036 return VERR_GENERAL_FAILURE;
7037 }
7038
7039 if (lDev >= 2 || lDev < 0)
7040 {
7041 AssertMsgFailed(("invalid controller device number: %d\n", lDev));
7042 return VERR_GENERAL_FAILURE;
7043 }
7044
7045 iLUN = 2*lPort + lDev;
7046 pcszDevice = "piix3ide";
7047 break;
7048 }
7049 case StorageControllerType_IntelAhci:
7050 {
7051 iLUN = lPort;
7052 pcszDevice = "ahci";
7053 break;
7054 }
7055 case StorageControllerType_BusLogic:
7056 {
7057 iLUN = lPort;
7058 pcszDevice = "buslogic";
7059 fSCSI = true;
7060 break;
7061 }
7062 case StorageControllerType_LsiLogic:
7063 {
7064 iLUN = lPort;
7065 pcszDevice = "lsilogicscsi";
7066 fSCSI = true;
7067 break;
7068 }
7069 default:
7070 {
7071 AssertMsgFailed(("invalid disk controller type: %d\n", enmController));
7072 return VERR_GENERAL_FAILURE;
7073 }
7074 }
7075
7076 /** @todo this should be unified with the relevant part of
7077 * Console::configConstructor to avoid inconsistencies. */
7078
7079 /*
7080 * Is there an existing LUN? If not create it.
7081 * We ASSUME that this will NEVER collide with the DVD.
7082 */
7083 PCFGMNODE pCfg;
7084 PCFGMNODE pLunL1;
7085
7086 /* SCSI has an extra driver between the device and the block driver. */
7087 if (fSCSI)
7088 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/AttachedDriver/", pcszDevice, lInstance, iLUN);
7089 else
7090 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/", pcszDevice, lInstance, iLUN);
7091
7092 if (!pLunL1)
7093 {
7094 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, lInstance);
7095 AssertReturn(pInst, VERR_INTERNAL_ERROR);
7096
7097 PCFGMNODE pLunL0;
7098 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", iLUN); RC_CHECK();
7099
7100 if (fSCSI)
7101 {
7102 rc = CFGMR3InsertString(pLunL0, "Driver", "SCSI"); RC_CHECK();
7103 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
7104
7105 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();
7106 }
7107
7108 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();
7109 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
7110 rc = CFGMR3InsertString(pCfg, "Type", "HardDisk"); RC_CHECK();
7111 rc = CFGMR3InsertInteger(pCfg, "Mountable", 0); RC_CHECK();
7112
7113 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();
7114 rc = CFGMR3InsertString(pLunL1, "Driver", "VD"); RC_CHECK();
7115 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
7116 }
7117 else
7118 {
7119#ifdef VBOX_STRICT
7120 char *pszDriver;
7121 rc = CFGMR3QueryStringAlloc(pLunL1, "Driver", &pszDriver); RC_CHECK();
7122 Assert(!strcmp(pszDriver, "VD"));
7123 MMR3HeapFree(pszDriver);
7124#endif
7125
7126 pCfg = CFGMR3GetChild(pLunL1, "Config");
7127 AssertReturn(pCfg, VERR_INTERNAL_ERROR);
7128
7129 /* Here used to be a lot of code checking if things have changed,
7130 * but that's not really worth it, as with snapshots there is always
7131 * some change, so the code was just logging useless information in
7132 * a hard to analyze form. */
7133
7134 /*
7135 * Detach the driver and replace the config node.
7136 */
7137 rc = PDMR3DeviceDetach(pVM, pcszDevice, 0, iLUN, PDMDEVATT_FLAGS_NOT_HOT_PLUG); RC_CHECK();
7138 CFGMR3RemoveNode(pCfg);
7139 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
7140 }
7141
7142 /*
7143 * Create the driver configuration.
7144 */
7145 hrc = hardDisk->COMGETTER(Location)(bstr.asOutParam()); H();
7146 LogFlowFunc (("LUN#%d: leaf location '%ls'\n", iLUN, bstr.raw()));
7147 rc = CFGMR3InsertString(pCfg, "Path", Utf8Str(bstr)); RC_CHECK();
7148 hrc = hardDisk->COMGETTER(Format)(bstr.asOutParam()); H();
7149 LogFlowFunc (("LUN#%d: leaf format '%ls'\n", iLUN, bstr.raw()));
7150 rc = CFGMR3InsertString(pCfg, "Format", Utf8Str(bstr)); RC_CHECK();
7151
7152 /* Pass all custom parameters. */
7153 bool fHostIP = true;
7154 SafeArray<BSTR> names;
7155 SafeArray<BSTR> values;
7156 hrc = hardDisk->GetProperties (NULL,
7157 ComSafeArrayAsOutParam (names),
7158 ComSafeArrayAsOutParam (values)); H();
7159
7160 if (names.size() != 0)
7161 {
7162 PCFGMNODE pVDC;
7163 rc = CFGMR3InsertNode (pCfg, "VDConfig", &pVDC); RC_CHECK();
7164 for (size_t i = 0; i < names.size(); ++ i)
7165 {
7166 if (values [i])
7167 {
7168 Utf8Str name = names [i];
7169 Utf8Str value = values [i];
7170 rc = CFGMR3InsertString (pVDC, name, value);
7171 if ( !(name.compare("HostIPStack"))
7172 && !(value.compare("0")))
7173 fHostIP = false;
7174 }
7175 }
7176 }
7177
7178 /* Create an inversed tree of parents. */
7179 ComPtr<IHardDisk> parentHardDisk = hardDisk;
7180 for (PCFGMNODE pParent = pCfg;;)
7181 {
7182 hrc = parentHardDisk->COMGETTER(Parent)(hardDisk.asOutParam()); H();
7183 if (hardDisk.isNull())
7184 break;
7185
7186 PCFGMNODE pCur;
7187 rc = CFGMR3InsertNode(pParent, "Parent", &pCur); RC_CHECK();
7188 hrc = hardDisk->COMGETTER(Location)(bstr.asOutParam()); H();
7189 rc = CFGMR3InsertString(pCur, "Path", Utf8Str(bstr)); RC_CHECK();
7190
7191 hrc = hardDisk->COMGETTER(Format)(bstr.asOutParam()); H();
7192 rc = CFGMR3InsertString(pCur, "Format", Utf8Str(bstr)); RC_CHECK();
7193
7194 /* Pass all custom parameters. */
7195 SafeArray<BSTR> names;
7196 SafeArray<BSTR> values;
7197 hrc = hardDisk->GetProperties (NULL,
7198 ComSafeArrayAsOutParam (names),
7199 ComSafeArrayAsOutParam (values));H();
7200
7201 if (names.size() != 0)
7202 {
7203 PCFGMNODE pVDC;
7204 rc = CFGMR3InsertNode (pCur, "VDConfig", &pVDC); RC_CHECK();
7205 for (size_t i = 0; i < names.size(); ++ i)
7206 {
7207 if (values [i])
7208 {
7209 Utf8Str name = names [i];
7210 Utf8Str value = values [i];
7211 rc = CFGMR3InsertString (pVDC, name, value);
7212 if ( !(name.compare("HostIPStack"))
7213 && !(value.compare("0")))
7214 fHostIP = false;
7215 }
7216 }
7217 }
7218
7219
7220 /* Custom code: put marker to not use host IP stack to driver
7221 * configuration node. Simplifies life of DrvVD a bit. */
7222 if (!fHostIP)
7223 {
7224 rc = CFGMR3InsertInteger (pCfg, "HostIPStack", 0); RC_CHECK();
7225 }
7226
7227
7228 /* next */
7229 pParent = pCur;
7230 parentHardDisk = hardDisk;
7231 }
7232
7233 CFGMR3Dump(CFGMR3GetRoot(pVM));
7234
7235 /*
7236 * Attach the new driver.
7237 */
7238 rc = PDMR3DeviceAttach(pVM, pcszDevice, 0, iLUN, NULL, PDMDEVATT_FLAGS_NOT_HOT_PLUG); RC_CHECK();
7239
7240 LogFlowFunc (("Returns success\n"));
7241 return rc;
7242}
7243
7244
7245/**
7246 * Thread for executing the saved state operation.
7247 *
7248 * @param Thread The thread handle.
7249 * @param pvUser Pointer to a VMSaveTask structure.
7250 * @return VINF_SUCCESS (ignored).
7251 *
7252 * @note Locks the Console object for writing.
7253 */
7254/*static*/
7255DECLCALLBACK (int) Console::saveStateThread (RTTHREAD Thread, void *pvUser)
7256{
7257 LogFlowFuncEnter();
7258
7259 std::auto_ptr <VMSaveTask> task (static_cast <VMSaveTask *> (pvUser));
7260 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7261
7262 Assert(task->mSavedStateFile.length());
7263 Assert(!task->mProgress.isNull());
7264
7265 const ComObjPtr<Console> &that = task->mConsole;
7266
7267 /*
7268 * Note: no need to use addCaller() to protect Console or addVMCaller() to
7269 * protect mpVM because VMSaveTask does that
7270 */
7271
7272 Utf8Str errMsg;
7273 HRESULT rc = S_OK;
7274
7275 if (task->mIsSnapshot)
7276 {
7277 Assert (!task->mServerProgress.isNull());
7278 LogFlowFunc (("Waiting until the server creates differencing VDIs...\n"));
7279
7280 rc = task->mServerProgress->WaitForCompletion (-1);
7281 if (SUCCEEDED(rc))
7282 {
7283 LONG iRc = S_OK;
7284 rc = task->mServerProgress->COMGETTER(ResultCode) (&iRc);
7285 if (SUCCEEDED(rc))
7286 rc = iRc;
7287 }
7288 }
7289
7290 if (SUCCEEDED(rc))
7291 {
7292 LogFlowFunc (("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7293
7294 int vrc = VMR3Save (that->mpVM, task->mSavedStateFile,
7295 Console::stateProgressCallback,
7296 static_cast <VMProgressTask *> (task.get()));
7297 if (VBOX_FAILURE (vrc))
7298 {
7299 errMsg = Utf8StrFmt (
7300 Console::tr ("Failed to save the machine state to '%s' (%Rrc)"),
7301 task->mSavedStateFile.raw(), vrc);
7302 rc = E_FAIL;
7303 }
7304 }
7305
7306 /* lock the console once we're going to access it */
7307 AutoWriteLock thatLock (that);
7308
7309 if (SUCCEEDED(rc))
7310 {
7311 if (task->mIsSnapshot)
7312 do
7313 {
7314 LogFlowFunc (("Reattaching new differencing hard disks...\n"));
7315
7316 com::SafeIfaceArray<IHardDiskAttachment> atts;
7317 rc = that->mMachine->
7318 COMGETTER(HardDiskAttachments) (ComSafeArrayAsOutParam (atts));
7319 if (FAILED (rc))
7320 break;
7321 for (size_t i = 0; i < atts.size(); ++ i)
7322 {
7323 PVMREQ pReq;
7324 ComPtr<IStorageController> controller;
7325 BSTR controllerName;
7326 ULONG lInstance;
7327 StorageControllerType_T enmController;
7328
7329 /*
7330 * We can't pass a storage controller object directly
7331 * (g++ complains about not being able to pass non POD types through '...')
7332 * so we have to query needed values here and pass them.
7333 */
7334 rc = atts[i]->COMGETTER(Controller)(&controllerName);
7335 if (FAILED (rc))
7336 break;
7337
7338 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7339 if (FAILED (rc))
7340 break;
7341
7342 rc = controller->COMGETTER(ControllerType)(&enmController);
7343 rc = controller->COMGETTER(Instance)(&lInstance);
7344 /*
7345 * don't leave the lock since reconfigureHardDisks isn't going
7346 * to access Console.
7347 */
7348 int vrc = VMR3ReqCall (that->mpVM, VMCPUID_ANY, &pReq, RT_INDEFINITE_WAIT,
7349 (PFNRT)reconfigureHardDisks, 5, that->mpVM, lInstance,
7350 enmController, atts [i], &rc);
7351 if (VBOX_SUCCESS (rc))
7352 rc = pReq->iStatus;
7353 VMR3ReqFree (pReq);
7354 if (FAILED (rc))
7355 break;
7356 if (VBOX_FAILURE (vrc))
7357 {
7358 errMsg = Utf8StrFmt (Console::tr ("%Rrc"), vrc);
7359 rc = E_FAIL;
7360 break;
7361 }
7362 }
7363 }
7364 while (0);
7365 }
7366
7367 /* finalize the procedure regardless of the result */
7368 if (task->mIsSnapshot)
7369 {
7370 /*
7371 * finalize the requested snapshot object.
7372 * This will reset the machine state to the state it had right
7373 * before calling mControl->BeginTakingSnapshot().
7374 */
7375 that->mControl->EndTakingSnapshot (SUCCEEDED(rc));
7376 }
7377 else
7378 {
7379 /*
7380 * finalize the requested save state procedure.
7381 * In case of success, the server will set the machine state to Saved;
7382 * in case of failure it will reset the it to the state it had right
7383 * before calling mControl->BeginSavingState().
7384 */
7385 that->mControl->EndSavingState (SUCCEEDED(rc));
7386 }
7387
7388 /* synchronize the state with the server */
7389 if (task->mIsSnapshot || FAILED (rc))
7390 {
7391 if (task->mLastMachineState == MachineState_Running)
7392 {
7393 /* restore the paused state if appropriate */
7394 that->setMachineStateLocally (MachineState_Paused);
7395 /* restore the running state if appropriate */
7396 that->Resume();
7397 }
7398 else
7399 that->setMachineStateLocally (task->mLastMachineState);
7400 }
7401 else
7402 {
7403 /*
7404 * The machine has been successfully saved, so power it down
7405 * (vmstateChangeCallback() will set state to Saved on success).
7406 * Note: we release the task's VM caller, otherwise it will
7407 * deadlock.
7408 */
7409 task->releaseVMCaller();
7410
7411 rc = that->powerDown();
7412 }
7413
7414 /* notify the progress object about operation completion */
7415 if (SUCCEEDED(rc))
7416 task->mProgress->notifyComplete (S_OK);
7417 else
7418 {
7419 if (errMsg.length())
7420 task->mProgress->notifyComplete(rc,
7421 COM_IIDOF(IConsole),
7422 Console::getComponentName(),
7423 errMsg);
7424 else
7425 task->mProgress->notifyComplete (rc);
7426 }
7427
7428 LogFlowFuncLeave();
7429 return VINF_SUCCESS;
7430}
7431
7432/**
7433 * Thread for powering down the Console.
7434 *
7435 * @param Thread The thread handle.
7436 * @param pvUser Pointer to the VMTask structure.
7437 * @return VINF_SUCCESS (ignored).
7438 *
7439 * @note Locks the Console object for writing.
7440 */
7441/*static*/
7442DECLCALLBACK (int) Console::powerDownThread (RTTHREAD Thread, void *pvUser)
7443{
7444 LogFlowFuncEnter();
7445
7446 std::auto_ptr <VMProgressTask> task (static_cast <VMProgressTask *> (pvUser));
7447 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7448
7449 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
7450
7451 const ComObjPtr<Console> &that = task->mConsole;
7452
7453 /* Note: no need to use addCaller() to protect Console because VMTask does
7454 * that */
7455
7456 /* wait until the method tat started us returns */
7457 AutoWriteLock thatLock (that);
7458
7459 /* release VM caller to avoid the powerDown() deadlock */
7460 task->releaseVMCaller();
7461
7462 that->powerDown (task->mProgress);
7463
7464 LogFlowFuncLeave();
7465 return VINF_SUCCESS;
7466}
7467
7468/**
7469 * The Main status driver instance data.
7470 */
7471typedef struct DRVMAINSTATUS
7472{
7473 /** The LED connectors. */
7474 PDMILEDCONNECTORS ILedConnectors;
7475 /** Pointer to the LED ports interface above us. */
7476 PPDMILEDPORTS pLedPorts;
7477 /** Pointer to the array of LED pointers. */
7478 PPDMLED *papLeds;
7479 /** The unit number corresponding to the first entry in the LED array. */
7480 RTUINT iFirstLUN;
7481 /** The unit number corresponding to the last entry in the LED array.
7482 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
7483 RTUINT iLastLUN;
7484} DRVMAINSTATUS, *PDRVMAINSTATUS;
7485
7486
7487/**
7488 * Notification about a unit which have been changed.
7489 *
7490 * The driver must discard any pointers to data owned by
7491 * the unit and requery it.
7492 *
7493 * @param pInterface Pointer to the interface structure containing the called function pointer.
7494 * @param iLUN The unit number.
7495 */
7496DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
7497{
7498 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
7499 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
7500 {
7501 PPDMLED pLed;
7502 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
7503 if (VBOX_FAILURE(rc))
7504 pLed = NULL;
7505 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
7506 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
7507 }
7508}
7509
7510
7511/**
7512 * Queries an interface to the driver.
7513 *
7514 * @returns Pointer to interface.
7515 * @returns NULL if the interface was not supported by the driver.
7516 * @param pInterface Pointer to this interface structure.
7517 * @param enmInterface The requested interface identification.
7518 */
7519DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
7520{
7521 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
7522 PDRVMAINSTATUS pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7523 switch (enmInterface)
7524 {
7525 case PDMINTERFACE_BASE:
7526 return &pDrvIns->IBase;
7527 case PDMINTERFACE_LED_CONNECTORS:
7528 return &pDrv->ILedConnectors;
7529 default:
7530 return NULL;
7531 }
7532}
7533
7534
7535/**
7536 * Destruct a status driver instance.
7537 *
7538 * @returns VBox status.
7539 * @param pDrvIns The driver instance data.
7540 */
7541DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
7542{
7543 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7544 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7545 if (pData->papLeds)
7546 {
7547 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
7548 while (iLed-- > 0)
7549 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL);
7550 }
7551}
7552
7553
7554/**
7555 * Construct a status driver instance.
7556 *
7557 * @returns VBox status.
7558 * @param pDrvIns The driver instance data.
7559 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
7560 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
7561 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
7562 * iInstance it's expected to be used a bit in this function.
7563 */
7564DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
7565{
7566 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7567 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7568
7569 /*
7570 * Validate configuration.
7571 */
7572 if (!CFGMR3AreValuesValid(pCfgHandle, "papLeds\0First\0Last\0"))
7573 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
7574 PPDMIBASE pBaseIgnore;
7575 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBaseIgnore);
7576 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
7577 {
7578 AssertMsgFailed(("Configuration error: Not possible to attach anything to this driver!\n"));
7579 return VERR_PDM_DRVINS_NO_ATTACH;
7580 }
7581
7582 /*
7583 * Data.
7584 */
7585 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
7586 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
7587
7588 /*
7589 * Read config.
7590 */
7591 rc = CFGMR3QueryPtr(pCfgHandle, "papLeds", (void **)&pData->papLeds);
7592 if (VBOX_FAILURE(rc))
7593 {
7594 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
7595 return rc;
7596 }
7597
7598 rc = CFGMR3QueryU32(pCfgHandle, "First", &pData->iFirstLUN);
7599 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7600 pData->iFirstLUN = 0;
7601 else if (VBOX_FAILURE(rc))
7602 {
7603 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
7604 return rc;
7605 }
7606
7607 rc = CFGMR3QueryU32(pCfgHandle, "Last", &pData->iLastLUN);
7608 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7609 pData->iLastLUN = 0;
7610 else if (VBOX_FAILURE(rc))
7611 {
7612 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
7613 return rc;
7614 }
7615 if (pData->iFirstLUN > pData->iLastLUN)
7616 {
7617 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
7618 return VERR_GENERAL_FAILURE;
7619 }
7620
7621 /*
7622 * Get the ILedPorts interface of the above driver/device and
7623 * query the LEDs we want.
7624 */
7625 pData->pLedPorts = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
7626 if (!pData->pLedPorts)
7627 {
7628 AssertMsgFailed(("Configuration error: No led ports interface above!\n"));
7629 return VERR_PDM_MISSING_INTERFACE_ABOVE;
7630 }
7631
7632 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; i++)
7633 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
7634
7635 return VINF_SUCCESS;
7636}
7637
7638
7639/**
7640 * Keyboard driver registration record.
7641 */
7642const PDMDRVREG Console::DrvStatusReg =
7643{
7644 /* u32Version */
7645 PDM_DRVREG_VERSION,
7646 /* szDriverName */
7647 "MainStatus",
7648 /* pszDescription */
7649 "Main status driver (Main as in the API).",
7650 /* fFlags */
7651 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
7652 /* fClass. */
7653 PDM_DRVREG_CLASS_STATUS,
7654 /* cMaxInstances */
7655 ~0,
7656 /* cbInstance */
7657 sizeof(DRVMAINSTATUS),
7658 /* pfnConstruct */
7659 Console::drvStatus_Construct,
7660 /* pfnDestruct */
7661 Console::drvStatus_Destruct,
7662 /* pfnIOCtl */
7663 NULL,
7664 /* pfnPowerOn */
7665 NULL,
7666 /* pfnReset */
7667 NULL,
7668 /* pfnSuspend */
7669 NULL,
7670 /* pfnResume */
7671 NULL,
7672 /* pfnDetach */
7673 NULL
7674};
7675
7676/**
7677 * Initializing the attachment type for the network adapters
7678 */
7679NetworkAttachmentType_T Console::meAttachmentType[] = {};
7680#ifdef VBOX_DYNAMIC_NET_ATTACH
7681Bstr Console::mHostInterface[];
7682Bstr Console::mInternalNetwork[];
7683Bstr Console::mNATNetwork[];
7684#endif
7685
7686/* 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