VirtualBox

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

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

ConsoleImpl,MachineImpl: Re-did the live snapshot bits.

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

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