VirtualBox

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

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

PDM: s/pCfgHandle/pCfg/g - part 2.

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