VirtualBox

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

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

VMM: State machine adjustments. Have only received basic testing.

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