VirtualBox

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

Last change on this file since 21835 was 21828, checked in by vboxsync, 16 years ago

ConsoleImpl.cpp: r=name is only used when reviewing (someone else's) code; @todo is requires double asterix: / @todo */.

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

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