VirtualBox

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

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

API/others: Renamed IConsole::discardSavedState to IConsole::forgetSavedState, added parameter. Deleted old IConsole::powerDown, renamed IConsole::powerDownAsync to IConsole::powerDown (as promised for 2.1). Implemented perl sample code for registering a hard disk. Cleaned up constant formatting in the API docs. Updated SDK changelog. Renamed com/errorprint2.h to com/errorprint.h, added a few assertion variants. Eliminated com/errorprint_legacy.h. Adjusted all files using the affected headers and APIs. Renamed tstHeadless2 to tstHeadless.

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