VirtualBox

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

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

FE/Qt4: only test if the guest entered the ACPI state if this information is really required

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

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