VirtualBox

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

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

Add hotplug flag to PDMR3DeviceAttach/Detach

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