VirtualBox

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

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

Main/GuestProps: extra logging

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

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