VirtualBox

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

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

Main,VMM: Debugging live snapshots.

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

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