VirtualBox

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

Last change on this file since 24323 was 24301, checked in by vboxsync, 15 years ago

Main,Frontends: Added two new running states: Teleporting and LiveSnapshotting. Also added TeleportingPausedVM. Renamed TeleportingFrom to TeleportingIn.

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