VirtualBox

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

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

pdmifs.h: another batch of _IID changes.

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

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