VirtualBox

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

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

FreeBSD: Use the old TAP code when bridging to a TAP device and set the SharedMaxOnWire flag for a WLAN interface (contributed by Juergen Lock, thanks)

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

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