VirtualBox

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

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

VMM,Main,VBoxBFE: Split up VMR3Load into VMR3LoadFromFile and VMR3LoadFromStream; added VMR3Migrate.

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

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