VirtualBox

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

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

Pass RDP client name via a guest property (xTracker #4123)

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

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