VirtualBox

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

Last change on this file since 12428 was 12126, checked in by vboxsync, 17 years ago

Drop existing RDP connection and allow the new client to connect, configurable, default off.

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