VirtualBox

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

Last change on this file since 25574 was 25574, checked in by vboxsync, 14 years ago

Main: ExtraData option to pass RDP credentials to the guest (xTracker 4514)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use