VirtualBox

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

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

Main: Enabled the live snapshot code (TakeSnapshot).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 238.7 KB
Line 
1/* $Id: ConsoleImpl.cpp 23567 2009-10-05 18:24:45Z 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 VMR3Load and VMR3Save.
6170 *
6171 * @param pVM The VM handle.
6172 * @param uPercent Completetion precentage (0-100).
6173 * @param pvUser Pointer to the VMProgressTask structure.
6174 * @return VINF_SUCCESS.
6175 */
6176/*static*/
6177DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6178{
6179 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6180 AssertReturn(task, VERR_INVALID_PARAMETER);
6181
6182 /* update the progress object */
6183 if (task->mProgress)
6184 task->mProgress->SetCurrentOperationProgress(uPercent);
6185
6186 return VINF_SUCCESS;
6187}
6188
6189/**
6190 * VM error callback function. Called by the various VM components.
6191 *
6192 * @param pVM VM handle. Can be NULL if an error occurred before
6193 * successfully creating a VM.
6194 * @param pvUser Pointer to the VMProgressTask structure.
6195 * @param rc VBox status code.
6196 * @param pszFormat Printf-like error message.
6197 * @param args Various number of arguments for the error message.
6198 *
6199 * @thread EMT, VMPowerUp...
6200 *
6201 * @note The VMProgressTask structure modified by this callback is not thread
6202 * safe.
6203 */
6204/* static */ DECLCALLBACK(void)
6205Console::setVMErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6206 const char *pszFormat, va_list args)
6207{
6208 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6209 AssertReturnVoid(task);
6210
6211 /* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */
6212 va_list va2;
6213 va_copy(va2, args); /* Have to make a copy here or GCC will break. */
6214
6215 /* append to the existing error message if any */
6216 if (task->mErrorMsg.length())
6217 task->mErrorMsg = Utf8StrFmt("%s.\n%N (%Rrc)", task->mErrorMsg.raw(),
6218 pszFormat, &va2, rc, rc);
6219 else
6220 task->mErrorMsg = Utf8StrFmt("%N (%Rrc)",
6221 pszFormat, &va2, rc, rc);
6222
6223 va_end (va2);
6224}
6225
6226/**
6227 * VM runtime error callback function.
6228 * See VMSetRuntimeError for the detailed description of parameters.
6229 *
6230 * @param pVM The VM handle.
6231 * @param pvUser The user argument.
6232 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6233 * @param pszErrorId Error ID string.
6234 * @param pszFormat Error message format string.
6235 * @param va Error message arguments.
6236 * @thread EMT.
6237 */
6238/* static */ DECLCALLBACK(void)
6239Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
6240 const char *pszErrorId,
6241 const char *pszFormat, va_list va)
6242{
6243 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6244 LogFlowFuncEnter();
6245
6246 Console *that = static_cast<Console *>(pvUser);
6247 AssertReturnVoid(that);
6248
6249 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
6250
6251 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
6252 fFatal, pszErrorId, message.raw()));
6253
6254 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
6255
6256 LogFlowFuncLeave();
6257}
6258
6259/**
6260 * Captures USB devices that match filters of the VM.
6261 * Called at VM startup.
6262 *
6263 * @param pVM The VM handle.
6264 *
6265 * @note The caller must lock this object for writing.
6266 */
6267HRESULT Console::captureUSBDevices(PVM pVM)
6268{
6269 LogFlowThisFunc(("\n"));
6270
6271 /* sanity check */
6272 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6273
6274 /* If the machine has an USB controller, ask the USB proxy service to
6275 * capture devices */
6276 PPDMIBASE pBase;
6277 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
6278 if (RT_SUCCESS(vrc))
6279 {
6280 /* leave the lock before calling Host in VBoxSVC since Host may call
6281 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6282 * produce an inter-process dead-lock otherwise. */
6283 AutoWriteLock alock(this);
6284 alock.leave();
6285
6286 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6287 ComAssertComRCRetRC(hrc);
6288 }
6289 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6290 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6291 vrc = VINF_SUCCESS;
6292 else
6293 AssertRC(vrc);
6294
6295 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
6296}
6297
6298
6299/**
6300 * Detach all USB device which are attached to the VM for the
6301 * purpose of clean up and such like.
6302 *
6303 * @note The caller must lock this object for writing.
6304 */
6305void Console::detachAllUSBDevices(bool aDone)
6306{
6307 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6308
6309 /* sanity check */
6310 AssertReturnVoid(isWriteLockOnCurrentThread());
6311
6312 mUSBDevices.clear();
6313
6314 /* leave the lock before calling Host in VBoxSVC since Host may call
6315 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6316 * produce an inter-process dead-lock otherwise. */
6317 AutoWriteLock alock(this);
6318 alock.leave();
6319
6320 mControl->DetachAllUSBDevices(aDone);
6321}
6322
6323/**
6324 * @note Locks this object for writing.
6325 */
6326void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6327{
6328 LogFlowThisFuncEnter();
6329 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
6330
6331 AutoCaller autoCaller(this);
6332 if (!autoCaller.isOk())
6333 {
6334 /* Console has been already uninitialized, deny request */
6335 AssertMsgFailed(("Console is already uninitialized\n"));
6336 LogFlowThisFunc(("Console is already uninitialized\n"));
6337 LogFlowThisFuncLeave();
6338 return;
6339 }
6340
6341 AutoWriteLock alock(this);
6342
6343 /*
6344 * Mark all existing remote USB devices as dirty.
6345 */
6346 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6347 while (it != mRemoteUSBDevices.end())
6348 {
6349 (*it)->dirty(true);
6350 ++ it;
6351 }
6352
6353 /*
6354 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
6355 */
6356 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
6357 VRDPUSBDEVICEDESC *e = pDevList;
6358
6359 /* The cbDevList condition must be checked first, because the function can
6360 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
6361 */
6362 while (cbDevList >= 2 && e->oNext)
6363 {
6364 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
6365 e->idVendor, e->idProduct,
6366 e->oProduct? (char *)e + e->oProduct: ""));
6367
6368 bool fNewDevice = true;
6369
6370 it = mRemoteUSBDevices.begin();
6371 while (it != mRemoteUSBDevices.end())
6372 {
6373 if ((*it)->devId() == e->id
6374 && (*it)->clientId() == u32ClientId)
6375 {
6376 /* The device is already in the list. */
6377 (*it)->dirty(false);
6378 fNewDevice = false;
6379 break;
6380 }
6381
6382 ++ it;
6383 }
6384
6385 if (fNewDevice)
6386 {
6387 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
6388 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
6389
6390 /* Create the device object and add the new device to list. */
6391 ComObjPtr<RemoteUSBDevice> device;
6392 device.createObject();
6393 device->init(u32ClientId, e);
6394
6395 mRemoteUSBDevices.push_back(device);
6396
6397 /* Check if the device is ok for current USB filters. */
6398 BOOL fMatched = FALSE;
6399 ULONG fMaskedIfs = 0;
6400
6401 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
6402
6403 AssertComRC(hrc);
6404
6405 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
6406
6407 if (fMatched)
6408 {
6409 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
6410
6411 /// @todo (r=dmik) warning reporting subsystem
6412
6413 if (hrc == S_OK)
6414 {
6415 LogFlowThisFunc(("Device attached\n"));
6416 device->captured(true);
6417 }
6418 }
6419 }
6420
6421 if (cbDevList < e->oNext)
6422 {
6423 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
6424 cbDevList, e->oNext));
6425 break;
6426 }
6427
6428 cbDevList -= e->oNext;
6429
6430 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
6431 }
6432
6433 /*
6434 * Remove dirty devices, that is those which are not reported by the server anymore.
6435 */
6436 for (;;)
6437 {
6438 ComObjPtr<RemoteUSBDevice> device;
6439
6440 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6441 while (it != mRemoteUSBDevices.end())
6442 {
6443 if ((*it)->dirty())
6444 {
6445 device = *it;
6446 break;
6447 }
6448
6449 ++ it;
6450 }
6451
6452 if (!device)
6453 {
6454 break;
6455 }
6456
6457 USHORT vendorId = 0;
6458 device->COMGETTER(VendorId)(&vendorId);
6459
6460 USHORT productId = 0;
6461 device->COMGETTER(ProductId)(&productId);
6462
6463 Bstr product;
6464 device->COMGETTER(Product)(product.asOutParam());
6465
6466 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
6467 vendorId, productId, product.raw()));
6468
6469 /* Detach the device from VM. */
6470 if (device->captured())
6471 {
6472 Bstr uuid;
6473 device->COMGETTER(Id)(uuid.asOutParam());
6474 onUSBDeviceDetach(uuid, NULL);
6475 }
6476
6477 /* And remove it from the list. */
6478 mRemoteUSBDevices.erase(it);
6479 }
6480
6481 LogFlowThisFuncLeave();
6482}
6483
6484/**
6485 * Thread function which starts the VM (also from saved state) and
6486 * track progress.
6487 *
6488 * @param Thread The thread id.
6489 * @param pvUser Pointer to a VMPowerUpTask structure.
6490 * @return VINF_SUCCESS (ignored).
6491 *
6492 * @note Locks the Console object for writing.
6493 */
6494/*static*/
6495DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
6496{
6497 LogFlowFuncEnter();
6498
6499 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
6500 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
6501
6502 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
6503 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
6504
6505#if defined(RT_OS_WINDOWS)
6506 {
6507 /* initialize COM */
6508 HRESULT hrc = CoInitializeEx(NULL,
6509 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
6510 COINIT_SPEED_OVER_MEMORY);
6511 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
6512 }
6513#endif
6514
6515 HRESULT rc = S_OK;
6516 int vrc = VINF_SUCCESS;
6517
6518 /* Set up a build identifier so that it can be seen from core dumps what
6519 * exact build was used to produce the core. */
6520 static char saBuildID[40];
6521 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
6522 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
6523
6524 ComObjPtr<Console> console = task->mConsole;
6525
6526 /* Note: no need to use addCaller() because VMPowerUpTask does that */
6527
6528 /* The lock is also used as a signal from the task initiator (which
6529 * releases it only after RTThreadCreate()) that we can start the job */
6530 AutoWriteLock alock(console);
6531
6532 /* sanity */
6533 Assert(console->mpVM == NULL);
6534
6535 try
6536 {
6537 /* wait for auto reset ops to complete so that we can successfully lock
6538 * the attached hard disks by calling LockMedia() below */
6539 for (VMPowerUpTask::ProgressList::const_iterator
6540 it = task->hardDiskProgresses.begin();
6541 it != task->hardDiskProgresses.end(); ++ it)
6542 {
6543 HRESULT rc2 = (*it)->WaitForCompletion(-1);
6544 AssertComRC(rc2);
6545 }
6546
6547 /* lock attached media. This method will also check their
6548 * accessibility. Note that the media will be unlocked automatically
6549 * by SessionMachine::setMachineState() when the VM is powered down. */
6550 rc = console->mControl->LockMedia();
6551 CheckComRCThrowRC(rc);
6552
6553#ifdef VBOX_WITH_VRDP
6554
6555 /* Create the VRDP server. In case of headless operation, this will
6556 * also create the framebuffer, required at VM creation.
6557 */
6558 ConsoleVRDPServer *server = console->consoleVRDPServer();
6559 Assert(server);
6560
6561 /* Does VRDP server call Console from the other thread?
6562 * Not sure (and can change), so leave the lock just in case.
6563 */
6564 alock.leave();
6565 vrc = server->Launch();
6566 alock.enter();
6567
6568 if (vrc == VERR_NET_ADDRESS_IN_USE)
6569 {
6570 Utf8Str errMsg;
6571 ULONG port = 0;
6572 console->mVRDPServer->COMGETTER(Port)(&port);
6573 errMsg = Utf8StrFmt(tr("VRDP server port %d is already in use"),
6574 port);
6575 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
6576 vrc, errMsg.raw()));
6577 }
6578 else if (RT_FAILURE(vrc))
6579 {
6580 Utf8Str errMsg;
6581 switch (vrc)
6582 {
6583 case VERR_FILE_NOT_FOUND:
6584 {
6585 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
6586 break;
6587 }
6588 default:
6589 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
6590 vrc);
6591 }
6592 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
6593 vrc, errMsg.raw()));
6594 throw setError(E_FAIL, errMsg.c_str());
6595 }
6596
6597#endif /* VBOX_WITH_VRDP */
6598
6599 ComPtr<IMachine> pMachine = console->machine();
6600 ULONG cCpus = 1;
6601 pMachine->COMGETTER(CPUCount)(&cCpus);
6602
6603 /*
6604 * Create the VM
6605 */
6606 PVM pVM;
6607 /*
6608 * leave the lock since EMT will call Console. It's safe because
6609 * mMachineState is either Starting or Restoring state here.
6610 */
6611 alock.leave();
6612
6613 vrc = VMR3Create(cCpus, task->mSetVMErrorCallback, task.get(),
6614 task->mConfigConstructor, static_cast<Console *>(console),
6615 &pVM);
6616
6617 alock.enter();
6618
6619#ifdef VBOX_WITH_VRDP
6620 /* Enable client connections to the server. */
6621 console->consoleVRDPServer()->EnableConnections();
6622#endif /* VBOX_WITH_VRDP */
6623
6624 if (RT_SUCCESS(vrc))
6625 {
6626 do
6627 {
6628 /*
6629 * Register our load/save state file handlers
6630 */
6631 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
6632 NULL, NULL, NULL,
6633 NULL, saveStateFileExec, NULL,
6634 NULL, loadStateFileExec, NULL,
6635 static_cast<Console *>(console));
6636 AssertRCBreak(vrc);
6637
6638 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
6639 AssertRC(vrc);
6640 if (RT_FAILURE(vrc))
6641 break;
6642
6643 /*
6644 * Synchronize debugger settings
6645 */
6646 MachineDebugger *machineDebugger = console->getMachineDebugger();
6647 if (machineDebugger)
6648 {
6649 machineDebugger->flushQueuedSettings();
6650 }
6651
6652 /*
6653 * Shared Folders
6654 */
6655 if (console->getVMMDev()->isShFlActive())
6656 {
6657 /* Does the code below call Console from the other thread?
6658 * Not sure, so leave the lock just in case. */
6659 alock.leave();
6660
6661 for (SharedFolderDataMap::const_iterator
6662 it = task->mSharedFolders.begin();
6663 it != task->mSharedFolders.end();
6664 ++ it)
6665 {
6666 rc = console->createSharedFolder((*it).first, (*it).second);
6667 CheckComRCBreakRC(rc);
6668 }
6669
6670 /* enter the lock again */
6671 alock.enter();
6672
6673 CheckComRCBreakRC(rc);
6674 }
6675
6676 /*
6677 * Capture USB devices.
6678 */
6679 rc = console->captureUSBDevices(pVM);
6680 CheckComRCBreakRC(rc);
6681
6682 /* leave the lock before a lengthy operation */
6683 alock.leave();
6684
6685 /* Load saved state? */
6686 if (task->mSavedStateFile.length())
6687 {
6688 LogFlowFunc(("Restoring saved state from '%s'...\n",
6689 task->mSavedStateFile.raw()));
6690
6691 vrc = VMR3Load(pVM,
6692 task->mSavedStateFile.c_str(),
6693 Console::stateProgressCallback,
6694 static_cast<VMProgressTask*>(task.get()));
6695
6696 if (RT_SUCCESS(vrc))
6697 {
6698 if (task->mStartPaused)
6699 /* done */
6700 console->setMachineState(MachineState_Paused);
6701 else
6702 {
6703 /* Start/Resume the VM execution */
6704 vrc = VMR3Resume(pVM);
6705 AssertRC(vrc);
6706 }
6707 }
6708
6709 /* Power off in case we failed loading or resuming the VM */
6710 if (RT_FAILURE(vrc))
6711 {
6712 int vrc2 = VMR3PowerOff(pVM);
6713 AssertRC(vrc2);
6714 }
6715 }
6716 else if (task->mStartPaused)
6717 /* done */
6718 console->setMachineState(MachineState_Paused);
6719 else
6720 {
6721 /* Power on the VM (i.e. start executing) */
6722 vrc = VMR3PowerOn(pVM);
6723 AssertRC(vrc);
6724 }
6725
6726 /* enter the lock again */
6727 alock.enter();
6728 }
6729 while (0);
6730
6731 /* On failure, destroy the VM */
6732 if (FAILED(rc) || RT_FAILURE(vrc))
6733 {
6734 /* preserve existing error info */
6735 ErrorInfoKeeper eik;
6736
6737 /* powerDown() will call VMR3Destroy() and do all necessary
6738 * cleanup (VRDP, USB devices) */
6739 HRESULT rc2 = console->powerDown();
6740 AssertComRC(rc2);
6741 }
6742 else
6743 {
6744 /*
6745 * Deregister the VMSetError callback. This is necessary as the
6746 * pfnVMAtError() function passed to VMR3Create() is supposed to
6747 * be sticky but our error callback isn't.
6748 */
6749 alock.leave();
6750 VMR3AtErrorDeregister(pVM, task->mSetVMErrorCallback, task.get());
6751 /** @todo register another VMSetError callback? */
6752 alock.enter();
6753 }
6754 }
6755 else
6756 {
6757 /*
6758 * If VMR3Create() failed it has released the VM memory.
6759 */
6760 console->mpVM = NULL;
6761 }
6762
6763 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
6764 {
6765 /* If VMR3Create() or one of the other calls in this function fail,
6766 * an appropriate error message has been set in task->mErrorMsg.
6767 * However since that happens via a callback, the rc status code in
6768 * this function is not updated.
6769 */
6770 if (!task->mErrorMsg.length())
6771 {
6772 /* If the error message is not set but we've got a failure,
6773 * convert the VBox status code into a meaningfulerror message.
6774 * This becomes unused once all the sources of errors set the
6775 * appropriate error message themselves.
6776 */
6777 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
6778 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
6779 vrc);
6780 }
6781
6782 /* Set the error message as the COM error.
6783 * Progress::notifyComplete() will pick it up later. */
6784 throw setError(E_FAIL, task->mErrorMsg.c_str());
6785 }
6786 }
6787 catch (HRESULT aRC) { rc = aRC; }
6788
6789 if (console->mMachineState == MachineState_Starting ||
6790 console->mMachineState == MachineState_Restoring)
6791 {
6792 /* We are still in the Starting/Restoring state. This means one of:
6793 *
6794 * 1) we failed before VMR3Create() was called;
6795 * 2) VMR3Create() failed.
6796 *
6797 * In both cases, there is no need to call powerDown(), but we still
6798 * need to go back to the PoweredOff/Saved state. Reuse
6799 * vmstateChangeCallback() for that purpose.
6800 */
6801
6802 /* preserve existing error info */
6803 ErrorInfoKeeper eik;
6804
6805 Assert(console->mpVM == NULL);
6806 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
6807 console);
6808 }
6809
6810 /*
6811 * Evaluate the final result. Note that the appropriate mMachineState value
6812 * is already set by vmstateChangeCallback() in all cases.
6813 */
6814
6815 /* leave the lock, don't need it any more */
6816 alock.leave();
6817
6818 if (SUCCEEDED(rc))
6819 {
6820 /* Notify the progress object of the success */
6821 task->mProgress->notifyComplete(S_OK);
6822 }
6823 else
6824 {
6825 /* The progress object will fetch the current error info */
6826 task->mProgress->notifyComplete(rc);
6827
6828 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
6829 }
6830
6831#if defined(RT_OS_WINDOWS)
6832 /* uninitialize COM */
6833 CoUninitialize();
6834#endif
6835
6836 LogFlowFuncLeave();
6837
6838 return VINF_SUCCESS;
6839}
6840
6841
6842/**
6843 * Reconfigures a VDI.
6844 *
6845 * @param pVM The VM handle.
6846 * @param lInstance The instance of the controller.
6847 * @param enmController The type of the controller.
6848 * @param hda The harddisk attachment.
6849 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
6850 * @return VBox status code.
6851 */
6852static DECLCALLBACK(int) reconfigureHardDisks(PVM pVM, ULONG lInstance,
6853 StorageControllerType_T enmController,
6854 IMediumAttachment *hda,
6855 HRESULT *phrc)
6856{
6857 LogFlowFunc(("pVM=%p hda=%p phrc=%p\n", pVM, hda, phrc));
6858
6859 int rc;
6860 HRESULT hrc;
6861 Bstr bstr;
6862 *phrc = S_OK;
6863#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
6864#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
6865
6866 /*
6867 * Figure out which IDE device this is.
6868 */
6869 ComPtr<IMedium> hardDisk;
6870 hrc = hda->COMGETTER(Medium)(hardDisk.asOutParam()); H();
6871 LONG lDev;
6872 hrc = hda->COMGETTER(Device)(&lDev); H();
6873 LONG lPort;
6874 hrc = hda->COMGETTER(Port)(&lPort); H();
6875
6876 int iLUN;
6877 const char *pcszDevice = NULL;
6878 bool fSCSI = false;
6879
6880 switch (enmController)
6881 {
6882 case StorageControllerType_PIIX3:
6883 case StorageControllerType_PIIX4:
6884 case StorageControllerType_ICH6:
6885 {
6886 if (lPort >= 2 || lPort < 0)
6887 {
6888 AssertMsgFailed(("invalid controller channel number: %d\n", lPort));
6889 return VERR_GENERAL_FAILURE;
6890 }
6891
6892 if (lDev >= 2 || lDev < 0)
6893 {
6894 AssertMsgFailed(("invalid controller device number: %d\n", lDev));
6895 return VERR_GENERAL_FAILURE;
6896 }
6897
6898 iLUN = 2*lPort + lDev;
6899 pcszDevice = "piix3ide";
6900 break;
6901 }
6902 case StorageControllerType_IntelAhci:
6903 {
6904 iLUN = lPort;
6905 pcszDevice = "ahci";
6906 break;
6907 }
6908 case StorageControllerType_BusLogic:
6909 {
6910 iLUN = lPort;
6911 pcszDevice = "buslogic";
6912 fSCSI = true;
6913 break;
6914 }
6915 case StorageControllerType_LsiLogic:
6916 {
6917 iLUN = lPort;
6918 pcszDevice = "lsilogicscsi";
6919 fSCSI = true;
6920 break;
6921 }
6922 default:
6923 {
6924 AssertMsgFailed(("invalid disk controller type: %d\n", enmController));
6925 return VERR_GENERAL_FAILURE;
6926 }
6927 }
6928
6929 /** @todo this should be unified with the relevant part of
6930 * Console::configConstructor to avoid inconsistencies. */
6931
6932 /*
6933 * Is there an existing LUN? If not create it.
6934 * We ASSUME that this will NEVER collide with the DVD.
6935 */
6936 PCFGMNODE pCfg;
6937 PCFGMNODE pLunL1;
6938
6939 /* SCSI has an extra driver between the device and the block driver. */
6940 if (fSCSI)
6941 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/AttachedDriver/", pcszDevice, lInstance, iLUN);
6942 else
6943 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/", pcszDevice, lInstance, iLUN);
6944
6945 if (!pLunL1)
6946 {
6947 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, lInstance);
6948 AssertReturn(pInst, VERR_INTERNAL_ERROR);
6949
6950 PCFGMNODE pLunL0;
6951 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", iLUN); RC_CHECK();
6952
6953 if (fSCSI)
6954 {
6955 rc = CFGMR3InsertString(pLunL0, "Driver", "SCSI"); RC_CHECK();
6956 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
6957
6958 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();
6959 }
6960
6961 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();
6962 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
6963 rc = CFGMR3InsertString(pCfg, "Type", "HardDisk"); RC_CHECK();
6964 rc = CFGMR3InsertInteger(pCfg, "Mountable", 0); RC_CHECK();
6965
6966 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();
6967 rc = CFGMR3InsertString(pLunL1, "Driver", "VD"); RC_CHECK();
6968 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
6969 }
6970 else
6971 {
6972#ifdef VBOX_STRICT
6973 char *pszDriver;
6974 rc = CFGMR3QueryStringAlloc(pLunL1, "Driver", &pszDriver); RC_CHECK();
6975 Assert(!strcmp(pszDriver, "VD"));
6976 MMR3HeapFree(pszDriver);
6977#endif
6978
6979 pCfg = CFGMR3GetChild(pLunL1, "Config");
6980 AssertReturn(pCfg, VERR_INTERNAL_ERROR);
6981
6982 /* Here used to be a lot of code checking if things have changed,
6983 * but that's not really worth it, as with snapshots there is always
6984 * some change, so the code was just logging useless information in
6985 * a hard to analyze form. */
6986
6987 /*
6988 * Detach the driver and replace the config node.
6989 */
6990 rc = PDMR3DeviceDetach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG); RC_CHECK();
6991 CFGMR3RemoveNode(pCfg);
6992 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
6993 }
6994
6995 /*
6996 * Create the driver configuration.
6997 */
6998 hrc = hardDisk->COMGETTER(Location)(bstr.asOutParam()); H();
6999 LogFlowFunc(("LUN#%d: leaf location '%ls'\n", iLUN, bstr.raw()));
7000 rc = CFGMR3InsertString(pCfg, "Path", Utf8Str(bstr).c_str()); RC_CHECK();
7001 hrc = hardDisk->COMGETTER(Format)(bstr.asOutParam()); H();
7002 LogFlowFunc(("LUN#%d: leaf format '%ls'\n", iLUN, bstr.raw()));
7003 rc = CFGMR3InsertString(pCfg, "Format", Utf8Str(bstr).c_str()); RC_CHECK();
7004
7005 /* Pass all custom parameters. */
7006 bool fHostIP = true;
7007 SafeArray<BSTR> names;
7008 SafeArray<BSTR> values;
7009 hrc = hardDisk->GetProperties(NULL,
7010 ComSafeArrayAsOutParam(names),
7011 ComSafeArrayAsOutParam(values)); H();
7012
7013 if (names.size() != 0)
7014 {
7015 PCFGMNODE pVDC;
7016 rc = CFGMR3InsertNode(pCfg, "VDConfig", &pVDC); RC_CHECK();
7017 for (size_t i = 0; i < names.size(); ++ i)
7018 {
7019 if (values[i])
7020 {
7021 Utf8Str name = names[i];
7022 Utf8Str value = values[i];
7023 rc = CFGMR3InsertString(pVDC, name.c_str(), value.c_str());
7024 if ( !(name.compare("HostIPStack"))
7025 && !(value.compare("0")))
7026 fHostIP = false;
7027 }
7028 }
7029 }
7030
7031 /* Create an inversed tree of parents. */
7032 ComPtr<IMedium> parentHardDisk = hardDisk;
7033 for (PCFGMNODE pParent = pCfg;;)
7034 {
7035 hrc = parentHardDisk->COMGETTER(Parent)(hardDisk.asOutParam()); H();
7036 if (hardDisk.isNull())
7037 break;
7038
7039 PCFGMNODE pCur;
7040 rc = CFGMR3InsertNode(pParent, "Parent", &pCur); RC_CHECK();
7041 hrc = hardDisk->COMGETTER(Location)(bstr.asOutParam()); H();
7042 rc = CFGMR3InsertString(pCur, "Path", Utf8Str(bstr).c_str()); RC_CHECK();
7043
7044 hrc = hardDisk->COMGETTER(Format)(bstr.asOutParam()); H();
7045 rc = CFGMR3InsertString(pCur, "Format", Utf8Str(bstr).c_str()); RC_CHECK();
7046
7047 /* Pass all custom parameters. */
7048 SafeArray<BSTR> names;
7049 SafeArray<BSTR> values;
7050 hrc = hardDisk->GetProperties(NULL,
7051 ComSafeArrayAsOutParam(names),
7052 ComSafeArrayAsOutParam(values)); H();
7053
7054 if (names.size() != 0)
7055 {
7056 PCFGMNODE pVDC;
7057 rc = CFGMR3InsertNode(pCur, "VDConfig", &pVDC); RC_CHECK();
7058 for (size_t i = 0; i < names.size(); ++ i)
7059 {
7060 if (values[i])
7061 {
7062 Utf8Str name = names[i];
7063 Utf8Str value = values[i];
7064 rc = CFGMR3InsertString(pVDC, name.c_str(), value.c_str());
7065 if ( !(name.compare("HostIPStack"))
7066 && !(value.compare("0")))
7067 fHostIP = false;
7068 }
7069 }
7070 }
7071
7072
7073 /* Custom code: put marker to not use host IP stack to driver
7074 * configuration node. Simplifies life of DrvVD a bit. */
7075 if (!fHostIP)
7076 {
7077 rc = CFGMR3InsertInteger(pCfg, "HostIPStack", 0); RC_CHECK();
7078 }
7079
7080
7081 /* next */
7082 pParent = pCur;
7083 parentHardDisk = hardDisk;
7084 }
7085
7086 CFGMR3Dump(CFGMR3GetRoot(pVM));
7087
7088 /*
7089 * Attach the new driver.
7090 */
7091 rc = PDMR3DeviceAttach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); RC_CHECK();
7092
7093 LogFlowFunc(("Returns success\n"));
7094 return rc;
7095}
7096
7097/**
7098 * Worker thread created by Console::TakeSnapshot.
7099 * @param Thread The current thread (ignored).
7100 * @param pvUser The task.
7101 * @return VINF_SUCCESS (ignored).
7102 */
7103/*static*/
7104DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7105{
7106 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7107
7108 // taking a snapshot consists of the following:
7109
7110 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7111 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7112 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7113 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7114 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7115
7116 bool fBeganTakingSnapshot = false;
7117
7118 AutoCaller autoCaller(pTask->mConsole);
7119 CheckComRCReturnRC(autoCaller.rc());
7120
7121 AutoWriteLock alock(pTask->mConsole);
7122
7123 HRESULT rc = S_OK;
7124
7125 Console *that = pTask->mConsole;
7126
7127 try
7128 {
7129 /* STEP 1 + 2:
7130 * request creating the diff images on the server and create the snapshot object
7131 * (this will set the machine state to Saving on the server to block
7132 * others from accessing this machine)
7133 */
7134 rc = pTask->mConsole->mControl->BeginTakingSnapshot(that,
7135 pTask->bstrName,
7136 pTask->bstrDescription,
7137 pTask->mProgress,
7138 pTask->fTakingSnapshotOnline,
7139 pTask->bstrSavedStateFile.asOutParam());
7140 if (FAILED(rc)) throw rc;
7141
7142 fBeganTakingSnapshot = true;
7143
7144 /*
7145 * state file is non-null only when the VM is paused
7146 * (i.e. creating a snapshot online)
7147 */
7148 ComAssertThrow( (!pTask->bstrSavedStateFile.isNull() && pTask->fTakingSnapshotOnline)
7149 || (pTask->bstrSavedStateFile.isNull() && !pTask->fTakingSnapshotOnline),
7150 rc = E_FAIL);
7151
7152 /* sync the state with the server */
7153 that->setMachineStateLocally(MachineState_Saving);
7154
7155 // STEP 3: save the VM state (if online)
7156 if (pTask->fTakingSnapshotOnline)
7157 {
7158 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7159
7160 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7161 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7162
7163 alock.leave();
7164
7165 int vrc = VMR3Save(that->mpVM,
7166 strSavedStateFile.c_str(),
7167 true /*fContinueAfterwards*/,
7168 Console::stateProgressCallback,
7169 (void*)pTask);
7170 if (RT_FAILURE(vrc))
7171 throw setError(E_FAIL,
7172 tr("Failed to save the machine state to '%s' (%Rrc)"),
7173 strSavedStateFile.c_str(), vrc);
7174
7175 alock.enter();
7176
7177 // STEP 4: reattach hard disks
7178 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7179
7180 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring hard disks")),
7181 1); // operation weight, same as computed when setting up progress object
7182
7183 com::SafeIfaceArray<IMediumAttachment> atts;
7184 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7185 if (FAILED(rc)) throw rc;
7186
7187 for (size_t i = 0;
7188 i < atts.size();
7189 ++i)
7190 {
7191 ComPtr<IStorageController> controller;
7192 BSTR controllerName;
7193 ULONG lInstance;
7194 StorageControllerType_T enmController;
7195
7196 /*
7197 * We can't pass a storage controller object directly
7198 * (g++ complains about not being able to pass non POD types through '...')
7199 * so we have to query needed values here and pass them.
7200 */
7201 rc = atts[i]->COMGETTER(Controller)(&controllerName);
7202 if (FAILED(rc))
7203 break;
7204
7205 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7206 if (FAILED(rc)) throw rc;
7207
7208 rc = controller->COMGETTER(ControllerType)(&enmController);
7209 rc = controller->COMGETTER(Instance)(&lInstance);
7210
7211 /*
7212 * don't leave the lock since reconfigureHardDisks isn't going
7213 * to access Console.
7214 */
7215 int vrc = VMR3ReqCallWait(that->mpVM,
7216 VMCPUID_ANY,
7217 (PFNRT)reconfigureHardDisks,
7218 5,
7219 that->mpVM,
7220 lInstance,
7221 enmController,
7222 atts[i],
7223 &rc);
7224 if (RT_FAILURE(vrc))
7225 throw setError(E_FAIL, Console::tr("%Rrc"), vrc);
7226 if (FAILED(rc)) throw rc;
7227 break;
7228 if (FAILED(rc))
7229 break;
7230 }
7231 }
7232
7233 /*
7234 * finalize the requested snapshot object.
7235 * This will reset the machine state to the state it had right
7236 * before calling mControl->BeginTakingSnapshot().
7237 */
7238 rc = that->mControl->EndTakingSnapshot(TRUE); // success
7239 // do not throw rc here because we can't call EndTakingSnapshot() twice
7240 }
7241 catch (HRESULT rc)
7242 {
7243 /* preserve existing error info */
7244 ErrorInfoKeeper eik;
7245
7246 if (fBeganTakingSnapshot)
7247 that->mControl->EndTakingSnapshot(FALSE); // failure
7248 }
7249
7250 pTask->mProgress->notifyComplete(rc);
7251
7252 delete pTask;
7253
7254 if (pTask->lastMachineState == MachineState_Running)
7255 {
7256 /* restore the paused state if appropriate */
7257 that->setMachineStateLocally(MachineState_Paused);
7258 /* restore the running state if appropriate */
7259 that->Resume();
7260 }
7261 else
7262 that->setMachineStateLocally(pTask->lastMachineState);
7263
7264 LogFlowFuncLeave();
7265 return VINF_SUCCESS;
7266}
7267
7268/**
7269 * Thread for executing the saved state operation.
7270 *
7271 * @param Thread The thread handle.
7272 * @param pvUser Pointer to a VMSaveTask structure.
7273 * @return VINF_SUCCESS (ignored).
7274 *
7275 * @note Locks the Console object for writing.
7276 */
7277/*static*/
7278DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
7279{
7280 LogFlowFuncEnter();
7281
7282 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
7283 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7284
7285 Assert(task->mSavedStateFile.length());
7286 Assert(!task->mProgress.isNull());
7287
7288 const ComObjPtr<Console> &that = task->mConsole;
7289 Utf8Str errMsg;
7290 HRESULT rc = S_OK;
7291
7292 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7293
7294 int vrc = VMR3Save(that->mpVM,
7295 task->mSavedStateFile.c_str(),
7296 false, /*fContinueAfterwards*/
7297 Console::stateProgressCallback,
7298 static_cast<VMProgressTask*>(task.get()));
7299 if (RT_FAILURE(vrc))
7300 {
7301 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
7302 task->mSavedStateFile.raw(), vrc);
7303 rc = E_FAIL;
7304 }
7305
7306 /* lock the console once we're going to access it */
7307 AutoWriteLock thatLock(that);
7308
7309 /*
7310 * finalize the requested save state procedure.
7311 * In case of success, the server will set the machine state to Saved;
7312 * in case of failure it will reset the it to the state it had right
7313 * before calling mControl->BeginSavingState().
7314 */
7315 that->mControl->EndSavingState(SUCCEEDED(rc));
7316
7317 /* synchronize the state with the server */
7318 if (!FAILED(rc))
7319 {
7320 /*
7321 * The machine has been successfully saved, so power it down
7322 * (vmstateChangeCallback() will set state to Saved on success).
7323 * Note: we release the task's VM caller, otherwise it will
7324 * deadlock.
7325 */
7326 task->releaseVMCaller();
7327
7328 rc = that->powerDown();
7329 }
7330
7331 /* notify the progress object about operation completion */
7332 if (SUCCEEDED(rc))
7333 task->mProgress->notifyComplete(S_OK);
7334 else
7335 {
7336 if (errMsg.length())
7337 task->mProgress->notifyComplete(rc,
7338 COM_IIDOF(IConsole),
7339 Console::getComponentName(),
7340 errMsg.c_str());
7341 else
7342 task->mProgress->notifyComplete(rc);
7343 }
7344
7345 LogFlowFuncLeave();
7346 return VINF_SUCCESS;
7347}
7348
7349/**
7350 * Thread for powering down the Console.
7351 *
7352 * @param Thread The thread handle.
7353 * @param pvUser Pointer to the VMTask structure.
7354 * @return VINF_SUCCESS (ignored).
7355 *
7356 * @note Locks the Console object for writing.
7357 */
7358/*static*/
7359DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
7360{
7361 LogFlowFuncEnter();
7362
7363 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
7364 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7365
7366 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
7367
7368 const ComObjPtr<Console> &that = task->mConsole;
7369
7370 /* Note: no need to use addCaller() to protect Console because VMTask does
7371 * that */
7372
7373 /* wait until the method tat started us returns */
7374 AutoWriteLock thatLock(that);
7375
7376 /* release VM caller to avoid the powerDown() deadlock */
7377 task->releaseVMCaller();
7378
7379 that->powerDown(task->mProgress);
7380
7381 LogFlowFuncLeave();
7382 return VINF_SUCCESS;
7383}
7384
7385/**
7386 * The Main status driver instance data.
7387 */
7388typedef struct DRVMAINSTATUS
7389{
7390 /** The LED connectors. */
7391 PDMILEDCONNECTORS ILedConnectors;
7392 /** Pointer to the LED ports interface above us. */
7393 PPDMILEDPORTS pLedPorts;
7394 /** Pointer to the array of LED pointers. */
7395 PPDMLED *papLeds;
7396 /** The unit number corresponding to the first entry in the LED array. */
7397 RTUINT iFirstLUN;
7398 /** The unit number corresponding to the last entry in the LED array.
7399 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
7400 RTUINT iLastLUN;
7401} DRVMAINSTATUS, *PDRVMAINSTATUS;
7402
7403
7404/**
7405 * Notification about a unit which have been changed.
7406 *
7407 * The driver must discard any pointers to data owned by
7408 * the unit and requery it.
7409 *
7410 * @param pInterface Pointer to the interface structure containing the called function pointer.
7411 * @param iLUN The unit number.
7412 */
7413DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
7414{
7415 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
7416 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
7417 {
7418 PPDMLED pLed;
7419 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
7420 if (RT_FAILURE(rc))
7421 pLed = NULL;
7422 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
7423 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
7424 }
7425}
7426
7427
7428/**
7429 * Queries an interface to the driver.
7430 *
7431 * @returns Pointer to interface.
7432 * @returns NULL if the interface was not supported by the driver.
7433 * @param pInterface Pointer to this interface structure.
7434 * @param enmInterface The requested interface identification.
7435 */
7436DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
7437{
7438 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
7439 PDRVMAINSTATUS pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7440 switch (enmInterface)
7441 {
7442 case PDMINTERFACE_BASE:
7443 return &pDrvIns->IBase;
7444 case PDMINTERFACE_LED_CONNECTORS:
7445 return &pDrv->ILedConnectors;
7446 default:
7447 return NULL;
7448 }
7449}
7450
7451
7452/**
7453 * Destruct a status driver instance.
7454 *
7455 * @returns VBox status.
7456 * @param pDrvIns The driver instance data.
7457 */
7458DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
7459{
7460 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7461 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7462 if (pData->papLeds)
7463 {
7464 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
7465 while (iLed-- > 0)
7466 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL);
7467 }
7468}
7469
7470
7471/**
7472 * Construct a status driver instance.
7473 *
7474 * @copydoc FNPDMDRVCONSTRUCT
7475 */
7476DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
7477{
7478 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7479 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7480
7481 /*
7482 * Validate configuration.
7483 */
7484 if (!CFGMR3AreValuesValid(pCfgHandle, "papLeds\0First\0Last\0"))
7485 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
7486 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
7487 ("Configuration error: Not possible to attach anything to this driver!\n"),
7488 VERR_PDM_DRVINS_NO_ATTACH);
7489
7490 /*
7491 * Data.
7492 */
7493 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
7494 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
7495
7496 /*
7497 * Read config.
7498 */
7499 int rc = CFGMR3QueryPtr(pCfgHandle, "papLeds", (void **)&pData->papLeds);
7500 if (RT_FAILURE(rc))
7501 {
7502 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
7503 return rc;
7504 }
7505
7506 rc = CFGMR3QueryU32(pCfgHandle, "First", &pData->iFirstLUN);
7507 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7508 pData->iFirstLUN = 0;
7509 else if (RT_FAILURE(rc))
7510 {
7511 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
7512 return rc;
7513 }
7514
7515 rc = CFGMR3QueryU32(pCfgHandle, "Last", &pData->iLastLUN);
7516 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7517 pData->iLastLUN = 0;
7518 else if (RT_FAILURE(rc))
7519 {
7520 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
7521 return rc;
7522 }
7523 if (pData->iFirstLUN > pData->iLastLUN)
7524 {
7525 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
7526 return VERR_GENERAL_FAILURE;
7527 }
7528
7529 /*
7530 * Get the ILedPorts interface of the above driver/device and
7531 * query the LEDs we want.
7532 */
7533 pData->pLedPorts = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
7534 if (!pData->pLedPorts)
7535 {
7536 AssertMsgFailed(("Configuration error: No led ports interface above!\n"));
7537 return VERR_PDM_MISSING_INTERFACE_ABOVE;
7538 }
7539
7540 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
7541 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
7542
7543 return VINF_SUCCESS;
7544}
7545
7546
7547/**
7548 * Keyboard driver registration record.
7549 */
7550const PDMDRVREG Console::DrvStatusReg =
7551{
7552 /* u32Version */
7553 PDM_DRVREG_VERSION,
7554 /* szDriverName */
7555 "MainStatus",
7556 /* pszDescription */
7557 "Main status driver (Main as in the API).",
7558 /* fFlags */
7559 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
7560 /* fClass. */
7561 PDM_DRVREG_CLASS_STATUS,
7562 /* cMaxInstances */
7563 ~0,
7564 /* cbInstance */
7565 sizeof(DRVMAINSTATUS),
7566 /* pfnConstruct */
7567 Console::drvStatus_Construct,
7568 /* pfnDestruct */
7569 Console::drvStatus_Destruct,
7570 /* pfnIOCtl */
7571 NULL,
7572 /* pfnPowerOn */
7573 NULL,
7574 /* pfnReset */
7575 NULL,
7576 /* pfnSuspend */
7577 NULL,
7578 /* pfnResume */
7579 NULL,
7580 /* pfnAttach */
7581 NULL,
7582 /* pfnDetach */
7583 NULL,
7584 /* pfnPowerOff */
7585 NULL,
7586 /* pfnSoftReset */
7587 NULL,
7588 /* u32EndVersion */
7589 PDM_DRVREG_VERSION
7590};
7591
7592/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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