VirtualBox

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

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

Main: move libxml2 to IPRT unconditionally (remove VBOX_WITH_LIBXML2_IN_VBOXRT); move xml classes to IPRT; introduce IPRT ministring class as base for both Utf8Str and xml.cpp, with better performance; introduce some Utf8Str helpers to avoid string buffer hacks in Main code; remove std::auto_ptr<> from some headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 237.3 KB
Line 
1/* $Id: ConsoleImpl.cpp 21079 2009-06-30 15:59:22Z 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.reserve(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 Utf8Buf.jolt();
1084 if (parm[2].type != VBOX_HGCM_SVC_PARM_32BIT)
1085 return setError (E_FAIL, tr ("Internal application error"));
1086 cchBuf = parm[2].u.uint32;
1087 }
1088 if (VERR_BUFFER_OVERFLOW == vrc)
1089 return setError (E_UNEXPECTED, tr ("Temporary failure due to guest activity, please retry"));
1090
1091 /*
1092 * Finally we have to unpack the data returned by the service into the safe
1093 * arrays supplied by the caller. We start by counting the number of entries.
1094 */
1095 const char *pszBuf
1096 = reinterpret_cast<const char *>(parm[1].u.pointer.addr);
1097 unsigned cEntries = 0;
1098 /* The list is terminated by a zero-length string at the end of a set
1099 * of four strings. */
1100 for (size_t i = 0; strlen(pszBuf + i) != 0; )
1101 {
1102 /* We are counting sets of four strings. */
1103 for (unsigned j = 0; j < 4; ++j)
1104 i += strlen(pszBuf + i) + 1;
1105 ++cEntries;
1106 }
1107
1108 /*
1109 * And now we create the COM safe arrays and fill them in.
1110 */
1111 com::SafeArray <BSTR> names(cEntries);
1112 com::SafeArray <BSTR> values(cEntries);
1113 com::SafeArray <ULONG64> timestamps(cEntries);
1114 com::SafeArray <BSTR> flags(cEntries);
1115 size_t iBuf = 0;
1116 /* Rely on the service to have formated the data correctly. */
1117 for (unsigned i = 0; i < cEntries; ++i)
1118 {
1119 size_t cchName = strlen(pszBuf + iBuf);
1120 Bstr(pszBuf + iBuf).detachTo(&names[i]);
1121 iBuf += cchName + 1;
1122 size_t cchValue = strlen(pszBuf + iBuf);
1123 Bstr(pszBuf + iBuf).detachTo(&values[i]);
1124 iBuf += cchValue + 1;
1125 size_t cchTimestamp = strlen(pszBuf + iBuf);
1126 timestamps[i] = RTStrToUInt64(pszBuf + iBuf);
1127 iBuf += cchTimestamp + 1;
1128 size_t cchFlags = strlen(pszBuf + iBuf);
1129 Bstr(pszBuf + iBuf).detachTo(&flags[i]);
1130 iBuf += cchFlags + 1;
1131 }
1132 names.detachTo(ComSafeArrayOutArg (aNames));
1133 values.detachTo(ComSafeArrayOutArg (aValues));
1134 timestamps.detachTo(ComSafeArrayOutArg (aTimestamps));
1135 flags.detachTo(ComSafeArrayOutArg (aFlags));
1136 return S_OK;
1137}
1138#endif
1139
1140
1141// IConsole properties
1142/////////////////////////////////////////////////////////////////////////////
1143
1144STDMETHODIMP Console::COMGETTER(Machine) (IMachine **aMachine)
1145{
1146 CheckComArgOutPointerValid(aMachine);
1147
1148 AutoCaller autoCaller (this);
1149 CheckComRCReturnRC (autoCaller.rc());
1150
1151 /* mMachine is constant during life time, no need to lock */
1152 mMachine.queryInterfaceTo (aMachine);
1153
1154 return S_OK;
1155}
1156
1157STDMETHODIMP Console::COMGETTER(State) (MachineState_T *aMachineState)
1158{
1159 CheckComArgOutPointerValid(aMachineState);
1160
1161 AutoCaller autoCaller (this);
1162 CheckComRCReturnRC (autoCaller.rc());
1163
1164 AutoReadLock alock (this);
1165
1166 /* we return our local state (since it's always the same as on the server) */
1167 *aMachineState = mMachineState;
1168
1169 return S_OK;
1170}
1171
1172STDMETHODIMP Console::COMGETTER(Guest) (IGuest **aGuest)
1173{
1174 CheckComArgOutPointerValid(aGuest);
1175
1176 AutoCaller autoCaller (this);
1177 CheckComRCReturnRC (autoCaller.rc());
1178
1179 /* mGuest is constant during life time, no need to lock */
1180 mGuest.queryInterfaceTo (aGuest);
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Console::COMGETTER(Keyboard) (IKeyboard **aKeyboard)
1186{
1187 CheckComArgOutPointerValid(aKeyboard);
1188
1189 AutoCaller autoCaller (this);
1190 CheckComRCReturnRC (autoCaller.rc());
1191
1192 /* mKeyboard is constant during life time, no need to lock */
1193 mKeyboard.queryInterfaceTo (aKeyboard);
1194
1195 return S_OK;
1196}
1197
1198STDMETHODIMP Console::COMGETTER(Mouse) (IMouse **aMouse)
1199{
1200 CheckComArgOutPointerValid(aMouse);
1201
1202 AutoCaller autoCaller (this);
1203 CheckComRCReturnRC (autoCaller.rc());
1204
1205 /* mMouse is constant during life time, no need to lock */
1206 mMouse.queryInterfaceTo (aMouse);
1207
1208 return S_OK;
1209}
1210
1211STDMETHODIMP Console::COMGETTER(Display) (IDisplay **aDisplay)
1212{
1213 CheckComArgOutPointerValid(aDisplay);
1214
1215 AutoCaller autoCaller (this);
1216 CheckComRCReturnRC (autoCaller.rc());
1217
1218 /* mDisplay is constant during life time, no need to lock */
1219 mDisplay.queryInterfaceTo (aDisplay);
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Console::COMGETTER(Debugger) (IMachineDebugger **aDebugger)
1225{
1226 CheckComArgOutPointerValid(aDebugger);
1227
1228 AutoCaller autoCaller (this);
1229 CheckComRCReturnRC (autoCaller.rc());
1230
1231 /* we need a write lock because of the lazy mDebugger initialization*/
1232 AutoWriteLock alock (this);
1233
1234 /* check if we have to create the debugger object */
1235 if (!mDebugger)
1236 {
1237 unconst (mDebugger).createObject();
1238 mDebugger->init (this);
1239 }
1240
1241 mDebugger.queryInterfaceTo (aDebugger);
1242
1243 return S_OK;
1244}
1245
1246STDMETHODIMP Console::COMGETTER(USBDevices) (ComSafeArrayOut (IUSBDevice *, aUSBDevices))
1247{
1248 CheckComArgOutSafeArrayPointerValid(aUSBDevices);
1249
1250 AutoCaller autoCaller (this);
1251 CheckComRCReturnRC (autoCaller.rc());
1252
1253 AutoReadLock alock (this);
1254
1255 SafeIfaceArray <IUSBDevice> collection (mUSBDevices);
1256 collection.detachTo (ComSafeArrayOutArg(aUSBDevices));
1257
1258 return S_OK;
1259}
1260
1261STDMETHODIMP Console::COMGETTER(RemoteUSBDevices) (ComSafeArrayOut (IHostUSBDevice *, aRemoteUSBDevices))
1262{
1263 CheckComArgOutSafeArrayPointerValid(aRemoteUSBDevices);
1264
1265 AutoCaller autoCaller (this);
1266 CheckComRCReturnRC (autoCaller.rc());
1267
1268 AutoReadLock alock (this);
1269
1270 SafeIfaceArray <IHostUSBDevice> collection (mRemoteUSBDevices);
1271 collection.detachTo (ComSafeArrayOutArg(aRemoteUSBDevices));
1272
1273 return S_OK;
1274}
1275
1276STDMETHODIMP Console::COMGETTER(RemoteDisplayInfo) (IRemoteDisplayInfo **aRemoteDisplayInfo)
1277{
1278 CheckComArgOutPointerValid(aRemoteDisplayInfo);
1279
1280 AutoCaller autoCaller (this);
1281 CheckComRCReturnRC (autoCaller.rc());
1282
1283 /* mDisplay is constant during life time, no need to lock */
1284 mRemoteDisplayInfo.queryInterfaceTo (aRemoteDisplayInfo);
1285
1286 return S_OK;
1287}
1288
1289STDMETHODIMP
1290Console::COMGETTER(SharedFolders) (ComSafeArrayOut (ISharedFolder *, aSharedFolders))
1291{
1292 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1293
1294 AutoCaller autoCaller (this);
1295 CheckComRCReturnRC (autoCaller.rc());
1296
1297 /* loadDataFromSavedState() needs a write lock */
1298 AutoWriteLock alock (this);
1299
1300 /* Read console data stored in the saved state file (if not yet done) */
1301 HRESULT rc = loadDataFromSavedState();
1302 CheckComRCReturnRC (rc);
1303
1304 SafeIfaceArray <ISharedFolder> sf (mSharedFolders);
1305 sf.detachTo (ComSafeArrayOutArg(aSharedFolders));
1306
1307 return S_OK;
1308}
1309
1310
1311// IConsole methods
1312/////////////////////////////////////////////////////////////////////////////
1313
1314
1315STDMETHODIMP Console::PowerUp (IProgress **aProgress)
1316{
1317 return powerUp (aProgress, false /* aPaused */);
1318}
1319
1320STDMETHODIMP Console::PowerUpPaused (IProgress **aProgress)
1321{
1322 return powerUp (aProgress, true /* aPaused */);
1323}
1324
1325STDMETHODIMP Console::PowerDown (IProgress **aProgress)
1326{
1327 if (aProgress == NULL)
1328 return E_POINTER;
1329
1330 LogFlowThisFuncEnter();
1331 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));
1332
1333 AutoCaller autoCaller (this);
1334 CheckComRCReturnRC (autoCaller.rc());
1335
1336 AutoWriteLock alock (this);
1337
1338 if (!Global::IsActive (mMachineState))
1339 {
1340 /* extra nice error message for a common case */
1341 if (mMachineState == MachineState_Saved)
1342 return setError (VBOX_E_INVALID_VM_STATE,
1343 tr ("Cannot power down a saved virtual machine"));
1344 else if (mMachineState == MachineState_Stopping)
1345 return setError (VBOX_E_INVALID_VM_STATE,
1346 tr ("Virtual machine is being powered down."));
1347 else
1348 return setError(VBOX_E_INVALID_VM_STATE,
1349 tr ("Invalid machine state: %d (must be Running, Paused "
1350 "or Stuck)"),
1351 mMachineState);
1352 }
1353
1354 LogFlowThisFunc (("Initiating SHUTDOWN request...\n"));
1355
1356 /* create an IProgress object to track progress of this operation */
1357 ComObjPtr <Progress> progress;
1358 progress.createObject();
1359 progress->init (static_cast <IConsole *> (this),
1360 Bstr (tr ("Stopping virtual machine")),
1361 FALSE /* aCancelable */);
1362
1363 /* setup task object and thread to carry out the operation asynchronously */
1364 std::auto_ptr <VMProgressTask> task (
1365 new VMProgressTask (this, progress, true /* aUsesVMPtr */));
1366 AssertReturn (task->isOk(), E_FAIL);
1367
1368 int vrc = RTThreadCreate (NULL, Console::powerDownThread,
1369 (void *) task.get(), 0,
1370 RTTHREADTYPE_MAIN_WORKER, 0,
1371 "VMPowerDown");
1372 ComAssertMsgRCRet (vrc,
1373 ("Could not create VMPowerDown thread (%Rrc)", vrc), E_FAIL);
1374
1375 /* task is now owned by powerDownThread(), so release it */
1376 task.release();
1377
1378 /* go to Stopping state to forbid state-dependant operations */
1379 setMachineState (MachineState_Stopping);
1380
1381 /* pass the progress to the caller */
1382 progress.queryInterfaceTo (aProgress);
1383
1384 LogFlowThisFuncLeave();
1385
1386 return S_OK;
1387}
1388
1389STDMETHODIMP Console::Reset()
1390{
1391 LogFlowThisFuncEnter();
1392 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));
1393
1394 AutoCaller autoCaller (this);
1395 CheckComRCReturnRC (autoCaller.rc());
1396
1397 AutoWriteLock alock (this);
1398
1399 if (mMachineState != MachineState_Running)
1400 return setError (VBOX_E_INVALID_VM_STATE,
1401 tr ("Invalid machine state: %d)"), mMachineState);
1402
1403 /* protect mpVM */
1404 AutoVMCaller autoVMCaller (this);
1405 CheckComRCReturnRC (autoVMCaller.rc());
1406
1407 /* leave the lock before a VMR3* call (EMT will call us back)! */
1408 alock.leave();
1409
1410 int vrc = VMR3Reset (mpVM);
1411
1412 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :
1413 setError (VBOX_E_VM_ERROR, tr ("Could not reset the machine (%Rrc)"), vrc);
1414
1415 LogFlowThisFunc (("mMachineState=%d, rc=%08X\n", mMachineState, rc));
1416 LogFlowThisFuncLeave();
1417 return rc;
1418}
1419
1420STDMETHODIMP Console::Pause()
1421{
1422 LogFlowThisFuncEnter();
1423
1424 AutoCaller autoCaller (this);
1425 CheckComRCReturnRC (autoCaller.rc());
1426
1427 AutoWriteLock alock (this);
1428
1429 if (mMachineState != MachineState_Running)
1430 return setError (VBOX_E_INVALID_VM_STATE,
1431 tr ("Invalid machine state: %d)"), mMachineState);
1432
1433 /* protect mpVM */
1434 AutoVMCaller autoVMCaller (this);
1435 CheckComRCReturnRC (autoVMCaller.rc());
1436
1437 LogFlowThisFunc (("Sending PAUSE request...\n"));
1438
1439 /* leave the lock before a VMR3* call (EMT will call us back)! */
1440 alock.leave();
1441
1442 int vrc = VMR3Suspend (mpVM);
1443
1444 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :
1445 setError (VBOX_E_VM_ERROR,
1446 tr ("Could not suspend the machine execution (%Rrc)"), vrc);
1447
1448 LogFlowThisFunc (("rc=%08X\n", rc));
1449 LogFlowThisFuncLeave();
1450 return rc;
1451}
1452
1453STDMETHODIMP Console::Resume()
1454{
1455 LogFlowThisFuncEnter();
1456
1457 AutoCaller autoCaller (this);
1458 CheckComRCReturnRC (autoCaller.rc());
1459
1460 AutoWriteLock alock (this);
1461
1462 if (mMachineState != MachineState_Paused)
1463 return setError (VBOX_E_INVALID_VM_STATE,
1464 tr ("Cannot resume the machine as it is not paused "
1465 "(machine state: %d)"), mMachineState);
1466
1467 /* protect mpVM */
1468 AutoVMCaller autoVMCaller (this);
1469 CheckComRCReturnRC (autoVMCaller.rc());
1470
1471 LogFlowThisFunc (("Sending RESUME request...\n"));
1472
1473 /* leave the lock before a VMR3* call (EMT will call us back)! */
1474 alock.leave();
1475
1476 int vrc;
1477 if (VMR3GetState(mpVM) == VMSTATE_CREATED)
1478 vrc = VMR3PowerOn (mpVM); /* (PowerUpPaused) */
1479 else
1480 vrc = VMR3Resume (mpVM);
1481
1482 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :
1483 setError (VBOX_E_VM_ERROR,
1484 tr ("Could not resume the machine execution (%Rrc)"), vrc);
1485
1486 LogFlowThisFunc (("rc=%08X\n", rc));
1487 LogFlowThisFuncLeave();
1488 return rc;
1489}
1490
1491STDMETHODIMP Console::PowerButton()
1492{
1493 LogFlowThisFuncEnter();
1494
1495 AutoCaller autoCaller (this);
1496 CheckComRCReturnRC (autoCaller.rc());
1497
1498 AutoWriteLock alock (this);
1499
1500 if (mMachineState != MachineState_Running)
1501 return setError (VBOX_E_INVALID_VM_STATE,
1502 tr ("Invalid machine state: %d)"), mMachineState);
1503
1504 /* protect mpVM */
1505 AutoVMCaller autoVMCaller (this);
1506 CheckComRCReturnRC (autoVMCaller.rc());
1507
1508 PPDMIBASE pBase;
1509 int vrc = PDMR3QueryDeviceLun (mpVM, "acpi", 0, 0, &pBase);
1510 if (VBOX_SUCCESS (vrc))
1511 {
1512 Assert (pBase);
1513 PPDMIACPIPORT pPort =
1514 (PPDMIACPIPORT) pBase->pfnQueryInterface(pBase, PDMINTERFACE_ACPI_PORT);
1515 vrc = pPort ? pPort->pfnPowerButtonPress(pPort) : VERR_INVALID_POINTER;
1516 }
1517
1518 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :
1519 setError (VBOX_E_PDM_ERROR,
1520 tr ("Controlled power off failed (%Rrc)"), vrc);
1521
1522 LogFlowThisFunc (("rc=%08X\n", rc));
1523 LogFlowThisFuncLeave();
1524 return rc;
1525}
1526
1527STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
1528{
1529 LogFlowThisFuncEnter();
1530
1531 CheckComArgOutPointerValid(aHandled);
1532
1533 *aHandled = FALSE;
1534
1535 AutoCaller autoCaller (this);
1536
1537 AutoWriteLock alock (this);
1538
1539 if (mMachineState != MachineState_Running)
1540 return setError (VBOX_E_INVALID_VM_STATE,
1541 tr ("Invalid machine state: %d)"), mMachineState);
1542
1543 /* protect mpVM */
1544 AutoVMCaller autoVMCaller (this);
1545 CheckComRCReturnRC (autoVMCaller.rc());
1546
1547 PPDMIBASE pBase;
1548 int vrc = PDMR3QueryDeviceLun (mpVM, "acpi", 0, 0, &pBase);
1549 bool handled = false;
1550 if (VBOX_SUCCESS (vrc))
1551 {
1552 Assert (pBase);
1553 PPDMIACPIPORT pPort =
1554 (PPDMIACPIPORT) pBase->pfnQueryInterface(pBase, PDMINTERFACE_ACPI_PORT);
1555 vrc = pPort ? pPort->pfnGetPowerButtonHandled(pPort, &handled) : VERR_INVALID_POINTER;
1556 }
1557
1558 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :
1559 setError (VBOX_E_PDM_ERROR,
1560 tr ("Checking if the ACPI Power Button event was handled by the "
1561 "guest OS failed (%Rrc)"), vrc);
1562
1563 *aHandled = handled;
1564
1565 LogFlowThisFunc (("rc=%08X\n", rc));
1566 LogFlowThisFuncLeave();
1567 return rc;
1568}
1569
1570STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
1571{
1572 LogFlowThisFuncEnter();
1573
1574 CheckComArgOutPointerValid(aEntered);
1575
1576 *aEntered = FALSE;
1577
1578 AutoCaller autoCaller (this);
1579
1580 AutoWriteLock alock (this);
1581
1582 if (mMachineState != MachineState_Running)
1583 return setError (VBOX_E_INVALID_VM_STATE,
1584 tr ("Invalid machine state %d when checking if the guest entered "
1585 "the ACPI mode)"), mMachineState);
1586
1587 /* protect mpVM */
1588 AutoVMCaller autoVMCaller (this);
1589 CheckComRCReturnRC (autoVMCaller.rc());
1590
1591 PPDMIBASE pBase;
1592 int vrc = PDMR3QueryDeviceLun (mpVM, "acpi", 0, 0, &pBase);
1593 bool entered = false;
1594 if (RT_SUCCESS (vrc))
1595 {
1596 Assert (pBase);
1597 PPDMIACPIPORT pPort =
1598 (PPDMIACPIPORT) pBase->pfnQueryInterface(pBase, PDMINTERFACE_ACPI_PORT);
1599 vrc = pPort ? pPort->pfnGetGuestEnteredACPIMode(pPort, &entered) : VERR_INVALID_POINTER;
1600 }
1601
1602 *aEntered = RT_SUCCESS (vrc) ? entered : false;
1603
1604 LogFlowThisFuncLeave();
1605 return S_OK;
1606}
1607
1608STDMETHODIMP Console::SleepButton()
1609{
1610 LogFlowThisFuncEnter();
1611
1612 AutoCaller autoCaller (this);
1613 CheckComRCReturnRC (autoCaller.rc());
1614
1615 AutoWriteLock alock (this);
1616
1617 if (mMachineState != MachineState_Running)
1618 return setError (VBOX_E_INVALID_VM_STATE,
1619 tr ("Invalid machine state: %d)"), mMachineState);
1620
1621 /* protect mpVM */
1622 AutoVMCaller autoVMCaller (this);
1623 CheckComRCReturnRC (autoVMCaller.rc());
1624
1625 PPDMIBASE pBase;
1626 int vrc = PDMR3QueryDeviceLun (mpVM, "acpi", 0, 0, &pBase);
1627 if (VBOX_SUCCESS (vrc))
1628 {
1629 Assert (pBase);
1630 PPDMIACPIPORT pPort =
1631 (PPDMIACPIPORT) pBase->pfnQueryInterface(pBase, PDMINTERFACE_ACPI_PORT);
1632 vrc = pPort ? pPort->pfnSleepButtonPress(pPort) : VERR_INVALID_POINTER;
1633 }
1634
1635 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :
1636 setError (VBOX_E_PDM_ERROR,
1637 tr ("Sending sleep button event failed (%Rrc)"), vrc);
1638
1639 LogFlowThisFunc (("rc=%08X\n", rc));
1640 LogFlowThisFuncLeave();
1641 return rc;
1642}
1643
1644STDMETHODIMP Console::SaveState (IProgress **aProgress)
1645{
1646 LogFlowThisFuncEnter();
1647 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));
1648
1649 CheckComArgOutPointerValid(aProgress);
1650
1651 AutoCaller autoCaller (this);
1652 CheckComRCReturnRC (autoCaller.rc());
1653
1654 AutoWriteLock alock (this);
1655
1656 if (mMachineState != MachineState_Running &&
1657 mMachineState != MachineState_Paused)
1658 {
1659 return setError (VBOX_E_INVALID_VM_STATE,
1660 tr ("Cannot save the execution state as the machine "
1661 "is not running or paused (machine state: %d)"), mMachineState);
1662 }
1663
1664 /* memorize the current machine state */
1665 MachineState_T lastMachineState = mMachineState;
1666
1667 if (mMachineState == MachineState_Running)
1668 {
1669 HRESULT rc = Pause();
1670 CheckComRCReturnRC (rc);
1671 }
1672
1673 HRESULT rc = S_OK;
1674
1675 /* create a progress object to track operation completion */
1676 ComObjPtr <Progress> progress;
1677 progress.createObject();
1678 progress->init (static_cast <IConsole *> (this),
1679 Bstr (tr ("Saving the execution state of the virtual machine")),
1680 FALSE /* aCancelable */);
1681
1682 bool beganSavingState = false;
1683 bool taskCreationFailed = false;
1684
1685 do
1686 {
1687 /* create a task object early to ensure mpVM protection is successful */
1688 std::auto_ptr <VMSaveTask> task (new VMSaveTask (this, progress));
1689 rc = task->rc();
1690 /*
1691 * If we fail here it means a PowerDown() call happened on another
1692 * thread while we were doing Pause() (which leaves the Console lock).
1693 * We assign PowerDown() a higher precedence than SaveState(),
1694 * therefore just return the error to the caller.
1695 */
1696 if (FAILED (rc))
1697 {
1698 taskCreationFailed = true;
1699 break;
1700 }
1701
1702 Bstr stateFilePath;
1703
1704 /*
1705 * request a saved state file path from the server
1706 * (this will set the machine state to Saving on the server to block
1707 * others from accessing this machine)
1708 */
1709 rc = mControl->BeginSavingState (progress, stateFilePath.asOutParam());
1710 CheckComRCBreakRC (rc);
1711
1712 beganSavingState = true;
1713
1714 /* sync the state with the server */
1715 setMachineStateLocally (MachineState_Saving);
1716
1717 /* ensure the directory for the saved state file exists */
1718 {
1719 Utf8Str dir = stateFilePath;
1720 dir.stripFilename();
1721 if (!RTDirExists(dir))
1722 {
1723 int vrc = RTDirCreateFullPath (dir, 0777);
1724 if (VBOX_FAILURE (vrc))
1725 {
1726 rc = setError (VBOX_E_FILE_ERROR,
1727 tr ("Could not create a directory '%s' to save the state to (%Rrc)"),
1728 dir.raw(), vrc);
1729 break;
1730 }
1731 }
1732 }
1733
1734 /* setup task object and thread to carry out the operation asynchronously */
1735 task->mIsSnapshot = false;
1736 task->mSavedStateFile = stateFilePath;
1737 /* set the state the operation thread will restore when it is finished */
1738 task->mLastMachineState = lastMachineState;
1739
1740 /* create a thread to wait until the VM state is saved */
1741 int vrc = RTThreadCreate (NULL, Console::saveStateThread, (void *) task.get(),
1742 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
1743
1744 ComAssertMsgRCBreak (vrc, ("Could not create VMSave thread (%Rrc)", vrc),
1745 rc = E_FAIL);
1746
1747 /* task is now owned by saveStateThread(), so release it */
1748 task.release();
1749
1750 /* return the progress to the caller */
1751 progress.queryInterfaceTo (aProgress);
1752 }
1753 while (0);
1754
1755 if (FAILED (rc) && !taskCreationFailed)
1756 {
1757 /* preserve existing error info */
1758 ErrorInfoKeeper eik;
1759
1760 if (beganSavingState)
1761 {
1762 /*
1763 * cancel the requested save state procedure.
1764 * This will reset the machine state to the state it had right
1765 * before calling mControl->BeginSavingState().
1766 */
1767 mControl->EndSavingState (FALSE);
1768 }
1769
1770 if (lastMachineState == MachineState_Running)
1771 {
1772 /* restore the paused state if appropriate */
1773 setMachineStateLocally (MachineState_Paused);
1774 /* restore the running state if appropriate */
1775 Resume();
1776 }
1777 else
1778 setMachineStateLocally (lastMachineState);
1779 }
1780
1781 LogFlowThisFunc (("rc=%08X\n", rc));
1782 LogFlowThisFuncLeave();
1783 return rc;
1784}
1785
1786STDMETHODIMP Console::AdoptSavedState (IN_BSTR aSavedStateFile)
1787{
1788 CheckComArgNotNull(aSavedStateFile);
1789
1790 AutoCaller autoCaller (this);
1791 CheckComRCReturnRC (autoCaller.rc());
1792
1793 AutoWriteLock alock (this);
1794
1795 if (mMachineState != MachineState_PoweredOff &&
1796 mMachineState != MachineState_Aborted)
1797 return setError (VBOX_E_INVALID_VM_STATE,
1798 tr ("Cannot adopt the saved machine state as the machine is "
1799 "not in Powered Off or Aborted state (machine state: %d)"),
1800 mMachineState);
1801
1802 return mControl->AdoptSavedState (aSavedStateFile);
1803}
1804
1805STDMETHODIMP Console::ForgetSavedState(BOOL aRemove)
1806{
1807 AutoCaller autoCaller (this);
1808 CheckComRCReturnRC (autoCaller.rc());
1809
1810 AutoWriteLock alock (this);
1811
1812 if (mMachineState != MachineState_Saved)
1813 return setError (VBOX_E_INVALID_VM_STATE,
1814 tr ("Cannot discard the machine state as the machine is "
1815 "not in the saved state (machine state: %d)"),
1816 mMachineState);
1817
1818 HRESULT rc = S_OK;
1819
1820 rc = mControl->SetRemoveSavedState(aRemove);
1821 CheckComRCReturnRC (rc);
1822
1823 /*
1824 * Saved -> PoweredOff transition will be detected in the SessionMachine
1825 * and properly handled.
1826 */
1827 rc = setMachineState (MachineState_PoweredOff);
1828
1829 return rc;
1830}
1831
1832/** read the value of a LEd. */
1833inline uint32_t readAndClearLed(PPDMLED pLed)
1834{
1835 if (!pLed)
1836 return 0;
1837 uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;
1838 pLed->Asserted.u32 = 0;
1839 return u32;
1840}
1841
1842STDMETHODIMP Console::GetDeviceActivity (DeviceType_T aDeviceType,
1843 DeviceActivity_T *aDeviceActivity)
1844{
1845 CheckComArgNotNull(aDeviceActivity);
1846
1847 AutoCaller autoCaller (this);
1848 CheckComRCReturnRC (autoCaller.rc());
1849
1850 /*
1851 * Note: we don't lock the console object here because
1852 * readAndClearLed() should be thread safe.
1853 */
1854
1855 /* Get LED array to read */
1856 PDMLEDCORE SumLed = {0};
1857 switch (aDeviceType)
1858 {
1859 case DeviceType_Floppy:
1860 {
1861 for (unsigned i = 0; i < RT_ELEMENTS(mapFDLeds); i++)
1862 SumLed.u32 |= readAndClearLed(mapFDLeds[i]);
1863 break;
1864 }
1865
1866 case DeviceType_DVD:
1867 {
1868 SumLed.u32 |= readAndClearLed(mapIDELeds[2]);
1869 break;
1870 }
1871
1872 case DeviceType_HardDisk:
1873 {
1874 SumLed.u32 |= readAndClearLed(mapIDELeds[0]);
1875 SumLed.u32 |= readAndClearLed(mapIDELeds[1]);
1876 SumLed.u32 |= readAndClearLed(mapIDELeds[3]);
1877 for (unsigned i = 0; i < RT_ELEMENTS(mapSATALeds); i++)
1878 SumLed.u32 |= readAndClearLed(mapSATALeds[i]);
1879 for (unsigned i = 0; i < RT_ELEMENTS(mapSCSILeds); i++)
1880 SumLed.u32 |= readAndClearLed(mapSCSILeds[i]);
1881 break;
1882 }
1883
1884 case DeviceType_Network:
1885 {
1886 for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); i++)
1887 SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);
1888 break;
1889 }
1890
1891 case DeviceType_USB:
1892 {
1893 for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); i++)
1894 SumLed.u32 |= readAndClearLed(mapUSBLed[i]);
1895 break;
1896 }
1897
1898 case DeviceType_SharedFolder:
1899 {
1900 SumLed.u32 |= readAndClearLed(mapSharedFolderLed);
1901 break;
1902 }
1903
1904 default:
1905 return setError (E_INVALIDARG,
1906 tr ("Invalid device type: %d"), aDeviceType);
1907 }
1908
1909 /* Compose the result */
1910 switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))
1911 {
1912 case 0:
1913 *aDeviceActivity = DeviceActivity_Idle;
1914 break;
1915 case PDMLED_READING:
1916 *aDeviceActivity = DeviceActivity_Reading;
1917 break;
1918 case PDMLED_WRITING:
1919 case PDMLED_READING | PDMLED_WRITING:
1920 *aDeviceActivity = DeviceActivity_Writing;
1921 break;
1922 }
1923
1924 return S_OK;
1925}
1926
1927STDMETHODIMP Console::AttachUSBDevice (IN_BSTR aId)
1928{
1929#ifdef VBOX_WITH_USB
1930 AutoCaller autoCaller (this);
1931 CheckComRCReturnRC (autoCaller.rc());
1932
1933 AutoWriteLock alock (this);
1934
1935 if (mMachineState != MachineState_Running &&
1936 mMachineState != MachineState_Paused)
1937 return setError (VBOX_E_INVALID_VM_STATE,
1938 tr ("Cannot attach a USB device to the machine which is not "
1939 "running or paused (machine state: %d)"), mMachineState);
1940
1941 /* protect mpVM */
1942 AutoVMCaller autoVMCaller (this);
1943 CheckComRCReturnRC (autoVMCaller.rc());
1944
1945 /* Don't proceed unless we've found the usb controller. */
1946 PPDMIBASE pBase = NULL;
1947 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);
1948 if (VBOX_FAILURE (vrc))
1949 return setError (VBOX_E_PDM_ERROR,
1950 tr ("The virtual machine does not have a USB controller"));
1951
1952 /* leave the lock because the USB Proxy service may call us back
1953 * (via onUSBDeviceAttach()) */
1954 alock.leave();
1955
1956 /* Request the device capture */
1957 HRESULT rc = mControl->CaptureUSBDevice (aId);
1958 CheckComRCReturnRC (rc);
1959
1960 return rc;
1961
1962#else /* !VBOX_WITH_USB */
1963 return setError (VBOX_E_PDM_ERROR,
1964 tr ("The virtual machine does not have a USB controller"));
1965#endif /* !VBOX_WITH_USB */
1966}
1967
1968STDMETHODIMP Console::DetachUSBDevice (IN_BSTR aId, IUSBDevice **aDevice)
1969{
1970#ifdef VBOX_WITH_USB
1971 CheckComArgOutPointerValid(aDevice);
1972
1973 AutoCaller autoCaller (this);
1974 CheckComRCReturnRC (autoCaller.rc());
1975
1976 AutoWriteLock alock (this);
1977
1978 /* Find it. */
1979 ComObjPtr <OUSBDevice> device;
1980 USBDeviceList::iterator it = mUSBDevices.begin();
1981 Guid uuid(aId);
1982 while (it != mUSBDevices.end())
1983 {
1984 if ((*it)->id() == uuid)
1985 {
1986 device = *it;
1987 break;
1988 }
1989 ++ it;
1990 }
1991
1992 if (!device)
1993 return setError (E_INVALIDARG,
1994 tr ("USB device with UUID {%RTuuid} is not attached to this machine"),
1995 Guid (aId).raw());
1996
1997 /*
1998 * Inform the USB device and USB proxy about what's cooking.
1999 */
2000 alock.leave();
2001 HRESULT rc2 = mControl->DetachUSBDevice (aId, false /* aDone */);
2002 if (FAILED (rc2))
2003 return rc2;
2004 alock.enter();
2005
2006 /* Request the PDM to detach the USB device. */
2007 HRESULT rc = detachUSBDevice (it);
2008
2009 if (SUCCEEDED (rc))
2010 {
2011 /* leave the lock since we don't need it any more (note though that
2012 * the USB Proxy service must not call us back here) */
2013 alock.leave();
2014
2015 /* Request the device release. Even if it fails, the device will
2016 * remain as held by proxy, which is OK for us (the VM process). */
2017 rc = mControl->DetachUSBDevice (aId, true /* aDone */);
2018 }
2019
2020 return rc;
2021
2022
2023#else /* !VBOX_WITH_USB */
2024 return setError (VBOX_E_PDM_ERROR,
2025 tr ("The virtual machine does not have a USB controller"));
2026#endif /* !VBOX_WITH_USB */
2027}
2028
2029STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDevice)
2030{
2031#ifdef VBOX_WITH_USB
2032 CheckComArgNotNull(aAddress);
2033 CheckComArgOutPointerValid(aDevice);
2034
2035 *aDevice = NULL;
2036
2037 SafeIfaceArray <IUSBDevice> devsvec;
2038 HRESULT rc = COMGETTER(USBDevices) (ComSafeArrayAsOutParam(devsvec));
2039 CheckComRCReturnRC (rc);
2040
2041 for (size_t i = 0; i < devsvec.size(); ++i)
2042 {
2043 Bstr address;
2044 rc = devsvec[i]->COMGETTER(Address) (address.asOutParam());
2045 CheckComRCReturnRC (rc);
2046 if (address == aAddress)
2047 {
2048 ComObjPtr<OUSBDevice> found;
2049 found.createObject();
2050 found->init (devsvec[i]);
2051 return found.queryInterfaceTo (aDevice);
2052 }
2053 }
2054
2055 return setErrorNoLog (VBOX_E_OBJECT_NOT_FOUND, tr (
2056 "Could not find a USB device with address '%ls'"),
2057 aAddress);
2058
2059#else /* !VBOX_WITH_USB */
2060 return E_NOTIMPL;
2061#endif /* !VBOX_WITH_USB */
2062}
2063
2064STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
2065{
2066#ifdef VBOX_WITH_USB
2067 CheckComArgExpr(aId, Guid (aId).isEmpty() == false);
2068 CheckComArgOutPointerValid(aDevice);
2069
2070 *aDevice = NULL;
2071
2072 SafeIfaceArray <IUSBDevice> devsvec;
2073 HRESULT rc = COMGETTER(USBDevices) (ComSafeArrayAsOutParam(devsvec));
2074 CheckComRCReturnRC (rc);
2075
2076 for (size_t i = 0; i < devsvec.size(); ++i)
2077 {
2078 Bstr id;
2079 rc = devsvec[i]->COMGETTER(Id) (id.asOutParam());
2080 CheckComRCReturnRC (rc);
2081 if (id == aId)
2082 {
2083 ComObjPtr<OUSBDevice> found;
2084 found.createObject();
2085 found->init(devsvec[i]);
2086 return found.queryInterfaceTo (aDevice);
2087 }
2088 }
2089
2090 return setErrorNoLog (VBOX_E_OBJECT_NOT_FOUND, tr (
2091 "Could not find a USB device with uuid {%RTuuid}"),
2092 Guid (aId).raw());
2093
2094#else /* !VBOX_WITH_USB */
2095 return E_NOTIMPL;
2096#endif /* !VBOX_WITH_USB */
2097}
2098
2099STDMETHODIMP
2100Console::CreateSharedFolder (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
2101{
2102 CheckComArgNotNull(aName);
2103 CheckComArgNotNull(aHostPath);
2104
2105 AutoCaller autoCaller (this);
2106 CheckComRCReturnRC (autoCaller.rc());
2107
2108 AutoWriteLock alock (this);
2109
2110 /// @todo see @todo in AttachUSBDevice() about the Paused state
2111 if (mMachineState == MachineState_Saved)
2112 return setError (VBOX_E_INVALID_VM_STATE,
2113 tr ("Cannot create a transient shared folder on the "
2114 "machine in the saved state"));
2115 if (mMachineState > MachineState_Paused)
2116 return setError (VBOX_E_INVALID_VM_STATE,
2117 tr ("Cannot create a transient shared folder on the "
2118 "machine while it is changing the state (machine state: %d)"),
2119 mMachineState);
2120
2121 ComObjPtr <SharedFolder> sharedFolder;
2122 HRESULT rc = findSharedFolder (aName, sharedFolder, false /* aSetError */);
2123 if (SUCCEEDED (rc))
2124 return setError (VBOX_E_FILE_ERROR,
2125 tr ("Shared folder named '%ls' already exists"), aName);
2126
2127 sharedFolder.createObject();
2128 rc = sharedFolder->init (this, aName, aHostPath, aWritable);
2129 CheckComRCReturnRC (rc);
2130
2131 /* protect mpVM (if not NULL) */
2132 AutoVMCallerQuietWeak autoVMCaller (this);
2133
2134 if (mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive())
2135 {
2136 /* If the VM is online and supports shared folders, share this folder
2137 * under the specified name. */
2138
2139 /* first, remove the machine or the global folder if there is any */
2140 SharedFolderDataMap::const_iterator it;
2141 if (findOtherSharedFolder (aName, it))
2142 {
2143 rc = removeSharedFolder (aName);
2144 CheckComRCReturnRC (rc);
2145 }
2146
2147 /* second, create the given folder */
2148 rc = createSharedFolder (aName, SharedFolderData (aHostPath, aWritable));
2149 CheckComRCReturnRC (rc);
2150 }
2151
2152 mSharedFolders.insert (std::make_pair (aName, sharedFolder));
2153
2154 /* notify console callbacks after the folder is added to the list */
2155 {
2156 CallbackList::iterator it = mCallbacks.begin();
2157 while (it != mCallbacks.end())
2158 (*it++)->OnSharedFolderChange (Scope_Session);
2159 }
2160
2161 return rc;
2162}
2163
2164STDMETHODIMP Console::RemoveSharedFolder (IN_BSTR aName)
2165{
2166 CheckComArgNotNull(aName);
2167
2168 AutoCaller autoCaller (this);
2169 CheckComRCReturnRC (autoCaller.rc());
2170
2171 AutoWriteLock alock (this);
2172
2173 /// @todo see @todo in AttachUSBDevice() about the Paused state
2174 if (mMachineState == MachineState_Saved)
2175 return setError (VBOX_E_INVALID_VM_STATE,
2176 tr ("Cannot remove a transient shared folder from the "
2177 "machine in the saved state"));
2178 if (mMachineState > MachineState_Paused)
2179 return setError (VBOX_E_INVALID_VM_STATE,
2180 tr ("Cannot remove a transient shared folder from the "
2181 "machine while it is changing the state (machine state: %d)"),
2182 mMachineState);
2183
2184 ComObjPtr <SharedFolder> sharedFolder;
2185 HRESULT rc = findSharedFolder (aName, sharedFolder, true /* aSetError */);
2186 CheckComRCReturnRC (rc);
2187
2188 /* protect mpVM (if not NULL) */
2189 AutoVMCallerQuietWeak autoVMCaller (this);
2190
2191 if (mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive())
2192 {
2193 /* if the VM is online and supports shared folders, UNshare this
2194 * folder. */
2195
2196 /* first, remove the given folder */
2197 rc = removeSharedFolder (aName);
2198 CheckComRCReturnRC (rc);
2199
2200 /* first, remove the machine or the global folder if there is any */
2201 SharedFolderDataMap::const_iterator it;
2202 if (findOtherSharedFolder (aName, it))
2203 {
2204 rc = createSharedFolder (aName, it->second);
2205 /* don't check rc here because we need to remove the console
2206 * folder from the collection even on failure */
2207 }
2208 }
2209
2210 mSharedFolders.erase (aName);
2211
2212 /* notify console callbacks after the folder is removed to the list */
2213 {
2214 CallbackList::iterator it = mCallbacks.begin();
2215 while (it != mCallbacks.end())
2216 (*it++)->OnSharedFolderChange (Scope_Session);
2217 }
2218
2219 return rc;
2220}
2221
2222STDMETHODIMP Console::TakeSnapshot (IN_BSTR aName, IN_BSTR aDescription,
2223 IProgress **aProgress)
2224{
2225 LogFlowThisFuncEnter();
2226 LogFlowThisFunc (("aName='%ls' mMachineState=%08X\n", aName, mMachineState));
2227
2228 CheckComArgNotNull(aName);
2229 CheckComArgOutPointerValid(aProgress);
2230
2231 AutoCaller autoCaller (this);
2232 CheckComRCReturnRC (autoCaller.rc());
2233
2234 AutoWriteLock alock (this);
2235
2236 if (Global::IsTransient (mMachineState))
2237 {
2238 return setError (VBOX_E_INVALID_VM_STATE,
2239 tr ("Cannot take a snapshot of the machine "
2240 "while it is changing the state (machine state: %d)"),
2241 mMachineState);
2242 }
2243
2244 /* memorize the current machine state */
2245 MachineState_T lastMachineState = mMachineState;
2246
2247 if (mMachineState == MachineState_Running)
2248 {
2249 HRESULT rc = Pause();
2250 CheckComRCReturnRC (rc);
2251 }
2252
2253 HRESULT rc = S_OK;
2254
2255 bool takingSnapshotOnline = mMachineState == MachineState_Paused;
2256
2257 /*
2258 * create a descriptionless VM-side progress object
2259 * (only when creating a snapshot online)
2260 */
2261 ComObjPtr <Progress> saveProgress;
2262 if (takingSnapshotOnline)
2263 {
2264 saveProgress.createObject();
2265 rc = saveProgress->init (FALSE, 1, Bstr (tr ("Saving the execution state")));
2266 AssertComRCReturn (rc, rc);
2267 }
2268
2269 bool beganTakingSnapshot = false;
2270 bool taskCreationFailed = false;
2271
2272 do
2273 {
2274 /* create a task object early to ensure mpVM protection is successful */
2275 std::auto_ptr <VMSaveTask> task;
2276 if (takingSnapshotOnline)
2277 {
2278 task.reset (new VMSaveTask (this, saveProgress));
2279 rc = task->rc();
2280 /*
2281 * If we fail here it means a PowerDown() call happened on another
2282 * thread while we were doing Pause() (which leaves the Console lock).
2283 * We assign PowerDown() a higher precedence than TakeSnapshot(),
2284 * therefore just return the error to the caller.
2285 */
2286 if (FAILED (rc))
2287 {
2288 taskCreationFailed = true;
2289 break;
2290 }
2291 }
2292
2293 Bstr stateFilePath;
2294 ComPtr <IProgress> serverProgress;
2295
2296 /*
2297 * request taking a new snapshot object on the server
2298 * (this will set the machine state to Saving on the server to block
2299 * others from accessing this machine)
2300 */
2301 rc = mControl->BeginTakingSnapshot (this, aName, aDescription,
2302 saveProgress, stateFilePath.asOutParam(),
2303 serverProgress.asOutParam());
2304 if (FAILED (rc))
2305 break;
2306
2307 /*
2308 * state file is non-null only when the VM is paused
2309 * (i.e. creating a snapshot online)
2310 */
2311 ComAssertBreak (
2312 (!stateFilePath.isNull() && takingSnapshotOnline) ||
2313 (stateFilePath.isNull() && !takingSnapshotOnline),
2314 rc = E_FAIL);
2315
2316 beganTakingSnapshot = true;
2317
2318 /* sync the state with the server */
2319 setMachineStateLocally (MachineState_Saving);
2320
2321 /*
2322 * create a combined VM-side progress object and start the save task
2323 * (only when creating a snapshot online)
2324 */
2325 ComObjPtr <CombinedProgress> combinedProgress;
2326 if (takingSnapshotOnline)
2327 {
2328 combinedProgress.createObject();
2329 rc = combinedProgress->init (static_cast <IConsole *> (this),
2330 Bstr (tr ("Taking snapshot of virtual machine")),
2331 serverProgress, saveProgress);
2332 AssertComRCBreakRC (rc);
2333
2334 /* setup task object and thread to carry out the operation asynchronously */
2335 task->mIsSnapshot = true;
2336 task->mSavedStateFile = stateFilePath;
2337 task->mServerProgress = serverProgress;
2338 /* set the state the operation thread will restore when it is finished */
2339 task->mLastMachineState = lastMachineState;
2340
2341 /* create a thread to wait until the VM state is saved */
2342 int vrc = RTThreadCreate (NULL, Console::saveStateThread, (void *) task.get(),
2343 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMTakeSnap");
2344
2345 ComAssertMsgRCBreak (vrc, ("Could not create VMTakeSnap thread (%Rrc)", vrc),
2346 rc = E_FAIL);
2347
2348 /* task is now owned by saveStateThread(), so release it */
2349 task.release();
2350 }
2351
2352 if (SUCCEEDED (rc))
2353 {
2354 /* return the correct progress to the caller */
2355 if (combinedProgress)
2356 combinedProgress.queryInterfaceTo (aProgress);
2357 else
2358 serverProgress.queryInterfaceTo (aProgress);
2359 }
2360 }
2361 while (0);
2362
2363 if (FAILED (rc) && !taskCreationFailed)
2364 {
2365 /* preserve existing error info */
2366 ErrorInfoKeeper eik;
2367
2368 if (beganTakingSnapshot && takingSnapshotOnline)
2369 {
2370 /*
2371 * cancel the requested snapshot (only when creating a snapshot
2372 * online, otherwise the server will cancel the snapshot itself).
2373 * This will reset the machine state to the state it had right
2374 * before calling mControl->BeginTakingSnapshot().
2375 */
2376 mControl->EndTakingSnapshot (FALSE);
2377 }
2378
2379 if (lastMachineState == MachineState_Running)
2380 {
2381 /* restore the paused state if appropriate */
2382 setMachineStateLocally (MachineState_Paused);
2383 /* restore the running state if appropriate */
2384 Resume();
2385 }
2386 else
2387 setMachineStateLocally (lastMachineState);
2388 }
2389
2390 LogFlowThisFunc (("rc=%08X\n", rc));
2391 LogFlowThisFuncLeave();
2392 return rc;
2393}
2394
2395STDMETHODIMP Console::DiscardSnapshot (IN_BSTR aId, IProgress **aProgress)
2396{
2397 CheckComArgExpr(aId, Guid (aId).isEmpty() == false);
2398 CheckComArgOutPointerValid(aProgress);
2399
2400 AutoCaller autoCaller (this);
2401 CheckComRCReturnRC (autoCaller.rc());
2402
2403 AutoWriteLock alock (this);
2404
2405 if (Global::IsOnlineOrTransient (mMachineState))
2406 return setError (VBOX_E_INVALID_VM_STATE,
2407 tr ("Cannot discard a snapshot of the running machine "
2408 "(machine state: %d)"),
2409 mMachineState);
2410
2411 MachineState_T machineState = MachineState_Null;
2412 HRESULT rc = mControl->DiscardSnapshot (this, aId, &machineState, aProgress);
2413 CheckComRCReturnRC (rc);
2414
2415 setMachineStateLocally (machineState);
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Console::DiscardCurrentState (IProgress **aProgress)
2420{
2421 AutoCaller autoCaller (this);
2422 CheckComRCReturnRC (autoCaller.rc());
2423
2424 AutoWriteLock alock (this);
2425
2426 if (Global::IsOnlineOrTransient (mMachineState))
2427 return setError (VBOX_E_INVALID_VM_STATE,
2428 tr ("Cannot discard the current state of the running machine "
2429 "(nachine state: %d)"),
2430 mMachineState);
2431
2432 MachineState_T machineState = MachineState_Null;
2433 HRESULT rc = mControl->DiscardCurrentState (this, &machineState, aProgress);
2434 CheckComRCReturnRC (rc);
2435
2436 setMachineStateLocally (machineState);
2437 return S_OK;
2438}
2439
2440STDMETHODIMP Console::DiscardCurrentSnapshotAndState (IProgress **aProgress)
2441{
2442 AutoCaller autoCaller (this);
2443 CheckComRCReturnRC (autoCaller.rc());
2444
2445 AutoWriteLock alock (this);
2446
2447 if (Global::IsOnlineOrTransient (mMachineState))
2448 return setError (VBOX_E_INVALID_VM_STATE,
2449 tr ("Cannot discard the current snapshot and state of the "
2450 "running machine (machine state: %d)"),
2451 mMachineState);
2452
2453 MachineState_T machineState = MachineState_Null;
2454 HRESULT rc =
2455 mControl->DiscardCurrentSnapshotAndState (this, &machineState, aProgress);
2456 CheckComRCReturnRC (rc);
2457
2458 setMachineStateLocally (machineState);
2459 return S_OK;
2460}
2461
2462STDMETHODIMP Console::RegisterCallback (IConsoleCallback *aCallback)
2463{
2464 CheckComArgNotNull(aCallback);
2465
2466 AutoCaller autoCaller (this);
2467 CheckComRCReturnRC (autoCaller.rc());
2468
2469#if 0 /** @todo r=bird,r=pritesh: must check that the interface id match correct or we might screw up with old code! */
2470 void *dummy;
2471 HRESULT hrc = aCallback->QueryInterface(NS_GET_IID(IConsoleCallback), &dummy);
2472 if (FAILED(hrc))
2473 return hrc;
2474 aCallback->Release();
2475#endif
2476
2477 AutoWriteLock alock (this);
2478
2479 mCallbacks.push_back (CallbackList::value_type (aCallback));
2480
2481 /* Inform the callback about the current status (for example, the new
2482 * callback must know the current mouse capabilities and the pointer
2483 * shape in order to properly integrate the mouse pointer). */
2484
2485 if (mCallbackData.mpsc.valid)
2486 aCallback->OnMousePointerShapeChange (mCallbackData.mpsc.visible,
2487 mCallbackData.mpsc.alpha,
2488 mCallbackData.mpsc.xHot,
2489 mCallbackData.mpsc.yHot,
2490 mCallbackData.mpsc.width,
2491 mCallbackData.mpsc.height,
2492 mCallbackData.mpsc.shape);
2493 if (mCallbackData.mcc.valid)
2494 aCallback->OnMouseCapabilityChange (mCallbackData.mcc.supportsAbsolute,
2495 mCallbackData.mcc.needsHostCursor);
2496
2497 aCallback->OnAdditionsStateChange();
2498
2499 if (mCallbackData.klc.valid)
2500 aCallback->OnKeyboardLedsChange (mCallbackData.klc.numLock,
2501 mCallbackData.klc.capsLock,
2502 mCallbackData.klc.scrollLock);
2503
2504 /* Note: we don't call OnStateChange for new callbacks because the
2505 * machine state is a) not actually changed on callback registration
2506 * and b) can be always queried from Console. */
2507
2508 return S_OK;
2509}
2510
2511STDMETHODIMP Console::UnregisterCallback (IConsoleCallback *aCallback)
2512{
2513 CheckComArgNotNull(aCallback);
2514
2515 AutoCaller autoCaller (this);
2516 CheckComRCReturnRC (autoCaller.rc());
2517
2518 AutoWriteLock alock (this);
2519
2520 CallbackList::iterator it;
2521 it = std::find (mCallbacks.begin(),
2522 mCallbacks.end(),
2523 CallbackList::value_type (aCallback));
2524 if (it == mCallbacks.end())
2525 return setError (E_INVALIDARG,
2526 tr ("The given callback handler is not registered"));
2527
2528 mCallbacks.erase (it);
2529 return S_OK;
2530}
2531
2532// Non-interface public methods
2533/////////////////////////////////////////////////////////////////////////////
2534
2535/**
2536 * Called by IInternalSessionControl::OnDVDDriveChange().
2537 *
2538 * @note Locks this object for writing.
2539 */
2540HRESULT Console::onDVDDriveChange()
2541{
2542 LogFlowThisFuncEnter();
2543
2544 AutoCaller autoCaller (this);
2545 AssertComRCReturnRC (autoCaller.rc());
2546
2547 /* doDriveChange() needs a write lock */
2548 AutoWriteLock alock (this);
2549
2550 /* Ignore callbacks when there's no VM around */
2551 if (!mpVM)
2552 return S_OK;
2553
2554 /* protect mpVM */
2555 AutoVMCaller autoVMCaller (this);
2556 CheckComRCReturnRC (autoVMCaller.rc());
2557
2558 /* Get the current DVD state */
2559 HRESULT rc;
2560 DriveState_T eState;
2561
2562 rc = mDVDDrive->COMGETTER (State) (&eState);
2563 ComAssertComRCRetRC (rc);
2564
2565 /* Paranoia */
2566 if ( eState == DriveState_NotMounted
2567 && meDVDState == DriveState_NotMounted)
2568 {
2569 LogFlowThisFunc (("Returns (NotMounted -> NotMounted)\n"));
2570 return S_OK;
2571 }
2572
2573 /* Get the path string and other relevant properties */
2574 Bstr Path;
2575 bool fPassthrough = false;
2576 switch (eState)
2577 {
2578 case DriveState_ImageMounted:
2579 {
2580 ComPtr <IDVDImage> ImagePtr;
2581 rc = mDVDDrive->GetImage (ImagePtr.asOutParam());
2582 if (SUCCEEDED (rc))
2583 rc = ImagePtr->COMGETTER(Location) (Path.asOutParam());
2584 break;
2585 }
2586
2587 case DriveState_HostDriveCaptured:
2588 {
2589 ComPtr <IHostDVDDrive> DrivePtr;
2590 BOOL enabled;
2591 rc = mDVDDrive->GetHostDrive (DrivePtr.asOutParam());
2592 if (SUCCEEDED (rc))
2593 rc = DrivePtr->COMGETTER (Name) (Path.asOutParam());
2594 if (SUCCEEDED (rc))
2595 rc = mDVDDrive->COMGETTER (Passthrough) (&enabled);
2596 if (SUCCEEDED (rc))
2597 fPassthrough = !!enabled;
2598 break;
2599 }
2600
2601 case DriveState_NotMounted:
2602 break;
2603
2604 default:
2605 AssertMsgFailed (("Invalid DriveState: %d\n", eState));
2606 rc = E_FAIL;
2607 break;
2608 }
2609
2610 AssertComRC (rc);
2611 if (SUCCEEDED (rc))
2612 {
2613 rc = doDriveChange ("piix3ide", 0, 2, eState, &meDVDState,
2614 Utf8Str (Path).raw(), fPassthrough);
2615
2616 /* notify console callbacks on success */
2617 if (SUCCEEDED (rc))
2618 {
2619 CallbackList::iterator it = mCallbacks.begin();
2620 while (it != mCallbacks.end())
2621 (*it++)->OnDVDDriveChange();
2622 }
2623 }
2624
2625 LogFlowThisFunc (("Returns %Rhrc (%#x)\n", rc, rc));
2626 LogFlowThisFuncLeave();
2627 return rc;
2628}
2629
2630
2631/**
2632 * Called by IInternalSessionControl::OnFloppyDriveChange().
2633 *
2634 * @note Locks this object for writing.
2635 */
2636HRESULT Console::onFloppyDriveChange()
2637{
2638 LogFlowThisFuncEnter();
2639
2640 AutoCaller autoCaller (this);
2641 AssertComRCReturnRC (autoCaller.rc());
2642
2643 /* doDriveChange() needs a write lock */
2644 AutoWriteLock alock (this);
2645
2646 /* Ignore callbacks when there's no VM around */
2647 if (!mpVM)
2648 return S_OK;
2649
2650 /* protect mpVM */
2651 AutoVMCaller autoVMCaller (this);
2652 CheckComRCReturnRC (autoVMCaller.rc());
2653
2654 /* Get the current floppy state */
2655 HRESULT rc;
2656 DriveState_T eState;
2657
2658 /* If the floppy drive is disabled, we're not interested */
2659 BOOL fEnabled;
2660 rc = mFloppyDrive->COMGETTER (Enabled) (&fEnabled);
2661 ComAssertComRCRetRC (rc);
2662
2663 if (!fEnabled)
2664 return S_OK;
2665
2666 rc = mFloppyDrive->COMGETTER (State) (&eState);
2667 ComAssertComRCRetRC (rc);
2668
2669 Log2 (("onFloppyDriveChange: eState=%d meFloppyState=%d\n", eState, meFloppyState));
2670
2671
2672 /* Paranoia */
2673 if ( eState == DriveState_NotMounted
2674 && meFloppyState == DriveState_NotMounted)
2675 {
2676 LogFlowThisFunc (("Returns (NotMounted -> NotMounted)\n"));
2677 return S_OK;
2678 }
2679
2680 /* Get the path string and other relevant properties */
2681 Bstr Path;
2682 switch (eState)
2683 {
2684 case DriveState_ImageMounted:
2685 {
2686 ComPtr<IFloppyImage> ImagePtr;
2687 rc = mFloppyDrive->GetImage (ImagePtr.asOutParam());
2688 if (SUCCEEDED (rc))
2689 rc = ImagePtr->COMGETTER(Location) (Path.asOutParam());
2690 break;
2691 }
2692
2693 case DriveState_HostDriveCaptured:
2694 {
2695 ComPtr <IHostFloppyDrive> DrivePtr;
2696 rc = mFloppyDrive->GetHostDrive (DrivePtr.asOutParam());
2697 if (SUCCEEDED (rc))
2698 rc = DrivePtr->COMGETTER (Name) (Path.asOutParam());
2699 break;
2700 }
2701
2702 case DriveState_NotMounted:
2703 break;
2704
2705 default:
2706 AssertMsgFailed (("Invalid DriveState: %d\n", eState));
2707 rc = E_FAIL;
2708 break;
2709 }
2710
2711 AssertComRC (rc);
2712 if (SUCCEEDED (rc))
2713 {
2714 rc = doDriveChange ("i82078", 0, 0, eState, &meFloppyState,
2715 Utf8Str (Path).raw(), false);
2716
2717 /* notify console callbacks on success */
2718 if (SUCCEEDED (rc))
2719 {
2720 CallbackList::iterator it = mCallbacks.begin();
2721 while (it != mCallbacks.end())
2722 (*it++)->OnFloppyDriveChange();
2723 }
2724 }
2725
2726 LogFlowThisFunc (("Returns %Rhrc (%#x)\n", rc, rc));
2727 LogFlowThisFuncLeave();
2728 return rc;
2729}
2730
2731
2732/**
2733 * Process a floppy or dvd change.
2734 *
2735 * @returns COM status code.
2736 *
2737 * @param pszDevice The PDM device name.
2738 * @param uInstance The PDM device instance.
2739 * @param uLun The PDM LUN number of the drive.
2740 * @param eState The new state.
2741 * @param peState Pointer to the variable keeping the actual state of the drive.
2742 * This will be both read and updated to eState or other appropriate state.
2743 * @param pszPath The path to the media / drive which is now being mounted / captured.
2744 * If NULL no media or drive is attached and the LUN will be configured with
2745 * the default block driver with no media. This will also be the state if
2746 * mounting / capturing the specified media / drive fails.
2747 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
2748 *
2749 * @note Locks this object for writing.
2750 */
2751HRESULT Console::doDriveChange (const char *pszDevice, unsigned uInstance, unsigned uLun, DriveState_T eState,
2752 DriveState_T *peState, const char *pszPath, bool fPassthrough)
2753{
2754 LogFlowThisFunc (("pszDevice=%p:{%s} uInstance=%u uLun=%u eState=%d "
2755 "peState=%p:{%d} pszPath=%p:{%s} fPassthrough=%d\n",
2756 pszDevice, pszDevice, uInstance, uLun, eState,
2757 peState, *peState, pszPath, pszPath, fPassthrough));
2758
2759 AutoCaller autoCaller (this);
2760 AssertComRCReturnRC (autoCaller.rc());
2761
2762 /* We will need to release the write lock before calling EMT */
2763 AutoWriteLock alock (this);
2764
2765 /* protect mpVM */
2766 AutoVMCaller autoVMCaller (this);
2767 CheckComRCReturnRC (autoVMCaller.rc());
2768
2769 /*
2770 * Call worker in EMT, that's faster and safer than doing everything
2771 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
2772 * here to make requests from under the lock in order to serialize them.
2773 */
2774 PVMREQ pReq;
2775 int vrc = VMR3ReqCall (mpVM, VMCPUID_ANY, &pReq, 0 /* no wait! */,
2776 (PFNRT) Console::changeDrive, 8,
2777 this, pszDevice, uInstance, uLun, eState, peState,
2778 pszPath, fPassthrough);
2779 /// @todo (r=dmik) bird, it would be nice to have a special VMR3Req method
2780 // for that purpose, that doesn't return useless VERR_TIMEOUT
2781 if (vrc == VERR_TIMEOUT)
2782 vrc = VINF_SUCCESS;
2783
2784 /* leave the lock before waiting for a result (EMT will call us back!) */
2785 alock.leave();
2786
2787 if (VBOX_SUCCESS (vrc))
2788 {
2789 vrc = VMR3ReqWait (pReq, RT_INDEFINITE_WAIT);
2790 AssertRC (vrc);
2791 if (VBOX_SUCCESS (vrc))
2792 vrc = pReq->iStatus;
2793 }
2794 VMR3ReqFree (pReq);
2795
2796 if (VBOX_SUCCESS (vrc))
2797 {
2798 LogFlowThisFunc (("Returns S_OK\n"));
2799 return S_OK;
2800 }
2801
2802 if (pszPath)
2803 return setError (E_FAIL,
2804 tr ("Could not mount the media/drive '%s' (%Rrc)"), pszPath, vrc);
2805
2806 return setError (E_FAIL,
2807 tr ("Could not unmount the currently mounted media/drive (%Rrc)"), vrc);
2808}
2809
2810
2811/**
2812 * Performs the Floppy/DVD change in EMT.
2813 *
2814 * @returns VBox status code.
2815 *
2816 * @param pThis Pointer to the Console object.
2817 * @param pszDevice The PDM device name.
2818 * @param uInstance The PDM device instance.
2819 * @param uLun The PDM LUN number of the drive.
2820 * @param eState The new state.
2821 * @param peState Pointer to the variable keeping the actual state of the drive.
2822 * This will be both read and updated to eState or other appropriate state.
2823 * @param pszPath The path to the media / drive which is now being mounted / captured.
2824 * If NULL no media or drive is attached and the LUN will be configured with
2825 * the default block driver with no media. This will also be the state if
2826 * mounting / capturing the specified media / drive fails.
2827 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
2828 *
2829 * @thread EMT
2830 * @note Locks the Console object for writing.
2831 */
2832DECLCALLBACK(int) Console::changeDrive (Console *pThis, const char *pszDevice, unsigned uInstance, unsigned uLun,
2833 DriveState_T eState, DriveState_T *peState,
2834 const char *pszPath, bool fPassthrough)
2835{
2836 LogFlowFunc (("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u eState=%d "
2837 "peState=%p:{%d} pszPath=%p:{%s} fPassthrough=%d\n",
2838 pThis, pszDevice, pszDevice, uInstance, uLun, eState,
2839 peState, *peState, pszPath, pszPath, fPassthrough));
2840
2841 AssertReturn (pThis, VERR_INVALID_PARAMETER);
2842
2843 AssertMsg ( (!strcmp (pszDevice, "i82078") && uLun == 0 && uInstance == 0)
2844 || (!strcmp (pszDevice, "piix3ide") && uLun == 2 && uInstance == 0),
2845 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
2846
2847 AutoCaller autoCaller (pThis);
2848 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);
2849
2850 /* protect mpVM */
2851 AutoVMCaller autoVMCaller (pThis);
2852 CheckComRCReturnRC (autoVMCaller.rc());
2853
2854 PVM pVM = pThis->mpVM;
2855
2856 /*
2857 * Suspend the VM first.
2858 *
2859 * The VM must not be running since it might have pending I/O to
2860 * the drive which is being changed.
2861 */
2862 bool fResume;
2863 VMSTATE enmVMState = VMR3GetState (pVM);
2864 switch (enmVMState)
2865 {
2866 case VMSTATE_RESETTING:
2867 case VMSTATE_RUNNING:
2868 {
2869 LogFlowFunc (("Suspending the VM...\n"));
2870 /* disable the callback to prevent Console-level state change */
2871 pThis->mVMStateChangeCallbackDisabled = true;
2872 int rc = VMR3Suspend (pVM);
2873 pThis->mVMStateChangeCallbackDisabled = false;
2874 AssertRCReturn (rc, rc);
2875 fResume = true;
2876 break;
2877 }
2878
2879 case VMSTATE_SUSPENDED:
2880 case VMSTATE_CREATED:
2881 case VMSTATE_OFF:
2882 fResume = false;
2883 break;
2884
2885 default:
2886 AssertMsgFailedReturn (("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
2887 }
2888
2889 int rc = VINF_SUCCESS;
2890 int rcRet = VINF_SUCCESS;
2891
2892 /*
2893 In general locking the object before doing VMR3* calls is quite safe
2894 here, since we're on EMT. Anyway we lock for write after eventually
2895 suspending the vm. The reason is that in the vmstateChangeCallback the
2896 var mVMStateChangeCallbackDisabled is checked under a lock also, which
2897 can lead to an dead lock. The write lock is necessary because we
2898 indirectly modify the meDVDState/meFloppyState members (pointed to by
2899 peState).
2900 */
2901 AutoWriteLock alock (pThis);
2902
2903 do
2904 {
2905 /*
2906 * Unmount existing media / detach host drive.
2907 */
2908 PPDMIMOUNT pIMount = NULL;
2909 switch (*peState)
2910 {
2911
2912 case DriveState_ImageMounted:
2913 {
2914 /*
2915 * Resolve the interface.
2916 */
2917 PPDMIBASE pBase;
2918 rc = PDMR3QueryLun (pVM, pszDevice, uInstance, uLun, &pBase);
2919 if (VBOX_FAILURE (rc))
2920 {
2921 if (rc == VERR_PDM_LUN_NOT_FOUND)
2922 rc = VINF_SUCCESS;
2923 AssertRC (rc);
2924 break;
2925 }
2926
2927 pIMount = (PPDMIMOUNT) pBase->pfnQueryInterface (pBase, PDMINTERFACE_MOUNT);
2928 AssertBreakStmt (pIMount, rc = VERR_INVALID_POINTER);
2929
2930 /*
2931 * Unmount the media.
2932 */
2933 rc = pIMount->pfnUnmount (pIMount, false);
2934 if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
2935 rc = VINF_SUCCESS;
2936 break;
2937 }
2938
2939 case DriveState_HostDriveCaptured:
2940 {
2941 rc = PDMR3DeviceDetach (pVM, pszDevice, uInstance, uLun);
2942 if (rc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)
2943 rc = VINF_SUCCESS;
2944 AssertRC (rc);
2945 break;
2946 }
2947
2948 case DriveState_NotMounted:
2949 break;
2950
2951 default:
2952 AssertMsgFailed (("Invalid *peState: %d\n", peState));
2953 break;
2954 }
2955
2956 if (VBOX_FAILURE (rc))
2957 {
2958 rcRet = rc;
2959 break;
2960 }
2961
2962 /*
2963 * Nothing is currently mounted.
2964 */
2965 *peState = DriveState_NotMounted;
2966
2967
2968 /*
2969 * Process the HostDriveCaptured state first, as the fallback path
2970 * means mounting the normal block driver without media.
2971 */
2972 if (eState == DriveState_HostDriveCaptured)
2973 {
2974 /*
2975 * Detach existing driver chain (block).
2976 */
2977 int rc = PDMR3DeviceDetach (pVM, pszDevice, uInstance, uLun);
2978 if (VBOX_FAILURE (rc))
2979 {
2980 if (rc == VERR_PDM_LUN_NOT_FOUND)
2981 rc = VINF_SUCCESS;
2982 AssertReleaseRC (rc);
2983 break; /* we're toast */
2984 }
2985 pIMount = NULL;
2986
2987 /*
2988 * Construct a new driver configuration.
2989 */
2990 PCFGMNODE pInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
2991 AssertRelease (pInst);
2992 /* nuke anything which might have been left behind. */
2993 CFGMR3RemoveNode (CFGMR3GetChildF (pInst, "LUN#%d", uLun));
2994
2995 /* create a new block driver config */
2996 PCFGMNODE pLunL0;
2997 PCFGMNODE pCfg;
2998 if ( VBOX_SUCCESS (rc = CFGMR3InsertNodeF (pInst, &pLunL0, "LUN#%u", uLun))
2999 && VBOX_SUCCESS (rc = CFGMR3InsertString (pLunL0, "Driver", !strcmp (pszDevice, "i82078") ? "HostFloppy" : "HostDVD"))
3000 && VBOX_SUCCESS (rc = CFGMR3InsertNode (pLunL0, "Config", &pCfg))
3001 && VBOX_SUCCESS (rc = CFGMR3InsertString (pCfg, "Path", pszPath))
3002 && VBOX_SUCCESS (rc = !strcmp (pszDevice, "i82078") ? VINF_SUCCESS : CFGMR3InsertInteger(pCfg, "Passthrough", fPassthrough)))
3003 {
3004 /*
3005 * Attempt to attach the driver.
3006 */
3007 rc = PDMR3DeviceAttach (pVM, pszDevice, uInstance, uLun, NULL);
3008 AssertRC (rc);
3009 }
3010 if (VBOX_FAILURE (rc))
3011 rcRet = rc;
3012 }
3013
3014 /*
3015 * Process the ImageMounted, NotMounted and failed HostDriveCapture cases.
3016 */
3017 rc = VINF_SUCCESS;
3018 switch (eState)
3019 {
3020#define RC_CHECK() do { if (VBOX_FAILURE (rc)) { AssertReleaseRC (rc); break; } } while (0)
3021
3022 case DriveState_HostDriveCaptured:
3023 if (VBOX_SUCCESS (rcRet))
3024 break;
3025 /* fallback: umounted block driver. */
3026 pszPath = NULL;
3027 eState = DriveState_NotMounted;
3028 /* fallthru */
3029 case DriveState_ImageMounted:
3030 case DriveState_NotMounted:
3031 {
3032 /*
3033 * Resolve the drive interface / create the driver.
3034 */
3035 if (!pIMount)
3036 {
3037 PPDMIBASE pBase;
3038 rc = PDMR3QueryLun (pVM, pszDevice, uInstance, uLun, &pBase);
3039 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
3040 {
3041 /*
3042 * We have to create it, so we'll do the full config setup and everything.
3043 */
3044 PCFGMNODE pIdeInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
3045 AssertRelease (pIdeInst);
3046
3047 /* nuke anything which might have been left behind. */
3048 CFGMR3RemoveNode (CFGMR3GetChildF (pIdeInst, "LUN#%d", uLun));
3049
3050 /* create a new block driver config */
3051 PCFGMNODE pLunL0;
3052 rc = CFGMR3InsertNodeF (pIdeInst, &pLunL0, "LUN#%d", uLun); RC_CHECK();
3053 rc = CFGMR3InsertString (pLunL0, "Driver", "Block"); RC_CHECK();
3054 PCFGMNODE pCfg;
3055 rc = CFGMR3InsertNode (pLunL0, "Config", &pCfg); RC_CHECK();
3056 rc = CFGMR3InsertString (pCfg, "Type", !strcmp (pszDevice, "i82078") ? "Floppy 1.44" : "DVD");
3057 RC_CHECK();
3058 rc = CFGMR3InsertInteger (pCfg, "Mountable", 1); RC_CHECK();
3059
3060 /*
3061 * Attach the driver.
3062 */
3063 rc = PDMR3DeviceAttach (pVM, pszDevice, uInstance, uLun, &pBase);
3064 RC_CHECK();
3065 }
3066 else if (VBOX_FAILURE(rc))
3067 {
3068 AssertRC (rc);
3069 return rc;
3070 }
3071
3072 pIMount = (PPDMIMOUNT) pBase->pfnQueryInterface (pBase, PDMINTERFACE_MOUNT);
3073 if (!pIMount)
3074 {
3075 AssertFailed();
3076 return rc;
3077 }
3078 }
3079
3080 /*
3081 * If we've got an image, let's mount it.
3082 */
3083 if (pszPath && *pszPath)
3084 {
3085 rc = pIMount->pfnMount (pIMount, pszPath, strcmp (pszDevice, "i82078") ? "MediaISO" : "RawImage");
3086 if (VBOX_FAILURE (rc))
3087 eState = DriveState_NotMounted;
3088 }
3089 break;
3090 }
3091
3092 default:
3093 AssertMsgFailed (("Invalid eState: %d\n", eState));
3094 break;
3095
3096#undef RC_CHECK
3097 }
3098
3099 if (VBOX_FAILURE (rc) && VBOX_SUCCESS (rcRet))
3100 rcRet = rc;
3101
3102 *peState = eState;
3103 }
3104 while (0);
3105
3106 /*
3107 Unlock before resuming because the vmstateChangeCallback problem
3108 described above.
3109 */
3110 alock.unlock();
3111
3112 /*
3113 * Resume the VM if necessary.
3114 */
3115 if (fResume)
3116 {
3117 LogFlowFunc (("Resuming the VM...\n"));
3118 /* disable the callback to prevent Console-level state change */
3119 pThis->mVMStateChangeCallbackDisabled = true;
3120 rc = VMR3Resume (pVM);
3121 pThis->mVMStateChangeCallbackDisabled = false;
3122 AssertRC (rc);
3123 if (VBOX_FAILURE (rc))
3124 {
3125 /* too bad, we failed. try to sync the console state with the VMM state */
3126 vmstateChangeCallback (pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3127 }
3128 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3129 // error (if any) will be hidden from the caller. For proper reporting
3130 // of such multiple errors to the caller we need to enhance the
3131 // IVurtualBoxError interface. For now, give the first error the higher
3132 // priority.
3133 if (VBOX_SUCCESS (rcRet))
3134 rcRet = rc;
3135 }
3136
3137 LogFlowFunc (("Returning %Rrc\n", rcRet));
3138 return rcRet;
3139}
3140
3141
3142/**
3143 * Called by IInternalSessionControl::OnNetworkAdapterChange().
3144 *
3145 * @note Locks this object for writing.
3146 */
3147HRESULT Console::onNetworkAdapterChange (INetworkAdapter *aNetworkAdapter)
3148{
3149 LogFlowThisFunc (("\n"));
3150
3151 AutoCaller autoCaller (this);
3152 AssertComRCReturnRC (autoCaller.rc());
3153
3154 AutoWriteLock alock (this);
3155
3156 /* Don't do anything if the VM isn't running */
3157 if (!mpVM)
3158 return S_OK;
3159
3160 /* protect mpVM */
3161 AutoVMCaller autoVMCaller (this);
3162 CheckComRCReturnRC (autoVMCaller.rc());
3163
3164#ifdef VBOX_DYNAMIC_NET_ATTACH
3165 /* Get the current network attachment type */
3166 HRESULT rc;
3167 NetworkAttachmentType_T eAttachmentType;
3168
3169 rc = aNetworkAdapter->COMGETTER (AttachmentType) (&eAttachmentType);
3170 ComAssertComRCRetRC (rc);
3171#endif /* VBOX_DYNAMIC_NET_ATTACH */
3172
3173 /* Get the properties we need from the adapter */
3174 BOOL fCableConnected;
3175#ifdef VBOX_DYNAMIC_NET_ATTACH
3176 rc = aNetworkAdapter->COMGETTER(CableConnected) (&fCableConnected);
3177#else /* !VBOX_DYNAMIC_NET_ATTACH */
3178 HRESULT rc = aNetworkAdapter->COMGETTER(CableConnected) (&fCableConnected);
3179#endif /* !VBOX_DYNAMIC_NET_ATTACH */
3180 AssertComRC(rc);
3181 if (SUCCEEDED(rc))
3182 {
3183 ULONG ulInstance;
3184 rc = aNetworkAdapter->COMGETTER(Slot) (&ulInstance);
3185 AssertComRC (rc);
3186 if (SUCCEEDED (rc))
3187 {
3188 /*
3189 * Find the pcnet instance, get the config interface and update
3190 * the link state.
3191 */
3192 PPDMIBASE pBase;
3193 const char *pszAdapterName = "pcnet";
3194#ifdef VBOX_WITH_E1000
3195 /*
3196 * Perhaps it would be much wiser to wrap both 'pcnet' and 'e1000'
3197 * into generic 'net' device.
3198 */
3199 NetworkAdapterType_T adapterType;
3200 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3201 AssertComRC(rc);
3202 if (adapterType == NetworkAdapterType_I82540EM ||
3203 adapterType == NetworkAdapterType_I82543GC ||
3204 adapterType == NetworkAdapterType_I82545EM)
3205 pszAdapterName = "e1000";
3206#endif
3207 int vrc = PDMR3QueryDeviceLun (mpVM, pszAdapterName,
3208 (unsigned) ulInstance, 0, &pBase);
3209 ComAssertRC (vrc);
3210 if (VBOX_SUCCESS (vrc))
3211 {
3212 Assert(pBase);
3213 PPDMINETWORKCONFIG pINetCfg = (PPDMINETWORKCONFIG) pBase->
3214 pfnQueryInterface(pBase, PDMINTERFACE_NETWORK_CONFIG);
3215 if (pINetCfg)
3216 {
3217 Log (("Console::onNetworkAdapterChange: setting link state to %d\n",
3218 fCableConnected));
3219 vrc = pINetCfg->pfnSetLinkState (pINetCfg,
3220 fCableConnected ? PDMNETWORKLINKSTATE_UP
3221 : PDMNETWORKLINKSTATE_DOWN);
3222 ComAssertRC (vrc);
3223 }
3224 }
3225
3226#ifdef VBOX_DYNAMIC_NET_ATTACH
3227 if (VBOX_SUCCESS (vrc) && !(eAttachmentType == meAttachmentType[ulInstance]))
3228 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3229#endif /* VBOX_DYNAMIC_NET_ATTACH */
3230
3231 if (VBOX_FAILURE (vrc))
3232 rc = E_FAIL;
3233 }
3234 }
3235
3236 /* notify console callbacks on success */
3237 if (SUCCEEDED (rc))
3238 {
3239 CallbackList::iterator it = mCallbacks.begin();
3240 while (it != mCallbacks.end())
3241 (*it++)->OnNetworkAdapterChange (aNetworkAdapter);
3242 }
3243
3244 LogFlowThisFunc (("Leaving rc=%#x\n", rc));
3245 return rc;
3246}
3247
3248
3249#ifdef VBOX_DYNAMIC_NET_ATTACH
3250/**
3251 * Process a network adaptor change.
3252 *
3253 * @returns COM status code.
3254 *
3255 * @param pszDevice The PDM device name.
3256 * @param uInstance The PDM device instance.
3257 * @param uLun The PDM LUN number of the drive.
3258 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3259 *
3260 * @note Locks this object for writing.
3261 */
3262HRESULT Console::doNetworkAdapterChange (const char *pszDevice,
3263 unsigned uInstance,
3264 unsigned uLun,
3265 INetworkAdapter *aNetworkAdapter)
3266{
3267 LogFlowThisFunc (("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3268 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3269
3270 AutoCaller autoCaller (this);
3271 AssertComRCReturnRC (autoCaller.rc());
3272
3273 /* We will need to release the write lock before calling EMT */
3274 AutoWriteLock alock (this);
3275
3276 /* protect mpVM */
3277 AutoVMCaller autoVMCaller (this);
3278 CheckComRCReturnRC (autoVMCaller.rc());
3279
3280 /*
3281 * Call worker in EMT, that's faster and safer than doing everything
3282 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3283 * here to make requests from under the lock in order to serialize them.
3284 */
3285 PVMREQ pReq;
3286 int vrc = VMR3ReqCall (mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */,
3287 (PFNRT) Console::changeNetworkAttachment, 5,
3288 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3289
3290 /* leave the lock before waiting for a result (EMT will call us back!) */
3291 alock.leave();
3292
3293 if (vrc == VERR_TIMEOUT || VBOX_SUCCESS (vrc))
3294 {
3295 vrc = VMR3ReqWait (pReq, RT_INDEFINITE_WAIT);
3296 AssertRC (vrc);
3297 if (VBOX_SUCCESS (vrc))
3298 vrc = pReq->iStatus;
3299 }
3300 VMR3ReqFree (pReq);
3301
3302 if (VBOX_SUCCESS (vrc))
3303 {
3304 LogFlowThisFunc (("Returns S_OK\n"));
3305 return S_OK;
3306 }
3307
3308 return setError (E_FAIL,
3309 tr ("Could not change the network adaptor attachement type (%Rrc)"), vrc);
3310}
3311
3312
3313/**
3314 * Performs the Network Adaptor change in EMT.
3315 *
3316 * @returns VBox status code.
3317 *
3318 * @param pThis Pointer to the Console object.
3319 * @param pszDevice The PDM device name.
3320 * @param uInstance The PDM device instance.
3321 * @param uLun The PDM LUN number of the drive.
3322 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3323 *
3324 * @thread EMT
3325 * @note Locks the Console object for writing.
3326 */
3327DECLCALLBACK(int) Console::changeNetworkAttachment (Console *pThis,
3328 const char *pszDevice,
3329 unsigned uInstance,
3330 unsigned uLun,
3331 INetworkAdapter *aNetworkAdapter)
3332{
3333 LogFlowFunc (("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3334 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3335
3336 AssertReturn (pThis, VERR_INVALID_PARAMETER);
3337
3338 AssertMsg ( (!strcmp (pszDevice, "pcnet") && uLun == 0 && uInstance < 8)
3339 || (!strcmp (pszDevice, "e1000") && uLun == 0 && uInstance < 8),
3340 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3341 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3342
3343 AutoCaller autoCaller (pThis);
3344 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);
3345
3346 /* protect mpVM */
3347 AutoVMCaller autoVMCaller (pThis);
3348 CheckComRCReturnRC (autoVMCaller.rc());
3349
3350 PVM pVM = pThis->mpVM;
3351
3352 /*
3353 * Suspend the VM first.
3354 *
3355 * The VM must not be running since it might have pending I/O to
3356 * the drive which is being changed.
3357 */
3358 bool fResume;
3359 VMSTATE enmVMState = VMR3GetState (pVM);
3360 switch (enmVMState)
3361 {
3362 case VMSTATE_RESETTING:
3363 case VMSTATE_RUNNING:
3364 {
3365 LogFlowFunc (("Suspending the VM...\n"));
3366 /* disable the callback to prevent Console-level state change */
3367 pThis->mVMStateChangeCallbackDisabled = true;
3368 int rc = VMR3Suspend (pVM);
3369 pThis->mVMStateChangeCallbackDisabled = false;
3370 AssertRCReturn (rc, rc);
3371 fResume = true;
3372 break;
3373 }
3374
3375 case VMSTATE_SUSPENDED:
3376 case VMSTATE_CREATED:
3377 case VMSTATE_OFF:
3378 fResume = false;
3379 break;
3380
3381 default:
3382 AssertMsgFailedReturn (("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3383 }
3384
3385 int rc = VINF_SUCCESS;
3386 int rcRet = VINF_SUCCESS;
3387
3388 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3389 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3390 PCFGMNODE pInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
3391 AssertRelease (pInst);
3392
3393 /* @todo r=pritesh Need to store the previous network configuration
3394 * and restore it if configNetwork fails, currently not sure if the
3395 * previous atachment will also cleanly reattach with the later one
3396 * failing to attach.
3397 */
3398 rcRet = configNetwork(pThis, pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
3399
3400 /*
3401 * Resume the VM if necessary.
3402 */
3403 if (fResume)
3404 {
3405 LogFlowFunc (("Resuming the VM...\n"));
3406 /* disable the callback to prevent Console-level state change */
3407 pThis->mVMStateChangeCallbackDisabled = true;
3408 rc = VMR3Resume (pVM);
3409 pThis->mVMStateChangeCallbackDisabled = false;
3410 AssertRC (rc);
3411 if (VBOX_FAILURE (rc))
3412 {
3413 /* too bad, we failed. try to sync the console state with the VMM state */
3414 vmstateChangeCallback (pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3415 }
3416 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3417 // error (if any) will be hidden from the caller. For proper reporting
3418 // of such multiple errors to the caller we need to enhance the
3419 // IVirtualBoxError interface. For now, give the first error the higher
3420 // priority.
3421 if (VBOX_SUCCESS (rcRet))
3422 rcRet = rc;
3423 }
3424
3425 LogFlowFunc (("Returning %Rrc\n", rcRet));
3426 return rcRet;
3427}
3428#endif /* VBOX_DYNAMIC_NET_ATTACH */
3429
3430
3431/**
3432 * Called by IInternalSessionControl::OnSerialPortChange().
3433 *
3434 * @note Locks this object for writing.
3435 */
3436HRESULT Console::onSerialPortChange (ISerialPort *aSerialPort)
3437{
3438 LogFlowThisFunc (("\n"));
3439
3440 AutoCaller autoCaller (this);
3441 AssertComRCReturnRC (autoCaller.rc());
3442
3443 AutoWriteLock alock (this);
3444
3445 /* Don't do anything if the VM isn't running */
3446 if (!mpVM)
3447 return S_OK;
3448
3449 HRESULT rc = S_OK;
3450
3451 /* protect mpVM */
3452 AutoVMCaller autoVMCaller (this);
3453 CheckComRCReturnRC (autoVMCaller.rc());
3454
3455 /* nothing to do so far */
3456
3457 /* notify console callbacks on success */
3458 if (SUCCEEDED (rc))
3459 {
3460 CallbackList::iterator it = mCallbacks.begin();
3461 while (it != mCallbacks.end())
3462 (*it++)->OnSerialPortChange (aSerialPort);
3463 }
3464
3465 LogFlowThisFunc (("Leaving rc=%#x\n", rc));
3466 return rc;
3467}
3468
3469/**
3470 * Called by IInternalSessionControl::OnParallelPortChange().
3471 *
3472 * @note Locks this object for writing.
3473 */
3474HRESULT Console::onParallelPortChange (IParallelPort *aParallelPort)
3475{
3476 LogFlowThisFunc (("\n"));
3477
3478 AutoCaller autoCaller (this);
3479 AssertComRCReturnRC (autoCaller.rc());
3480
3481 AutoWriteLock alock (this);
3482
3483 /* Don't do anything if the VM isn't running */
3484 if (!mpVM)
3485 return S_OK;
3486
3487 HRESULT rc = S_OK;
3488
3489 /* protect mpVM */
3490 AutoVMCaller autoVMCaller (this);
3491 CheckComRCReturnRC (autoVMCaller.rc());
3492
3493 /* nothing to do so far */
3494
3495 /* notify console callbacks on success */
3496 if (SUCCEEDED (rc))
3497 {
3498 CallbackList::iterator it = mCallbacks.begin();
3499 while (it != mCallbacks.end())
3500 (*it++)->OnParallelPortChange (aParallelPort);
3501 }
3502
3503 LogFlowThisFunc (("Leaving rc=%#x\n", rc));
3504 return rc;
3505}
3506
3507/**
3508 * Called by IInternalSessionControl::OnStorageControllerChange().
3509 *
3510 * @note Locks this object for writing.
3511 */
3512HRESULT Console::onStorageControllerChange ()
3513{
3514 LogFlowThisFunc (("\n"));
3515
3516 AutoCaller autoCaller (this);
3517 AssertComRCReturnRC (autoCaller.rc());
3518
3519 AutoWriteLock alock (this);
3520
3521 /* Don't do anything if the VM isn't running */
3522 if (!mpVM)
3523 return S_OK;
3524
3525 HRESULT rc = S_OK;
3526
3527 /* protect mpVM */
3528 AutoVMCaller autoVMCaller (this);
3529 CheckComRCReturnRC (autoVMCaller.rc());
3530
3531 /* nothing to do so far */
3532
3533 /* notify console callbacks on success */
3534 if (SUCCEEDED (rc))
3535 {
3536 CallbackList::iterator it = mCallbacks.begin();
3537 while (it != mCallbacks.end())
3538 (*it++)->OnStorageControllerChange ();
3539 }
3540
3541 LogFlowThisFunc (("Leaving rc=%#x\n", rc));
3542 return rc;
3543}
3544
3545/**
3546 * Called by IInternalSessionControl::OnVRDPServerChange().
3547 *
3548 * @note Locks this object for writing.
3549 */
3550HRESULT Console::onVRDPServerChange()
3551{
3552 AutoCaller autoCaller (this);
3553 AssertComRCReturnRC (autoCaller.rc());
3554
3555 AutoWriteLock alock (this);
3556
3557 HRESULT rc = S_OK;
3558
3559 if (mVRDPServer && mMachineState == MachineState_Running)
3560 {
3561 BOOL vrdpEnabled = FALSE;
3562
3563 rc = mVRDPServer->COMGETTER(Enabled) (&vrdpEnabled);
3564 ComAssertComRCRetRC (rc);
3565
3566 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
3567 alock.leave();
3568
3569 if (vrdpEnabled)
3570 {
3571 // If there was no VRDP server started the 'stop' will do nothing.
3572 // However if a server was started and this notification was called,
3573 // we have to restart the server.
3574 mConsoleVRDPServer->Stop ();
3575
3576 if (VBOX_FAILURE(mConsoleVRDPServer->Launch ()))
3577 {
3578 rc = E_FAIL;
3579 }
3580 else
3581 {
3582 mConsoleVRDPServer->EnableConnections ();
3583 }
3584 }
3585 else
3586 {
3587 mConsoleVRDPServer->Stop ();
3588 }
3589
3590 alock.enter();
3591 }
3592
3593 /* notify console callbacks on success */
3594 if (SUCCEEDED (rc))
3595 {
3596 CallbackList::iterator it = mCallbacks.begin();
3597 while (it != mCallbacks.end())
3598 (*it++)->OnVRDPServerChange();
3599 }
3600
3601 return rc;
3602}
3603
3604/**
3605 * Called by IInternalSessionControl::OnUSBControllerChange().
3606 *
3607 * @note Locks this object for writing.
3608 */
3609HRESULT Console::onUSBControllerChange()
3610{
3611 LogFlowThisFunc (("\n"));
3612
3613 AutoCaller autoCaller (this);
3614 AssertComRCReturnRC (autoCaller.rc());
3615
3616 AutoWriteLock alock (this);
3617
3618 /* Ignore if no VM is running yet. */
3619 if (!mpVM)
3620 return S_OK;
3621
3622 HRESULT rc = S_OK;
3623
3624/// @todo (dmik)
3625// check for the Enabled state and disable virtual USB controller??
3626// Anyway, if we want to query the machine's USB Controller we need to cache
3627// it to mUSBController in #init() (as it is done with mDVDDrive).
3628//
3629// bird: While the VM supports hot-plugging, I doubt any guest can handle it at this time... :-)
3630//
3631// /* protect mpVM */
3632// AutoVMCaller autoVMCaller (this);
3633// CheckComRCReturnRC (autoVMCaller.rc());
3634
3635 /* notify console callbacks on success */
3636 if (SUCCEEDED (rc))
3637 {
3638 CallbackList::iterator it = mCallbacks.begin();
3639 while (it != mCallbacks.end())
3640 (*it++)->OnUSBControllerChange();
3641 }
3642
3643 return rc;
3644}
3645
3646/**
3647 * Called by IInternalSessionControl::OnSharedFolderChange().
3648 *
3649 * @note Locks this object for writing.
3650 */
3651HRESULT Console::onSharedFolderChange (BOOL aGlobal)
3652{
3653 LogFlowThisFunc (("aGlobal=%RTbool\n", aGlobal));
3654
3655 AutoCaller autoCaller (this);
3656 AssertComRCReturnRC (autoCaller.rc());
3657
3658 AutoWriteLock alock (this);
3659
3660 HRESULT rc = fetchSharedFolders (aGlobal);
3661
3662 /* notify console callbacks on success */
3663 if (SUCCEEDED (rc))
3664 {
3665 CallbackList::iterator it = mCallbacks.begin();
3666 while (it != mCallbacks.end())
3667 (*it++)->OnSharedFolderChange (aGlobal ? (Scope_T) Scope_Global
3668 : (Scope_T) Scope_Machine);
3669 }
3670
3671 return rc;
3672}
3673
3674/**
3675 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
3676 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
3677 * returns TRUE for a given remote USB device.
3678 *
3679 * @return S_OK if the device was attached to the VM.
3680 * @return failure if not attached.
3681 *
3682 * @param aDevice
3683 * The device in question.
3684 * @param aMaskedIfs
3685 * The interfaces to hide from the guest.
3686 *
3687 * @note Locks this object for writing.
3688 */
3689HRESULT Console::onUSBDeviceAttach (IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
3690{
3691#ifdef VBOX_WITH_USB
3692 LogFlowThisFunc (("aDevice=%p aError=%p\n", aDevice, aError));
3693
3694 AutoCaller autoCaller (this);
3695 ComAssertComRCRetRC (autoCaller.rc());
3696
3697 AutoWriteLock alock (this);
3698
3699 /* protect mpVM (we don't need error info, since it's a callback) */
3700 AutoVMCallerQuiet autoVMCaller (this);
3701 if (FAILED (autoVMCaller.rc()))
3702 {
3703 /* The VM may be no more operational when this message arrives
3704 * (e.g. it may be Saving or Stopping or just PoweredOff) --
3705 * autoVMCaller.rc() will return a failure in this case. */
3706 LogFlowThisFunc (("Attach request ignored (mMachineState=%d).\n",
3707 mMachineState));
3708 return autoVMCaller.rc();
3709 }
3710
3711 if (aError != NULL)
3712 {
3713 /* notify callbacks about the error */
3714 onUSBDeviceStateChange (aDevice, true /* aAttached */, aError);
3715 return S_OK;
3716 }
3717
3718 /* Don't proceed unless there's at least one USB hub. */
3719 if (!PDMR3USBHasHub (mpVM))
3720 {
3721 LogFlowThisFunc (("Attach request ignored (no USB controller).\n"));
3722 return E_FAIL;
3723 }
3724
3725 HRESULT rc = attachUSBDevice (aDevice, aMaskedIfs);
3726 if (FAILED (rc))
3727 {
3728 /* take the current error info */
3729 com::ErrorInfoKeeper eik;
3730 /* the error must be a VirtualBoxErrorInfo instance */
3731 ComPtr <IVirtualBoxErrorInfo> error = eik.takeError();
3732 Assert (!error.isNull());
3733 if (!error.isNull())
3734 {
3735 /* notify callbacks about the error */
3736 onUSBDeviceStateChange (aDevice, true /* aAttached */, error);
3737 }
3738 }
3739
3740 return rc;
3741
3742#else /* !VBOX_WITH_USB */
3743 return E_FAIL;
3744#endif /* !VBOX_WITH_USB */
3745}
3746
3747/**
3748 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
3749 * processRemoteUSBDevices().
3750 *
3751 * @note Locks this object for writing.
3752 */
3753HRESULT Console::onUSBDeviceDetach (IN_BSTR aId,
3754 IVirtualBoxErrorInfo *aError)
3755{
3756#ifdef VBOX_WITH_USB
3757 Guid Uuid (aId);
3758 LogFlowThisFunc (("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
3759
3760 AutoCaller autoCaller (this);
3761 AssertComRCReturnRC (autoCaller.rc());
3762
3763 AutoWriteLock alock (this);
3764
3765 /* Find the device. */
3766 ComObjPtr <OUSBDevice> device;
3767 USBDeviceList::iterator it = mUSBDevices.begin();
3768 while (it != mUSBDevices.end())
3769 {
3770 LogFlowThisFunc (("it={%RTuuid}\n", (*it)->id().raw()));
3771 if ((*it)->id() == Uuid)
3772 {
3773 device = *it;
3774 break;
3775 }
3776 ++ it;
3777 }
3778
3779
3780 if (device.isNull())
3781 {
3782 LogFlowThisFunc (("USB device not found.\n"));
3783
3784 /* The VM may be no more operational when this message arrives
3785 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
3786 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
3787 * failure in this case. */
3788
3789 AutoVMCallerQuiet autoVMCaller (this);
3790 if (FAILED (autoVMCaller.rc()))
3791 {
3792 LogFlowThisFunc (("Detach request ignored (mMachineState=%d).\n",
3793 mMachineState));
3794 return autoVMCaller.rc();
3795 }
3796
3797 /* the device must be in the list otherwise */
3798 AssertFailedReturn (E_FAIL);
3799 }
3800
3801 if (aError != NULL)
3802 {
3803 /* notify callback about an error */
3804 onUSBDeviceStateChange (device, false /* aAttached */, aError);
3805 return S_OK;
3806 }
3807
3808 HRESULT rc = detachUSBDevice (it);
3809
3810 if (FAILED (rc))
3811 {
3812 /* take the current error info */
3813 com::ErrorInfoKeeper eik;
3814 /* the error must be a VirtualBoxErrorInfo instance */
3815 ComPtr <IVirtualBoxErrorInfo> error = eik.takeError();
3816 Assert (!error.isNull());
3817 if (!error.isNull())
3818 {
3819 /* notify callbacks about the error */
3820 onUSBDeviceStateChange (device, false /* aAttached */, error);
3821 }
3822 }
3823
3824 return rc;
3825
3826#else /* !VBOX_WITH_USB */
3827 return E_FAIL;
3828#endif /* !VBOX_WITH_USB */
3829}
3830
3831/**
3832 * @note Temporarily locks this object for writing.
3833 */
3834HRESULT Console::getGuestProperty (IN_BSTR aName, BSTR *aValue,
3835 ULONG64 *aTimestamp, BSTR *aFlags)
3836{
3837#if !defined (VBOX_WITH_GUEST_PROPS)
3838 ReturnComNotImplemented();
3839#else
3840 if (!VALID_PTR (aName))
3841 return E_INVALIDARG;
3842 if (!VALID_PTR (aValue))
3843 return E_POINTER;
3844 if ((aTimestamp != NULL) && !VALID_PTR (aTimestamp))
3845 return E_POINTER;
3846 if ((aFlags != NULL) && !VALID_PTR (aFlags))
3847 return E_POINTER;
3848
3849 AutoCaller autoCaller (this);
3850 AssertComRCReturnRC (autoCaller.rc());
3851
3852 /* protect mpVM (if not NULL) */
3853 AutoVMCallerWeak autoVMCaller (this);
3854 CheckComRCReturnRC (autoVMCaller.rc());
3855
3856 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
3857 * autoVMCaller, so there is no need to hold a lock of this */
3858
3859 HRESULT rc = E_UNEXPECTED;
3860 using namespace guestProp;
3861
3862 VBOXHGCMSVCPARM parm[4];
3863 Utf8Str Utf8Name = aName;
3864 AssertReturn(!Utf8Name.isNull(), E_OUTOFMEMORY);
3865 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
3866
3867 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
3868 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
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 Utf8Str strBuffer(pszBuffer);
3885 strBuffer.cloneTo(aValue);
3886
3887 *aTimestamp = parm[2].u.uint64;
3888
3889 size_t iFlags = strBuffer.length() + 1;
3890 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
3891 }
3892 else
3893 aValue = NULL;
3894 }
3895 else
3896 rc = setError (E_UNEXPECTED,
3897 tr ("The service call failed with the error %Rrc"), vrc);
3898 return rc;
3899#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3900}
3901
3902/**
3903 * @note Temporarily locks this object for writing.
3904 */
3905HRESULT Console::setGuestProperty (IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
3906{
3907#if !defined (VBOX_WITH_GUEST_PROPS)
3908 ReturnComNotImplemented();
3909#else
3910 if (!VALID_PTR (aName))
3911 return E_INVALIDARG;
3912 if ((aValue != NULL) && !VALID_PTR (aValue))
3913 return E_INVALIDARG;
3914 if ((aFlags != NULL) && !VALID_PTR (aFlags))
3915 return E_INVALIDARG;
3916
3917 AutoCaller autoCaller (this);
3918 AssertComRCReturnRC (autoCaller.rc());
3919
3920 /* protect mpVM (if not NULL) */
3921 AutoVMCallerWeak autoVMCaller (this);
3922 CheckComRCReturnRC (autoVMCaller.rc());
3923
3924 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
3925 * autoVMCaller, so there is no need to hold a lock of this */
3926
3927 HRESULT rc = E_UNEXPECTED;
3928 using namespace guestProp;
3929
3930 VBOXHGCMSVCPARM parm[3];
3931 Utf8Str Utf8Name = aName;
3932 int vrc = VINF_SUCCESS;
3933
3934 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
3935 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
3936 /* The + 1 is the null terminator */
3937 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
3938 Utf8Str Utf8Value = aValue;
3939 if (aValue != NULL)
3940 {
3941 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
3942 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
3943 /* The + 1 is the null terminator */
3944 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
3945 }
3946 Utf8Str Utf8Flags = aFlags;
3947 if (aFlags != NULL)
3948 {
3949 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
3950 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
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