VirtualBox

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

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

ConsoleImpl.cpp: Allow changing the network attachment only when the VM is running or suspended

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