VirtualBox

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

Last change on this file since 23419 was 23419, checked in by vboxsync, 16 years ago

Main/ConsoleImpl: make DVD attachments at runtime work. floppy still untested

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

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