VirtualBox

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

Last change on this file since 24354 was 24353, checked in by vboxsync, 15 years ago

Main,VM: Working the teleportation state. Made VMR3Teleport report back in clear text whether it suspended the VM or not.

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