VirtualBox

source: vbox/trunk/src/VBox/Main/MachineImpl.cpp@ 27684

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

Main/Machine: fix resource leak introduced by last change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 352.6 KB
Line 
1/* $Id: MachineImpl.cpp 27680 2010-03-24 18:28:43Z vboxsync $ */
2
3/** @file
4 * Implementation of IMachine in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/* Make sure all the stdint.h macros are included - must come first! */
24#ifndef __STDC_LIMIT_MACROS
25# define __STDC_LIMIT_MACROS
26#endif
27#ifndef __STDC_CONSTANT_MACROS
28# define __STDC_CONSTANT_MACROS
29#endif
30
31#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
32# include <errno.h>
33# include <sys/types.h>
34# include <sys/stat.h>
35# include <sys/ipc.h>
36# include <sys/sem.h>
37#endif
38
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "ProgressImpl.h"
42#include "MediumAttachmentImpl.h"
43#include "MediumImpl.h"
44#include "USBControllerImpl.h"
45#include "HostImpl.h"
46#include "SharedFolderImpl.h"
47#include "GuestOSTypeImpl.h"
48#include "VirtualBoxErrorInfoImpl.h"
49#include "GuestImpl.h"
50#include "StorageControllerImpl.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "Logging.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/string.h>
68
69#include <VBox/com/array.h>
70
71#include <VBox/err.h>
72#include <VBox/param.h>
73#include <VBox/settings.h>
74#include <VBox/ssm.h>
75
76#ifdef VBOX_WITH_GUEST_PROPS
77# include <VBox/HostServices/GuestPropertySvc.h>
78# include <VBox/com/array.h>
79#endif
80
81#include <algorithm>
82
83#include <typeinfo>
84
85#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
86#define HOSTSUFF_EXE ".exe"
87#else /* !RT_OS_WINDOWS */
88#define HOSTSUFF_EXE ""
89#endif /* !RT_OS_WINDOWS */
90
91// defines / prototypes
92/////////////////////////////////////////////////////////////////////////////
93
94/////////////////////////////////////////////////////////////////////////////
95// Machine::Data structure
96/////////////////////////////////////////////////////////////////////////////
97
98Machine::Data::Data()
99{
100 mRegistered = FALSE;
101 mAccessible = FALSE;
102 /* mUuid is initialized in Machine::init() */
103
104 mMachineState = MachineState_PoweredOff;
105 RTTimeNow(&mLastStateChange);
106
107 mMachineStateDeps = 0;
108 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
109 mMachineStateChangePending = 0;
110
111 mCurrentStateModified = TRUE;
112 mHandleCfgFile = NIL_RTFILE;
113
114 mSession.mPid = NIL_RTPROCESS;
115 mSession.mState = SessionState_Closed;
116}
117
118Machine::Data::~Data()
119{
120 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
121 {
122 RTSemEventMultiDestroy(mMachineStateDepsSem);
123 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
124 }
125}
126
127/////////////////////////////////////////////////////////////////////////////
128// Machine::UserData structure
129/////////////////////////////////////////////////////////////////////////////
130
131Machine::UserData::UserData()
132{
133 /* default values for a newly created machine */
134
135 mNameSync = TRUE;
136 mTeleporterEnabled = FALSE;
137 mTeleporterPort = 0;
138
139 /* mName, mOSTypeId, mSnapshotFolder, mSnapshotFolderFull are initialized in
140 * Machine::init() */
141}
142
143Machine::UserData::~UserData()
144{
145}
146
147/////////////////////////////////////////////////////////////////////////////
148// Machine::HWData structure
149/////////////////////////////////////////////////////////////////////////////
150
151Machine::HWData::HWData()
152{
153 /* default values for a newly created machine */
154 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
155 mMemorySize = 128;
156 mCPUCount = 1;
157 mCPUHotPlugEnabled = false;
158 mMemoryBalloonSize = 0;
159 mStatisticsUpdateInterval = 0;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mHWVirtExEnabled = true;
165 mHWVirtExNestedPagingEnabled = true;
166#if HC_ARCH_BITS == 64
167 /* Default value decision pending. */
168 mHWVirtExLargePagesEnabled = false;
169#else
170 /* Not supported on 32 bits hosts. */
171 mHWVirtExLargePagesEnabled = false;
172#endif
173 mHWVirtExVPIDEnabled = true;
174#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
175 mHWVirtExExclusive = false;
176#else
177 mHWVirtExExclusive = true;
178#endif
179#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
180 mPAEEnabled = true;
181#else
182 mPAEEnabled = false;
183#endif
184 mSyntheticCpu = false;
185 mPropertyServiceActive = false;
186 mHpetEnabled = false;
187
188 /* default boot order: floppy - DVD - HDD */
189 mBootOrder[0] = DeviceType_Floppy;
190 mBootOrder[1] = DeviceType_DVD;
191 mBootOrder[2] = DeviceType_HardDisk;
192 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
193 mBootOrder[i] = DeviceType_Null;
194
195 mClipboardMode = ClipboardMode_Bidirectional;
196 mGuestPropertyNotificationPatterns = "";
197
198 mFirmwareType = FirmwareType_BIOS;
199 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
200 mPointingHidType = PointingHidType_PS2Mouse;
201
202 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
203 mCPUAttached[i] = false;
204
205 mIoMgrType = IoMgrType_Async;
206#if defined(RT_OS_LINUX)
207 mIoBackendType = IoBackendType_Unbuffered;
208#else
209 mIoBackendType = IoBackendType_Buffered;
210#endif
211 mIoCacheEnabled = true;
212 mIoCacheSize = 5; /* 5MB */
213 mIoBandwidthMax = 0; /* Unlimited */
214}
215
216Machine::HWData::~HWData()
217{
218}
219
220/////////////////////////////////////////////////////////////////////////////
221// Machine::HDData structure
222/////////////////////////////////////////////////////////////////////////////
223
224Machine::MediaData::MediaData()
225{
226}
227
228Machine::MediaData::~MediaData()
229{
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine class
234/////////////////////////////////////////////////////////////////////////////
235
236// constructor / destructor
237/////////////////////////////////////////////////////////////////////////////
238
239Machine::Machine()
240 : mPeer(NULL), mParent(NULL)
241{}
242
243Machine::~Machine()
244{}
245
246HRESULT Machine::FinalConstruct()
247{
248 LogFlowThisFunc(("\n"));
249 return S_OK;
250}
251
252void Machine::FinalRelease()
253{
254 LogFlowThisFunc(("\n"));
255 uninit();
256}
257
258/**
259 * Initializes the instance.
260 *
261 * @param aParent Associated parent object
262 * @param aConfigFile Local file system path to the VM settings file (can
263 * be relative to the VirtualBox config directory).
264 * @param aMode Init_New, Init_Existing or Init_Registered
265 * @param aName name for the machine when aMode is Init_New
266 * (ignored otherwise)
267 * @param aOsType OS Type of this machine
268 * @param aOverride |TRUE| to override VM config file existence checks.
269 * |FALSE| refuses to overwrite existing VM configs.
270 * @param aNameSync |TRUE| to automatically sync settings dir and file
271 * name with the machine name. |FALSE| is used for legacy
272 * machines where the file name is specified by the
273 * user and should never change. Used only in Init_New
274 * mode (ignored otherwise).
275 * @param aId UUID of the machine. Required for aMode==Init_Registered
276 * and optional for aMode==Init_New. Used for consistency
277 * check when aMode is Init_Registered; must match UUID
278 * stored in the settings file. Used for predefining the
279 * UUID of a VM when aMode is Init_New.
280 *
281 * @return Success indicator. if not S_OK, the machine object is invalid
282 */
283HRESULT Machine::init(VirtualBox *aParent,
284 const Utf8Str &strConfigFile,
285 InitMode aMode,
286 CBSTR aName /* = NULL */,
287 GuestOSType *aOsType /* = NULL */,
288 BOOL aOverride /* = FALSE */,
289 BOOL aNameSync /* = TRUE */,
290 const Guid *aId /* = NULL */)
291{
292 LogFlowThisFuncEnter();
293 LogFlowThisFunc(("aConfigFile='%s', aMode=%d\n", strConfigFile.raw(), aMode));
294
295 AssertReturn(aParent, E_INVALIDARG);
296 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
297 AssertReturn(aMode != Init_New || (aName != NULL && *aName != '\0'),
298 E_INVALIDARG);
299 AssertReturn(aMode != Init_Registered || aId != NULL, E_FAIL);
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = S_OK;
306
307 /* share the parent weakly */
308 unconst(mParent) = aParent;
309
310 m_flModifications = 0;
311
312 /* allocate the essential machine data structure (the rest will be
313 * allocated later by initDataAndChildObjects() */
314 mData.allocate();
315
316 mData->m_pMachineConfigFile = NULL;
317 m_flModifications = 0;
318
319 /* memorize the config file name (as provided) */
320 mData->m_strConfigFile = strConfigFile;
321
322 /* get the full file name */
323 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
324 if (RT_FAILURE(vrc1))
325 return setError(VBOX_E_FILE_ERROR,
326 tr("Invalid machine settings file name '%s' (%Rrc)"),
327 strConfigFile.raw(),
328 vrc1);
329
330 if (aMode == Init_Registered)
331 {
332 mData->mRegistered = TRUE;
333
334 /* store the supplied UUID (will be used to check for UUID consistency
335 * in loadSettings() */
336 unconst(mData->mUuid) = *aId;
337
338 // now load the settings from XML:
339 rc = registeredInit();
340 }
341 else
342 {
343 if (aMode == Init_Import)
344 {
345 // we're reading the settings file below
346 }
347 else if (aMode == Init_New)
348 {
349 /* check for the file existence */
350 RTFILE f = NIL_RTFILE;
351 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
352 if ( RT_SUCCESS(vrc)
353 || vrc == VERR_SHARING_VIOLATION
354 )
355 {
356 if (!aOverride)
357 {
358 rc = setError(VBOX_E_FILE_ERROR,
359 tr("Machine settings file '%s' already exists"),
360 mData->m_strConfigFileFull.raw());
361 }
362 if (RT_SUCCESS(vrc))
363 RTFileClose(f);
364 }
365 else
366 {
367 if ( vrc != VERR_FILE_NOT_FOUND
368 && vrc != VERR_PATH_NOT_FOUND
369 )
370 rc = setError(VBOX_E_FILE_ERROR,
371 tr("Invalid machine settings file name '%s' (%Rrc)"),
372 mData->m_strConfigFileFull.raw(),
373 vrc);
374 }
375
376 // create an empty machine config
377 mData->m_pMachineConfigFile = new settings::MachineConfigFile(NULL);
378 }
379 else
380 AssertFailed();
381
382 if (SUCCEEDED(rc))
383 rc = initDataAndChildObjects();
384
385 if (SUCCEEDED(rc))
386 {
387 /* set to true now to cause uninit() to call
388 * uninitDataAndChildObjects() on failure */
389 mData->mAccessible = TRUE;
390
391 if (aMode != Init_New)
392 {
393 rc = loadSettings(false /* aRegistered */);
394 }
395 else
396 {
397 /* create the machine UUID */
398 if (aId)
399 unconst(mData->mUuid) = *aId;
400 else
401 unconst(mData->mUuid).create();
402
403 /* memorize the provided new machine's name */
404 mUserData->mName = aName;
405 mUserData->mNameSync = aNameSync;
406
407 /* initialize the default snapshots folder
408 * (note: depends on the name value set above!) */
409 rc = COMSETTER(SnapshotFolder)(NULL);
410 AssertComRC(rc);
411
412 if (aOsType)
413 {
414 /* Store OS type */
415 mUserData->mOSTypeId = aOsType->id();
416
417 /* Apply BIOS defaults */
418 mBIOSSettings->applyDefaults(aOsType);
419
420 /* Apply network adapters defaults */
421 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot)
422 mNetworkAdapters[slot]->applyDefaults(aOsType);
423
424 /* Apply serial port defaults */
425 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
426 mSerialPorts[slot]->applyDefaults(aOsType);
427 }
428 }
429
430 /* commit all changes made during the initialization */
431 if (SUCCEEDED(rc))
432 commit();
433 }
434 }
435
436 /* Confirm a successful initialization when it's the case */
437 if (SUCCEEDED(rc))
438 {
439 if (mData->mAccessible)
440 autoInitSpan.setSucceeded();
441 else
442 autoInitSpan.setLimited();
443 }
444
445 LogFlowThisFunc(("mName='%ls', mRegistered=%RTbool, mAccessible=%RTbool "
446 "rc=%08X\n",
447 !!mUserData ? mUserData->mName.raw() : NULL,
448 mData->mRegistered, mData->mAccessible, rc));
449
450 LogFlowThisFuncLeave();
451
452 return rc;
453}
454
455/**
456 * Initializes the registered machine by loading the settings file.
457 * This method is separated from #init() in order to make it possible to
458 * retry the operation after VirtualBox startup instead of refusing to
459 * startup the whole VirtualBox server in case if the settings file of some
460 * registered VM is invalid or inaccessible.
461 *
462 * @note Must be always called from this object's write lock
463 * (unless called from #init() that doesn't need any locking).
464 * @note Locks the mUSBController method for writing.
465 * @note Subclasses must not call this method.
466 */
467HRESULT Machine::registeredInit()
468{
469 AssertReturn(getClassID() == clsidMachine, E_FAIL);
470 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
471 AssertReturn(!mData->mAccessible, E_FAIL);
472
473 HRESULT rc = initDataAndChildObjects();
474
475 if (SUCCEEDED(rc))
476 {
477 /* Temporarily reset the registered flag in order to let setters
478 * potentially called from loadSettings() succeed (isMutable() used in
479 * all setters will return FALSE for a Machine instance if mRegistered
480 * is TRUE). */
481 mData->mRegistered = FALSE;
482
483 rc = loadSettings(true /* aRegistered */);
484
485 /* Restore the registered flag (even on failure) */
486 mData->mRegistered = TRUE;
487 }
488
489 if (SUCCEEDED(rc))
490 {
491 /* Set mAccessible to TRUE only if we successfully locked and loaded
492 * the settings file */
493 mData->mAccessible = TRUE;
494
495 /* commit all changes made during loading the settings file */
496 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
497 }
498 else
499 {
500 /* If the machine is registered, then, instead of returning a
501 * failure, we mark it as inaccessible and set the result to
502 * success to give it a try later */
503
504 /* fetch the current error info */
505 mData->mAccessError = com::ErrorInfo();
506 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
507 mData->mUuid.raw(),
508 mData->mAccessError.getText().raw()));
509
510 /* rollback all changes */
511 rollback(false /* aNotify */);
512
513 /* uninitialize the common part to make sure all data is reset to
514 * default (null) values */
515 uninitDataAndChildObjects();
516
517 rc = S_OK;
518 }
519
520 return rc;
521}
522
523/**
524 * Uninitializes the instance.
525 * Called either from FinalRelease() or by the parent when it gets destroyed.
526 *
527 * @note The caller of this method must make sure that this object
528 * a) doesn't have active callers on the current thread and b) is not locked
529 * by the current thread; otherwise uninit() will hang either a) due to
530 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
531 * a dead-lock caused by this thread waiting for all callers on the other
532 * threads are done but preventing them from doing so by holding a lock.
533 */
534void Machine::uninit()
535{
536 LogFlowThisFuncEnter();
537
538 Assert(!isWriteLockOnCurrentThread());
539
540 /* Enclose the state transition Ready->InUninit->NotReady */
541 AutoUninitSpan autoUninitSpan(this);
542 if (autoUninitSpan.uninitDone())
543 return;
544
545 Assert(getClassID() == clsidMachine);
546 Assert(!!mData);
547
548 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
549 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
550
551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
552
553 if (!mData->mSession.mMachine.isNull())
554 {
555 /* Theoretically, this can only happen if the VirtualBox server has been
556 * terminated while there were clients running that owned open direct
557 * sessions. Since in this case we are definitely called by
558 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
559 * won't happen on the client watcher thread (because it does
560 * VirtualBox::addCaller() for the duration of the
561 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
562 * cannot happen until the VirtualBox caller is released). This is
563 * important, because SessionMachine::uninit() cannot correctly operate
564 * after we return from this method (it expects the Machine instance is
565 * still valid). We'll call it ourselves below.
566 */
567 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
568 (SessionMachine*)mData->mSession.mMachine));
569
570 if (Global::IsOnlineOrTransient(mData->mMachineState))
571 {
572 LogWarningThisFunc(("Setting state to Aborted!\n"));
573 /* set machine state using SessionMachine reimplementation */
574 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
575 }
576
577 /*
578 * Uninitialize SessionMachine using public uninit() to indicate
579 * an unexpected uninitialization.
580 */
581 mData->mSession.mMachine->uninit();
582 /* SessionMachine::uninit() must set mSession.mMachine to null */
583 Assert(mData->mSession.mMachine.isNull());
584 }
585
586 /* the lock is no more necessary (SessionMachine is uninitialized) */
587 alock.leave();
588
589 // has machine been modified?
590 if (m_flModifications)
591 {
592 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
593 rollback(false /* aNotify */);
594 }
595
596 if (mData->mAccessible)
597 uninitDataAndChildObjects();
598
599 /* free the essential data structure last */
600 mData.free();
601
602 LogFlowThisFuncLeave();
603}
604
605// IMachine properties
606/////////////////////////////////////////////////////////////////////////////
607
608STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
609{
610 CheckComArgOutPointerValid(aParent);
611
612 AutoLimitedCaller autoCaller(this);
613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
614
615 /* mParent is constant during life time, no need to lock */
616 ComObjPtr<VirtualBox> pVirtualBox(mParent);
617 pVirtualBox.queryInterfaceTo(aParent);
618
619 return S_OK;
620}
621
622STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
623{
624 CheckComArgOutPointerValid(aAccessible);
625
626 AutoLimitedCaller autoCaller(this);
627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
628
629 LogFlowThisFunc(("ENTER\n"));
630
631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
632
633 HRESULT rc = S_OK;
634
635 if (!mData->mAccessible)
636 {
637 /* try to initialize the VM once more if not accessible */
638
639 AutoReinitSpan autoReinitSpan(this);
640 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
641
642#ifdef DEBUG
643 LogFlowThisFunc(("Dumping media backreferences\n"));
644 mParent->dumpAllBackRefs();
645#endif
646
647 if (mData->m_pMachineConfigFile)
648 {
649 // reset the XML file to force loadSettings() (called from registeredInit())
650 // to parse it again; the file might have changed
651 delete mData->m_pMachineConfigFile;
652 mData->m_pMachineConfigFile = NULL;
653 }
654
655 rc = registeredInit();
656
657 if (SUCCEEDED(rc) && mData->mAccessible)
658 {
659 autoReinitSpan.setSucceeded();
660
661 /* make sure interesting parties will notice the accessibility
662 * state change */
663 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
664 mParent->onMachineDataChange(mData->mUuid);
665 }
666 }
667
668 if (SUCCEEDED(rc))
669 *aAccessible = mData->mAccessible;
670
671 LogFlowThisFuncLeave();
672
673 return rc;
674}
675
676STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
677{
678 CheckComArgOutPointerValid(aAccessError);
679
680 AutoLimitedCaller autoCaller(this);
681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
682
683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
684
685 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
686 {
687 /* return shortly */
688 aAccessError = NULL;
689 return S_OK;
690 }
691
692 HRESULT rc = S_OK;
693
694 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
695 rc = errorInfo.createObject();
696 if (SUCCEEDED(rc))
697 {
698 errorInfo->init(mData->mAccessError.getResultCode(),
699 mData->mAccessError.getInterfaceID(),
700 mData->mAccessError.getComponent(),
701 mData->mAccessError.getText());
702 rc = errorInfo.queryInterfaceTo(aAccessError);
703 }
704
705 return rc;
706}
707
708STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
709{
710 CheckComArgOutPointerValid(aName);
711
712 AutoCaller autoCaller(this);
713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
714
715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
716
717 mUserData->mName.cloneTo(aName);
718
719 return S_OK;
720}
721
722STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
723{
724 CheckComArgStrNotEmptyOrNull(aName);
725
726 AutoCaller autoCaller(this);
727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
728
729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
730
731 HRESULT rc = checkStateDependency(MutableStateDep);
732 if (FAILED(rc)) return rc;
733
734 setModified(IsModified_MachineData);
735 mUserData.backup();
736 mUserData->mName = aName;
737
738 return S_OK;
739}
740
741STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
742{
743 CheckComArgOutPointerValid(aDescription);
744
745 AutoCaller autoCaller(this);
746 if (FAILED(autoCaller.rc())) return autoCaller.rc();
747
748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
749
750 mUserData->mDescription.cloneTo(aDescription);
751
752 return S_OK;
753}
754
755STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
756{
757 AutoCaller autoCaller(this);
758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
759
760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
761
762 HRESULT rc = checkStateDependency(MutableStateDep);
763 if (FAILED(rc)) return rc;
764
765 setModified(IsModified_MachineData);
766 mUserData.backup();
767 mUserData->mDescription = aDescription;
768
769 return S_OK;
770}
771
772STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
773{
774 CheckComArgOutPointerValid(aId);
775
776 AutoLimitedCaller autoCaller(this);
777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
778
779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
780
781 mData->mUuid.toUtf16().cloneTo(aId);
782
783 return S_OK;
784}
785
786STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
787{
788 CheckComArgOutPointerValid(aOSTypeId);
789
790 AutoCaller autoCaller(this);
791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
792
793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
794
795 mUserData->mOSTypeId.cloneTo(aOSTypeId);
796
797 return S_OK;
798}
799
800STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
801{
802 CheckComArgStrNotEmptyOrNull(aOSTypeId);
803
804 AutoCaller autoCaller(this);
805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
806
807 /* look up the object by Id to check it is valid */
808 ComPtr<IGuestOSType> guestOSType;
809 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
810 if (FAILED(rc)) return rc;
811
812 /* when setting, always use the "etalon" value for consistency -- lookup
813 * by ID is case-insensitive and the input value may have different case */
814 Bstr osTypeId;
815 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
816 if (FAILED(rc)) return rc;
817
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819
820 rc = checkStateDependency(MutableStateDep);
821 if (FAILED(rc)) return rc;
822
823 setModified(IsModified_MachineData);
824 mUserData.backup();
825 mUserData->mOSTypeId = osTypeId;
826
827 return S_OK;
828}
829
830
831STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
832{
833 CheckComArgOutPointerValid(aFirmwareType);
834
835 AutoCaller autoCaller(this);
836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
837
838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
839
840 *aFirmwareType = mHWData->mFirmwareType;
841
842 return S_OK;
843}
844
845STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
846{
847 AutoCaller autoCaller(this);
848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
850
851 int rc = checkStateDependency(MutableStateDep);
852 if (FAILED(rc)) return rc;
853
854 setModified(IsModified_MachineData);
855 mHWData.backup();
856 mHWData->mFirmwareType = aFirmwareType;
857
858 return S_OK;
859}
860
861STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
862{
863 CheckComArgOutPointerValid(aKeyboardHidType);
864
865 AutoCaller autoCaller(this);
866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
867
868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
869
870 *aKeyboardHidType = mHWData->mKeyboardHidType;
871
872 return S_OK;
873}
874
875STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
876{
877 AutoCaller autoCaller(this);
878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
880
881 int rc = checkStateDependency(MutableStateDep);
882 if (FAILED(rc)) return rc;
883
884 setModified(IsModified_MachineData);
885 mHWData.backup();
886 mHWData->mKeyboardHidType = aKeyboardHidType;
887
888 return S_OK;
889}
890
891STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
892{
893 CheckComArgOutPointerValid(aPointingHidType);
894
895 AutoCaller autoCaller(this);
896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
897
898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
899
900 *aPointingHidType = mHWData->mPointingHidType;
901
902 return S_OK;
903}
904
905STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
906{
907 AutoCaller autoCaller(this);
908 if (FAILED(autoCaller.rc())) return autoCaller.rc();
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 int rc = checkStateDependency(MutableStateDep);
912 if (FAILED(rc)) return rc;
913
914 setModified(IsModified_MachineData);
915 mHWData.backup();
916 mHWData->mPointingHidType = aPointingHidType;
917
918 return S_OK;
919}
920
921STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
922{
923 if (!aHWVersion)
924 return E_POINTER;
925
926 AutoCaller autoCaller(this);
927 if (FAILED(autoCaller.rc())) return autoCaller.rc();
928
929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
930
931 mHWData->mHWVersion.cloneTo(aHWVersion);
932
933 return S_OK;
934}
935
936STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
937{
938 /* check known version */
939 Utf8Str hwVersion = aHWVersion;
940 if ( hwVersion.compare("1") != 0
941 && hwVersion.compare("2") != 0)
942 return setError(E_INVALIDARG,
943 tr("Invalid hardware version: %ls\n"), aHWVersion);
944
945 AutoCaller autoCaller(this);
946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
947
948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
949
950 HRESULT rc = checkStateDependency(MutableStateDep);
951 if (FAILED(rc)) return rc;
952
953 setModified(IsModified_MachineData);
954 mHWData.backup();
955 mHWData->mHWVersion = hwVersion;
956
957 return S_OK;
958}
959
960STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
961{
962 CheckComArgOutPointerValid(aUUID);
963
964 AutoCaller autoCaller(this);
965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
966
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (!mHWData->mHardwareUUID.isEmpty())
970 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
971 else
972 mData->mUuid.toUtf16().cloneTo(aUUID);
973
974 return S_OK;
975}
976
977STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
978{
979 Guid hardwareUUID(aUUID);
980 if (hardwareUUID.isEmpty())
981 return E_INVALIDARG;
982
983 AutoCaller autoCaller(this);
984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
985
986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
987
988 HRESULT rc = checkStateDependency(MutableStateDep);
989 if (FAILED(rc)) return rc;
990
991 setModified(IsModified_MachineData);
992 mHWData.backup();
993 if (hardwareUUID == mData->mUuid)
994 mHWData->mHardwareUUID.clear();
995 else
996 mHWData->mHardwareUUID = hardwareUUID;
997
998 return S_OK;
999}
1000
1001STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1002{
1003 if (!memorySize)
1004 return E_POINTER;
1005
1006 AutoCaller autoCaller(this);
1007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1008
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 *memorySize = mHWData->mMemorySize;
1012
1013 return S_OK;
1014}
1015
1016STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1017{
1018 /* check RAM limits */
1019 if ( memorySize < MM_RAM_MIN_IN_MB
1020 || memorySize > MM_RAM_MAX_IN_MB
1021 )
1022 return setError(E_INVALIDARG,
1023 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1024 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1025
1026 AutoCaller autoCaller(this);
1027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1028
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 HRESULT rc = checkStateDependency(MutableStateDep);
1032 if (FAILED(rc)) return rc;
1033
1034 setModified(IsModified_MachineData);
1035 mHWData.backup();
1036 mHWData->mMemorySize = memorySize;
1037
1038 return S_OK;
1039}
1040
1041STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1042{
1043 if (!CPUCount)
1044 return E_POINTER;
1045
1046 AutoCaller autoCaller(this);
1047 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1048
1049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1050
1051 *CPUCount = mHWData->mCPUCount;
1052
1053 return S_OK;
1054}
1055
1056STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1057{
1058 /* check CPU limits */
1059 if ( CPUCount < SchemaDefs::MinCPUCount
1060 || CPUCount > SchemaDefs::MaxCPUCount
1061 )
1062 return setError(E_INVALIDARG,
1063 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1064 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1065
1066 AutoCaller autoCaller(this);
1067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1068
1069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1070
1071 /* We cant go below the current number of CPUs if hotplug is enabled*/
1072 if (mHWData->mCPUHotPlugEnabled)
1073 {
1074 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1075 {
1076 if (mHWData->mCPUAttached[idx])
1077 return setError(E_INVALIDARG,
1078 tr(": %lu (must be higher than or equal to %lu)"),
1079 CPUCount, idx+1);
1080 }
1081 }
1082
1083 HRESULT rc = checkStateDependency(MutableStateDep);
1084 if (FAILED(rc)) return rc;
1085
1086 setModified(IsModified_MachineData);
1087 mHWData.backup();
1088 mHWData->mCPUCount = CPUCount;
1089
1090 return S_OK;
1091}
1092
1093STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1094{
1095 if (!enabled)
1096 return E_POINTER;
1097
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100
1101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1102
1103 *enabled = mHWData->mCPUHotPlugEnabled;
1104
1105 return S_OK;
1106}
1107
1108STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1109{
1110 HRESULT rc = S_OK;
1111
1112 AutoCaller autoCaller(this);
1113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1114
1115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1116
1117 rc = checkStateDependency(MutableStateDep);
1118 if (FAILED(rc)) return rc;
1119
1120 if (mHWData->mCPUHotPlugEnabled != enabled)
1121 {
1122 if (enabled)
1123 {
1124 setModified(IsModified_MachineData);
1125 mHWData.backup();
1126
1127 /* Add the amount of CPUs currently attached */
1128 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1129 {
1130 mHWData->mCPUAttached[i] = true;
1131 }
1132 }
1133 else
1134 {
1135 /*
1136 * We can disable hotplug only if the amount of maximum CPUs is equal
1137 * to the amount of attached CPUs
1138 */
1139 unsigned cCpusAttached = 0;
1140 unsigned iHighestId = 0;
1141
1142 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1143 {
1144 if (mHWData->mCPUAttached[i])
1145 {
1146 cCpusAttached++;
1147 iHighestId = i;
1148 }
1149 }
1150
1151 if ( (cCpusAttached != mHWData->mCPUCount)
1152 || (iHighestId >= mHWData->mCPUCount))
1153 return setError(E_INVALIDARG,
1154 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
1155
1156 setModified(IsModified_MachineData);
1157 mHWData.backup();
1158 }
1159 }
1160
1161 mHWData->mCPUHotPlugEnabled = enabled;
1162
1163 return rc;
1164}
1165
1166STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1167{
1168 CheckComArgOutPointerValid(enabled);
1169
1170 AutoCaller autoCaller(this);
1171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 *enabled = mHWData->mHpetEnabled;
1175
1176 return S_OK;
1177}
1178
1179STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1180{
1181 HRESULT rc = S_OK;
1182
1183 AutoCaller autoCaller(this);
1184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 rc = checkStateDependency(MutableStateDep);
1188 if (FAILED(rc)) return rc;
1189
1190 setModified(IsModified_MachineData);
1191 mHWData.backup();
1192
1193 mHWData->mHpetEnabled = enabled;
1194
1195 return rc;
1196}
1197
1198STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1199{
1200 if (!memorySize)
1201 return E_POINTER;
1202
1203 AutoCaller autoCaller(this);
1204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1205
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *memorySize = mHWData->mVRAMSize;
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1214{
1215 /* check VRAM limits */
1216 if (memorySize < SchemaDefs::MinGuestVRAM ||
1217 memorySize > SchemaDefs::MaxGuestVRAM)
1218 return setError(E_INVALIDARG,
1219 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1220 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1221
1222 AutoCaller autoCaller(this);
1223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1224
1225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1226
1227 HRESULT rc = checkStateDependency(MutableStateDep);
1228 if (FAILED(rc)) return rc;
1229
1230 setModified(IsModified_MachineData);
1231 mHWData.backup();
1232 mHWData->mVRAMSize = memorySize;
1233
1234 return S_OK;
1235}
1236
1237/** @todo this method should not be public */
1238STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1239{
1240 if (!memoryBalloonSize)
1241 return E_POINTER;
1242
1243 AutoCaller autoCaller(this);
1244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1245
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1249
1250 return S_OK;
1251}
1252
1253/** @todo this method should not be public */
1254STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1255{
1256 /* check limits */
1257 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1258 return setError(E_INVALIDARG,
1259 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1260 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1261
1262 AutoCaller autoCaller(this);
1263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1264
1265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1266
1267 HRESULT rc = checkStateDependency(MutableStateDep);
1268 if (FAILED(rc)) return rc;
1269
1270 setModified(IsModified_MachineData);
1271 mHWData.backup();
1272 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1273
1274 return S_OK;
1275}
1276
1277/** @todo this method should not be public */
1278STDMETHODIMP Machine::COMGETTER(StatisticsUpdateInterval)(ULONG *statisticsUpdateInterval)
1279{
1280 if (!statisticsUpdateInterval)
1281 return E_POINTER;
1282
1283 AutoCaller autoCaller(this);
1284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1285
1286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 *statisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
1289
1290 return S_OK;
1291}
1292
1293/** @todo this method should not be public */
1294STDMETHODIMP Machine::COMSETTER(StatisticsUpdateInterval)(ULONG statisticsUpdateInterval)
1295{
1296 AutoCaller autoCaller(this);
1297 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1298
1299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1300
1301 HRESULT rc = checkStateDependency(MutableStateDep);
1302 if (FAILED(rc)) return rc;
1303
1304 setModified(IsModified_MachineData);
1305 mHWData.backup();
1306 mHWData->mStatisticsUpdateInterval = statisticsUpdateInterval;
1307
1308 return S_OK;
1309}
1310
1311
1312STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1313{
1314 if (!enabled)
1315 return E_POINTER;
1316
1317 AutoCaller autoCaller(this);
1318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1319
1320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1321
1322 *enabled = mHWData->mAccelerate3DEnabled;
1323
1324 return S_OK;
1325}
1326
1327STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1328{
1329 AutoCaller autoCaller(this);
1330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1331
1332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1333
1334 HRESULT rc = checkStateDependency(MutableStateDep);
1335 if (FAILED(rc)) return rc;
1336
1337 /** @todo check validity! */
1338
1339 setModified(IsModified_MachineData);
1340 mHWData.backup();
1341 mHWData->mAccelerate3DEnabled = enable;
1342
1343 return S_OK;
1344}
1345
1346
1347STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1348{
1349 if (!enabled)
1350 return E_POINTER;
1351
1352 AutoCaller autoCaller(this);
1353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1354
1355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1356
1357 *enabled = mHWData->mAccelerate2DVideoEnabled;
1358
1359 return S_OK;
1360}
1361
1362STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1363{
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 /** @todo check validity! */
1373
1374 setModified(IsModified_MachineData);
1375 mHWData.backup();
1376 mHWData->mAccelerate2DVideoEnabled = enable;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1382{
1383 if (!monitorCount)
1384 return E_POINTER;
1385
1386 AutoCaller autoCaller(this);
1387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1388
1389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1390
1391 *monitorCount = mHWData->mMonitorCount;
1392
1393 return S_OK;
1394}
1395
1396STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1397{
1398 /* make sure monitor count is a sensible number */
1399 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1400 return setError(E_INVALIDARG,
1401 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1402 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1403
1404 AutoCaller autoCaller(this);
1405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1406
1407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1408
1409 HRESULT rc = checkStateDependency(MutableStateDep);
1410 if (FAILED(rc)) return rc;
1411
1412 setModified(IsModified_MachineData);
1413 mHWData.backup();
1414 mHWData->mMonitorCount = monitorCount;
1415
1416 return S_OK;
1417}
1418
1419STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1420{
1421 if (!biosSettings)
1422 return E_POINTER;
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 /* mBIOSSettings is constant during life time, no need to lock */
1428 mBIOSSettings.queryInterfaceTo(biosSettings);
1429
1430 return S_OK;
1431}
1432
1433STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1434{
1435 if (!aVal)
1436 return E_POINTER;
1437
1438 AutoCaller autoCaller(this);
1439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1440
1441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1442
1443 switch(property)
1444 {
1445 case CPUPropertyType_PAE:
1446 *aVal = mHWData->mPAEEnabled;
1447 break;
1448
1449 case CPUPropertyType_Synthetic:
1450 *aVal = mHWData->mSyntheticCpu;
1451 break;
1452
1453 default:
1454 return E_INVALIDARG;
1455 }
1456 return S_OK;
1457}
1458
1459STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1460{
1461 AutoCaller autoCaller(this);
1462 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1463
1464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1465
1466 HRESULT rc = checkStateDependency(MutableStateDep);
1467 if (FAILED(rc)) return rc;
1468
1469 switch(property)
1470 {
1471 case CPUPropertyType_PAE:
1472 setModified(IsModified_MachineData);
1473 mHWData.backup();
1474 mHWData->mPAEEnabled = !!aVal;
1475 break;
1476
1477 case CPUPropertyType_Synthetic:
1478 setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mSyntheticCpu = !!aVal;
1481 break;
1482
1483 default:
1484 return E_INVALIDARG;
1485 }
1486 return S_OK;
1487}
1488
1489STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1490{
1491 CheckComArgOutPointerValid(aValEax);
1492 CheckComArgOutPointerValid(aValEbx);
1493 CheckComArgOutPointerValid(aValEcx);
1494 CheckComArgOutPointerValid(aValEdx);
1495
1496 AutoCaller autoCaller(this);
1497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1498
1499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1500
1501 switch(aId)
1502 {
1503 case 0x0:
1504 case 0x1:
1505 case 0x2:
1506 case 0x3:
1507 case 0x4:
1508 case 0x5:
1509 case 0x6:
1510 case 0x7:
1511 case 0x8:
1512 case 0x9:
1513 case 0xA:
1514 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1515 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1516
1517 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1518 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1519 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1520 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1521 break;
1522
1523 case 0x80000000:
1524 case 0x80000001:
1525 case 0x80000002:
1526 case 0x80000003:
1527 case 0x80000004:
1528 case 0x80000005:
1529 case 0x80000006:
1530 case 0x80000007:
1531 case 0x80000008:
1532 case 0x80000009:
1533 case 0x8000000A:
1534 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1535 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1536
1537 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1538 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1539 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1540 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1541 break;
1542
1543 default:
1544 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1545 }
1546 return S_OK;
1547}
1548
1549STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1550{
1551 AutoCaller autoCaller(this);
1552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 HRESULT rc = checkStateDependency(MutableStateDep);
1557 if (FAILED(rc)) return rc;
1558
1559 switch(aId)
1560 {
1561 case 0x0:
1562 case 0x1:
1563 case 0x2:
1564 case 0x3:
1565 case 0x4:
1566 case 0x5:
1567 case 0x6:
1568 case 0x7:
1569 case 0x8:
1570 case 0x9:
1571 case 0xA:
1572 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1573 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1574 setModified(IsModified_MachineData);
1575 mHWData.backup();
1576 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1577 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1578 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1579 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
1580 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
1581 break;
1582
1583 case 0x80000000:
1584 case 0x80000001:
1585 case 0x80000002:
1586 case 0x80000003:
1587 case 0x80000004:
1588 case 0x80000005:
1589 case 0x80000006:
1590 case 0x80000007:
1591 case 0x80000008:
1592 case 0x80000009:
1593 case 0x8000000A:
1594 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1595 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1596 setModified(IsModified_MachineData);
1597 mHWData.backup();
1598 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
1599 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
1600 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
1601 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
1602 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
1603 break;
1604
1605 default:
1606 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1607 }
1608 return S_OK;
1609}
1610
1611STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
1612{
1613 AutoCaller autoCaller(this);
1614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1615
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 switch(aId)
1622 {
1623 case 0x0:
1624 case 0x1:
1625 case 0x2:
1626 case 0x3:
1627 case 0x4:
1628 case 0x5:
1629 case 0x6:
1630 case 0x7:
1631 case 0x8:
1632 case 0x9:
1633 case 0xA:
1634 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1635 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1636 setModified(IsModified_MachineData);
1637 mHWData.backup();
1638 /* Invalidate leaf. */
1639 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
1640 break;
1641
1642 case 0x80000000:
1643 case 0x80000001:
1644 case 0x80000002:
1645 case 0x80000003:
1646 case 0x80000004:
1647 case 0x80000005:
1648 case 0x80000006:
1649 case 0x80000007:
1650 case 0x80000008:
1651 case 0x80000009:
1652 case 0x8000000A:
1653 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1654 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1655 setModified(IsModified_MachineData);
1656 mHWData.backup();
1657 /* Invalidate leaf. */
1658 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
1659 break;
1660
1661 default:
1662 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1663 }
1664 return S_OK;
1665}
1666
1667STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
1668{
1669 AutoCaller autoCaller(this);
1670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1671
1672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 HRESULT rc = checkStateDependency(MutableStateDep);
1675 if (FAILED(rc)) return rc;
1676
1677 setModified(IsModified_MachineData);
1678 mHWData.backup();
1679
1680 /* Invalidate all standard leafs. */
1681 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
1682 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
1683
1684 /* Invalidate all extended leafs. */
1685 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
1686 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
1687
1688 return S_OK;
1689}
1690
1691STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
1692{
1693 if (!aVal)
1694 return E_POINTER;
1695
1696 AutoCaller autoCaller(this);
1697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1698
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 switch(property)
1702 {
1703 case HWVirtExPropertyType_Enabled:
1704 *aVal = mHWData->mHWVirtExEnabled;
1705 break;
1706
1707 case HWVirtExPropertyType_Exclusive:
1708 *aVal = mHWData->mHWVirtExExclusive;
1709 break;
1710
1711 case HWVirtExPropertyType_VPID:
1712 *aVal = mHWData->mHWVirtExVPIDEnabled;
1713 break;
1714
1715 case HWVirtExPropertyType_NestedPaging:
1716 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
1717 break;
1718
1719 case HWVirtExPropertyType_LargePages:
1720 *aVal = mHWData->mHWVirtExLargePagesEnabled;
1721 break;
1722
1723 default:
1724 return E_INVALIDARG;
1725 }
1726 return S_OK;
1727}
1728
1729STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
1730{
1731 AutoCaller autoCaller(this);
1732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1733
1734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 HRESULT rc = checkStateDependency(MutableStateDep);
1737 if (FAILED(rc)) return rc;
1738
1739 switch(property)
1740 {
1741 case HWVirtExPropertyType_Enabled:
1742 setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 mHWData->mHWVirtExEnabled = !!aVal;
1745 break;
1746
1747 case HWVirtExPropertyType_Exclusive:
1748 setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 mHWData->mHWVirtExExclusive = !!aVal;
1751 break;
1752
1753 case HWVirtExPropertyType_VPID:
1754 setModified(IsModified_MachineData);
1755 mHWData.backup();
1756 mHWData->mHWVirtExVPIDEnabled = !!aVal;
1757 break;
1758
1759 case HWVirtExPropertyType_NestedPaging:
1760 setModified(IsModified_MachineData);
1761 mHWData.backup();
1762 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
1763 break;
1764
1765 case HWVirtExPropertyType_LargePages:
1766 setModified(IsModified_MachineData);
1767 mHWData.backup();
1768 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
1769 break;
1770
1771 default:
1772 return E_INVALIDARG;
1773 }
1774
1775 return S_OK;
1776}
1777
1778STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
1779{
1780 CheckComArgOutPointerValid(aSnapshotFolder);
1781
1782 AutoCaller autoCaller(this);
1783 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1784
1785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 mUserData->mSnapshotFolderFull.cloneTo(aSnapshotFolder);
1788
1789 return S_OK;
1790}
1791
1792STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
1793{
1794 /* @todo (r=dmik):
1795 * 1. Allow to change the name of the snapshot folder containing snapshots
1796 * 2. Rename the folder on disk instead of just changing the property
1797 * value (to be smart and not to leave garbage). Note that it cannot be
1798 * done here because the change may be rolled back. Thus, the right
1799 * place is #saveSettings().
1800 */
1801
1802 AutoCaller autoCaller(this);
1803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1804
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 HRESULT rc = checkStateDependency(MutableStateDep);
1808 if (FAILED(rc)) return rc;
1809
1810 if (!mData->mCurrentSnapshot.isNull())
1811 return setError(E_FAIL,
1812 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
1813
1814 Utf8Str snapshotFolder = aSnapshotFolder;
1815
1816 if (snapshotFolder.isEmpty())
1817 {
1818 if (isInOwnDir())
1819 {
1820 /* the default snapshots folder is 'Snapshots' in the machine dir */
1821 snapshotFolder = "Snapshots";
1822 }
1823 else
1824 {
1825 /* the default snapshots folder is {UUID}, for backwards
1826 * compatibility and to resolve conflicts */
1827 snapshotFolder = Utf8StrFmt("{%RTuuid}", mData->mUuid.raw());
1828 }
1829 }
1830
1831 int vrc = calculateFullPath(snapshotFolder, snapshotFolder);
1832 if (RT_FAILURE(vrc))
1833 return setError(E_FAIL,
1834 tr("Invalid snapshot folder '%ls' (%Rrc)"),
1835 aSnapshotFolder, vrc);
1836
1837 setModified(IsModified_MachineData);
1838 mUserData.backup();
1839 mUserData->mSnapshotFolder = aSnapshotFolder;
1840 mUserData->mSnapshotFolderFull = snapshotFolder;
1841
1842 return S_OK;
1843}
1844
1845STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
1846{
1847 if (ComSafeArrayOutIsNull(aAttachments))
1848 return E_POINTER;
1849
1850 AutoCaller autoCaller(this);
1851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1852
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854
1855 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
1856 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
1857
1858 return S_OK;
1859}
1860
1861STDMETHODIMP Machine::COMGETTER(VRDPServer)(IVRDPServer **vrdpServer)
1862{
1863#ifdef VBOX_WITH_VRDP
1864 if (!vrdpServer)
1865 return E_POINTER;
1866
1867 AutoCaller autoCaller(this);
1868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1869
1870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1871
1872 Assert(!!mVRDPServer);
1873 mVRDPServer.queryInterfaceTo(vrdpServer);
1874
1875 return S_OK;
1876#else
1877 NOREF(vrdpServer);
1878 ReturnComNotImplemented();
1879#endif
1880}
1881
1882STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
1883{
1884 if (!audioAdapter)
1885 return E_POINTER;
1886
1887 AutoCaller autoCaller(this);
1888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1889
1890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 mAudioAdapter.queryInterfaceTo(audioAdapter);
1893 return S_OK;
1894}
1895
1896STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
1897{
1898#ifdef VBOX_WITH_VUSB
1899 CheckComArgOutPointerValid(aUSBController);
1900
1901 AutoCaller autoCaller(this);
1902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1903 MultiResult rc (S_OK);
1904
1905# ifdef VBOX_WITH_USB
1906 rc = mParent->host()->checkUSBProxyService();
1907 if (FAILED(rc)) return rc;
1908# endif
1909
1910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 return rc = mUSBController.queryInterfaceTo(aUSBController);
1913#else
1914 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1915 * extended error info to indicate that USB is simply not available
1916 * (w/o treting it as a failure), for example, as in OSE */
1917 NOREF(aUSBController);
1918 ReturnComNotImplemented();
1919#endif /* VBOX_WITH_VUSB */
1920}
1921
1922STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
1923{
1924 CheckComArgOutPointerValid(aFilePath);
1925
1926 AutoLimitedCaller autoCaller(this);
1927 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1928
1929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1930
1931 mData->m_strConfigFileFull.cloneTo(aFilePath);
1932 return S_OK;
1933}
1934
1935STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
1936{
1937 CheckComArgOutPointerValid(aModified);
1938
1939 AutoCaller autoCaller(this);
1940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1941
1942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
1944 HRESULT rc = checkStateDependency(MutableStateDep);
1945 if (FAILED(rc)) return rc;
1946
1947 if (mData->mInitMode == Init_New)
1948 /*
1949 * if this is a new machine then no config file exists yet, so always return TRUE
1950 */
1951 *aModified = TRUE;
1952 else
1953 *aModified = (m_flModifications != 0);
1954
1955 return S_OK;
1956}
1957
1958STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
1959{
1960 CheckComArgOutPointerValid(aSessionState);
1961
1962 AutoCaller autoCaller(this);
1963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1964
1965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
1967 *aSessionState = mData->mSession.mState;
1968
1969 return S_OK;
1970}
1971
1972STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
1973{
1974 CheckComArgOutPointerValid(aSessionType);
1975
1976 AutoCaller autoCaller(this);
1977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1978
1979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 mData->mSession.mType.cloneTo(aSessionType);
1982
1983 return S_OK;
1984}
1985
1986STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
1987{
1988 CheckComArgOutPointerValid(aSessionPid);
1989
1990 AutoCaller autoCaller(this);
1991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1992
1993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1994
1995 *aSessionPid = mData->mSession.mPid;
1996
1997 return S_OK;
1998}
1999
2000STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2001{
2002 if (!machineState)
2003 return E_POINTER;
2004
2005 AutoCaller autoCaller(this);
2006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2007
2008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
2010 *machineState = mData->mMachineState;
2011
2012 return S_OK;
2013}
2014
2015STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2016{
2017 CheckComArgOutPointerValid(aLastStateChange);
2018
2019 AutoCaller autoCaller(this);
2020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2021
2022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2023
2024 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2025
2026 return S_OK;
2027}
2028
2029STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2030{
2031 CheckComArgOutPointerValid(aStateFilePath);
2032
2033 AutoCaller autoCaller(this);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2037
2038 mSSData->mStateFilePath.cloneTo(aStateFilePath);
2039
2040 return S_OK;
2041}
2042
2043STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2044{
2045 CheckComArgOutPointerValid(aLogFolder);
2046
2047 AutoCaller autoCaller(this);
2048 AssertComRCReturnRC(autoCaller.rc());
2049
2050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2051
2052 Utf8Str logFolder;
2053 getLogFolder(logFolder);
2054
2055 Bstr (logFolder).cloneTo(aLogFolder);
2056
2057 return S_OK;
2058}
2059
2060STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2061{
2062 CheckComArgOutPointerValid(aCurrentSnapshot);
2063
2064 AutoCaller autoCaller(this);
2065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2066
2067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2068
2069 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2070
2071 return S_OK;
2072}
2073
2074STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2075{
2076 CheckComArgOutPointerValid(aSnapshotCount);
2077
2078 AutoCaller autoCaller(this);
2079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2080
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2084 ? 0
2085 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2086
2087 return S_OK;
2088}
2089
2090STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2091{
2092 CheckComArgOutPointerValid(aCurrentStateModified);
2093
2094 AutoCaller autoCaller(this);
2095 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2096
2097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 /* Note: for machines with no snapshots, we always return FALSE
2100 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2101 * reasons :) */
2102
2103 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2104 ? FALSE
2105 : mData->mCurrentStateModified;
2106
2107 return S_OK;
2108}
2109
2110STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2111{
2112 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2113
2114 AutoCaller autoCaller(this);
2115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2116
2117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2118
2119 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2120 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2121
2122 return S_OK;
2123}
2124
2125STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2126{
2127 CheckComArgOutPointerValid(aClipboardMode);
2128
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 *aClipboardMode = mHWData->mClipboardMode;
2135
2136 return S_OK;
2137}
2138
2139STDMETHODIMP
2140Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2141{
2142 AutoCaller autoCaller(this);
2143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2144
2145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 HRESULT rc = checkStateDependency(MutableStateDep);
2148 if (FAILED(rc)) return rc;
2149
2150 setModified(IsModified_MachineData);
2151 mHWData.backup();
2152 mHWData->mClipboardMode = aClipboardMode;
2153
2154 return S_OK;
2155}
2156
2157STDMETHODIMP
2158Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2159{
2160 CheckComArgOutPointerValid(aPatterns);
2161
2162 AutoCaller autoCaller(this);
2163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2164
2165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2166
2167 try
2168 {
2169 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2170 }
2171 catch (...)
2172 {
2173 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
2174 }
2175
2176 return S_OK;
2177}
2178
2179STDMETHODIMP
2180Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2181{
2182 AutoCaller autoCaller(this);
2183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2184
2185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 HRESULT rc = checkStateDependency(MutableStateDep);
2188 if (FAILED(rc)) return rc;
2189
2190 setModified(IsModified_MachineData);
2191 mHWData.backup();
2192 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2193 return rc;
2194}
2195
2196STDMETHODIMP
2197Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2198{
2199 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2200
2201 AutoCaller autoCaller(this);
2202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2203
2204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2205
2206 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2207 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2208
2209 return S_OK;
2210}
2211
2212STDMETHODIMP
2213Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2214{
2215 CheckComArgOutPointerValid(aEnabled);
2216
2217 AutoCaller autoCaller(this);
2218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2219
2220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2221
2222 *aEnabled = mUserData->mTeleporterEnabled;
2223
2224 return S_OK;
2225}
2226
2227STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2228{
2229 AutoCaller autoCaller(this);
2230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2231
2232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
2234 /* Only allow it to be set to true when PoweredOff or Aborted.
2235 (Clearing it is always permitted.) */
2236 if ( aEnabled
2237 && mData->mRegistered
2238 && ( getClassID() != clsidSessionMachine
2239 || ( mData->mMachineState != MachineState_PoweredOff
2240 && mData->mMachineState != MachineState_Teleported
2241 && mData->mMachineState != MachineState_Aborted
2242 )
2243 )
2244 )
2245 return setError(VBOX_E_INVALID_VM_STATE,
2246 tr("The machine is not powered off (state is %s)"),
2247 Global::stringifyMachineState(mData->mMachineState));
2248
2249 setModified(IsModified_MachineData);
2250 mUserData.backup();
2251 mUserData->mTeleporterEnabled = aEnabled;
2252
2253 return S_OK;
2254}
2255
2256STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2257{
2258 CheckComArgOutPointerValid(aPort);
2259
2260 AutoCaller autoCaller(this);
2261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2262
2263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2264
2265 *aPort = mUserData->mTeleporterPort;
2266
2267 return S_OK;
2268}
2269
2270STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2271{
2272 if (aPort >= _64K)
2273 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2274
2275 AutoCaller autoCaller(this);
2276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2277
2278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2279
2280 HRESULT rc = checkStateDependency(MutableStateDep);
2281 if (FAILED(rc)) return rc;
2282
2283 setModified(IsModified_MachineData);
2284 mUserData.backup();
2285 mUserData->mTeleporterPort = aPort;
2286
2287 return S_OK;
2288}
2289
2290STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2291{
2292 CheckComArgOutPointerValid(aAddress);
2293
2294 AutoCaller autoCaller(this);
2295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2296
2297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2298
2299 mUserData->mTeleporterAddress.cloneTo(aAddress);
2300
2301 return S_OK;
2302}
2303
2304STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2305{
2306 AutoCaller autoCaller(this);
2307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2308
2309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
2311 HRESULT rc = checkStateDependency(MutableStateDep);
2312 if (FAILED(rc)) return rc;
2313
2314 setModified(IsModified_MachineData);
2315 mUserData.backup();
2316 mUserData->mTeleporterAddress = aAddress;
2317
2318 return S_OK;
2319}
2320
2321STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2322{
2323 CheckComArgOutPointerValid(aPassword);
2324
2325 AutoCaller autoCaller(this);
2326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2327
2328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2329
2330 mUserData->mTeleporterPassword.cloneTo(aPassword);
2331
2332 return S_OK;
2333}
2334
2335STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2336{
2337 AutoCaller autoCaller(this);
2338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2339
2340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2341
2342 HRESULT rc = checkStateDependency(MutableStateDep);
2343 if (FAILED(rc)) return rc;
2344
2345 setModified(IsModified_MachineData);
2346 mUserData.backup();
2347 mUserData->mTeleporterPassword = aPassword;
2348
2349 return S_OK;
2350}
2351
2352STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2353{
2354 CheckComArgOutPointerValid(aEnabled);
2355
2356 AutoCaller autoCaller(this);
2357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2358
2359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 *aEnabled = mUserData->mRTCUseUTC;
2362
2363 return S_OK;
2364}
2365
2366STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2367{
2368 AutoCaller autoCaller(this);
2369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2370
2371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 /* Only allow it to be set to true when PoweredOff or Aborted.
2374 (Clearing it is always permitted.) */
2375 if ( aEnabled
2376 && mData->mRegistered
2377 && ( getClassID() != clsidSessionMachine
2378 || ( mData->mMachineState != MachineState_PoweredOff
2379 && mData->mMachineState != MachineState_Teleported
2380 && mData->mMachineState != MachineState_Aborted
2381 )
2382 )
2383 )
2384 return setError(VBOX_E_INVALID_VM_STATE,
2385 tr("The machine is not powered off (state is %s)"),
2386 Global::stringifyMachineState(mData->mMachineState));
2387
2388 setModified(IsModified_MachineData);
2389 mUserData.backup();
2390 mUserData->mRTCUseUTC = aEnabled;
2391
2392 return S_OK;
2393}
2394
2395STDMETHODIMP Machine::COMGETTER(IoMgr)(IoMgrType_T *aIoMgrType)
2396{
2397 CheckComArgOutPointerValid(aIoMgrType);
2398
2399 AutoCaller autoCaller(this);
2400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2401
2402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2403
2404 *aIoMgrType = mHWData->mIoMgrType;
2405
2406 return S_OK;
2407}
2408
2409STDMETHODIMP Machine::COMSETTER(IoMgr)(IoMgrType_T aIoMgrType)
2410{
2411 if ( aIoMgrType != IoMgrType_Async
2412 && aIoMgrType != IoMgrType_Simple)
2413 return E_INVALIDARG;
2414
2415 AutoCaller autoCaller(this);
2416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2417
2418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
2420 HRESULT rc = checkStateDependency(MutableStateDep);
2421 if (FAILED(rc)) return rc;
2422
2423 setModified(IsModified_MachineData);
2424 mHWData.backup();
2425 mHWData->mIoMgrType = aIoMgrType;
2426
2427 return S_OK;
2428}
2429
2430STDMETHODIMP Machine::COMGETTER(IoBackend)(IoBackendType_T *aIoBackendType)
2431{
2432 CheckComArgOutPointerValid(aIoBackendType);
2433
2434 AutoCaller autoCaller(this);
2435 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2436
2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 *aIoBackendType = mHWData->mIoBackendType;
2440
2441 return S_OK;
2442}
2443
2444STDMETHODIMP Machine::COMSETTER(IoBackend)(IoBackendType_T aIoBackendType)
2445{
2446 if ( aIoBackendType != IoBackendType_Buffered
2447 && aIoBackendType != IoBackendType_Unbuffered)
2448 return E_INVALIDARG;
2449
2450 AutoCaller autoCaller(this);
2451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2452
2453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2454
2455 HRESULT rc = checkStateDependency(MutableStateDep);
2456 if (FAILED(rc)) return rc;
2457
2458 setModified(IsModified_MachineData);
2459 mHWData.backup();
2460 mHWData->mIoBackendType = aIoBackendType;
2461
2462 return S_OK;
2463}
2464
2465STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
2466{
2467 CheckComArgOutPointerValid(aEnabled);
2468
2469 AutoCaller autoCaller(this);
2470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2471
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 *aEnabled = mHWData->mIoCacheEnabled;
2475
2476 return S_OK;
2477}
2478
2479STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
2480{
2481 AutoCaller autoCaller(this);
2482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2483
2484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 HRESULT rc = checkStateDependency(MutableStateDep);
2487 if (FAILED(rc)) return rc;
2488
2489 setModified(IsModified_MachineData);
2490 mHWData.backup();
2491 mHWData->mIoCacheEnabled = aEnabled;
2492
2493 return S_OK;
2494}
2495
2496STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
2497{
2498 CheckComArgOutPointerValid(aIoCacheSize);
2499
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 *aIoCacheSize = mHWData->mIoCacheSize;
2506
2507 return S_OK;
2508}
2509
2510STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
2511{
2512 AutoCaller autoCaller(this);
2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2514
2515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 HRESULT rc = checkStateDependency(MutableStateDep);
2518 if (FAILED(rc)) return rc;
2519
2520 setModified(IsModified_MachineData);
2521 mHWData.backup();
2522 mHWData->mIoCacheSize = aIoCacheSize;
2523
2524 return S_OK;
2525}
2526
2527STDMETHODIMP Machine::COMGETTER(IoBandwidthMax)(ULONG *aIoBandwidthMax)
2528{
2529 CheckComArgOutPointerValid(aIoBandwidthMax);
2530
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 *aIoBandwidthMax = mHWData->mIoBandwidthMax;
2537
2538 return S_OK;
2539}
2540
2541STDMETHODIMP Machine::COMSETTER(IoBandwidthMax)(ULONG aIoBandwidthMax)
2542{
2543 AutoCaller autoCaller(this);
2544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2545
2546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 HRESULT rc = checkStateDependency(MutableStateDep);
2549 if (FAILED(rc)) return rc;
2550
2551 setModified(IsModified_MachineData);
2552 mHWData.backup();
2553 mHWData->mIoBandwidthMax = aIoBandwidthMax;
2554
2555 return S_OK;
2556}
2557
2558STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
2559{
2560 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
2561 return setError(E_INVALIDARG,
2562 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
2563 aPosition, SchemaDefs::MaxBootPosition);
2564
2565 if (aDevice == DeviceType_USB)
2566 return setError(E_NOTIMPL,
2567 tr("Booting from USB device is currently not supported"));
2568
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 HRESULT rc = checkStateDependency(MutableStateDep);
2575 if (FAILED(rc)) return rc;
2576
2577 setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mBootOrder[aPosition - 1] = aDevice;
2580
2581 return S_OK;
2582}
2583
2584STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
2585{
2586 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
2587 return setError(E_INVALIDARG,
2588 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
2589 aPosition, SchemaDefs::MaxBootPosition);
2590
2591 AutoCaller autoCaller(this);
2592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2593
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 *aDevice = mHWData->mBootOrder[aPosition - 1];
2597
2598 return S_OK;
2599}
2600
2601STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
2602 LONG aControllerPort,
2603 LONG aDevice,
2604 DeviceType_T aType,
2605 IN_BSTR aId)
2606{
2607 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n",
2608 aControllerName, aControllerPort, aDevice, aType, aId));
2609
2610 CheckComArgStrNotEmptyOrNull(aControllerName);
2611
2612 AutoCaller autoCaller(this);
2613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2614
2615 // if this becomes true then we need to call saveSettings in the end
2616 // @todo r=dj there is no error handling so far...
2617 bool fNeedsSaveSettings = false;
2618
2619 // request the host lock first, since might be calling Host methods for getting host drives;
2620 // next, protect the media tree all the while we're in here, as well as our member variables
2621 AutoMultiWriteLock3 alock(mParent->host()->lockHandle(),
2622 this->lockHandle(),
2623 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2624
2625 HRESULT rc = checkStateDependency(MutableStateDep);
2626 if (FAILED(rc)) return rc;
2627
2628 /// @todo NEWMEDIA implicit machine registration
2629 if (!mData->mRegistered)
2630 return setError(VBOX_E_INVALID_OBJECT_STATE,
2631 tr("Cannot attach storage devices to an unregistered machine"));
2632
2633 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2634
2635 if (Global::IsOnlineOrTransient(mData->mMachineState))
2636 return setError(VBOX_E_INVALID_VM_STATE,
2637 tr("Invalid machine state: %s"),
2638 Global::stringifyMachineState(mData->mMachineState));
2639
2640 /* Check for an existing controller. */
2641 ComObjPtr<StorageController> ctl;
2642 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
2643 if (FAILED(rc)) return rc;
2644
2645 /* check that the port and device are not out of range. */
2646 ULONG portCount;
2647 ULONG devicesPerPort;
2648 rc = ctl->COMGETTER(PortCount)(&portCount);
2649 if (FAILED(rc)) return rc;
2650 rc = ctl->COMGETTER(MaxDevicesPerPortCount)(&devicesPerPort);
2651 if (FAILED(rc)) return rc;
2652
2653 if ( (aControllerPort < 0)
2654 || (aControllerPort >= (LONG)portCount)
2655 || (aDevice < 0)
2656 || (aDevice >= (LONG)devicesPerPort)
2657 )
2658 return setError(E_INVALIDARG,
2659 tr("The port and/or count parameter are out of range [%lu:%lu]"),
2660 portCount,
2661 devicesPerPort);
2662
2663 /* check if the device slot is already busy */
2664 MediumAttachment *pAttachTemp;
2665 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
2666 aControllerName,
2667 aControllerPort,
2668 aDevice)))
2669 {
2670 Medium *pMedium = pAttachTemp->getMedium();
2671 if (pMedium)
2672 {
2673 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
2674 return setError(VBOX_E_OBJECT_IN_USE,
2675 tr("Medium '%s' is already attached to device slot %d on port %d of controller '%ls' of this virtual machine"),
2676 pMedium->getLocationFull().raw(),
2677 aDevice,
2678 aControllerPort,
2679 aControllerName);
2680 }
2681 else
2682 return setError(VBOX_E_OBJECT_IN_USE,
2683 tr("Device is already attached to slot %d on port %d of controller '%ls' of this virtual machine"),
2684 aDevice, aControllerPort, aControllerName);
2685 }
2686
2687 Guid uuid(aId);
2688
2689 ComObjPtr<Medium> medium;
2690 switch (aType)
2691 {
2692 case DeviceType_HardDisk:
2693 /* find a hard disk by UUID */
2694 rc = mParent->findHardDisk(&uuid, NULL, true /* aSetError */, &medium);
2695 if (FAILED(rc)) return rc;
2696 break;
2697
2698 case DeviceType_DVD: // @todo r=dj eliminate this, replace with findDVDImage
2699 if (!uuid.isEmpty())
2700 {
2701 /* first search for host drive */
2702 SafeIfaceArray<IMedium> drivevec;
2703 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2704 if (SUCCEEDED(rc))
2705 {
2706 for (size_t i = 0; i < drivevec.size(); ++i)
2707 {
2708 /// @todo eliminate this conversion
2709 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2710 if (med->getId() == uuid)
2711 {
2712 medium = med;
2713 break;
2714 }
2715 }
2716 }
2717
2718 if (medium.isNull())
2719 {
2720 /* find a DVD image by UUID */
2721 rc = mParent->findDVDImage(&uuid, NULL, true /* aSetError */, &medium);
2722 if (FAILED(rc)) return rc;
2723 }
2724 }
2725 else
2726 {
2727 /* null UUID means null medium, which needs no code */
2728 }
2729 break;
2730
2731 case DeviceType_Floppy: // @todo r=dj eliminate this, replace with findFloppyImage
2732 if (!uuid.isEmpty())
2733 {
2734 /* first search for host drive */
2735 SafeIfaceArray<IMedium> drivevec;
2736 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2737 if (SUCCEEDED(rc))
2738 {
2739 for (size_t i = 0; i < drivevec.size(); ++i)
2740 {
2741 /// @todo eliminate this conversion
2742 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2743 if (med->getId() == uuid)
2744 {
2745 medium = med;
2746 break;
2747 }
2748 }
2749 }
2750
2751 if (medium.isNull())
2752 {
2753 /* find a floppy image by UUID */
2754 rc = mParent->findFloppyImage(&uuid, NULL, true /* aSetError */, &medium);
2755 if (FAILED(rc)) return rc;
2756 }
2757 }
2758 else
2759 {
2760 /* null UUID means null medium, which needs no code */
2761 }
2762 break;
2763
2764 default:
2765 return setError(E_INVALIDARG,
2766 tr("The device type %d is not recognized"),
2767 (int)aType);
2768 }
2769
2770 AutoCaller mediumCaller(medium);
2771 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2772
2773 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
2774
2775 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
2776 && !medium.isNull()
2777 )
2778 return setError(VBOX_E_OBJECT_IN_USE,
2779 tr("Medium '%s' is already attached to this virtual machine"),
2780 medium->getLocationFull().raw());
2781
2782 bool indirect = false;
2783 if (!medium.isNull())
2784 indirect = medium->isReadOnly();
2785 bool associate = true;
2786
2787 do
2788 {
2789 if (aType == DeviceType_HardDisk && mMediaData.isBackedUp())
2790 {
2791 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
2792
2793 /* check if the medium was attached to the VM before we started
2794 * changing attachments in which case the attachment just needs to
2795 * be restored */
2796 if ((pAttachTemp = findAttachment(oldAtts, medium)))
2797 {
2798 AssertReturn(!indirect, E_FAIL);
2799
2800 /* see if it's the same bus/channel/device */
2801 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
2802 {
2803 /* the simplest case: restore the whole attachment
2804 * and return, nothing else to do */
2805 mMediaData->mAttachments.push_back(pAttachTemp);
2806 return S_OK;
2807 }
2808
2809 /* bus/channel/device differ; we need a new attachment object,
2810 * but don't try to associate it again */
2811 associate = false;
2812 break;
2813 }
2814 }
2815
2816 /* go further only if the attachment is to be indirect */
2817 if (!indirect)
2818 break;
2819
2820 /* perform the so called smart attachment logic for indirect
2821 * attachments. Note that smart attachment is only applicable to base
2822 * hard disks. */
2823
2824 if (medium->getParent().isNull())
2825 {
2826 /* first, investigate the backup copy of the current hard disk
2827 * attachments to make it possible to re-attach existing diffs to
2828 * another device slot w/o losing their contents */
2829 if (mMediaData.isBackedUp())
2830 {
2831 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
2832
2833 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
2834 uint32_t foundLevel = 0;
2835
2836 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
2837 it != oldAtts.end();
2838 ++it)
2839 {
2840 uint32_t level = 0;
2841 MediumAttachment *pAttach = *it;
2842 ComObjPtr<Medium> pMedium = pAttach->getMedium();
2843 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
2844 if (pMedium.isNull())
2845 continue;
2846
2847 if (pMedium->getBase(&level).equalsTo(medium))
2848 {
2849 /* skip the hard disk if its currently attached (we
2850 * cannot attach the same hard disk twice) */
2851 if (findAttachment(mMediaData->mAttachments,
2852 pMedium))
2853 continue;
2854
2855 /* matched device, channel and bus (i.e. attached to the
2856 * same place) will win and immediately stop the search;
2857 * otherwise the attachment that has the youngest
2858 * descendant of medium will be used
2859 */
2860 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
2861 {
2862 /* the simplest case: restore the whole attachment
2863 * and return, nothing else to do */
2864 mMediaData->mAttachments.push_back(*it);
2865 return S_OK;
2866 }
2867 else if ( foundIt == oldAtts.end()
2868 || level > foundLevel /* prefer younger */
2869 )
2870 {
2871 foundIt = it;
2872 foundLevel = level;
2873 }
2874 }
2875 }
2876
2877 if (foundIt != oldAtts.end())
2878 {
2879 /* use the previously attached hard disk */
2880 medium = (*foundIt)->getMedium();
2881 mediumCaller.attach(medium);
2882 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2883 mediumLock.attach(medium);
2884 /* not implicit, doesn't require association with this VM */
2885 indirect = false;
2886 associate = false;
2887 /* go right to the MediumAttachment creation */
2888 break;
2889 }
2890 }
2891
2892 /* then, search through snapshots for the best diff in the given
2893 * hard disk's chain to base the new diff on */
2894
2895 ComObjPtr<Medium> base;
2896 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
2897 while (snap)
2898 {
2899 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
2900
2901 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
2902
2903 MediaData::AttachmentList::const_iterator foundIt = snapAtts.end();
2904 uint32_t foundLevel = 0;
2905
2906 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
2907 it != snapAtts.end();
2908 ++it)
2909 {
2910 MediumAttachment *pAttach = *it;
2911 ComObjPtr<Medium> pMedium = pAttach->getMedium();
2912 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
2913 if (pMedium.isNull())
2914 continue;
2915
2916 uint32_t level = 0;
2917 if (pMedium->getBase(&level).equalsTo(medium))
2918 {
2919 /* matched device, channel and bus (i.e. attached to the
2920 * same place) will win and immediately stop the search;
2921 * otherwise the attachment that has the youngest
2922 * descendant of medium will be used
2923 */
2924 if ( (*it)->getDevice() == aDevice
2925 && (*it)->getPort() == aControllerPort
2926 && (*it)->getControllerName() == aControllerName
2927 )
2928 {
2929 foundIt = it;
2930 break;
2931 }
2932 else if ( foundIt == snapAtts.end()
2933 || level > foundLevel /* prefer younger */
2934 )
2935 {
2936 foundIt = it;
2937 foundLevel = level;
2938 }
2939 }
2940 }
2941
2942 if (foundIt != snapAtts.end())
2943 {
2944 base = (*foundIt)->getMedium();
2945 break;
2946 }
2947
2948 snap = snap->getParent();
2949 }
2950
2951 /* found a suitable diff, use it as a base */
2952 if (!base.isNull())
2953 {
2954 medium = base;
2955 mediumCaller.attach(medium);
2956 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2957 mediumLock.attach(medium);
2958 }
2959 }
2960
2961 ComObjPtr<Medium> diff;
2962 diff.createObject();
2963 rc = diff->init(mParent,
2964 medium->preferredDiffFormat().raw(),
2965 BstrFmt("%ls"RTPATH_SLASH_STR,
2966 mUserData->mSnapshotFolderFull.raw()).raw(),
2967 &fNeedsSaveSettings);
2968 if (FAILED(rc)) return rc;
2969
2970 /* make sure the hard disk is not modified before createDiffStorage() */
2971 rc = medium->LockRead(NULL);
2972 if (FAILED(rc)) return rc;
2973
2974 /* will leave the lock before the potentially lengthy operation, so
2975 * protect with the special state */
2976 MachineState_T oldState = mData->mMachineState;
2977 setMachineState(MachineState_SettingUp);
2978
2979 mediumLock.leave();
2980 alock.leave();
2981
2982 rc = medium->createDiffStorageAndWait(diff, MediumVariant_Standard, &fNeedsSaveSettings);
2983
2984 alock.enter();
2985 mediumLock.enter();
2986
2987 setMachineState(oldState);
2988
2989 medium->UnlockRead(NULL);
2990
2991 if (FAILED(rc)) return rc;
2992
2993 /* use the created diff for the actual attachment */
2994 medium = diff;
2995 mediumCaller.attach(medium);
2996 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2997 mediumLock.attach(medium);
2998 }
2999 while (0);
3000
3001 ComObjPtr<MediumAttachment> attachment;
3002 attachment.createObject();
3003 rc = attachment->init(this, medium, aControllerName, aControllerPort, aDevice, aType, indirect);
3004 if (FAILED(rc)) return rc;
3005
3006 if (associate && !medium.isNull())
3007 {
3008 /* as the last step, associate the medium to the VM */
3009 rc = medium->attachTo(mData->mUuid);
3010 /* here we can fail because of Deleting, or being in process of
3011 * creating a Diff */
3012 if (FAILED(rc)) return rc;
3013 }
3014
3015 /* success: finally remember the attachment */
3016 setModified(IsModified_Storage);
3017 mMediaData.backup();
3018 mMediaData->mAttachments.push_back(attachment);
3019
3020 if (fNeedsSaveSettings)
3021 {
3022 mediumLock.release();
3023 alock.release();
3024
3025 AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
3026 mParent->saveSettings();
3027 }
3028
3029 return rc;
3030}
3031
3032STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3033 LONG aDevice)
3034{
3035 CheckComArgStrNotEmptyOrNull(aControllerName);
3036
3037 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3038 aControllerName, aControllerPort, aDevice));
3039
3040 AutoCaller autoCaller(this);
3041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3042
3043 bool fNeedsSaveSettings = false;
3044
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 HRESULT rc = checkStateDependency(MutableStateDep);
3048 if (FAILED(rc)) return rc;
3049
3050 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3051
3052 if (Global::IsOnlineOrTransient(mData->mMachineState))
3053 return setError(VBOX_E_INVALID_VM_STATE,
3054 tr("Invalid machine state: %s"),
3055 Global::stringifyMachineState(mData->mMachineState));
3056
3057 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3058 aControllerName,
3059 aControllerPort,
3060 aDevice);
3061 if (!pAttach)
3062 return setError(VBOX_E_OBJECT_NOT_FOUND,
3063 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3064 aDevice, aControllerPort, aControllerName);
3065
3066 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
3067 DeviceType_T mediumType = pAttach->getType();
3068
3069 if (pAttach->isImplicit())
3070 {
3071 /* attempt to implicitly delete the implicitly created diff */
3072
3073 /// @todo move the implicit flag from MediumAttachment to Medium
3074 /// and forbid any hard disk operation when it is implicit. Or maybe
3075 /// a special media state for it to make it even more simple.
3076
3077 Assert(mMediaData.isBackedUp());
3078
3079 /* will leave the lock before the potentially lengthy operation, so
3080 * protect with the special state */
3081 MachineState_T oldState = mData->mMachineState;
3082 setMachineState(MachineState_SettingUp);
3083
3084 alock.leave();
3085
3086 rc = oldmedium->deleteStorageAndWait(NULL /*aProgress*/, &fNeedsSaveSettings);
3087
3088 alock.enter();
3089
3090 setMachineState(oldState);
3091
3092 if (FAILED(rc)) return rc;
3093 }
3094
3095 setModified(IsModified_Storage);
3096 mMediaData.backup();
3097
3098 /* we cannot use erase (it) below because backup() above will create
3099 * a copy of the list and make this copy active, but the iterator
3100 * still refers to the original and is not valid for the copy */
3101 mMediaData->mAttachments.remove(pAttach);
3102
3103 /* For non-hard disk media, detach straight away. */
3104 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3105 oldmedium->detachFrom(mData->mUuid);
3106
3107 if (fNeedsSaveSettings)
3108 {
3109 bool fNeedsGlobalSaveSettings = false;
3110 saveSettings(&fNeedsGlobalSaveSettings);
3111
3112 if (fNeedsGlobalSaveSettings)
3113 {
3114 alock.release();
3115 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
3116 mParent->saveSettings();
3117 }
3118 }
3119
3120 return S_OK;
3121}
3122
3123STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
3124 LONG aDevice, BOOL aPassthrough)
3125{
3126 CheckComArgStrNotEmptyOrNull(aControllerName);
3127
3128 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
3129 aControllerName, aControllerPort, aDevice, aPassthrough));
3130
3131 AutoCaller autoCaller(this);
3132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3133
3134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3135
3136 HRESULT rc = checkStateDependency(MutableStateDep);
3137 if (FAILED(rc)) return rc;
3138
3139 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3140
3141 if (Global::IsOnlineOrTransient(mData->mMachineState))
3142 return setError(VBOX_E_INVALID_VM_STATE,
3143 tr("Invalid machine state: %s"),
3144 Global::stringifyMachineState(mData->mMachineState));
3145
3146 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3147 aControllerName,
3148 aControllerPort,
3149 aDevice);
3150 if (!pAttach)
3151 return setError(VBOX_E_OBJECT_NOT_FOUND,
3152 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3153 aDevice, aControllerPort, aControllerName);
3154
3155
3156 setModified(IsModified_Storage);
3157 mMediaData.backup();
3158
3159 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3160
3161 if (pAttach->getType() != DeviceType_DVD)
3162 return setError(E_INVALIDARG,
3163 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
3164 aDevice, aControllerPort, aControllerName);
3165 pAttach->updatePassthrough(!!aPassthrough);
3166
3167 return S_OK;
3168}
3169
3170STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
3171 LONG aControllerPort,
3172 LONG aDevice,
3173 IN_BSTR aId,
3174 BOOL aForce)
3175{
3176 int rc = S_OK;
3177 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
3178 aControllerName, aControllerPort, aDevice, aForce));
3179
3180 CheckComArgStrNotEmptyOrNull(aControllerName);
3181
3182 AutoCaller autoCaller(this);
3183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3184
3185 // we're calling host methods for getting DVD and floppy drives so lock host first
3186 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3187
3188 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3189 aControllerName,
3190 aControllerPort,
3191 aDevice);
3192 if (pAttach.isNull())
3193 return setError(VBOX_E_OBJECT_NOT_FOUND,
3194 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
3195 aDevice, aControllerPort, aControllerName);
3196
3197 /* Remember previously mounted medium. The medium before taking the
3198 * backup is not necessarily the same thing. */
3199 ComObjPtr<Medium> oldmedium;
3200 oldmedium = pAttach->getMedium();
3201
3202 Guid uuid(aId);
3203 ComObjPtr<Medium> medium;
3204 DeviceType_T mediumType = pAttach->getType();
3205 switch (mediumType)
3206 {
3207 case DeviceType_DVD:
3208 if (!uuid.isEmpty())
3209 {
3210 /* find a DVD by host device UUID */
3211 MediaList llHostDVDDrives;
3212 rc = mParent->host()->getDVDDrives(llHostDVDDrives);
3213 if (SUCCEEDED(rc))
3214 {
3215 for (MediaList::iterator it = llHostDVDDrives.begin();
3216 it != llHostDVDDrives.end();
3217 ++it)
3218 {
3219 ComObjPtr<Medium> &p = *it;
3220 if (uuid == p->getId())
3221 {
3222 medium = p;
3223 break;
3224 }
3225 }
3226 }
3227 /* find a DVD by UUID */
3228 if (medium.isNull())
3229 rc = mParent->findDVDImage(&uuid, NULL, true /* aDoSetError */, &medium);
3230 }
3231 if (FAILED(rc)) return rc;
3232 break;
3233 case DeviceType_Floppy:
3234 if (!uuid.isEmpty())
3235 {
3236 /* find a Floppy by host device UUID */
3237 MediaList llHostFloppyDrives;
3238 rc = mParent->host()->getFloppyDrives(llHostFloppyDrives);
3239 if (SUCCEEDED(rc))
3240 {
3241 for (MediaList::iterator it = llHostFloppyDrives.begin();
3242 it != llHostFloppyDrives.end();
3243 ++it)
3244 {
3245 ComObjPtr<Medium> &p = *it;
3246 if (uuid == p->getId())
3247 {
3248 medium = p;
3249 break;
3250 }
3251 }
3252 }
3253 /* find a Floppy by UUID */
3254 if (medium.isNull())
3255 rc = mParent->findFloppyImage(&uuid, NULL, true /* aDoSetError */, &medium);
3256 }
3257 if (FAILED(rc)) return rc;
3258 break;
3259 default:
3260 return setError(VBOX_E_INVALID_OBJECT_STATE,
3261 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
3262 aDevice, aControllerPort, aControllerName);
3263 }
3264
3265 if (SUCCEEDED(rc))
3266 {
3267 setModified(IsModified_Storage);
3268 mMediaData.backup();
3269
3270 /* The backup operation makes the pAttach reference point to the
3271 * old settings. Re-get the correct reference. */
3272 pAttach = findAttachment(mMediaData->mAttachments,
3273 aControllerName,
3274 aControllerPort,
3275 aDevice);
3276 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3277 /* For non-hard disk media, detach straight away. */
3278 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3279 oldmedium->detachFrom(mData->mUuid);
3280 if (!medium.isNull())
3281 medium->attachTo(mData->mUuid);
3282 pAttach->updateMedium(medium, false /* aImplicit */);
3283 setModified(IsModified_Storage);
3284 }
3285
3286 alock.leave();
3287 rc = onMediumChange(pAttach, aForce);
3288 alock.enter();
3289
3290 /* On error roll back this change only. */
3291 if (FAILED(rc))
3292 {
3293 if (!medium.isNull())
3294 medium->detachFrom(mData->mUuid);
3295 pAttach = findAttachment(mMediaData->mAttachments,
3296 aControllerName,
3297 aControllerPort,
3298 aDevice);
3299 /* If the attachment is gone in the mean time, bail out. */
3300 if (pAttach.isNull())
3301 return rc;
3302 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3303 /* For non-hard disk media, re-attach straight away. */
3304 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3305 oldmedium->attachTo(mData->mUuid);
3306 pAttach->updateMedium(oldmedium, false /* aImplicit */);
3307 }
3308
3309 return rc;
3310}
3311
3312STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
3313 LONG aControllerPort,
3314 LONG aDevice,
3315 IMedium **aMedium)
3316{
3317 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3318 aControllerName, aControllerPort, aDevice));
3319
3320 CheckComArgStrNotEmptyOrNull(aControllerName);
3321 CheckComArgOutPointerValid(aMedium);
3322
3323 AutoCaller autoCaller(this);
3324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3325
3326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3327
3328 *aMedium = NULL;
3329
3330 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3331 aControllerName,
3332 aControllerPort,
3333 aDevice);
3334 if (pAttach.isNull())
3335 return setError(VBOX_E_OBJECT_NOT_FOUND,
3336 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3337 aDevice, aControllerPort, aControllerName);
3338
3339 pAttach->getMedium().queryInterfaceTo(aMedium);
3340
3341 return S_OK;
3342}
3343
3344STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
3345{
3346 CheckComArgOutPointerValid(port);
3347 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
3348
3349 AutoCaller autoCaller(this);
3350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3351
3352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3353
3354 mSerialPorts[slot].queryInterfaceTo(port);
3355
3356 return S_OK;
3357}
3358
3359STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
3360{
3361 CheckComArgOutPointerValid(port);
3362 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
3363
3364 AutoCaller autoCaller(this);
3365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3366
3367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3368
3369 mParallelPorts[slot].queryInterfaceTo(port);
3370
3371 return S_OK;
3372}
3373
3374STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
3375{
3376 CheckComArgOutPointerValid(adapter);
3377 CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters));
3378
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3381
3382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3383
3384 mNetworkAdapters[slot].queryInterfaceTo(adapter);
3385
3386 return S_OK;
3387}
3388
3389STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
3390{
3391 if (ComSafeArrayOutIsNull(aKeys))
3392 return E_POINTER;
3393
3394 AutoCaller autoCaller(this);
3395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3396
3397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3398
3399 com::SafeArray<BSTR> saKeys(mData->m_pMachineConfigFile->mapExtraDataItems.size());
3400 int i = 0;
3401 for (settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.begin();
3402 it != mData->m_pMachineConfigFile->mapExtraDataItems.end();
3403 ++it, ++i)
3404 {
3405 const Utf8Str &strKey = it->first;
3406 strKey.cloneTo(&saKeys[i]);
3407 }
3408 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
3409
3410 return S_OK;
3411 }
3412
3413 /**
3414 * @note Locks this object for reading.
3415 */
3416STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
3417 BSTR *aValue)
3418{
3419 CheckComArgStrNotEmptyOrNull(aKey);
3420 CheckComArgOutPointerValid(aValue);
3421
3422 AutoCaller autoCaller(this);
3423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3424
3425 /* start with nothing found */
3426 Bstr bstrResult("");
3427
3428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3429
3430 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
3431 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3432 // found:
3433 bstrResult = it->second; // source is a Utf8Str
3434
3435 /* return the result to caller (may be empty) */
3436 bstrResult.cloneTo(aValue);
3437
3438 return S_OK;
3439}
3440
3441 /**
3442 * @note Locks mParent for writing + this object for writing.
3443 */
3444STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
3445{
3446 CheckComArgStrNotEmptyOrNull(aKey);
3447
3448 AutoCaller autoCaller(this);
3449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3450
3451 Utf8Str strKey(aKey);
3452 Utf8Str strValue(aValue);
3453 Utf8Str strOldValue; // empty
3454
3455 // locking note: we only hold the read lock briefly to look up the old value,
3456 // then release it and call the onExtraCanChange callbacks. There is a small
3457 // chance of a race insofar as the callback might be called twice if two callers
3458 // change the same key at the same time, but that's a much better solution
3459 // than the deadlock we had here before. The actual changing of the extradata
3460 // is then performed under the write lock and race-free.
3461
3462 // look up the old value first; if nothing's changed then we need not do anything
3463 {
3464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
3465 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(strKey);
3466 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3467 strOldValue = it->second;
3468 }
3469
3470 bool fChanged;
3471 if ((fChanged = (strOldValue != strValue)))
3472 {
3473 // ask for permission from all listeners outside the locks;
3474 // onExtraDataCanChange() only briefly requests the VirtualBox
3475 // lock to copy the list of callbacks to invoke
3476 Bstr error;
3477 Bstr bstrValue(aValue);
3478
3479 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
3480 {
3481 const char *sep = error.isEmpty() ? "" : ": ";
3482 CBSTR err = error.raw();
3483 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
3484 sep, err));
3485 return setError(E_ACCESSDENIED,
3486 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
3487 aKey,
3488 bstrValue.raw(),
3489 sep,
3490 err);
3491 }
3492
3493 // data is changing and change not vetoed: then write it out under the lock
3494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3495
3496 if (getClassID() == clsidSnapshotMachine)
3497 {
3498 HRESULT rc = checkStateDependency(MutableStateDep);
3499 if (FAILED(rc)) return rc;
3500 }
3501
3502 if (strValue.isEmpty())
3503 mData->m_pMachineConfigFile->mapExtraDataItems.erase(strKey);
3504 else
3505 mData->m_pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
3506 // creates a new key if needed
3507
3508 bool fNeedsGlobalSaveSettings = false;
3509 saveSettings(&fNeedsGlobalSaveSettings);
3510
3511 if (fNeedsGlobalSaveSettings)
3512 {
3513 alock.release();
3514 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
3515 mParent->saveSettings();
3516 }
3517 }
3518
3519 // fire notification outside the lock
3520 if (fChanged)
3521 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
3522
3523 return S_OK;
3524}
3525
3526STDMETHODIMP Machine::SaveSettings()
3527{
3528 AutoCaller autoCaller(this);
3529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3530
3531 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
3532
3533 /* when there was auto-conversion, we want to save the file even if
3534 * the VM is saved */
3535 HRESULT rc = checkStateDependency(MutableStateDep);
3536 if (FAILED(rc)) return rc;
3537
3538 /* the settings file path may never be null */
3539 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
3540
3541 /* save all VM data excluding snapshots */
3542 bool fNeedsGlobalSaveSettings = false;
3543 rc = saveSettings(&fNeedsGlobalSaveSettings);
3544 mlock.release();
3545
3546 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
3547 {
3548 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
3549 rc = mParent->saveSettings();
3550 }
3551
3552 return rc;
3553}
3554
3555STDMETHODIMP Machine::DiscardSettings()
3556{
3557 AutoCaller autoCaller(this);
3558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3559
3560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3561
3562 HRESULT rc = checkStateDependency(MutableStateDep);
3563 if (FAILED(rc)) return rc;
3564
3565 /*
3566 * during this rollback, the session will be notified if data has
3567 * been actually changed
3568 */
3569 rollback(true /* aNotify */);
3570
3571 return S_OK;
3572}
3573
3574STDMETHODIMP Machine::DeleteSettings()
3575{
3576 AutoCaller autoCaller(this);
3577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3578
3579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3580
3581 HRESULT rc = checkStateDependency(MutableStateDep);
3582 if (FAILED(rc)) return rc;
3583
3584 if (mData->mRegistered)
3585 return setError(VBOX_E_INVALID_VM_STATE,
3586 tr("Cannot delete settings of a registered machine"));
3587
3588 /* delete the settings only when the file actually exists */
3589 if (mData->m_pMachineConfigFile->fileExists())
3590 {
3591 int vrc = RTFileDelete(mData->m_strConfigFileFull.c_str());
3592 if (RT_FAILURE(vrc))
3593 return setError(VBOX_E_IPRT_ERROR,
3594 tr("Could not delete the settings file '%s' (%Rrc)"),
3595 mData->m_strConfigFileFull.raw(),
3596 vrc);
3597
3598 /* delete the Logs folder, nothing important should be left
3599 * there (we don't check for errors because the user might have
3600 * some private files there that we don't want to delete) */
3601 Utf8Str logFolder;
3602 getLogFolder(logFolder);
3603 Assert(logFolder.length());
3604 if (RTDirExists(logFolder.c_str()))
3605 {
3606 /* Delete all VBox.log[.N] files from the Logs folder
3607 * (this must be in sync with the rotation logic in
3608 * Console::powerUpThread()). Also, delete the VBox.png[.N]
3609 * files that may have been created by the GUI. */
3610 Utf8Str log = Utf8StrFmt("%s/VBox.log", logFolder.raw());
3611 RTFileDelete(log.c_str());
3612 log = Utf8StrFmt("%s/VBox.png", logFolder.raw());
3613 RTFileDelete(log.c_str());
3614 for (int i = 3; i >= 0; i--)
3615 {
3616 log = Utf8StrFmt("%s/VBox.log.%d", logFolder.raw(), i);
3617 RTFileDelete(log.c_str());
3618 log = Utf8StrFmt("%s/VBox.png.%d", logFolder.raw(), i);
3619 RTFileDelete(log.c_str());
3620 }
3621
3622 RTDirRemove(logFolder.c_str());
3623 }
3624
3625 /* delete the Snapshots folder, nothing important should be left
3626 * there (we don't check for errors because the user might have
3627 * some private files there that we don't want to delete) */
3628 Utf8Str snapshotFolder(mUserData->mSnapshotFolderFull);
3629 Assert(snapshotFolder.length());
3630 if (RTDirExists(snapshotFolder.c_str()))
3631 RTDirRemove(snapshotFolder.c_str());
3632
3633 /* delete the directory that contains the settings file, but only
3634 * if it matches the VM name (i.e. a structure created by default in
3635 * prepareSaveSettings()) */
3636 {
3637 Utf8Str settingsDir;
3638 if (isInOwnDir(&settingsDir))
3639 RTDirRemove(settingsDir.c_str());
3640 }
3641 }
3642
3643 return S_OK;
3644}
3645
3646STDMETHODIMP Machine::GetSnapshot(IN_BSTR aId, ISnapshot **aSnapshot)
3647{
3648 CheckComArgOutPointerValid(aSnapshot);
3649
3650 AutoCaller autoCaller(this);
3651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3652
3653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3654
3655 Guid uuid(aId);
3656 /* Todo: fix this properly by perhaps introducing an isValid method for the Guid class */
3657 if ( (aId)
3658 && (*aId != '\0') // an empty Bstr means "get root snapshot", so don't fail on that
3659 && (uuid.isEmpty()))
3660 {
3661 RTUUID uuidTemp;
3662 /* Either it's a null UUID or the conversion failed. (null uuid has a special meaning in findSnapshot) */
3663 if (RT_FAILURE(RTUuidFromUtf16(&uuidTemp, aId)))
3664 return setError(E_FAIL,
3665 tr("Could not find a snapshot with UUID {%ls}"),
3666 aId);
3667 }
3668
3669 ComObjPtr<Snapshot> snapshot;
3670
3671 HRESULT rc = findSnapshot(uuid, snapshot, true /* aSetError */);
3672 snapshot.queryInterfaceTo(aSnapshot);
3673
3674 return rc;
3675}
3676
3677STDMETHODIMP Machine::FindSnapshot(IN_BSTR aName, ISnapshot **aSnapshot)
3678{
3679 CheckComArgStrNotEmptyOrNull(aName);
3680 CheckComArgOutPointerValid(aSnapshot);
3681
3682 AutoCaller autoCaller(this);
3683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3684
3685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3686
3687 ComObjPtr<Snapshot> snapshot;
3688
3689 HRESULT rc = findSnapshot(aName, snapshot, true /* aSetError */);
3690 snapshot.queryInterfaceTo(aSnapshot);
3691
3692 return rc;
3693}
3694
3695STDMETHODIMP Machine::SetCurrentSnapshot(IN_BSTR /* aId */)
3696{
3697 /// @todo (dmik) don't forget to set
3698 // mData->mCurrentStateModified to FALSE
3699
3700 return setError(E_NOTIMPL, "Not implemented");
3701}
3702
3703STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
3704{
3705 CheckComArgStrNotEmptyOrNull(aName);
3706 CheckComArgStrNotEmptyOrNull(aHostPath);
3707
3708 AutoCaller autoCaller(this);
3709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3710
3711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3712
3713 HRESULT rc = checkStateDependency(MutableStateDep);
3714 if (FAILED(rc)) return rc;
3715
3716 ComObjPtr<SharedFolder> sharedFolder;
3717 rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
3718 if (SUCCEEDED(rc))
3719 return setError(VBOX_E_OBJECT_IN_USE,
3720 tr("Shared folder named '%ls' already exists"),
3721 aName);
3722
3723 sharedFolder.createObject();
3724 rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable);
3725 if (FAILED(rc)) return rc;
3726
3727 setModified(IsModified_SharedFolders);
3728 mHWData.backup();
3729 mHWData->mSharedFolders.push_back(sharedFolder);
3730
3731 /* inform the direct session if any */
3732 alock.leave();
3733 onSharedFolderChange();
3734
3735 return S_OK;
3736}
3737
3738STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
3739{
3740 CheckComArgStrNotEmptyOrNull(aName);
3741
3742 AutoCaller autoCaller(this);
3743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3744
3745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3746
3747 HRESULT rc = checkStateDependency(MutableStateDep);
3748 if (FAILED(rc)) return rc;
3749
3750 ComObjPtr<SharedFolder> sharedFolder;
3751 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
3752 if (FAILED(rc)) return rc;
3753
3754 setModified(IsModified_SharedFolders);
3755 mHWData.backup();
3756 mHWData->mSharedFolders.remove(sharedFolder);
3757
3758 /* inform the direct session if any */
3759 alock.leave();
3760 onSharedFolderChange();
3761
3762 return S_OK;
3763}
3764
3765STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
3766{
3767 CheckComArgOutPointerValid(aCanShow);
3768
3769 /* start with No */
3770 *aCanShow = FALSE;
3771
3772 AutoCaller autoCaller(this);
3773 AssertComRCReturnRC(autoCaller.rc());
3774
3775 ComPtr<IInternalSessionControl> directControl;
3776 {
3777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3778
3779 if (mData->mSession.mState != SessionState_Open)
3780 return setError(VBOX_E_INVALID_VM_STATE,
3781 tr("Machine session is not open (session state: %s)"),
3782 Global::stringifySessionState(mData->mSession.mState));
3783
3784 directControl = mData->mSession.mDirectControl;
3785 }
3786
3787 /* ignore calls made after #OnSessionEnd() is called */
3788 if (!directControl)
3789 return S_OK;
3790
3791 ULONG64 dummy;
3792 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
3793}
3794
3795STDMETHODIMP Machine::ShowConsoleWindow(ULONG64 *aWinId)
3796{
3797 CheckComArgOutPointerValid(aWinId);
3798
3799 AutoCaller autoCaller(this);
3800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3801
3802 ComPtr<IInternalSessionControl> directControl;
3803 {
3804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3805
3806 if (mData->mSession.mState != SessionState_Open)
3807 return setError(E_FAIL,
3808 tr("Machine session is not open (session state: %s)"),
3809 Global::stringifySessionState(mData->mSession.mState));
3810
3811 directControl = mData->mSession.mDirectControl;
3812 }
3813
3814 /* ignore calls made after #OnSessionEnd() is called */
3815 if (!directControl)
3816 return S_OK;
3817
3818 BOOL dummy;
3819 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
3820}
3821
3822STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
3823 BSTR *aValue,
3824 ULONG64 *aTimestamp,
3825 BSTR *aFlags)
3826{
3827#ifndef VBOX_WITH_GUEST_PROPS
3828 ReturnComNotImplemented();
3829#else // VBOX_WITH_GUEST_PROPS
3830 CheckComArgStrNotEmptyOrNull(aName);
3831 CheckComArgOutPointerValid(aValue);
3832 CheckComArgOutPointerValid(aTimestamp);
3833 CheckComArgOutPointerValid(aFlags);
3834
3835 AutoCaller autoCaller(this);
3836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3837
3838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3839
3840 using namespace guestProp;
3841 HRESULT rc = E_FAIL;
3842
3843 Utf8Str strName(aName);
3844
3845 if (!mHWData->mPropertyServiceActive)
3846 {
3847 bool found = false;
3848 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
3849 (it != mHWData->mGuestProperties.end()) && !found;
3850 ++it)
3851 {
3852 if (it->strName == strName)
3853 {
3854 char szFlags[MAX_FLAGS_LEN + 1];
3855 it->strValue.cloneTo(aValue);
3856 *aTimestamp = it->mTimestamp;
3857 writeFlags(it->mFlags, szFlags);
3858 Bstr(szFlags).cloneTo(aFlags);
3859 found = true;
3860 }
3861 }
3862 rc = S_OK;
3863 }
3864 else
3865 {
3866 ComPtr<IInternalSessionControl> directControl =
3867 mData->mSession.mDirectControl;
3868
3869 /* just be on the safe side when calling another process */
3870 alock.release();
3871
3872 /* fail if we were called after #OnSessionEnd() is called. This is a
3873 * silly race condition. */
3874
3875 if (!directControl)
3876 rc = E_FAIL;
3877 else
3878 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
3879 false /* isSetter */,
3880 aValue, aTimestamp, aFlags);
3881 }
3882 return rc;
3883#endif // VBOX_WITH_GUEST_PROPS
3884}
3885
3886STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
3887{
3888 ULONG64 dummyTimestamp;
3889 BSTR dummyFlags;
3890 return GetGuestProperty(aName, aValue, &dummyTimestamp, &dummyFlags);
3891}
3892
3893STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, ULONG64 *aTimestamp)
3894{
3895 BSTR dummyValue;
3896 BSTR dummyFlags;
3897 return GetGuestProperty(aName, &dummyValue, aTimestamp, &dummyFlags);
3898}
3899
3900STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName,
3901 IN_BSTR aValue,
3902 IN_BSTR aFlags)
3903{
3904#ifndef VBOX_WITH_GUEST_PROPS
3905 ReturnComNotImplemented();
3906#else // VBOX_WITH_GUEST_PROPS
3907 using namespace guestProp;
3908
3909 CheckComArgStrNotEmptyOrNull(aName);
3910 if ((aFlags != NULL) && !VALID_PTR(aFlags))
3911 return E_INVALIDARG;
3912
3913 HRESULT rc = S_OK;
3914
3915 try
3916 {
3917 Utf8Str utf8Name(aName);
3918 Utf8Str utf8Flags(aFlags);
3919
3920 AutoCaller autoCaller(this);
3921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3922
3923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3924
3925 rc = checkStateDependency(MutableStateDep);
3926 if (FAILED(rc)) return rc;
3927
3928 rc = S_OK;
3929 uint32_t fFlags = NILFLAG;
3930 if ( (aFlags != NULL)
3931 && RT_FAILURE(validateFlags(utf8Flags.raw(), &fFlags))
3932 )
3933 return setError(E_INVALIDARG,
3934 tr("Invalid flag values: '%ls'"),
3935 aFlags);
3936
3937 if (!mHWData->mPropertyServiceActive)
3938 {
3939 bool found = false;
3940 HWData::GuestProperty property;
3941 property.mFlags = NILFLAG;
3942
3943 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I know,
3944 * this is simple and do an OK job atm.) */
3945 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3946 it != mHWData->mGuestProperties.end();
3947 ++it)
3948 if (it->strName == utf8Name)
3949 {
3950 property = *it;
3951 if (it->mFlags & (RDONLYHOST))
3952 rc = setError(E_ACCESSDENIED,
3953 tr("The property '%ls' cannot be changed by the host"),
3954 aName);
3955 else
3956 {
3957 setModified(IsModified_MachineData);
3958 mHWData.backup(); // @todo r=dj backup in a loop?!?
3959
3960 /* The backup() operation invalidates our iterator, so
3961 * get a new one. */
3962 for (it = mHWData->mGuestProperties.begin();
3963 it->strName != utf8Name;
3964 ++it)
3965 ;
3966 mHWData->mGuestProperties.erase(it);
3967 }
3968 found = true;
3969 break;
3970 }
3971 if (found && SUCCEEDED(rc))
3972 {
3973 if (*aValue)
3974 {
3975 RTTIMESPEC time;
3976 property.strValue = aValue;
3977 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3978 if (aFlags != NULL)
3979 property.mFlags = fFlags;
3980 mHWData->mGuestProperties.push_back(property);
3981 }
3982 }
3983 else if (SUCCEEDED(rc) && *aValue)
3984 {
3985 RTTIMESPEC time;
3986 setModified(IsModified_MachineData);
3987 mHWData.backup();
3988 property.strName = aName;
3989 property.strValue = aValue;
3990 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3991 property.mFlags = fFlags;
3992 mHWData->mGuestProperties.push_back(property);
3993 }
3994 if ( SUCCEEDED(rc)
3995 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
3996 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(), RTSTR_MAX,
3997 utf8Name.raw(), RTSTR_MAX, NULL) )
3998 )
3999 {
4000 /** @todo r=bird: Why aren't we leaving the lock here? The
4001 * same code in PushGuestProperty does... */
4002 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
4003 }
4004 }
4005 else
4006 {
4007 ComPtr<IInternalSessionControl> directControl =
4008 mData->mSession.mDirectControl;
4009
4010 /* just be on the safe side when calling another process */
4011 alock.leave();
4012
4013 BSTR dummy = NULL;
4014 ULONG64 dummy64;
4015 if (!directControl)
4016 rc = E_FAIL;
4017 else
4018 rc = directControl->AccessGuestProperty(aName,
4019 *aValue ? aValue : NULL, /** @todo Fix when adding DeleteGuestProperty(), see defect. */
4020 aFlags,
4021 true /* isSetter */,
4022 &dummy, &dummy64, &dummy);
4023 }
4024 }
4025 catch (std::bad_alloc &)
4026 {
4027 rc = E_OUTOFMEMORY;
4028 }
4029
4030 return rc;
4031#endif // VBOX_WITH_GUEST_PROPS
4032}
4033
4034STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
4035{
4036 return SetGuestProperty(aName, aValue, NULL);
4037}
4038
4039STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
4040 ComSafeArrayOut(BSTR, aNames),
4041 ComSafeArrayOut(BSTR, aValues),
4042 ComSafeArrayOut(ULONG64, aTimestamps),
4043 ComSafeArrayOut(BSTR, aFlags))
4044{
4045#ifndef VBOX_WITH_GUEST_PROPS
4046 ReturnComNotImplemented();
4047#else // VBOX_WITH_GUEST_PROPS
4048 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4049 return E_POINTER;
4050
4051 CheckComArgOutSafeArrayPointerValid(aNames);
4052 CheckComArgOutSafeArrayPointerValid(aValues);
4053 CheckComArgOutSafeArrayPointerValid(aTimestamps);
4054 CheckComArgOutSafeArrayPointerValid(aFlags);
4055
4056 AutoCaller autoCaller(this);
4057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4058
4059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4060
4061 using namespace guestProp;
4062 HRESULT rc = E_FAIL;
4063
4064 Utf8Str strPatterns(aPatterns);
4065
4066 if (!mHWData->mPropertyServiceActive)
4067 {
4068
4069 /*
4070 * Look for matching patterns and build up a list.
4071 */
4072 HWData::GuestPropertyList propList;
4073 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
4074 it != mHWData->mGuestProperties.end();
4075 ++it)
4076 if ( strPatterns.isEmpty()
4077 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
4078 RTSTR_MAX,
4079 it->strName.raw(),
4080 RTSTR_MAX, NULL)
4081 )
4082 propList.push_back(*it);
4083
4084 /*
4085 * And build up the arrays for returning the property information.
4086 */
4087 size_t cEntries = propList.size();
4088 SafeArray<BSTR> names(cEntries);
4089 SafeArray<BSTR> values(cEntries);
4090 SafeArray<ULONG64> timestamps(cEntries);
4091 SafeArray<BSTR> flags(cEntries);
4092 size_t iProp = 0;
4093 for (HWData::GuestPropertyList::iterator it = propList.begin();
4094 it != propList.end();
4095 ++it)
4096 {
4097 char szFlags[MAX_FLAGS_LEN + 1];
4098 it->strName.cloneTo(&names[iProp]);
4099 it->strValue.cloneTo(&values[iProp]);
4100 timestamps[iProp] = it->mTimestamp;
4101 writeFlags(it->mFlags, szFlags);
4102 Bstr(szFlags).cloneTo(&flags[iProp]);
4103 ++iProp;
4104 }
4105 names.detachTo(ComSafeArrayOutArg(aNames));
4106 values.detachTo(ComSafeArrayOutArg(aValues));
4107 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
4108 flags.detachTo(ComSafeArrayOutArg(aFlags));
4109 rc = S_OK;
4110 }
4111 else
4112 {
4113 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
4114
4115 /* just be on the safe side when calling another process */
4116 alock.release();
4117
4118 if (!directControl)
4119 rc = E_FAIL;
4120 else
4121 rc = directControl->EnumerateGuestProperties(aPatterns,
4122 ComSafeArrayOutArg(aNames),
4123 ComSafeArrayOutArg(aValues),
4124 ComSafeArrayOutArg(aTimestamps),
4125 ComSafeArrayOutArg(aFlags));
4126 }
4127 return rc;
4128#endif // VBOX_WITH_GUEST_PROPS
4129}
4130
4131STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
4132 ComSafeArrayOut(IMediumAttachment*, aAttachments))
4133{
4134 MediaData::AttachmentList atts;
4135
4136 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
4137 if (FAILED(rc)) return rc;
4138
4139 SafeIfaceArray<IMediumAttachment> attachments(atts);
4140 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
4141
4142 return S_OK;
4143}
4144
4145STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
4146 LONG aControllerPort,
4147 LONG aDevice,
4148 IMediumAttachment **aAttachment)
4149{
4150 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4151 aControllerName, aControllerPort, aDevice));
4152
4153 CheckComArgStrNotEmptyOrNull(aControllerName);
4154 CheckComArgOutPointerValid(aAttachment);
4155
4156 AutoCaller autoCaller(this);
4157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4158
4159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4160
4161 *aAttachment = NULL;
4162
4163 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4164 aControllerName,
4165 aControllerPort,
4166 aDevice);
4167 if (pAttach.isNull())
4168 return setError(VBOX_E_OBJECT_NOT_FOUND,
4169 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4170 aDevice, aControllerPort, aControllerName);
4171
4172 pAttach.queryInterfaceTo(aAttachment);
4173
4174 return S_OK;
4175}
4176
4177STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
4178 StorageBus_T aConnectionType,
4179 IStorageController **controller)
4180{
4181 CheckComArgStrNotEmptyOrNull(aName);
4182
4183 if ( (aConnectionType <= StorageBus_Null)
4184 || (aConnectionType > StorageBus_SAS))
4185 return setError(E_INVALIDARG,
4186 tr("Invalid connection type: %d"),
4187 aConnectionType);
4188
4189 AutoCaller autoCaller(this);
4190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4191
4192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4193
4194 HRESULT rc = checkStateDependency(MutableStateDep);
4195 if (FAILED(rc)) return rc;
4196
4197 /* try to find one with the name first. */
4198 ComObjPtr<StorageController> ctrl;
4199
4200 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
4201 if (SUCCEEDED(rc))
4202 return setError(VBOX_E_OBJECT_IN_USE,
4203 tr("Storage controller named '%ls' already exists"),
4204 aName);
4205
4206 ctrl.createObject();
4207
4208 /* get a new instance number for the storage controller */
4209 ULONG ulInstance = 0;
4210 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
4211 it != mStorageControllers->end();
4212 ++it)
4213 {
4214 if ((*it)->getStorageBus() == aConnectionType)
4215 {
4216 ULONG ulCurInst = (*it)->getInstance();
4217
4218 if (ulCurInst >= ulInstance)
4219 ulInstance = ulCurInst + 1;
4220 }
4221 }
4222
4223 rc = ctrl->init(this, aName, aConnectionType, ulInstance);
4224 if (FAILED(rc)) return rc;
4225
4226 setModified(IsModified_Storage);
4227 mStorageControllers.backup();
4228 mStorageControllers->push_back(ctrl);
4229
4230 ctrl.queryInterfaceTo(controller);
4231
4232 /* inform the direct session if any */
4233 alock.leave();
4234 onStorageControllerChange();
4235
4236 return S_OK;
4237}
4238
4239STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
4240 IStorageController **aStorageController)
4241{
4242 CheckComArgStrNotEmptyOrNull(aName);
4243
4244 AutoCaller autoCaller(this);
4245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4246
4247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4248
4249 ComObjPtr<StorageController> ctrl;
4250
4251 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
4252 if (SUCCEEDED(rc))
4253 ctrl.queryInterfaceTo(aStorageController);
4254
4255 return rc;
4256}
4257
4258STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
4259 IStorageController **aStorageController)
4260{
4261 AutoCaller autoCaller(this);
4262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4263
4264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4265
4266 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
4267 it != mStorageControllers->end();
4268 ++it)
4269 {
4270 if ((*it)->getInstance() == aInstance)
4271 {
4272 (*it).queryInterfaceTo(aStorageController);
4273 return S_OK;
4274 }
4275 }
4276
4277 return setError(VBOX_E_OBJECT_NOT_FOUND,
4278 tr("Could not find a storage controller with instance number '%lu'"),
4279 aInstance);
4280}
4281
4282STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
4283{
4284 CheckComArgStrNotEmptyOrNull(aName);
4285
4286 AutoCaller autoCaller(this);
4287 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4288
4289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4290
4291 HRESULT rc = checkStateDependency(MutableStateDep);
4292 if (FAILED(rc)) return rc;
4293
4294 ComObjPtr<StorageController> ctrl;
4295 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
4296 if (FAILED(rc)) return rc;
4297
4298 /* We can remove the controller only if there is no device attached. */
4299 /* check if the device slot is already busy */
4300 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
4301 it != mMediaData->mAttachments.end();
4302 ++it)
4303 {
4304 if ((*it)->getControllerName() == aName)
4305 return setError(VBOX_E_OBJECT_IN_USE,
4306 tr("Storage controller named '%ls' has still devices attached"),
4307 aName);
4308 }
4309
4310 /* We can remove it now. */
4311 setModified(IsModified_Storage);
4312 mStorageControllers.backup();
4313
4314 ctrl->unshare();
4315
4316 mStorageControllers->remove(ctrl);
4317
4318 /* inform the direct session if any */
4319 alock.leave();
4320 onStorageControllerChange();
4321
4322 return S_OK;
4323}
4324
4325/* @todo where is the right place for this? */
4326#define sSSMDisplayScreenshotVer 0x00010001
4327
4328static int readSavedDisplayScreenshot(Utf8Str *pStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
4329{
4330 LogFlowFunc(("u32Type = %d [%s]\n", u32Type, pStateFilePath->raw()));
4331
4332 /* @todo cache read data */
4333 if (pStateFilePath->isEmpty())
4334 {
4335 /* No saved state data. */
4336 return VERR_NOT_SUPPORTED;
4337 }
4338
4339 uint8_t *pu8Data = NULL;
4340 uint32_t cbData = 0;
4341 uint32_t u32Width = 0;
4342 uint32_t u32Height = 0;
4343
4344 PSSMHANDLE pSSM;
4345 int rc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
4346 if (RT_SUCCESS(rc))
4347 {
4348 uint32_t uVersion;
4349 rc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
4350 if (RT_SUCCESS(rc))
4351 {
4352 if (uVersion == sSSMDisplayScreenshotVer)
4353 {
4354 uint32_t cBlocks;
4355 rc = SSMR3GetU32(pSSM, &cBlocks);
4356 AssertRCReturn(rc, rc);
4357
4358 for (uint32_t i = 0; i < cBlocks; i++)
4359 {
4360 uint32_t cbBlock;
4361 rc = SSMR3GetU32(pSSM, &cbBlock);
4362 AssertRCBreak(rc);
4363
4364 uint32_t typeOfBlock;
4365 rc = SSMR3GetU32(pSSM, &typeOfBlock);
4366 AssertRCBreak(rc);
4367
4368 LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
4369
4370 if (typeOfBlock == u32Type)
4371 {
4372 if (cbBlock > 2 * sizeof(uint32_t))
4373 {
4374 cbData = cbBlock - 2 * sizeof(uint32_t);
4375 pu8Data = (uint8_t *)RTMemAlloc(cbData);
4376 if (pu8Data == NULL)
4377 {
4378 rc = VERR_NO_MEMORY;
4379 break;
4380 }
4381
4382 rc = SSMR3GetU32(pSSM, &u32Width);
4383 AssertRCBreak(rc);
4384 rc = SSMR3GetU32(pSSM, &u32Height);
4385 AssertRCBreak(rc);
4386 rc = SSMR3GetMem(pSSM, pu8Data, cbData);
4387 AssertRCBreak(rc);
4388 }
4389 else
4390 {
4391 /* No saved state data. */
4392 rc = VERR_NOT_SUPPORTED;
4393 }
4394
4395 break;
4396 }
4397 else
4398 {
4399 /* displaySSMSaveScreenshot did not write any data, if
4400 * cbBlock was == 2 * sizeof (uint32_t).
4401 */
4402 if (cbBlock > 2 * sizeof (uint32_t))
4403 {
4404 rc = SSMR3Skip(pSSM, cbBlock);
4405 AssertRCBreak(rc);
4406 }
4407 }
4408 }
4409 }
4410 else
4411 {
4412 rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4413 }
4414 }
4415
4416 SSMR3Close(pSSM);
4417 }
4418
4419 if (RT_SUCCESS(rc))
4420 {
4421 if (u32Type == 0 && cbData % 4 != 0)
4422 {
4423 /* Bitmap is 32bpp, so data is invalid. */
4424 rc = VERR_SSM_UNEXPECTED_DATA;
4425 }
4426 }
4427
4428 if (RT_SUCCESS(rc))
4429 {
4430 *ppu8Data = pu8Data;
4431 *pcbData = cbData;
4432 *pu32Width = u32Width;
4433 *pu32Height = u32Height;
4434 LogFlowFunc(("cbData %d, u32Width %d, u32Height %d\n", cbData, u32Width, u32Height));
4435 }
4436
4437 LogFlowFunc(("rc %Rrc\n", rc));
4438 return rc;
4439}
4440
4441static void freeSavedDisplayScreenshot(uint8_t *pu8Data)
4442{
4443 /* @todo not necessary when caching is implemented. */
4444 RTMemFree(pu8Data);
4445}
4446
4447STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4448{
4449 LogFlowThisFunc(("\n"));
4450
4451 CheckComArgNotNull(aSize);
4452 CheckComArgNotNull(aWidth);
4453 CheckComArgNotNull(aHeight);
4454
4455 AutoCaller autoCaller(this);
4456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4457
4458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4459
4460 uint8_t *pu8Data = NULL;
4461 uint32_t cbData = 0;
4462 uint32_t u32Width = 0;
4463 uint32_t u32Height = 0;
4464
4465 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4466
4467 if (RT_FAILURE(vrc))
4468 return setError(VBOX_E_IPRT_ERROR,
4469 tr("Saved screenshot data is not available (%Rrc)"),
4470 vrc);
4471
4472 *aSize = cbData;
4473 *aWidth = u32Width;
4474 *aHeight = u32Height;
4475
4476 freeSavedDisplayScreenshot(pu8Data);
4477
4478 return S_OK;
4479}
4480
4481STDMETHODIMP Machine::ReadSavedThumbnailToArray(BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4482{
4483 LogFlowThisFunc(("\n"));
4484
4485 CheckComArgNotNull(aWidth);
4486 CheckComArgNotNull(aHeight);
4487 CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4488
4489 AutoCaller autoCaller(this);
4490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4491
4492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 uint8_t *pu8Data = NULL;
4495 uint32_t cbData = 0;
4496 uint32_t u32Width = 0;
4497 uint32_t u32Height = 0;
4498
4499 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4500
4501 if (RT_FAILURE(vrc))
4502 return setError(VBOX_E_IPRT_ERROR,
4503 tr("Saved screenshot data is not available (%Rrc)"),
4504 vrc);
4505
4506 *aWidth = u32Width;
4507 *aHeight = u32Height;
4508
4509 com::SafeArray<BYTE> bitmap(cbData);
4510 /* Convert pixels to format expected by the API caller. */
4511 if (aBGR)
4512 {
4513 /* [0] B, [1] G, [2] R, [3] A. */
4514 for (unsigned i = 0; i < cbData; i += 4)
4515 {
4516 bitmap[i] = pu8Data[i];
4517 bitmap[i + 1] = pu8Data[i + 1];
4518 bitmap[i + 2] = pu8Data[i + 2];
4519 bitmap[i + 3] = 0xff;
4520 }
4521 }
4522 else
4523 {
4524 /* [0] R, [1] G, [2] B, [3] A. */
4525 for (unsigned i = 0; i < cbData; i += 4)
4526 {
4527 bitmap[i] = pu8Data[i + 2];
4528 bitmap[i + 1] = pu8Data[i + 1];
4529 bitmap[i + 2] = pu8Data[i];
4530 bitmap[i + 3] = 0xff;
4531 }
4532 }
4533 bitmap.detachTo(ComSafeArrayOutArg(aData));
4534
4535 freeSavedDisplayScreenshot(pu8Data);
4536
4537 return S_OK;
4538}
4539
4540STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4541{
4542 LogFlowThisFunc(("\n"));
4543
4544 CheckComArgNotNull(aSize);
4545 CheckComArgNotNull(aWidth);
4546 CheckComArgNotNull(aHeight);
4547
4548 AutoCaller autoCaller(this);
4549 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4550
4551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4552
4553 uint8_t *pu8Data = NULL;
4554 uint32_t cbData = 0;
4555 uint32_t u32Width = 0;
4556 uint32_t u32Height = 0;
4557
4558 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4559
4560 if (RT_FAILURE(vrc))
4561 return setError(VBOX_E_IPRT_ERROR,
4562 tr("Saved screenshot data is not available (%Rrc)"),
4563 vrc);
4564
4565 *aSize = cbData;
4566 *aWidth = u32Width;
4567 *aHeight = u32Height;
4568
4569 freeSavedDisplayScreenshot(pu8Data);
4570
4571 return S_OK;
4572}
4573
4574STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4575{
4576 LogFlowThisFunc(("\n"));
4577
4578 CheckComArgNotNull(aWidth);
4579 CheckComArgNotNull(aHeight);
4580 CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4581
4582 AutoCaller autoCaller(this);
4583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4584
4585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4586
4587 uint8_t *pu8Data = NULL;
4588 uint32_t cbData = 0;
4589 uint32_t u32Width = 0;
4590 uint32_t u32Height = 0;
4591
4592 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4593
4594 if (RT_FAILURE(vrc))
4595 return setError(VBOX_E_IPRT_ERROR,
4596 tr("Saved screenshot data is not available (%Rrc)"),
4597 vrc);
4598
4599 *aWidth = u32Width;
4600 *aHeight = u32Height;
4601
4602 com::SafeArray<BYTE> png(cbData);
4603 for (unsigned i = 0; i < cbData; i++)
4604 png[i] = pu8Data[i];
4605 png.detachTo(ComSafeArrayOutArg(aData));
4606
4607 freeSavedDisplayScreenshot(pu8Data);
4608
4609 return S_OK;
4610}
4611
4612STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
4613{
4614 HRESULT rc = S_OK;
4615 LogFlowThisFunc(("\n"));
4616
4617 AutoCaller autoCaller(this);
4618 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4619
4620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4621
4622 if (!mHWData->mCPUHotPlugEnabled)
4623 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
4624
4625 if (aCpu >= mHWData->mCPUCount)
4626 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
4627
4628 if (mHWData->mCPUAttached[aCpu])
4629 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
4630
4631 alock.leave();
4632 rc = onCPUChange(aCpu, false);
4633 alock.enter();
4634 if (FAILED(rc)) return rc;
4635
4636 setModified(IsModified_MachineData);
4637 mHWData.backup();
4638 mHWData->mCPUAttached[aCpu] = true;
4639
4640 /* Save settings if online */
4641 if (Global::IsOnline(mData->mMachineState))
4642 SaveSettings();
4643
4644 return S_OK;
4645}
4646
4647STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
4648{
4649 HRESULT rc = S_OK;
4650 LogFlowThisFunc(("\n"));
4651
4652 AutoCaller autoCaller(this);
4653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4654
4655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4656
4657 if (!mHWData->mCPUHotPlugEnabled)
4658 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
4659
4660 if (aCpu >= SchemaDefs::MaxCPUCount)
4661 return setError(E_INVALIDARG,
4662 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
4663 SchemaDefs::MaxCPUCount);
4664
4665 if (!mHWData->mCPUAttached[aCpu])
4666 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
4667
4668 /* CPU 0 can't be detached */
4669 if (aCpu == 0)
4670 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
4671
4672 alock.leave();
4673 rc = onCPUChange(aCpu, true);
4674 alock.enter();
4675 if (FAILED(rc)) return rc;
4676
4677 setModified(IsModified_MachineData);
4678 mHWData.backup();
4679 mHWData->mCPUAttached[aCpu] = false;
4680
4681 /* Save settings if online */
4682 if (Global::IsOnline(mData->mMachineState))
4683 SaveSettings();
4684
4685 return S_OK;
4686}
4687
4688STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
4689{
4690 LogFlowThisFunc(("\n"));
4691
4692 CheckComArgNotNull(aCpuAttached);
4693
4694 *aCpuAttached = false;
4695
4696 AutoCaller autoCaller(this);
4697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4698
4699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4700
4701 /* If hotplug is enabled the CPU is always enabled. */
4702 if (!mHWData->mCPUHotPlugEnabled)
4703 {
4704 if (aCpu < mHWData->mCPUCount)
4705 *aCpuAttached = true;
4706 }
4707 else
4708 {
4709 if (aCpu < SchemaDefs::MaxCPUCount)
4710 *aCpuAttached = mHWData->mCPUAttached[aCpu];
4711 }
4712
4713 return S_OK;
4714}
4715
4716// public methods for internal purposes
4717/////////////////////////////////////////////////////////////////////////////
4718
4719/**
4720 * Adds the given IsModified_* flag to the dirty flags of the machine.
4721 * This must be called either during loadSettings or under the machine write lock.
4722 * @param fl
4723 */
4724void Machine::setModified(uint32_t fl)
4725{
4726 m_flModifications |= fl;
4727}
4728
4729/**
4730 * Saves the registry entry of this machine to the given configuration node.
4731 *
4732 * @param aEntryNode Node to save the registry entry to.
4733 *
4734 * @note locks this object for reading.
4735 */
4736HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
4737{
4738 AutoLimitedCaller autoCaller(this);
4739 AssertComRCReturnRC(autoCaller.rc());
4740
4741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 data.uuid = mData->mUuid;
4744 data.strSettingsFile = mData->m_strConfigFile;
4745
4746 return S_OK;
4747}
4748
4749/**
4750 * Calculates the absolute path of the given path taking the directory of the
4751 * machine settings file as the current directory.
4752 *
4753 * @param aPath Path to calculate the absolute path for.
4754 * @param aResult Where to put the result (used only on success, can be the
4755 * same Utf8Str instance as passed in @a aPath).
4756 * @return IPRT result.
4757 *
4758 * @note Locks this object for reading.
4759 */
4760int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4761{
4762 AutoCaller autoCaller(this);
4763 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4764
4765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4766
4767 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
4768
4769 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
4770
4771 strSettingsDir.stripFilename();
4772 char folder[RTPATH_MAX];
4773 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
4774 if (RT_SUCCESS(vrc))
4775 aResult = folder;
4776
4777 return vrc;
4778}
4779
4780/**
4781 * Tries to calculate the relative path of the given absolute path using the
4782 * directory of the machine settings file as the base directory.
4783 *
4784 * @param aPath Absolute path to calculate the relative path for.
4785 * @param aResult Where to put the result (used only when it's possible to
4786 * make a relative path from the given absolute path; otherwise
4787 * left untouched).
4788 *
4789 * @note Locks this object for reading.
4790 */
4791void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
4792{
4793 AutoCaller autoCaller(this);
4794 AssertComRCReturn(autoCaller.rc(), (void)0);
4795
4796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4797
4798 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
4799
4800 Utf8Str settingsDir = mData->m_strConfigFileFull;
4801
4802 settingsDir.stripFilename();
4803 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
4804 {
4805 /* when assigning, we create a separate Utf8Str instance because both
4806 * aPath and aResult can point to the same memory location when this
4807 * func is called (if we just do aResult = aPath, aResult will be freed
4808 * first, and since its the same as aPath, an attempt to copy garbage
4809 * will be made. */
4810 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
4811 }
4812}
4813
4814/**
4815 * Returns the full path to the machine's log folder in the
4816 * \a aLogFolder argument.
4817 */
4818void Machine::getLogFolder(Utf8Str &aLogFolder)
4819{
4820 AutoCaller autoCaller(this);
4821 AssertComRCReturnVoid(autoCaller.rc());
4822
4823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4824
4825 Utf8Str settingsDir;
4826 if (isInOwnDir(&settingsDir))
4827 {
4828 /* Log folder is <Machines>/<VM_Name>/Logs */
4829 aLogFolder = Utf8StrFmt("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
4830 }
4831 else
4832 {
4833 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
4834 Assert(!mUserData->mSnapshotFolderFull.isEmpty());
4835 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
4836 RTPATH_DELIMITER);
4837 }
4838}
4839
4840/**
4841 * @note Locks this object for writing, calls the client process (outside the
4842 * lock).
4843 */
4844HRESULT Machine::openSession(IInternalSessionControl *aControl)
4845{
4846 LogFlowThisFuncEnter();
4847
4848 AssertReturn(aControl, E_FAIL);
4849
4850 AutoCaller autoCaller(this);
4851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4852
4853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4854
4855 if (!mData->mRegistered)
4856 return setError(E_UNEXPECTED,
4857 tr("The machine '%ls' is not registered"),
4858 mUserData->mName.raw());
4859
4860 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4861
4862 /* Hack: in case the session is closing and there is a progress object
4863 * which allows waiting for the session to be closed, take the opportunity
4864 * and do a limited wait (max. 1 second). This helps a lot when the system
4865 * is busy and thus session closing can take a little while. */
4866 if ( mData->mSession.mState == SessionState_Closing
4867 && mData->mSession.mProgress)
4868 {
4869 alock.leave();
4870 mData->mSession.mProgress->WaitForCompletion(1000);
4871 alock.enter();
4872 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4873 }
4874
4875 if (mData->mSession.mState == SessionState_Open ||
4876 mData->mSession.mState == SessionState_Closing)
4877 return setError(VBOX_E_INVALID_OBJECT_STATE,
4878 tr("A session for the machine '%ls' is currently open (or being closed)"),
4879 mUserData->mName.raw());
4880
4881 /* may not be busy */
4882 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
4883
4884 /* get the session PID */
4885 RTPROCESS pid = NIL_RTPROCESS;
4886 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
4887 aControl->GetPID((ULONG *) &pid);
4888 Assert(pid != NIL_RTPROCESS);
4889
4890 if (mData->mSession.mState == SessionState_Spawning)
4891 {
4892 /* This machine is awaiting for a spawning session to be opened, so
4893 * reject any other open attempts from processes other than one
4894 * started by #openRemoteSession(). */
4895
4896 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
4897 mData->mSession.mPid, mData->mSession.mPid));
4898 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
4899
4900 if (mData->mSession.mPid != pid)
4901 return setError(E_ACCESSDENIED,
4902 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
4903 "session with the machine named '%ls', while only a process "
4904 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
4905 pid, mUserData->mName.raw(), mData->mSession.mPid);
4906 }
4907
4908 /* create a SessionMachine object */
4909 ComObjPtr<SessionMachine> sessionMachine;
4910 sessionMachine.createObject();
4911 HRESULT rc = sessionMachine->init(this);
4912 AssertComRC(rc);
4913
4914 /* NOTE: doing return from this function after this point but
4915 * before the end is forbidden since it may call SessionMachine::uninit()
4916 * (through the ComObjPtr's destructor) which requests the VirtualBox write
4917 * lock while still holding the Machine lock in alock so that a deadlock
4918 * is possible due to the wrong lock order. */
4919
4920 if (SUCCEEDED(rc))
4921 {
4922#ifdef VBOX_WITH_RESOURCE_USAGE_API
4923 registerMetrics(mParent->performanceCollector(), this, pid);
4924#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4925
4926 /*
4927 * Set the session state to Spawning to protect against subsequent
4928 * attempts to open a session and to unregister the machine after
4929 * we leave the lock.
4930 */
4931 SessionState_T origState = mData->mSession.mState;
4932 mData->mSession.mState = SessionState_Spawning;
4933
4934 /*
4935 * Leave the lock before calling the client process -- it will call
4936 * Machine/SessionMachine methods. Leaving the lock here is quite safe
4937 * because the state is Spawning, so that openRemotesession() and
4938 * openExistingSession() calls will fail. This method, called before we
4939 * enter the lock again, will fail because of the wrong PID.
4940 *
4941 * Note that mData->mSession.mRemoteControls accessed outside
4942 * the lock may not be modified when state is Spawning, so it's safe.
4943 */
4944 alock.leave();
4945
4946 LogFlowThisFunc(("Calling AssignMachine()...\n"));
4947 rc = aControl->AssignMachine(sessionMachine);
4948 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
4949
4950 /* The failure may occur w/o any error info (from RPC), so provide one */
4951 if (FAILED(rc))
4952 setError(VBOX_E_VM_ERROR,
4953 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4954
4955 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
4956 {
4957 /* complete the remote session initialization */
4958
4959 /* get the console from the direct session */
4960 ComPtr<IConsole> console;
4961 rc = aControl->GetRemoteConsole(console.asOutParam());
4962 ComAssertComRC(rc);
4963
4964 if (SUCCEEDED(rc) && !console)
4965 {
4966 ComAssert(!!console);
4967 rc = E_FAIL;
4968 }
4969
4970 /* assign machine & console to the remote session */
4971 if (SUCCEEDED(rc))
4972 {
4973 /*
4974 * after openRemoteSession(), the first and the only
4975 * entry in remoteControls is that remote session
4976 */
4977 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4978 rc = mData->mSession.mRemoteControls.front()->
4979 AssignRemoteMachine(sessionMachine, console);
4980 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4981
4982 /* The failure may occur w/o any error info (from RPC), so provide one */
4983 if (FAILED(rc))
4984 setError(VBOX_E_VM_ERROR,
4985 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
4986 }
4987
4988 if (FAILED(rc))
4989 aControl->Uninitialize();
4990 }
4991
4992 /* enter the lock again */
4993 alock.enter();
4994
4995 /* Restore the session state */
4996 mData->mSession.mState = origState;
4997 }
4998
4999 /* finalize spawning anyway (this is why we don't return on errors above) */
5000 if (mData->mSession.mState == SessionState_Spawning)
5001 {
5002 /* Note that the progress object is finalized later */
5003
5004 /* We don't reset mSession.mPid here because it is necessary for
5005 * SessionMachine::uninit() to reap the child process later. */
5006
5007 if (FAILED(rc))
5008 {
5009 /* Close the remote session, remove the remote control from the list
5010 * and reset session state to Closed (@note keep the code in sync
5011 * with the relevant part in openSession()). */
5012
5013 Assert(mData->mSession.mRemoteControls.size() == 1);
5014 if (mData->mSession.mRemoteControls.size() == 1)
5015 {
5016 ErrorInfoKeeper eik;
5017 mData->mSession.mRemoteControls.front()->Uninitialize();
5018 }
5019
5020 mData->mSession.mRemoteControls.clear();
5021 mData->mSession.mState = SessionState_Closed;
5022 }
5023 }
5024 else
5025 {
5026 /* memorize PID of the directly opened session */
5027 if (SUCCEEDED(rc))
5028 mData->mSession.mPid = pid;
5029 }
5030
5031 if (SUCCEEDED(rc))
5032 {
5033 /* memorize the direct session control and cache IUnknown for it */
5034 mData->mSession.mDirectControl = aControl;
5035 mData->mSession.mState = SessionState_Open;
5036 /* associate the SessionMachine with this Machine */
5037 mData->mSession.mMachine = sessionMachine;
5038
5039 /* request an IUnknown pointer early from the remote party for later
5040 * identity checks (it will be internally cached within mDirectControl
5041 * at least on XPCOM) */
5042 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
5043 NOREF(unk);
5044 }
5045
5046 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
5047 * would break the lock order */
5048 alock.leave();
5049
5050 /* uninitialize the created session machine on failure */
5051 if (FAILED(rc))
5052 sessionMachine->uninit();
5053
5054 LogFlowThisFunc(("rc=%08X\n", rc));
5055 LogFlowThisFuncLeave();
5056 return rc;
5057}
5058
5059/**
5060 * @note Locks this object for writing, calls the client process
5061 * (inside the lock).
5062 */
5063HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
5064 IN_BSTR aType,
5065 IN_BSTR aEnvironment,
5066 Progress *aProgress)
5067{
5068 LogFlowThisFuncEnter();
5069
5070 AssertReturn(aControl, E_FAIL);
5071 AssertReturn(aProgress, E_FAIL);
5072
5073 AutoCaller autoCaller(this);
5074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5075
5076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5077
5078 if (!mData->mRegistered)
5079 return setError(E_UNEXPECTED,
5080 tr("The machine '%ls' is not registered"),
5081 mUserData->mName.raw());
5082
5083 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5084
5085 if (mData->mSession.mState == SessionState_Open ||
5086 mData->mSession.mState == SessionState_Spawning ||
5087 mData->mSession.mState == SessionState_Closing)
5088 return setError(VBOX_E_INVALID_OBJECT_STATE,
5089 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
5090 mUserData->mName.raw());
5091
5092 /* may not be busy */
5093 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
5094
5095 /* get the path to the executable */
5096 char szPath[RTPATH_MAX];
5097 RTPathAppPrivateArch(szPath, RTPATH_MAX);
5098 size_t sz = strlen(szPath);
5099 szPath[sz++] = RTPATH_DELIMITER;
5100 szPath[sz] = 0;
5101 char *cmd = szPath + sz;
5102 sz = RTPATH_MAX - sz;
5103
5104 int vrc = VINF_SUCCESS;
5105 RTPROCESS pid = NIL_RTPROCESS;
5106
5107 RTENV env = RTENV_DEFAULT;
5108
5109 if (aEnvironment != NULL && *aEnvironment)
5110 {
5111 char *newEnvStr = NULL;
5112
5113 do
5114 {
5115 /* clone the current environment */
5116 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
5117 AssertRCBreakStmt(vrc2, vrc = vrc2);
5118
5119 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
5120 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
5121
5122 /* put new variables to the environment
5123 * (ignore empty variable names here since RTEnv API
5124 * intentionally doesn't do that) */
5125 char *var = newEnvStr;
5126 for (char *p = newEnvStr; *p; ++p)
5127 {
5128 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
5129 {
5130 *p = '\0';
5131 if (*var)
5132 {
5133 char *val = strchr(var, '=');
5134 if (val)
5135 {
5136 *val++ = '\0';
5137 vrc2 = RTEnvSetEx(env, var, val);
5138 }
5139 else
5140 vrc2 = RTEnvUnsetEx(env, var);
5141 if (RT_FAILURE(vrc2))
5142 break;
5143 }
5144 var = p + 1;
5145 }
5146 }
5147 if (RT_SUCCESS(vrc2) && *var)
5148 vrc2 = RTEnvPutEx(env, var);
5149
5150 AssertRCBreakStmt(vrc2, vrc = vrc2);
5151 }
5152 while (0);
5153
5154 if (newEnvStr != NULL)
5155 RTStrFree(newEnvStr);
5156 }
5157
5158 Utf8Str strType(aType);
5159
5160 /* Qt is default */
5161#ifdef VBOX_WITH_QTGUI
5162 if (strType == "gui" || strType == "GUI/Qt")
5163 {
5164# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
5165 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
5166# else
5167 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
5168# endif
5169 Assert(sz >= sizeof(VirtualBox_exe));
5170 strcpy(cmd, VirtualBox_exe);
5171
5172 Utf8Str idStr = mData->mUuid.toString();
5173# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
5174 const char * args[] = {szPath, "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
5175# else
5176 Utf8Str strName = mUserData->mName;
5177 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
5178# endif
5179 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5180 }
5181#else /* !VBOX_WITH_QTGUI */
5182 if (0)
5183 ;
5184#endif /* VBOX_WITH_QTGUI */
5185
5186 else
5187
5188#ifdef VBOX_WITH_VBOXSDL
5189 if (strType == "sdl" || strType == "GUI/SDL")
5190 {
5191 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
5192 Assert(sz >= sizeof(VBoxSDL_exe));
5193 strcpy(cmd, VBoxSDL_exe);
5194
5195 Utf8Str idStr = mData->mUuid.toString();
5196# ifdef RT_OS_WINDOWS
5197 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
5198# else
5199 Utf8Str strName = mUserData->mName;
5200 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0 };
5201# endif
5202 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5203 }
5204#else /* !VBOX_WITH_VBOXSDL */
5205 if (0)
5206 ;
5207#endif /* !VBOX_WITH_VBOXSDL */
5208
5209 else
5210
5211#ifdef VBOX_WITH_HEADLESS
5212 if ( strType == "headless"
5213 || strType == "capture"
5214#ifdef VBOX_WITH_VRDP
5215 || strType == "vrdp"
5216#endif
5217 )
5218 {
5219 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
5220 Assert(sz >= sizeof(VBoxHeadless_exe));
5221 strcpy(cmd, VBoxHeadless_exe);
5222
5223 Utf8Str idStr = mData->mUuid.toString();
5224 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
5225# ifdef RT_OS_WINDOWS
5226 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
5227# else
5228 Utf8Str strName = mUserData->mName;
5229 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
5230# endif
5231#ifdef VBOX_WITH_VRDP
5232 if (strType == "headless")
5233 {
5234 unsigned pos = RT_ELEMENTS(args) - 3;
5235 args[pos++] = "--vrdp";
5236 args[pos] = "off";
5237 }
5238#endif
5239 if (strType == "capture")
5240 {
5241 unsigned pos = RT_ELEMENTS(args) - 3;
5242 args[pos] = "--capture";
5243 }
5244 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5245 }
5246#else /* !VBOX_WITH_HEADLESS */
5247 if (0)
5248 ;
5249#endif /* !VBOX_WITH_HEADLESS */
5250 else
5251 {
5252 RTEnvDestroy(env);
5253 return setError(E_INVALIDARG,
5254 tr("Invalid session type: '%s'"),
5255 strType.c_str());
5256 }
5257
5258 RTEnvDestroy(env);
5259
5260 if (RT_FAILURE(vrc))
5261 return setError(VBOX_E_IPRT_ERROR,
5262 tr("Could not launch a process for the machine '%ls' (%Rrc)"),
5263 mUserData->mName.raw(), vrc);
5264
5265 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
5266
5267 /*
5268 * Note that we don't leave the lock here before calling the client,
5269 * because it doesn't need to call us back if called with a NULL argument.
5270 * Leaving the lock herer is dangerous because we didn't prepare the
5271 * launch data yet, but the client we've just started may happen to be
5272 * too fast and call openSession() that will fail (because of PID, etc.),
5273 * so that the Machine will never get out of the Spawning session state.
5274 */
5275
5276 /* inform the session that it will be a remote one */
5277 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
5278 HRESULT rc = aControl->AssignMachine(NULL);
5279 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
5280
5281 if (FAILED(rc))
5282 {
5283 /* restore the session state */
5284 mData->mSession.mState = SessionState_Closed;
5285 /* The failure may occur w/o any error info (from RPC), so provide one */
5286 return setError(VBOX_E_VM_ERROR,
5287 tr("Failed to assign the machine to the session (%Rrc)"), rc);
5288 }
5289
5290 /* attach launch data to the machine */
5291 Assert(mData->mSession.mPid == NIL_RTPROCESS);
5292 mData->mSession.mRemoteControls.push_back (aControl);
5293 mData->mSession.mProgress = aProgress;
5294 mData->mSession.mPid = pid;
5295 mData->mSession.mState = SessionState_Spawning;
5296 mData->mSession.mType = strType;
5297
5298 LogFlowThisFuncLeave();
5299 return S_OK;
5300}
5301
5302/**
5303 * @note Locks this object for writing, calls the client process
5304 * (outside the lock).
5305 */
5306HRESULT Machine::openExistingSession(IInternalSessionControl *aControl)
5307{
5308 LogFlowThisFuncEnter();
5309
5310 AssertReturn(aControl, E_FAIL);
5311
5312 AutoCaller autoCaller(this);
5313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5314
5315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5316
5317 if (!mData->mRegistered)
5318 return setError(E_UNEXPECTED,
5319 tr("The machine '%ls' is not registered"),
5320 mUserData->mName.raw());
5321
5322 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5323
5324 if (mData->mSession.mState != SessionState_Open)
5325 return setError(VBOX_E_INVALID_SESSION_STATE,
5326 tr("The machine '%ls' does not have an open session"),
5327 mUserData->mName.raw());
5328
5329 ComAssertRet(!mData->mSession.mDirectControl.isNull(), E_FAIL);
5330
5331 // copy member variables before leaving lock
5332 ComPtr<IInternalSessionControl> pDirectControl = mData->mSession.mDirectControl;
5333 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
5334 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
5335
5336 /*
5337 * Leave the lock before calling the client process. It's safe here
5338 * since the only thing to do after we get the lock again is to add
5339 * the remote control to the list (which doesn't directly influence
5340 * anything).
5341 */
5342 alock.leave();
5343
5344 // get the console from the direct session (this is a remote call)
5345 ComPtr<IConsole> pConsole;
5346 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
5347 HRESULT rc = pDirectControl->GetRemoteConsole(pConsole.asOutParam());
5348 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
5349 if (FAILED (rc))
5350 /* The failure may occur w/o any error info (from RPC), so provide one */
5351 return setError (VBOX_E_VM_ERROR,
5352 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
5353
5354 ComAssertRet(!pConsole.isNull(), E_FAIL);
5355
5356 /* attach the remote session to the machine */
5357 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
5358 rc = aControl->AssignRemoteMachine(pSessionMachine, pConsole);
5359 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
5360
5361 /* The failure may occur w/o any error info (from RPC), so provide one */
5362 if (FAILED(rc))
5363 return setError(VBOX_E_VM_ERROR,
5364 tr("Failed to assign the machine to the session (%Rrc)"),
5365 rc);
5366
5367 alock.enter();
5368
5369 /* need to revalidate the state after entering the lock again */
5370 if (mData->mSession.mState != SessionState_Open)
5371 {
5372 aControl->Uninitialize();
5373
5374 return setError(VBOX_E_INVALID_SESSION_STATE,
5375 tr("The machine '%ls' does not have an open session"),
5376 mUserData->mName.raw());
5377 }
5378
5379 /* store the control in the list */
5380 mData->mSession.mRemoteControls.push_back(aControl);
5381
5382 LogFlowThisFuncLeave();
5383 return S_OK;
5384}
5385
5386/**
5387 * Returns @c true if the given machine has an open direct session and returns
5388 * the session machine instance and additional session data (on some platforms)
5389 * if so.
5390 *
5391 * Note that when the method returns @c false, the arguments remain unchanged.
5392 *
5393 * @param aMachine Session machine object.
5394 * @param aControl Direct session control object (optional).
5395 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
5396 *
5397 * @note locks this object for reading.
5398 */
5399#if defined(RT_OS_WINDOWS)
5400bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5401 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5402 HANDLE *aIPCSem /*= NULL*/,
5403 bool aAllowClosing /*= false*/)
5404#elif defined(RT_OS_OS2)
5405bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5406 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5407 HMTX *aIPCSem /*= NULL*/,
5408 bool aAllowClosing /*= false*/)
5409#else
5410bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5411 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5412 bool aAllowClosing /*= false*/)
5413#endif
5414{
5415 AutoLimitedCaller autoCaller(this);
5416 AssertComRCReturn(autoCaller.rc(), false);
5417
5418 /* just return false for inaccessible machines */
5419 if (autoCaller.state() != Ready)
5420 return false;
5421
5422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5423
5424 if (mData->mSession.mState == SessionState_Open ||
5425 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
5426 {
5427 AssertReturn(!mData->mSession.mMachine.isNull(), false);
5428
5429 aMachine = mData->mSession.mMachine;
5430
5431 if (aControl != NULL)
5432 *aControl = mData->mSession.mDirectControl;
5433
5434#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5435 /* Additional session data */
5436 if (aIPCSem != NULL)
5437 *aIPCSem = aMachine->mIPCSem;
5438#endif
5439 return true;
5440 }
5441
5442 return false;
5443}
5444
5445/**
5446 * Returns @c true if the given machine has an spawning direct session and
5447 * returns and additional session data (on some platforms) if so.
5448 *
5449 * Note that when the method returns @c false, the arguments remain unchanged.
5450 *
5451 * @param aPID PID of the spawned direct session process.
5452 *
5453 * @note locks this object for reading.
5454 */
5455#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5456bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
5457#else
5458bool Machine::isSessionSpawning()
5459#endif
5460{
5461 AutoLimitedCaller autoCaller(this);
5462 AssertComRCReturn(autoCaller.rc(), false);
5463
5464 /* just return false for inaccessible machines */
5465 if (autoCaller.state() != Ready)
5466 return false;
5467
5468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5469
5470 if (mData->mSession.mState == SessionState_Spawning)
5471 {
5472#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5473 /* Additional session data */
5474 if (aPID != NULL)
5475 {
5476 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
5477 *aPID = mData->mSession.mPid;
5478 }
5479#endif
5480 return true;
5481 }
5482
5483 return false;
5484}
5485
5486/**
5487 * Called from the client watcher thread to check for unexpected client process
5488 * death during Session_Spawning state (e.g. before it successfully opened a
5489 * direct session).
5490 *
5491 * On Win32 and on OS/2, this method is called only when we've got the
5492 * direct client's process termination notification, so it always returns @c
5493 * true.
5494 *
5495 * On other platforms, this method returns @c true if the client process is
5496 * terminated and @c false if it's still alive.
5497 *
5498 * @note Locks this object for writing.
5499 */
5500bool Machine::checkForSpawnFailure()
5501{
5502 AutoCaller autoCaller(this);
5503 if (!autoCaller.isOk())
5504 {
5505 /* nothing to do */
5506 LogFlowThisFunc(("Already uninitialized!\n"));
5507 return true;
5508 }
5509
5510 /* VirtualBox::addProcessToReap() needs a write lock */
5511 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
5512
5513 if (mData->mSession.mState != SessionState_Spawning)
5514 {
5515 /* nothing to do */
5516 LogFlowThisFunc(("Not spawning any more!\n"));
5517 return true;
5518 }
5519
5520 HRESULT rc = S_OK;
5521
5522#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5523
5524 /* the process was already unexpectedly terminated, we just need to set an
5525 * error and finalize session spawning */
5526 rc = setError(E_FAIL,
5527 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5528 getName().raw());
5529#else
5530
5531 /* PID not yet initialized, skip check. */
5532 if (mData->mSession.mPid == NIL_RTPROCESS)
5533 return false;
5534
5535 RTPROCSTATUS status;
5536 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
5537 &status);
5538
5539 if (vrc != VERR_PROCESS_RUNNING)
5540 rc = setError(E_FAIL,
5541 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5542 getName().raw());
5543#endif
5544
5545 if (FAILED(rc))
5546 {
5547 /* Close the remote session, remove the remote control from the list
5548 * and reset session state to Closed (@note keep the code in sync with
5549 * the relevant part in checkForSpawnFailure()). */
5550
5551 Assert(mData->mSession.mRemoteControls.size() == 1);
5552 if (mData->mSession.mRemoteControls.size() == 1)
5553 {
5554 ErrorInfoKeeper eik;
5555 mData->mSession.mRemoteControls.front()->Uninitialize();
5556 }
5557
5558 mData->mSession.mRemoteControls.clear();
5559 mData->mSession.mState = SessionState_Closed;
5560
5561 /* finalize the progress after setting the state */
5562 if (!mData->mSession.mProgress.isNull())
5563 {
5564 mData->mSession.mProgress->notifyComplete(rc);
5565 mData->mSession.mProgress.setNull();
5566 }
5567
5568 mParent->addProcessToReap(mData->mSession.mPid);
5569 mData->mSession.mPid = NIL_RTPROCESS;
5570
5571 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
5572 return true;
5573 }
5574
5575 return false;
5576}
5577
5578/**
5579 * Checks that the registered flag of the machine can be set according to
5580 * the argument and sets it. On success, commits and saves all settings.
5581 *
5582 * @note When this machine is inaccessible, the only valid value for \a
5583 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
5584 * inaccessible machines are not currently supported. Note that unregistering
5585 * an inaccessible machine will \b uninitialize this machine object. Therefore,
5586 * the caller must make sure there are no active Machine::addCaller() calls
5587 * on the current thread because this will block Machine::uninit().
5588 *
5589 * @note Must be called from mParent's write lock. Locks this object and
5590 * children for writing.
5591 */
5592HRESULT Machine::trySetRegistered(BOOL argNewRegistered)
5593{
5594 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5595
5596 AutoLimitedCaller autoCaller(this);
5597 AssertComRCReturnRC(autoCaller.rc());
5598
5599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5600
5601 /* wait for state dependants to drop to zero */
5602 ensureNoStateDependencies();
5603
5604 ComAssertRet(mData->mRegistered != argNewRegistered, E_FAIL);
5605
5606 if (!mData->mAccessible)
5607 {
5608 /* A special case: the machine is not accessible. */
5609
5610 /* inaccessible machines can only be unregistered */
5611 AssertReturn(!argNewRegistered, E_FAIL);
5612
5613 /* Uninitialize ourselves here because currently there may be no
5614 * unregistered that are inaccessible (this state combination is not
5615 * supported). Note releasing the caller and leaving the lock before
5616 * calling uninit() */
5617
5618 alock.leave();
5619 autoCaller.release();
5620
5621 uninit();
5622
5623 return S_OK;
5624 }
5625
5626 AssertReturn(autoCaller.state() == Ready, E_FAIL);
5627
5628 if (argNewRegistered)
5629 {
5630 if (mData->mRegistered)
5631 return setError(VBOX_E_INVALID_OBJECT_STATE,
5632 tr("The machine '%ls' with UUID {%s} is already registered"),
5633 mUserData->mName.raw(),
5634 mData->mUuid.toString().raw());
5635 }
5636 else
5637 {
5638 if (mData->mMachineState == MachineState_Saved)
5639 return setError(VBOX_E_INVALID_VM_STATE,
5640 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
5641 mUserData->mName.raw());
5642
5643 size_t snapshotCount = 0;
5644 if (mData->mFirstSnapshot)
5645 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5646 if (snapshotCount)
5647 return setError(VBOX_E_INVALID_OBJECT_STATE,
5648 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
5649 mUserData->mName.raw(), snapshotCount);
5650
5651 if (mData->mSession.mState != SessionState_Closed)
5652 return setError(VBOX_E_INVALID_OBJECT_STATE,
5653 tr("Cannot unregister the machine '%ls' because it has an open session"),
5654 mUserData->mName.raw());
5655
5656 if (mMediaData->mAttachments.size() != 0)
5657 return setError(VBOX_E_INVALID_OBJECT_STATE,
5658 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
5659 mUserData->mName.raw(),
5660 mMediaData->mAttachments.size());
5661
5662 /* Note that we do not prevent unregistration of a DVD or Floppy image
5663 * is attached: as opposed to hard disks detaching such an image
5664 * implicitly in this method (which we will do below) won't have any
5665 * side effects (like detached orphan base and diff hard disks etc).*/
5666 }
5667
5668 HRESULT rc = S_OK;
5669
5670 // Ensure the settings are saved. If we are going to be registered and
5671 // no config file exists yet, create it by calling saveSettings() too.
5672 if ( (m_flModifications)
5673 || (argNewRegistered && !mData->m_pMachineConfigFile->fileExists())
5674 )
5675 {
5676 rc = saveSettings(NULL);
5677 // no need to check whether VirtualBox.xml needs saving too since
5678 // we can't have a machine XML file rename pending
5679 if (FAILED(rc)) return rc;
5680 }
5681
5682 /* more config checking goes here */
5683
5684 if (SUCCEEDED(rc))
5685 {
5686 /* we may have had implicit modifications we want to fix on success */
5687 commit();
5688
5689 mData->mRegistered = argNewRegistered;
5690 }
5691 else
5692 {
5693 /* we may have had implicit modifications we want to cancel on failure*/
5694 rollback(false /* aNotify */);
5695 }
5696
5697 return rc;
5698}
5699
5700/**
5701 * Increases the number of objects dependent on the machine state or on the
5702 * registered state. Guarantees that these two states will not change at least
5703 * until #releaseStateDependency() is called.
5704 *
5705 * Depending on the @a aDepType value, additional state checks may be made.
5706 * These checks will set extended error info on failure. See
5707 * #checkStateDependency() for more info.
5708 *
5709 * If this method returns a failure, the dependency is not added and the caller
5710 * is not allowed to rely on any particular machine state or registration state
5711 * value and may return the failed result code to the upper level.
5712 *
5713 * @param aDepType Dependency type to add.
5714 * @param aState Current machine state (NULL if not interested).
5715 * @param aRegistered Current registered state (NULL if not interested).
5716 *
5717 * @note Locks this object for writing.
5718 */
5719HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
5720 MachineState_T *aState /* = NULL */,
5721 BOOL *aRegistered /* = NULL */)
5722{
5723 AutoCaller autoCaller(this);
5724 AssertComRCReturnRC(autoCaller.rc());
5725
5726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5727
5728 HRESULT rc = checkStateDependency(aDepType);
5729 if (FAILED(rc)) return rc;
5730
5731 {
5732 if (mData->mMachineStateChangePending != 0)
5733 {
5734 /* ensureNoStateDependencies() is waiting for state dependencies to
5735 * drop to zero so don't add more. It may make sense to wait a bit
5736 * and retry before reporting an error (since the pending state
5737 * transition should be really quick) but let's just assert for
5738 * now to see if it ever happens on practice. */
5739
5740 AssertFailed();
5741
5742 return setError(E_ACCESSDENIED,
5743 tr("Machine state change is in progress. Please retry the operation later."));
5744 }
5745
5746 ++mData->mMachineStateDeps;
5747 Assert(mData->mMachineStateDeps != 0 /* overflow */);
5748 }
5749
5750 if (aState)
5751 *aState = mData->mMachineState;
5752 if (aRegistered)
5753 *aRegistered = mData->mRegistered;
5754
5755 return S_OK;
5756}
5757
5758/**
5759 * Decreases the number of objects dependent on the machine state.
5760 * Must always complete the #addStateDependency() call after the state
5761 * dependency is no more necessary.
5762 */
5763void Machine::releaseStateDependency()
5764{
5765 AutoCaller autoCaller(this);
5766 AssertComRCReturnVoid(autoCaller.rc());
5767
5768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5769
5770 /* releaseStateDependency() w/o addStateDependency()? */
5771 AssertReturnVoid(mData->mMachineStateDeps != 0);
5772 -- mData->mMachineStateDeps;
5773
5774 if (mData->mMachineStateDeps == 0)
5775 {
5776 /* inform ensureNoStateDependencies() that there are no more deps */
5777 if (mData->mMachineStateChangePending != 0)
5778 {
5779 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
5780 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
5781 }
5782 }
5783}
5784
5785// protected methods
5786/////////////////////////////////////////////////////////////////////////////
5787
5788/**
5789 * Performs machine state checks based on the @a aDepType value. If a check
5790 * fails, this method will set extended error info, otherwise it will return
5791 * S_OK. It is supposed, that on failure, the caller will immedieately return
5792 * the return value of this method to the upper level.
5793 *
5794 * When @a aDepType is AnyStateDep, this method always returns S_OK.
5795 *
5796 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
5797 * current state of this machine object allows to change settings of the
5798 * machine (i.e. the machine is not registered, or registered but not running
5799 * and not saved). It is useful to call this method from Machine setters
5800 * before performing any change.
5801 *
5802 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
5803 * as for MutableStateDep except that if the machine is saved, S_OK is also
5804 * returned. This is useful in setters which allow changing machine
5805 * properties when it is in the saved state.
5806 *
5807 * @param aDepType Dependency type to check.
5808 *
5809 * @note Non Machine based classes should use #addStateDependency() and
5810 * #releaseStateDependency() methods or the smart AutoStateDependency
5811 * template.
5812 *
5813 * @note This method must be called from under this object's read or write
5814 * lock.
5815 */
5816HRESULT Machine::checkStateDependency(StateDependency aDepType)
5817{
5818 switch (aDepType)
5819 {
5820 case AnyStateDep:
5821 {
5822 break;
5823 }
5824 case MutableStateDep:
5825 {
5826 if ( mData->mRegistered
5827 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5828 || ( mData->mMachineState != MachineState_Paused
5829 && mData->mMachineState != MachineState_Running
5830 && mData->mMachineState != MachineState_Aborted
5831 && mData->mMachineState != MachineState_Teleported
5832 && mData->mMachineState != MachineState_PoweredOff
5833 )
5834 )
5835 )
5836 return setError(VBOX_E_INVALID_VM_STATE,
5837 tr("The machine is not mutable (state is %s)"),
5838 Global::stringifyMachineState(mData->mMachineState));
5839 break;
5840 }
5841 case MutableOrSavedStateDep:
5842 {
5843 if ( mData->mRegistered
5844 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5845 || ( mData->mMachineState != MachineState_Paused
5846 && mData->mMachineState != MachineState_Running
5847 && mData->mMachineState != MachineState_Aborted
5848 && mData->mMachineState != MachineState_Teleported
5849 && mData->mMachineState != MachineState_Saved
5850 && mData->mMachineState != MachineState_PoweredOff
5851 )
5852 )
5853 )
5854 return setError(VBOX_E_INVALID_VM_STATE,
5855 tr("The machine is not mutable (state is %s)"),
5856 Global::stringifyMachineState(mData->mMachineState));
5857 break;
5858 }
5859 }
5860
5861 return S_OK;
5862}
5863
5864/**
5865 * Helper to initialize all associated child objects and allocate data
5866 * structures.
5867 *
5868 * This method must be called as a part of the object's initialization procedure
5869 * (usually done in the #init() method).
5870 *
5871 * @note Must be called only from #init() or from #registeredInit().
5872 */
5873HRESULT Machine::initDataAndChildObjects()
5874{
5875 AutoCaller autoCaller(this);
5876 AssertComRCReturnRC(autoCaller.rc());
5877 AssertComRCReturn(autoCaller.state() == InInit ||
5878 autoCaller.state() == Limited, E_FAIL);
5879
5880 AssertReturn(!mData->mAccessible, E_FAIL);
5881
5882 /* allocate data structures */
5883 mSSData.allocate();
5884 mUserData.allocate();
5885 mHWData.allocate();
5886 mMediaData.allocate();
5887 mStorageControllers.allocate();
5888
5889 /* initialize mOSTypeId */
5890 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
5891
5892 /* create associated BIOS settings object */
5893 unconst(mBIOSSettings).createObject();
5894 mBIOSSettings->init(this);
5895
5896#ifdef VBOX_WITH_VRDP
5897 /* create an associated VRDPServer object (default is disabled) */
5898 unconst(mVRDPServer).createObject();
5899 mVRDPServer->init(this);
5900#endif
5901
5902 /* create associated serial port objects */
5903 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
5904 {
5905 unconst(mSerialPorts[slot]).createObject();
5906 mSerialPorts[slot]->init(this, slot);
5907 }
5908
5909 /* create associated parallel port objects */
5910 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
5911 {
5912 unconst(mParallelPorts[slot]).createObject();
5913 mParallelPorts[slot]->init(this, slot);
5914 }
5915
5916 /* create the audio adapter object (always present, default is disabled) */
5917 unconst(mAudioAdapter).createObject();
5918 mAudioAdapter->init(this);
5919
5920 /* create the USB controller object (always present, default is disabled) */
5921 unconst(mUSBController).createObject();
5922 mUSBController->init(this);
5923
5924 /* create associated network adapter objects */
5925 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++)
5926 {
5927 unconst(mNetworkAdapters[slot]).createObject();
5928 mNetworkAdapters[slot]->init(this, slot);
5929 }
5930
5931 return S_OK;
5932}
5933
5934/**
5935 * Helper to uninitialize all associated child objects and to free all data
5936 * structures.
5937 *
5938 * This method must be called as a part of the object's uninitialization
5939 * procedure (usually done in the #uninit() method).
5940 *
5941 * @note Must be called only from #uninit() or from #registeredInit().
5942 */
5943void Machine::uninitDataAndChildObjects()
5944{
5945 AutoCaller autoCaller(this);
5946 AssertComRCReturnVoid(autoCaller.rc());
5947 AssertComRCReturnVoid( autoCaller.state() == InUninit
5948 || autoCaller.state() == Limited);
5949
5950 /* uninit all children using addDependentChild()/removeDependentChild()
5951 * in their init()/uninit() methods */
5952 uninitDependentChildren();
5953
5954 /* tell all our other child objects we've been uninitialized */
5955
5956 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
5957 {
5958 if (mNetworkAdapters[slot])
5959 {
5960 mNetworkAdapters[slot]->uninit();
5961 unconst(mNetworkAdapters[slot]).setNull();
5962 }
5963 }
5964
5965 if (mUSBController)
5966 {
5967 mUSBController->uninit();
5968 unconst(mUSBController).setNull();
5969 }
5970
5971 if (mAudioAdapter)
5972 {
5973 mAudioAdapter->uninit();
5974 unconst(mAudioAdapter).setNull();
5975 }
5976
5977 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
5978 {
5979 if (mParallelPorts[slot])
5980 {
5981 mParallelPorts[slot]->uninit();
5982 unconst(mParallelPorts[slot]).setNull();
5983 }
5984 }
5985
5986 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
5987 {
5988 if (mSerialPorts[slot])
5989 {
5990 mSerialPorts[slot]->uninit();
5991 unconst(mSerialPorts[slot]).setNull();
5992 }
5993 }
5994
5995#ifdef VBOX_WITH_VRDP
5996 if (mVRDPServer)
5997 {
5998 mVRDPServer->uninit();
5999 unconst(mVRDPServer).setNull();
6000 }
6001#endif
6002
6003 if (mBIOSSettings)
6004 {
6005 mBIOSSettings->uninit();
6006 unconst(mBIOSSettings).setNull();
6007 }
6008
6009 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
6010 * instance is uninitialized; SessionMachine instances refer to real
6011 * Machine hard disks). This is necessary for a clean re-initialization of
6012 * the VM after successfully re-checking the accessibility state. Note
6013 * that in case of normal Machine or SnapshotMachine uninitialization (as
6014 * a result of unregistering or discarding the snapshot), outdated hard
6015 * disk attachments will already be uninitialized and deleted, so this
6016 * code will not affect them. */
6017 VBoxClsID clsid = getClassID();
6018 if ( !!mMediaData
6019 && (clsid == clsidMachine || clsid == clsidSnapshotMachine)
6020 )
6021 {
6022 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6023 it != mMediaData->mAttachments.end();
6024 ++it)
6025 {
6026 ComObjPtr<Medium> hd = (*it)->getMedium();
6027 if (hd.isNull())
6028 continue;
6029 HRESULT rc = hd->detachFrom(mData->mUuid, getSnapshotId());
6030 AssertComRC(rc);
6031 }
6032 }
6033
6034 if (getClassID() == clsidMachine)
6035 {
6036 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
6037 if (mData->mFirstSnapshot)
6038 {
6039 // snapshots tree is protected by media write lock; strictly
6040 // this isn't necessary here since we're deleting the entire
6041 // machine, but otherwise we assert in Snapshot::uninit()
6042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6043 mData->mFirstSnapshot->uninit();
6044 mData->mFirstSnapshot.setNull();
6045 }
6046
6047 mData->mCurrentSnapshot.setNull();
6048 }
6049
6050 /* free data structures (the essential mData structure is not freed here
6051 * since it may be still in use) */
6052 mMediaData.free();
6053 mStorageControllers.free();
6054 mHWData.free();
6055 mUserData.free();
6056 mSSData.free();
6057}
6058
6059/**
6060 * Returns a pointer to the Machine object for this machine that acts like a
6061 * parent for complex machine data objects such as shared folders, etc.
6062 *
6063 * For primary Machine objects and for SnapshotMachine objects, returns this
6064 * object's pointer itself. For SessoinMachine objects, returns the peer
6065 * (primary) machine pointer.
6066 */
6067Machine* Machine::getMachine()
6068{
6069 if (getClassID() == clsidSessionMachine)
6070 return (Machine*)mPeer;
6071 return this;
6072}
6073
6074/**
6075 * Makes sure that there are no machine state dependants. If necessary, waits
6076 * for the number of dependants to drop to zero.
6077 *
6078 * Make sure this method is called from under this object's write lock to
6079 * guarantee that no new dependants may be added when this method returns
6080 * control to the caller.
6081 *
6082 * @note Locks this object for writing. The lock will be released while waiting
6083 * (if necessary).
6084 *
6085 * @warning To be used only in methods that change the machine state!
6086 */
6087void Machine::ensureNoStateDependencies()
6088{
6089 AssertReturnVoid(isWriteLockOnCurrentThread());
6090
6091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 /* Wait for all state dependants if necessary */
6094 if (mData->mMachineStateDeps != 0)
6095 {
6096 /* lazy semaphore creation */
6097 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
6098 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
6099
6100 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
6101 mData->mMachineStateDeps));
6102
6103 ++mData->mMachineStateChangePending;
6104
6105 /* reset the semaphore before waiting, the last dependant will signal
6106 * it */
6107 RTSemEventMultiReset(mData->mMachineStateDepsSem);
6108
6109 alock.leave();
6110
6111 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
6112
6113 alock.enter();
6114
6115 -- mData->mMachineStateChangePending;
6116 }
6117}
6118
6119/**
6120 * Changes the machine state and informs callbacks.
6121 *
6122 * This method is not intended to fail so it either returns S_OK or asserts (and
6123 * returns a failure).
6124 *
6125 * @note Locks this object for writing.
6126 */
6127HRESULT Machine::setMachineState(MachineState_T aMachineState)
6128{
6129 LogFlowThisFuncEnter();
6130 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
6131
6132 AutoCaller autoCaller(this);
6133 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6134
6135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 /* wait for state dependants to drop to zero */
6138 ensureNoStateDependencies();
6139
6140 if (mData->mMachineState != aMachineState)
6141 {
6142 mData->mMachineState = aMachineState;
6143
6144 RTTimeNow(&mData->mLastStateChange);
6145
6146 mParent->onMachineStateChange(mData->mUuid, aMachineState);
6147 }
6148
6149 LogFlowThisFuncLeave();
6150 return S_OK;
6151}
6152
6153/**
6154 * Searches for a shared folder with the given logical name
6155 * in the collection of shared folders.
6156 *
6157 * @param aName logical name of the shared folder
6158 * @param aSharedFolder where to return the found object
6159 * @param aSetError whether to set the error info if the folder is
6160 * not found
6161 * @return
6162 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
6163 *
6164 * @note
6165 * must be called from under the object's lock!
6166 */
6167HRESULT Machine::findSharedFolder(CBSTR aName,
6168 ComObjPtr<SharedFolder> &aSharedFolder,
6169 bool aSetError /* = false */)
6170{
6171 bool found = false;
6172 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6173 !found && it != mHWData->mSharedFolders.end();
6174 ++it)
6175 {
6176 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
6177 found = (*it)->getName() == aName;
6178 if (found)
6179 aSharedFolder = *it;
6180 }
6181
6182 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
6183
6184 if (aSetError && !found)
6185 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
6186
6187 return rc;
6188}
6189
6190/**
6191 * Loads all the VM settings by walking down the <Machine> node.
6192 *
6193 * @param aRegistered true when the machine is being loaded on VirtualBox
6194 * startup
6195 *
6196 * @note This method is intended to be called only from init(), so it assumes
6197 * all machine data fields have appropriate default values when it is called.
6198 *
6199 * @note Doesn't lock any objects.
6200 */
6201HRESULT Machine::loadSettings(bool aRegistered)
6202{
6203 LogFlowThisFuncEnter();
6204 AssertReturn(getClassID() == clsidMachine, E_FAIL);
6205
6206 AutoCaller autoCaller(this);
6207 AssertReturn(autoCaller.state() == InInit, E_FAIL);
6208
6209 HRESULT rc = S_OK;
6210
6211 try
6212 {
6213 Assert(mData->m_pMachineConfigFile == NULL);
6214
6215 // load and parse machine XML; this will throw on XML or logic errors
6216 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
6217
6218 /* If the stored UUID is not empty, it means the registered machine
6219 * is being loaded. Compare the loaded UUID with the stored one taken
6220 * from the global registry. */
6221 if (!mData->mUuid.isEmpty())
6222 {
6223 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
6224 {
6225 throw setError(E_FAIL,
6226 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
6227 mData->m_pMachineConfigFile->uuid.raw(),
6228 mData->m_strConfigFileFull.raw(),
6229 mData->mUuid.toString().raw(),
6230 mParent->settingsFilePath().raw());
6231 }
6232 }
6233 else
6234 unconst(mData->mUuid) = mData->m_pMachineConfigFile->uuid;
6235
6236 /* name (required) */
6237 mUserData->mName = mData->m_pMachineConfigFile->strName;
6238
6239 /* nameSync (optional, default is true) */
6240 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
6241
6242 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
6243
6244 // guest OS type
6245 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
6246 /* look up the object by Id to check it is valid */
6247 ComPtr<IGuestOSType> guestOSType;
6248 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
6249 guestOSType.asOutParam());
6250 if (FAILED(rc)) throw rc;
6251
6252 // stateFile (optional)
6253 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
6254 mSSData->mStateFilePath.setNull();
6255 else
6256 {
6257 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
6258 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
6259 if (RT_FAILURE(vrc))
6260 throw setError(E_FAIL,
6261 tr("Invalid saved state file path '%s' (%Rrc)"),
6262 mData->m_pMachineConfigFile->strStateFile.raw(),
6263 vrc);
6264 mSSData->mStateFilePath = stateFilePathFull;
6265 }
6266
6267 /* snapshotFolder (optional) */
6268 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
6269 if (FAILED(rc)) throw rc;
6270
6271 /* currentStateModified (optional, default is true) */
6272 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
6273
6274 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
6275
6276 /* teleportation */
6277 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
6278 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
6279 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
6280 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
6281
6282 /* RTC */
6283 mUserData->mRTCUseUTC = mData->m_pMachineConfigFile->fRTCUseUTC;
6284
6285 /*
6286 * note: all mUserData members must be assigned prior this point because
6287 * we need to commit changes in order to let mUserData be shared by all
6288 * snapshot machine instances.
6289 */
6290 mUserData.commitCopy();
6291
6292 /* Snapshot node (optional) */
6293 size_t cRootSnapshots;
6294 if ((cRootSnapshots = mData->m_pMachineConfigFile->llFirstSnapshot.size()))
6295 {
6296 // there must be only one root snapshot
6297 Assert(cRootSnapshots == 1);
6298
6299 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6300
6301 rc = loadSnapshot(snap,
6302 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
6303 NULL); // no parent == first snapshot
6304 if (FAILED(rc)) throw rc;
6305 }
6306
6307 /* Hardware node (required) */
6308 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
6309 if (FAILED(rc)) throw rc;
6310
6311 /* Load storage controllers */
6312 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
6313 if (FAILED(rc)) throw rc;
6314
6315 /*
6316 * NOTE: the assignment below must be the last thing to do,
6317 * otherwise it will be not possible to change the settings
6318 * somewehere in the code above because all setters will be
6319 * blocked by checkStateDependency(MutableStateDep).
6320 */
6321
6322 /* set the machine state to Aborted or Saved when appropriate */
6323 if (mData->m_pMachineConfigFile->fAborted)
6324 {
6325 Assert(!mSSData->mStateFilePath.isEmpty());
6326 mSSData->mStateFilePath.setNull();
6327
6328 /* no need to use setMachineState() during init() */
6329 mData->mMachineState = MachineState_Aborted;
6330 }
6331 else if (!mSSData->mStateFilePath.isEmpty())
6332 {
6333 /* no need to use setMachineState() during init() */
6334 mData->mMachineState = MachineState_Saved;
6335 }
6336
6337 // after loading settings, we are no longer different from the XML on disk
6338 m_flModifications = 0;
6339 }
6340 catch (HRESULT err)
6341 {
6342 /* we assume that error info is set by the thrower */
6343 rc = err;
6344 }
6345 catch (...)
6346 {
6347 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
6348 }
6349
6350 LogFlowThisFuncLeave();
6351 return rc;
6352}
6353
6354/**
6355 * Recursively loads all snapshots starting from the given.
6356 *
6357 * @param aNode <Snapshot> node.
6358 * @param aCurSnapshotId Current snapshot ID from the settings file.
6359 * @param aParentSnapshot Parent snapshot.
6360 */
6361HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
6362 const Guid &aCurSnapshotId,
6363 Snapshot *aParentSnapshot)
6364{
6365 AssertReturn(getClassID() == clsidMachine, E_FAIL);
6366
6367 HRESULT rc = S_OK;
6368
6369 Utf8Str strStateFile;
6370 if (!data.strStateFile.isEmpty())
6371 {
6372 /* optional */
6373 strStateFile = data.strStateFile;
6374 int vrc = calculateFullPath(strStateFile, strStateFile);
6375 if (RT_FAILURE(vrc))
6376 return setError(E_FAIL,
6377 tr("Invalid saved state file path '%s' (%Rrc)"),
6378 strStateFile.raw(),
6379 vrc);
6380 }
6381
6382 /* create a snapshot machine object */
6383 ComObjPtr<SnapshotMachine> pSnapshotMachine;
6384 pSnapshotMachine.createObject();
6385 rc = pSnapshotMachine->init(this,
6386 data.hardware,
6387 data.storage,
6388 data.uuid,
6389 strStateFile);
6390 if (FAILED(rc)) return rc;
6391
6392 /* create a snapshot object */
6393 ComObjPtr<Snapshot> pSnapshot;
6394 pSnapshot.createObject();
6395 /* initialize the snapshot */
6396 rc = pSnapshot->init(mParent, // VirtualBox object
6397 data.uuid,
6398 data.strName,
6399 data.strDescription,
6400 data.timestamp,
6401 pSnapshotMachine,
6402 aParentSnapshot);
6403 if (FAILED(rc)) return rc;
6404
6405 /* memorize the first snapshot if necessary */
6406 if (!mData->mFirstSnapshot)
6407 mData->mFirstSnapshot = pSnapshot;
6408
6409 /* memorize the current snapshot when appropriate */
6410 if ( !mData->mCurrentSnapshot
6411 && pSnapshot->getId() == aCurSnapshotId
6412 )
6413 mData->mCurrentSnapshot = pSnapshot;
6414
6415 // now create the children
6416 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
6417 it != data.llChildSnapshots.end();
6418 ++it)
6419 {
6420 const settings::Snapshot &childData = *it;
6421 // recurse
6422 rc = loadSnapshot(childData,
6423 aCurSnapshotId,
6424 pSnapshot); // parent = the one we created above
6425 if (FAILED(rc)) return rc;
6426 }
6427
6428 return rc;
6429}
6430
6431/**
6432 * @param aNode <Hardware> node.
6433 */
6434HRESULT Machine::loadHardware(const settings::Hardware &data)
6435{
6436 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6437
6438 HRESULT rc = S_OK;
6439
6440 try
6441 {
6442 /* The hardware version attribute (optional). */
6443 mHWData->mHWVersion = data.strVersion;
6444 mHWData->mHardwareUUID = data.uuid;
6445
6446 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
6447 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
6448 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
6449 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
6450 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
6451 mHWData->mPAEEnabled = data.fPAE;
6452 mHWData->mSyntheticCpu = data.fSyntheticCpu;
6453
6454 mHWData->mCPUCount = data.cCPUs;
6455 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
6456
6457 // cpu
6458 if (mHWData->mCPUHotPlugEnabled)
6459 {
6460 for (settings::CpuList::const_iterator it = data.llCpus.begin();
6461 it != data.llCpus.end();
6462 ++it)
6463 {
6464 const settings::Cpu &cpu = *it;
6465
6466 mHWData->mCPUAttached[cpu.ulId] = true;
6467 }
6468 }
6469
6470 // cpuid leafs
6471 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
6472 it != data.llCpuIdLeafs.end();
6473 ++it)
6474 {
6475 const settings::CpuIdLeaf &leaf = *it;
6476
6477 switch (leaf.ulId)
6478 {
6479 case 0x0:
6480 case 0x1:
6481 case 0x2:
6482 case 0x3:
6483 case 0x4:
6484 case 0x5:
6485 case 0x6:
6486 case 0x7:
6487 case 0x8:
6488 case 0x9:
6489 case 0xA:
6490 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
6491 break;
6492
6493 case 0x80000000:
6494 case 0x80000001:
6495 case 0x80000002:
6496 case 0x80000003:
6497 case 0x80000004:
6498 case 0x80000005:
6499 case 0x80000006:
6500 case 0x80000007:
6501 case 0x80000008:
6502 case 0x80000009:
6503 case 0x8000000A:
6504 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
6505 break;
6506
6507 default:
6508 /* just ignore */
6509 break;
6510 }
6511 }
6512
6513 mHWData->mMemorySize = data.ulMemorySizeMB;
6514
6515 // boot order
6516 for (size_t i = 0;
6517 i < RT_ELEMENTS(mHWData->mBootOrder);
6518 i++)
6519 {
6520 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
6521 if (it == data.mapBootOrder.end())
6522 mHWData->mBootOrder[i] = DeviceType_Null;
6523 else
6524 mHWData->mBootOrder[i] = it->second;
6525 }
6526
6527 mHWData->mVRAMSize = data.ulVRAMSizeMB;
6528 mHWData->mMonitorCount = data.cMonitors;
6529 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
6530 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
6531 mHWData->mFirmwareType = data.firmwareType;
6532 mHWData->mPointingHidType = data.pointingHidType;
6533 mHWData->mKeyboardHidType = data.keyboardHidType;
6534 mHWData->mHpetEnabled = data.fHpetEnabled;
6535
6536#ifdef VBOX_WITH_VRDP
6537 /* RemoteDisplay */
6538 rc = mVRDPServer->loadSettings(data.vrdpSettings);
6539 if (FAILED(rc)) return rc;
6540#endif
6541
6542 /* BIOS */
6543 rc = mBIOSSettings->loadSettings(data.biosSettings);
6544 if (FAILED(rc)) return rc;
6545
6546 /* USB Controller */
6547 rc = mUSBController->loadSettings(data.usbController);
6548 if (FAILED(rc)) return rc;
6549
6550 // network adapters
6551 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
6552 it != data.llNetworkAdapters.end();
6553 ++it)
6554 {
6555 const settings::NetworkAdapter &nic = *it;
6556
6557 /* slot unicity is guaranteed by XML Schema */
6558 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
6559 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
6560 if (FAILED(rc)) return rc;
6561 }
6562
6563 // serial ports
6564 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
6565 it != data.llSerialPorts.end();
6566 ++it)
6567 {
6568 const settings::SerialPort &s = *it;
6569
6570 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
6571 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
6572 if (FAILED(rc)) return rc;
6573 }
6574
6575 // parallel ports (optional)
6576 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
6577 it != data.llParallelPorts.end();
6578 ++it)
6579 {
6580 const settings::ParallelPort &p = *it;
6581
6582 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
6583 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
6584 if (FAILED(rc)) return rc;
6585 }
6586
6587 /* AudioAdapter */
6588 rc = mAudioAdapter->loadSettings(data.audioAdapter);
6589 if (FAILED(rc)) return rc;
6590
6591 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
6592 it != data.llSharedFolders.end();
6593 ++it)
6594 {
6595 const settings::SharedFolder &sf = *it;
6596 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
6597 if (FAILED(rc)) return rc;
6598 }
6599
6600 // Clipboard
6601 mHWData->mClipboardMode = data.clipboardMode;
6602
6603 // guest settings
6604 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
6605 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
6606
6607 // IO settings
6608 mHWData->mIoMgrType = data.ioSettings.ioMgrType;
6609 mHWData->mIoBackendType = data.ioSettings.ioBackendType;
6610 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
6611 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
6612 mHWData->mIoBandwidthMax = data.ioSettings.ulIoBandwidthMax;
6613
6614#ifdef VBOX_WITH_GUEST_PROPS
6615 /* Guest properties (optional) */
6616 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
6617 it != data.llGuestProperties.end();
6618 ++it)
6619 {
6620 const settings::GuestProperty &prop = *it;
6621 uint32_t fFlags = guestProp::NILFLAG;
6622 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
6623 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
6624 mHWData->mGuestProperties.push_back(property);
6625 }
6626
6627 mHWData->mPropertyServiceActive = false;
6628 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
6629#endif /* VBOX_WITH_GUEST_PROPS defined */
6630 }
6631 catch(std::bad_alloc &)
6632 {
6633 return E_OUTOFMEMORY;
6634 }
6635
6636 AssertComRC(rc);
6637 return rc;
6638}
6639
6640 /**
6641 * @param aNode <StorageControllers> node.
6642 */
6643HRESULT Machine::loadStorageControllers(const settings::Storage &data,
6644 bool aRegistered,
6645 const Guid *aSnapshotId /* = NULL */)
6646{
6647 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6648
6649 HRESULT rc = S_OK;
6650
6651 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
6652 it != data.llStorageControllers.end();
6653 ++it)
6654 {
6655 const settings::StorageController &ctlData = *it;
6656
6657 ComObjPtr<StorageController> pCtl;
6658 /* Try to find one with the name first. */
6659 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
6660 if (SUCCEEDED(rc))
6661 return setError(VBOX_E_OBJECT_IN_USE,
6662 tr("Storage controller named '%s' already exists"),
6663 ctlData.strName.raw());
6664
6665 pCtl.createObject();
6666 rc = pCtl->init(this,
6667 ctlData.strName,
6668 ctlData.storageBus,
6669 ctlData.ulInstance);
6670 if (FAILED(rc)) return rc;
6671
6672 mStorageControllers->push_back(pCtl);
6673
6674 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
6675 if (FAILED(rc)) return rc;
6676
6677 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
6678 if (FAILED(rc)) return rc;
6679
6680 /* Set IDE emulation settings (only for AHCI controller). */
6681 if (ctlData.controllerType == StorageControllerType_IntelAhci)
6682 {
6683 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
6684 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
6685 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
6686 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
6687 )
6688 return rc;
6689 }
6690
6691 /* Load the attached devices now. */
6692 rc = loadStorageDevices(pCtl,
6693 ctlData,
6694 aRegistered,
6695 aSnapshotId);
6696 if (FAILED(rc)) return rc;
6697 }
6698
6699 return S_OK;
6700}
6701
6702/**
6703 * @param aNode <HardDiskAttachments> node.
6704 * @param aRegistered true when the machine is being loaded on VirtualBox
6705 * startup, or when a snapshot is being loaded (which
6706 * currently can happen on startup only)
6707 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
6708 *
6709 * @note Lock mParent for reading and hard disks for writing before calling.
6710 */
6711HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
6712 const settings::StorageController &data,
6713 bool aRegistered,
6714 const Guid *aSnapshotId /*= NULL*/)
6715{
6716 AssertReturn( (getClassID() == clsidMachine && aSnapshotId == NULL)
6717 || (getClassID() == clsidSnapshotMachine && aSnapshotId != NULL),
6718 E_FAIL);
6719
6720 HRESULT rc = S_OK;
6721
6722 if (!aRegistered && data.llAttachedDevices.size() > 0)
6723 /* when the machine is being loaded (opened) from a file, it cannot
6724 * have hard disks attached (this should not happen normally,
6725 * because we don't allow to attach hard disks to an unregistered
6726 * VM at all */
6727 return setError(E_FAIL,
6728 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
6729 mUserData->mName.raw(),
6730 data.llAttachedDevices.size());
6731
6732 /* paranoia: detect duplicate attachments */
6733 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6734 it != data.llAttachedDevices.end();
6735 ++it)
6736 {
6737 for (settings::AttachedDevicesList::const_iterator it2 = it;
6738 it2 != data.llAttachedDevices.end();
6739 ++it2)
6740 {
6741 if (it == it2)
6742 continue;
6743
6744 if ( (*it).lPort == (*it2).lPort
6745 && (*it).lDevice == (*it2).lDevice)
6746 {
6747 return setError(E_FAIL,
6748 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
6749 aStorageController->getName().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
6750 }
6751 }
6752 }
6753
6754 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6755 it != data.llAttachedDevices.end();
6756 ++it)
6757 {
6758 const settings::AttachedDevice &dev = *it;
6759 ComObjPtr<Medium> medium;
6760
6761 switch (dev.deviceType)
6762 {
6763 case DeviceType_Floppy:
6764 /* find a floppy by UUID */
6765 if (!dev.uuid.isEmpty())
6766 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6767 /* find a floppy by host device name */
6768 else if (!dev.strHostDriveSrc.isEmpty())
6769 {
6770 SafeIfaceArray<IMedium> drivevec;
6771 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
6772 if (SUCCEEDED(rc))
6773 {
6774 for (size_t i = 0; i < drivevec.size(); ++i)
6775 {
6776 /// @todo eliminate this conversion
6777 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6778 if ( dev.strHostDriveSrc == med->getName()
6779 || dev.strHostDriveSrc == med->getLocation())
6780 {
6781 medium = med;
6782 break;
6783 }
6784 }
6785 }
6786 }
6787 break;
6788
6789 case DeviceType_DVD:
6790 /* find a DVD by UUID */
6791 if (!dev.uuid.isEmpty())
6792 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6793 /* find a DVD by host device name */
6794 else if (!dev.strHostDriveSrc.isEmpty())
6795 {
6796 SafeIfaceArray<IMedium> drivevec;
6797 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
6798 if (SUCCEEDED(rc))
6799 {
6800 for (size_t i = 0; i < drivevec.size(); ++i)
6801 {
6802 Bstr hostDriveSrc(dev.strHostDriveSrc);
6803 /// @todo eliminate this conversion
6804 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6805 if ( hostDriveSrc == med->getName()
6806 || hostDriveSrc == med->getLocation())
6807 {
6808 medium = med;
6809 break;
6810 }
6811 }
6812 }
6813 }
6814 break;
6815
6816 case DeviceType_HardDisk:
6817 {
6818 /* find a hard disk by UUID */
6819 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6820 if (FAILED(rc))
6821 {
6822 VBoxClsID clsid = getClassID();
6823 if (clsid == clsidSnapshotMachine)
6824 {
6825 // wrap another error message around the "cannot find hard disk" set by findHardDisk
6826 // so the user knows that the bad disk is in a snapshot somewhere
6827 com::ErrorInfo info;
6828 return setError(E_FAIL,
6829 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
6830 aSnapshotId->raw(),
6831 info.getText().raw());
6832 }
6833 else
6834 return rc;
6835 }
6836
6837 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
6838
6839 if (medium->getType() == MediumType_Immutable)
6840 {
6841 if (getClassID() == clsidSnapshotMachine)
6842 return setError(E_FAIL,
6843 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
6844 "of the virtual machine '%ls' ('%s')"),
6845 medium->getLocationFull().raw(),
6846 dev.uuid.raw(),
6847 aSnapshotId->raw(),
6848 mUserData->mName.raw(),
6849 mData->m_strConfigFileFull.raw());
6850
6851 return setError(E_FAIL,
6852 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
6853 medium->getLocationFull().raw(),
6854 dev.uuid.raw(),
6855 mUserData->mName.raw(),
6856 mData->m_strConfigFileFull.raw());
6857 }
6858
6859 if ( getClassID() != clsidSnapshotMachine
6860 && medium->getChildren().size() != 0
6861 )
6862 return setError(E_FAIL,
6863 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
6864 "because it has %d differencing child hard disks"),
6865 medium->getLocationFull().raw(),
6866 dev.uuid.raw(),
6867 mUserData->mName.raw(),
6868 mData->m_strConfigFileFull.raw(),
6869 medium->getChildren().size());
6870
6871 if (findAttachment(mMediaData->mAttachments,
6872 medium))
6873 return setError(E_FAIL,
6874 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
6875 medium->getLocationFull().raw(),
6876 dev.uuid.raw(),
6877 mUserData->mName.raw(),
6878 mData->m_strConfigFileFull.raw());
6879
6880 break;
6881 }
6882
6883 default:
6884 return setError(E_FAIL,
6885 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
6886 medium->getLocationFull().raw(),
6887 mUserData->mName.raw(),
6888 mData->m_strConfigFileFull.raw());
6889 }
6890
6891 if (FAILED(rc))
6892 break;
6893
6894 const Bstr controllerName = aStorageController->getName();
6895 ComObjPtr<MediumAttachment> pAttachment;
6896 pAttachment.createObject();
6897 rc = pAttachment->init(this,
6898 medium,
6899 controllerName,
6900 dev.lPort,
6901 dev.lDevice,
6902 dev.deviceType,
6903 dev.fPassThrough);
6904 if (FAILED(rc)) break;
6905
6906 /* associate the medium with this machine and snapshot */
6907 if (!medium.isNull())
6908 {
6909 if (getClassID() == clsidSnapshotMachine)
6910 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
6911 else
6912 rc = medium->attachTo(mData->mUuid);
6913 }
6914
6915 if (FAILED(rc))
6916 break;
6917
6918 /* back up mMediaData to let registeredInit() properly rollback on failure
6919 * (= limited accessibility) */
6920 setModified(IsModified_Storage);
6921 mMediaData.backup();
6922 mMediaData->mAttachments.push_back(pAttachment);
6923 }
6924
6925 return rc;
6926}
6927
6928/**
6929 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
6930 *
6931 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
6932 * @param aSnapshot where to return the found snapshot
6933 * @param aSetError true to set extended error info on failure
6934 */
6935HRESULT Machine::findSnapshot(const Guid &aId,
6936 ComObjPtr<Snapshot> &aSnapshot,
6937 bool aSetError /* = false */)
6938{
6939 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
6940
6941 if (!mData->mFirstSnapshot)
6942 {
6943 if (aSetError)
6944 return setError(E_FAIL,
6945 tr("This machine does not have any snapshots"));
6946 return E_FAIL;
6947 }
6948
6949 if (aId.isEmpty())
6950 aSnapshot = mData->mFirstSnapshot;
6951 else
6952 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
6953
6954 if (!aSnapshot)
6955 {
6956 if (aSetError)
6957 return setError(E_FAIL,
6958 tr("Could not find a snapshot with UUID {%s}"),
6959 aId.toString().raw());
6960 return E_FAIL;
6961 }
6962
6963 return S_OK;
6964}
6965
6966/**
6967 * Returns the snapshot with the given name or fails of no such snapshot.
6968 *
6969 * @param aName snapshot name to find
6970 * @param aSnapshot where to return the found snapshot
6971 * @param aSetError true to set extended error info on failure
6972 */
6973HRESULT Machine::findSnapshot(IN_BSTR aName,
6974 ComObjPtr<Snapshot> &aSnapshot,
6975 bool aSetError /* = false */)
6976{
6977 AssertReturn(aName, E_INVALIDARG);
6978
6979 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 if (!mData->mFirstSnapshot)
6982 {
6983 if (aSetError)
6984 return setError(VBOX_E_OBJECT_NOT_FOUND,
6985 tr("This machine does not have any snapshots"));
6986 return VBOX_E_OBJECT_NOT_FOUND;
6987 }
6988
6989 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aName);
6990
6991 if (!aSnapshot)
6992 {
6993 if (aSetError)
6994 return setError(VBOX_E_OBJECT_NOT_FOUND,
6995 tr("Could not find a snapshot named '%ls'"), aName);
6996 return VBOX_E_OBJECT_NOT_FOUND;
6997 }
6998
6999 return S_OK;
7000}
7001
7002/**
7003 * Returns a storage controller object with the given name.
7004 *
7005 * @param aName storage controller name to find
7006 * @param aStorageController where to return the found storage controller
7007 * @param aSetError true to set extended error info on failure
7008 */
7009HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
7010 ComObjPtr<StorageController> &aStorageController,
7011 bool aSetError /* = false */)
7012{
7013 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
7014
7015 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7016 it != mStorageControllers->end();
7017 ++it)
7018 {
7019 if ((*it)->getName() == aName)
7020 {
7021 aStorageController = (*it);
7022 return S_OK;
7023 }
7024 }
7025
7026 if (aSetError)
7027 return setError(VBOX_E_OBJECT_NOT_FOUND,
7028 tr("Could not find a storage controller named '%s'"),
7029 aName.raw());
7030 return VBOX_E_OBJECT_NOT_FOUND;
7031}
7032
7033HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
7034 MediaData::AttachmentList &atts)
7035{
7036 AutoCaller autoCaller(this);
7037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7038
7039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
7042 it != mMediaData->mAttachments.end();
7043 ++it)
7044 {
7045 if ((*it)->getControllerName() == aName)
7046 atts.push_back(*it);
7047 }
7048
7049 return S_OK;
7050}
7051
7052/**
7053 * Helper for #saveSettings. Cares about renaming the settings directory and
7054 * file if the machine name was changed and about creating a new settings file
7055 * if this is a new machine.
7056 *
7057 * @note Must be never called directly but only from #saveSettings().
7058 *
7059 * @param aRenamed receives |true| if the name was changed and the settings
7060 * file was renamed as a result, or |false| otherwise. The
7061 * value makes sense only on success.
7062 * @param aNew receives |true| if a virgin settings file was created.
7063 */
7064HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
7065{
7066 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7067
7068 HRESULT rc = S_OK;
7069
7070 bool fSettingsFileIsNew = !mData->m_pMachineConfigFile->fileExists();
7071
7072 /* attempt to rename the settings file if machine name is changed */
7073 if ( mUserData->mNameSync
7074 && mUserData.isBackedUp()
7075 && mUserData.backedUpData()->mName != mUserData->mName
7076 )
7077 {
7078 bool dirRenamed = false;
7079 bool fileRenamed = false;
7080
7081 Utf8Str configFile, newConfigFile;
7082 Utf8Str configDir, newConfigDir;
7083
7084 do
7085 {
7086 int vrc = VINF_SUCCESS;
7087
7088 Utf8Str name = mUserData.backedUpData()->mName;
7089 Utf8Str newName = mUserData->mName;
7090
7091 configFile = mData->m_strConfigFileFull;
7092
7093 /* first, rename the directory if it matches the machine name */
7094 configDir = configFile;
7095 configDir.stripFilename();
7096 newConfigDir = configDir;
7097 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
7098 {
7099 newConfigDir.stripFilename();
7100 newConfigDir = Utf8StrFmt("%s%c%s",
7101 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
7102 /* new dir and old dir cannot be equal here because of 'if'
7103 * above and because name != newName */
7104 Assert(configDir != newConfigDir);
7105 if (!fSettingsFileIsNew)
7106 {
7107 /* perform real rename only if the machine is not new */
7108 vrc = RTPathRename(configDir.raw(), newConfigDir.raw(), 0);
7109 if (RT_FAILURE(vrc))
7110 {
7111 rc = setError(E_FAIL,
7112 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
7113 configDir.raw(),
7114 newConfigDir.raw(),
7115 vrc);
7116 break;
7117 }
7118 dirRenamed = true;
7119 }
7120 }
7121
7122 newConfigFile = Utf8StrFmt("%s%c%s.xml",
7123 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
7124
7125 /* then try to rename the settings file itself */
7126 if (newConfigFile != configFile)
7127 {
7128 /* get the path to old settings file in renamed directory */
7129 configFile = Utf8StrFmt("%s%c%s",
7130 newConfigDir.raw(),
7131 RTPATH_DELIMITER,
7132 RTPathFilename(configFile.c_str()));
7133 if (!fSettingsFileIsNew)
7134 {
7135 /* perform real rename only if the machine is not new */
7136 vrc = RTFileRename(configFile.raw(), newConfigFile.raw(), 0);
7137 if (RT_FAILURE(vrc))
7138 {
7139 rc = setError(E_FAIL,
7140 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
7141 configFile.raw(),
7142 newConfigFile.raw(),
7143 vrc);
7144 break;
7145 }
7146 fileRenamed = true;
7147 }
7148 }
7149
7150 /* update m_strConfigFileFull amd mConfigFile */
7151 mData->m_strConfigFileFull = newConfigFile;
7152
7153 // compute the relative path too
7154 Utf8Str path = newConfigFile;
7155 mParent->calculateRelativePath(path, path);
7156 mData->m_strConfigFile = path;
7157
7158 // store the old and new so that VirtualBox::saveSettings() can update
7159 // the media registry
7160 if ( mData->mRegistered
7161 && configDir != newConfigDir)
7162 {
7163 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
7164
7165 if (pfNeedsGlobalSaveSettings)
7166 *pfNeedsGlobalSaveSettings = true;
7167 }
7168
7169 /* update the snapshot folder */
7170 path = mUserData->mSnapshotFolderFull;
7171 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7172 {
7173 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
7174 path.raw() + configDir.length());
7175 mUserData->mSnapshotFolderFull = path;
7176 calculateRelativePath(path, path);
7177 mUserData->mSnapshotFolder = path;
7178 }
7179
7180 /* update the saved state file path */
7181 path = mSSData->mStateFilePath;
7182 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7183 {
7184 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
7185 path.raw() + configDir.length());
7186 mSSData->mStateFilePath = path;
7187 }
7188
7189 /* Update saved state file paths of all online snapshots.
7190 * Note that saveSettings() will recognize name change
7191 * and will save all snapshots in this case. */
7192 if (mData->mFirstSnapshot)
7193 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
7194 newConfigDir.c_str());
7195 }
7196 while (0);
7197
7198 if (FAILED(rc))
7199 {
7200 /* silently try to rename everything back */
7201 if (fileRenamed)
7202 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
7203 if (dirRenamed)
7204 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
7205 }
7206
7207 if (FAILED(rc)) return rc;
7208 }
7209
7210 if (fSettingsFileIsNew)
7211 {
7212 /* create a virgin config file */
7213 int vrc = VINF_SUCCESS;
7214
7215 /* ensure the settings directory exists */
7216 Utf8Str path(mData->m_strConfigFileFull);
7217 path.stripFilename();
7218 if (!RTDirExists(path.c_str()))
7219 {
7220 vrc = RTDirCreateFullPath(path.c_str(), 0777);
7221 if (RT_FAILURE(vrc))
7222 {
7223 return setError(E_FAIL,
7224 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
7225 path.raw(),
7226 vrc);
7227 }
7228 }
7229
7230 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
7231 path = Utf8Str(mData->m_strConfigFileFull);
7232 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
7233 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
7234 if (RT_FAILURE(vrc))
7235 {
7236 mData->mHandleCfgFile = NIL_RTFILE;
7237 return setError(E_FAIL,
7238 tr("Could not create the settings file '%s' (%Rrc)"),
7239 path.raw(),
7240 vrc);
7241 }
7242 RTFileClose(mData->mHandleCfgFile);
7243 }
7244
7245 return rc;
7246}
7247
7248/**
7249 * Saves and commits machine data, user data and hardware data.
7250 *
7251 * Note that on failure, the data remains uncommitted.
7252 *
7253 * @a aFlags may combine the following flags:
7254 *
7255 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
7256 * Used when saving settings after an operation that makes them 100%
7257 * correspond to the settings from the current snapshot.
7258 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
7259 * #isReallyModified() returns false. This is necessary for cases when we
7260 * change machine data directly, not through the backup()/commit() mechanism.
7261 * - SaveS_Force: settings will be saved without doing a deep compare of the
7262 * settings structures. This is used when this is called because snapshots
7263 * have changed to avoid the overhead of the deep compare.
7264 *
7265 * @note Must be called from under this object's write lock. Locks children for
7266 * writing.
7267 *
7268 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
7269 * initialized to false and that will be set to true by this function if
7270 * the caller must invoke VirtualBox::saveSettings() because the global
7271 * settings have changed. This will happen if a machine rename has been
7272 * saved and the global machine and media registries will therefore need
7273 * updating.
7274 */
7275HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
7276 int aFlags /*= 0*/)
7277{
7278 LogFlowThisFuncEnter();
7279
7280 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7281
7282 /* make sure child objects are unable to modify the settings while we are
7283 * saving them */
7284 ensureNoStateDependencies();
7285
7286 AssertReturn( getClassID() == clsidMachine
7287 || getClassID() == clsidSessionMachine,
7288 E_FAIL);
7289
7290 HRESULT rc = S_OK;
7291 bool fNeedsWrite = false;
7292
7293 /* First, prepare to save settings. It will care about renaming the
7294 * settings directory and file if the machine name was changed and about
7295 * creating a new settings file if this is a new machine. */
7296 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
7297 if (FAILED(rc)) return rc;
7298
7299 // keep a pointer to the current settings structures
7300 settings::MachineConfigFile *pOldConfig = mData->m_pMachineConfigFile;
7301
7302 try
7303 {
7304 // make a fresh one to have everyone write stuff into
7305 mData->m_pMachineConfigFile = new settings::MachineConfigFile(NULL);
7306 mData->m_pMachineConfigFile->copyBaseFrom(*pOldConfig);
7307
7308 // deep copy extradata
7309 mData->m_pMachineConfigFile->mapExtraDataItems = pOldConfig->mapExtraDataItems;
7310
7311 mData->m_pMachineConfigFile->uuid = mData->mUuid;
7312 mData->m_pMachineConfigFile->strName = mUserData->mName;
7313 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
7314 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
7315 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
7316
7317 if ( mData->mMachineState == MachineState_Saved
7318 || mData->mMachineState == MachineState_Restoring
7319 // when deleting a snapshot we may or may not have a saved state in the current state,
7320 // so let's not assert here please
7321 || ( (mData->mMachineState == MachineState_DeletingSnapshot)
7322 && (!mSSData->mStateFilePath.isEmpty())
7323 )
7324 )
7325 {
7326 Assert(!mSSData->mStateFilePath.isEmpty());
7327 /* try to make the file name relative to the settings file dir */
7328 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7329 }
7330 else
7331 {
7332 Assert(mSSData->mStateFilePath.isEmpty());
7333 mData->m_pMachineConfigFile->strStateFile.setNull();
7334 }
7335
7336 if (mData->mCurrentSnapshot)
7337 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
7338 else
7339 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
7340
7341 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
7342 // mData->m_pMachineConfigFile->fCurrentStateModified is special, see below
7343 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7344 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7345/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7346
7347 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
7348 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
7349 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
7350 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
7351
7352 mData->m_pMachineConfigFile->fRTCUseUTC = !!mUserData->mRTCUseUTC;
7353
7354 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
7355 if (FAILED(rc)) throw rc;
7356
7357 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
7358 if (FAILED(rc)) throw rc;
7359
7360 // save snapshots
7361 rc = saveAllSnapshots();
7362 if (FAILED(rc)) throw rc;
7363
7364 if (aFlags & SaveS_ResetCurStateModified)
7365 {
7366 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
7367 mData->mCurrentStateModified = FALSE;
7368 fNeedsWrite = true; // always, no need to compare
7369 }
7370 else if (aFlags & SaveS_Force)
7371 {
7372 fNeedsWrite = true; // always, no need to compare
7373 }
7374 else
7375 {
7376 if (!mData->mCurrentStateModified)
7377 {
7378 // do a deep compare of the settings that we just saved with the settings
7379 // previously stored in the config file; this invokes MachineConfigFile::operator==
7380 // which does a deep compare of all the settings, which is expensive but less expensive
7381 // than writing out XML in vain
7382 bool fAnySettingsChanged = (*mData->m_pMachineConfigFile == *pOldConfig);
7383
7384 // could still be modified if any settings changed
7385 mData->mCurrentStateModified = fAnySettingsChanged;
7386
7387 fNeedsWrite = fAnySettingsChanged;
7388 }
7389 else
7390 fNeedsWrite = true;
7391 }
7392
7393 mData->m_pMachineConfigFile->fCurrentStateModified = !!mData->mCurrentStateModified;
7394
7395 if (fNeedsWrite)
7396 // now spit it all out!
7397 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7398
7399 // after saving settings, we are no longer different from the XML on disk
7400 m_flModifications = 0;
7401 }
7402 catch (HRESULT err)
7403 {
7404 // we assume that error info is set by the thrower
7405 rc = err;
7406
7407 // restore old config
7408 delete mData->m_pMachineConfigFile;
7409 mData->m_pMachineConfigFile = pOldConfig;
7410 }
7411 catch (...)
7412 {
7413 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7414 }
7415
7416 if (SUCCEEDED(rc))
7417 {
7418 commit();
7419 delete pOldConfig;
7420 }
7421
7422 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
7423 {
7424 /* Fire the data change event, even on failure (since we've already
7425 * committed all data). This is done only for SessionMachines because
7426 * mutable Machine instances are always not registered (i.e. private
7427 * to the client process that creates them) and thus don't need to
7428 * inform callbacks. */
7429 if (getClassID() == clsidSessionMachine)
7430 mParent->onMachineDataChange(mData->mUuid);
7431 }
7432
7433 LogFlowThisFunc(("rc=%08X\n", rc));
7434 LogFlowThisFuncLeave();
7435 return rc;
7436}
7437
7438HRESULT Machine::saveAllSnapshots()
7439{
7440 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7441
7442 HRESULT rc = S_OK;
7443
7444 try
7445 {
7446 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
7447
7448 if (mData->mFirstSnapshot)
7449 {
7450 settings::Snapshot snapNew;
7451 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
7452
7453 // get reference to the fresh copy of the snapshot on the list and
7454 // work on that copy directly to avoid excessive copying later
7455 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
7456
7457 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
7458 if (FAILED(rc)) throw rc;
7459 }
7460
7461// if (mType == IsSessionMachine)
7462// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
7463
7464 }
7465 catch (HRESULT err)
7466 {
7467 /* we assume that error info is set by the thrower */
7468 rc = err;
7469 }
7470 catch (...)
7471 {
7472 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7473 }
7474
7475 return rc;
7476}
7477
7478/**
7479 * Saves the VM hardware configuration. It is assumed that the
7480 * given node is empty.
7481 *
7482 * @param aNode <Hardware> node to save the VM hardware confguration to.
7483 */
7484HRESULT Machine::saveHardware(settings::Hardware &data)
7485{
7486 HRESULT rc = S_OK;
7487
7488 try
7489 {
7490 /* The hardware version attribute (optional).
7491 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
7492 if ( mHWData->mHWVersion == "1"
7493 && mSSData->mStateFilePath.isEmpty()
7494 )
7495 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
7496
7497 data.strVersion = mHWData->mHWVersion;
7498 data.uuid = mHWData->mHardwareUUID;
7499
7500 // CPU
7501 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
7502 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
7503 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
7504 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
7505 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
7506 data.fPAE = !!mHWData->mPAEEnabled;
7507 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
7508
7509 /* Standard and Extended CPUID leafs. */
7510 data.llCpuIdLeafs.clear();
7511 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
7512 {
7513 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
7514 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
7515 }
7516 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
7517 {
7518 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
7519 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
7520 }
7521
7522 data.cCPUs = mHWData->mCPUCount;
7523 data.fCpuHotPlug = mHWData->mCPUHotPlugEnabled;
7524
7525 data.llCpus.clear();
7526 if (data.fCpuHotPlug)
7527 {
7528 for (unsigned idx = 0; idx < data.cCPUs; idx++)
7529 {
7530 if (mHWData->mCPUAttached[idx])
7531 {
7532 settings::Cpu cpu;
7533 cpu.ulId = idx;
7534 data.llCpus.push_back(cpu);
7535 }
7536 }
7537 }
7538
7539 // memory
7540 data.ulMemorySizeMB = mHWData->mMemorySize;
7541
7542 // firmware
7543 data.firmwareType = mHWData->mFirmwareType;
7544
7545 // HID
7546 data.pointingHidType = mHWData->mPointingHidType;
7547 data.keyboardHidType = mHWData->mKeyboardHidType;
7548
7549 // HPET
7550 data.fHpetEnabled = mHWData->mHpetEnabled;
7551
7552 // boot order
7553 data.mapBootOrder.clear();
7554 for (size_t i = 0;
7555 i < RT_ELEMENTS(mHWData->mBootOrder);
7556 ++i)
7557 data.mapBootOrder[i] = mHWData->mBootOrder[i];
7558
7559 // display
7560 data.ulVRAMSizeMB = mHWData->mVRAMSize;
7561 data.cMonitors = mHWData->mMonitorCount;
7562 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
7563 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
7564
7565#ifdef VBOX_WITH_VRDP
7566 /* VRDP settings (optional) */
7567 rc = mVRDPServer->saveSettings(data.vrdpSettings);
7568 if (FAILED(rc)) throw rc;
7569#endif
7570
7571 /* BIOS (required) */
7572 rc = mBIOSSettings->saveSettings(data.biosSettings);
7573 if (FAILED(rc)) throw rc;
7574
7575 /* USB Controller (required) */
7576 rc = mUSBController->saveSettings(data.usbController);
7577 if (FAILED(rc)) throw rc;
7578
7579 /* Network adapters (required) */
7580 data.llNetworkAdapters.clear();
7581 for (ULONG slot = 0;
7582 slot < RT_ELEMENTS(mNetworkAdapters);
7583 ++slot)
7584 {
7585 settings::NetworkAdapter nic;
7586 nic.ulSlot = slot;
7587 rc = mNetworkAdapters[slot]->saveSettings(nic);
7588 if (FAILED(rc)) throw rc;
7589
7590 data.llNetworkAdapters.push_back(nic);
7591 }
7592
7593 /* Serial ports */
7594 data.llSerialPorts.clear();
7595 for (ULONG slot = 0;
7596 slot < RT_ELEMENTS(mSerialPorts);
7597 ++slot)
7598 {
7599 settings::SerialPort s;
7600 s.ulSlot = slot;
7601 rc = mSerialPorts[slot]->saveSettings(s);
7602 if (FAILED(rc)) return rc;
7603
7604 data.llSerialPorts.push_back(s);
7605 }
7606
7607 /* Parallel ports */
7608 data.llParallelPorts.clear();
7609 for (ULONG slot = 0;
7610 slot < RT_ELEMENTS(mParallelPorts);
7611 ++slot)
7612 {
7613 settings::ParallelPort p;
7614 p.ulSlot = slot;
7615 rc = mParallelPorts[slot]->saveSettings(p);
7616 if (FAILED(rc)) return rc;
7617
7618 data.llParallelPorts.push_back(p);
7619 }
7620
7621 /* Audio adapter */
7622 rc = mAudioAdapter->saveSettings(data.audioAdapter);
7623 if (FAILED(rc)) return rc;
7624
7625 /* Shared folders */
7626 data.llSharedFolders.clear();
7627 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7628 it != mHWData->mSharedFolders.end();
7629 ++it)
7630 {
7631 ComObjPtr<SharedFolder> pFolder = *it;
7632 settings::SharedFolder sf;
7633 sf.strName = pFolder->getName();
7634 sf.strHostPath = pFolder->getHostPath();
7635 sf.fWritable = !!pFolder->isWritable();
7636
7637 data.llSharedFolders.push_back(sf);
7638 }
7639
7640 // clipboard
7641 data.clipboardMode = mHWData->mClipboardMode;
7642
7643 /* Guest */
7644 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7645 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
7646
7647 // IO settings
7648 data.ioSettings.ioMgrType = mHWData->mIoMgrType;
7649 data.ioSettings.ioBackendType = mHWData->mIoBackendType;
7650 data.ioSettings.fIoCacheEnabled = mHWData->mIoCacheEnabled;
7651 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
7652 data.ioSettings.ulIoBandwidthMax = mHWData->mIoBandwidthMax;
7653
7654 // guest properties
7655 data.llGuestProperties.clear();
7656#ifdef VBOX_WITH_GUEST_PROPS
7657 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
7658 it != mHWData->mGuestProperties.end();
7659 ++it)
7660 {
7661 HWData::GuestProperty property = *it;
7662
7663 settings::GuestProperty prop;
7664 prop.strName = property.strName;
7665 prop.strValue = property.strValue;
7666 prop.timestamp = property.mTimestamp;
7667 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7668 guestProp::writeFlags(property.mFlags, szFlags);
7669 prop.strFlags = szFlags;
7670
7671 data.llGuestProperties.push_back(prop);
7672 }
7673
7674 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7675#endif /* VBOX_WITH_GUEST_PROPS defined */
7676 }
7677 catch(std::bad_alloc &)
7678 {
7679 return E_OUTOFMEMORY;
7680 }
7681
7682 AssertComRC(rc);
7683 return rc;
7684}
7685
7686/**
7687 * Saves the storage controller configuration.
7688 *
7689 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
7690 */
7691HRESULT Machine::saveStorageControllers(settings::Storage &data)
7692{
7693 data.llStorageControllers.clear();
7694
7695 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7696 it != mStorageControllers->end();
7697 ++it)
7698 {
7699 HRESULT rc;
7700 ComObjPtr<StorageController> pCtl = *it;
7701
7702 settings::StorageController ctl;
7703 ctl.strName = pCtl->getName();
7704 ctl.controllerType = pCtl->getControllerType();
7705 ctl.storageBus = pCtl->getStorageBus();
7706 ctl.ulInstance = pCtl->getInstance();
7707
7708 /* Save the port count. */
7709 ULONG portCount;
7710 rc = pCtl->COMGETTER(PortCount)(&portCount);
7711 ComAssertComRCRet(rc, rc);
7712 ctl.ulPortCount = portCount;
7713
7714 /* Save IDE emulation settings. */
7715 if (ctl.controllerType == StorageControllerType_IntelAhci)
7716 {
7717 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
7718 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
7719 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
7720 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
7721 )
7722 ComAssertComRCRet(rc, rc);
7723 }
7724
7725 /* save the devices now. */
7726 rc = saveStorageDevices(pCtl, ctl);
7727 ComAssertComRCRet(rc, rc);
7728
7729 data.llStorageControllers.push_back(ctl);
7730 }
7731
7732 return S_OK;
7733}
7734
7735/**
7736 * Saves the hard disk confguration.
7737 */
7738HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
7739 settings::StorageController &data)
7740{
7741 MediaData::AttachmentList atts;
7742
7743 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()), atts);
7744 if (FAILED(rc)) return rc;
7745
7746 data.llAttachedDevices.clear();
7747 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7748 it != atts.end();
7749 ++it)
7750 {
7751 settings::AttachedDevice dev;
7752
7753 MediumAttachment *pAttach = *it;
7754 Medium *pMedium = pAttach->getMedium();
7755
7756 dev.deviceType = pAttach->getType();
7757 dev.lPort = pAttach->getPort();
7758 dev.lDevice = pAttach->getDevice();
7759 if (pMedium)
7760 {
7761 BOOL fHostDrive = FALSE;
7762 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
7763 if (FAILED(rc))
7764 return rc;
7765 if (fHostDrive)
7766 dev.strHostDriveSrc = pMedium->getLocation();
7767 else
7768 dev.uuid = pMedium->getId();
7769 dev.fPassThrough = pAttach->getPassthrough();
7770 }
7771
7772 data.llAttachedDevices.push_back(dev);
7773 }
7774
7775 return S_OK;
7776}
7777
7778/**
7779 * Saves machine state settings as defined by aFlags
7780 * (SaveSTS_* values).
7781 *
7782 * @param aFlags Combination of SaveSTS_* flags.
7783 *
7784 * @note Locks objects for writing.
7785 */
7786HRESULT Machine::saveStateSettings(int aFlags)
7787{
7788 if (aFlags == 0)
7789 return S_OK;
7790
7791 AutoCaller autoCaller(this);
7792 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7793
7794 /* This object's write lock is also necessary to serialize file access
7795 * (prevent concurrent reads and writes) */
7796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 HRESULT rc = S_OK;
7799
7800 Assert(mData->m_pMachineConfigFile);
7801
7802 try
7803 {
7804 if (aFlags & SaveSTS_CurStateModified)
7805 mData->m_pMachineConfigFile->fCurrentStateModified = true;
7806
7807 if (aFlags & SaveSTS_StateFilePath)
7808 {
7809 if (!mSSData->mStateFilePath.isEmpty())
7810 /* try to make the file name relative to the settings file dir */
7811 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7812 else
7813 mData->m_pMachineConfigFile->strStateFile.setNull();
7814 }
7815
7816 if (aFlags & SaveSTS_StateTimeStamp)
7817 {
7818 Assert( mData->mMachineState != MachineState_Aborted
7819 || mSSData->mStateFilePath.isEmpty());
7820
7821 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7822
7823 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7824//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7825 }
7826
7827 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7828 }
7829 catch (...)
7830 {
7831 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7832 }
7833
7834 return rc;
7835}
7836
7837/**
7838 * Creates differencing hard disks for all normal hard disks attached to this
7839 * machine and a new set of attachments to refer to created disks.
7840 *
7841 * Used when taking a snapshot or when discarding the current state.
7842 *
7843 * This method assumes that mMediaData contains the original hard disk attachments
7844 * it needs to create diffs for. On success, these attachments will be replaced
7845 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
7846 * called to delete created diffs which will also rollback mMediaData and restore
7847 * whatever was backed up before calling this method.
7848 *
7849 * Attachments with non-normal hard disks are left as is.
7850 *
7851 * If @a aOnline is @c false then the original hard disks that require implicit
7852 * diffs will be locked for reading. Otherwise it is assumed that they are
7853 * already locked for writing (when the VM was started). Note that in the latter
7854 * case it is responsibility of the caller to lock the newly created diffs for
7855 * writing if this method succeeds.
7856 *
7857 * @param aFolder Folder where to create diff hard disks.
7858 * @param aProgress Progress object to run (must contain at least as
7859 * many operations left as the number of hard disks
7860 * attached).
7861 * @param aOnline Whether the VM was online prior to this operation.
7862 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
7863 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
7864 *
7865 * @note The progress object is not marked as completed, neither on success nor
7866 * on failure. This is a responsibility of the caller.
7867 *
7868 * @note Locks this object for writing.
7869 */
7870HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
7871 IProgress *aProgress,
7872 ULONG aWeight,
7873 bool aOnline,
7874 bool *pfNeedsSaveSettings)
7875{
7876 AssertReturn(!aFolder.isEmpty(), E_FAIL);
7877
7878 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
7879
7880 AutoCaller autoCaller(this);
7881 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7882
7883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7884
7885 /* must be in a protective state because we leave the lock below */
7886 AssertReturn( mData->mMachineState == MachineState_Saving
7887 || mData->mMachineState == MachineState_LiveSnapshotting
7888 || mData->mMachineState == MachineState_RestoringSnapshot
7889 || mData->mMachineState == MachineState_DeletingSnapshot
7890 , E_FAIL);
7891
7892 HRESULT rc = S_OK;
7893
7894 MediaList lockedMedia;
7895
7896 try
7897 {
7898 if (!aOnline)
7899 {
7900 /* lock all attached hard disks early to detect "in use"
7901 * situations before creating actual diffs */
7902 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7903 it != mMediaData->mAttachments.end();
7904 ++it)
7905 {
7906 MediumAttachment* pAtt = *it;
7907 if (pAtt->getType() == DeviceType_HardDisk)
7908 {
7909 Medium* pHD = pAtt->getMedium();
7910 Assert(pHD);
7911 rc = pHD->LockRead(NULL);
7912 if (FAILED(rc)) throw rc;
7913 lockedMedia.push_back(pHD);
7914 }
7915 }
7916 }
7917
7918 /* remember the current list (note that we don't use backup() since
7919 * mMediaData may be already backed up) */
7920 MediaData::AttachmentList atts = mMediaData->mAttachments;
7921
7922 /* start from scratch */
7923 mMediaData->mAttachments.clear();
7924
7925 /* go through remembered attachments and create diffs for normal hard
7926 * disks and attach them */
7927 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7928 it != atts.end();
7929 ++it)
7930 {
7931 MediumAttachment* pAtt = *it;
7932
7933 DeviceType_T devType = pAtt->getType();
7934 Medium* medium = pAtt->getMedium();
7935
7936 if ( devType != DeviceType_HardDisk
7937 || medium == NULL
7938 || medium->getType() != MediumType_Normal)
7939 {
7940 /* copy the attachment as is */
7941
7942 /** @todo the progress object created in Console::TakeSnaphot
7943 * only expects operations for hard disks. Later other
7944 * device types need to show up in the progress as well. */
7945 if (devType == DeviceType_HardDisk)
7946 {
7947 if (medium == NULL)
7948 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
7949 aWeight); // weight
7950 else
7951 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
7952 medium->getBase()->getName().raw()),
7953 aWeight); // weight
7954 }
7955
7956 mMediaData->mAttachments.push_back(pAtt);
7957 continue;
7958 }
7959
7960 /* need a diff */
7961 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
7962 medium->getBase()->getName().raw()),
7963 aWeight); // weight
7964
7965 ComObjPtr<Medium> diff;
7966 diff.createObject();
7967 rc = diff->init(mParent,
7968 medium->preferredDiffFormat().raw(),
7969 BstrFmt("%ls"RTPATH_SLASH_STR,
7970 mUserData->mSnapshotFolderFull.raw()).raw(),
7971 pfNeedsSaveSettings);
7972 if (FAILED(rc)) throw rc;
7973
7974 /* leave the lock before the potentially lengthy operation */
7975 alock.leave();
7976
7977 rc = medium->createDiffStorageAndWait(diff,
7978 MediumVariant_Standard,
7979 pfNeedsSaveSettings);
7980
7981 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
7982 * the push_back? Looks like we're going to leave medium with the
7983 * wrong kind of lock (general issue with if we fail anywhere at all)
7984 * and an orphaned VDI in the snapshots folder. */
7985 // at this point, the old image is still locked for writing, but instead
7986 // we need the new diff image locked for writing and lock the previously
7987 // current one for reading only
7988 if (aOnline)
7989 {
7990 diff->LockWrite(NULL);
7991 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7992 medium->UnlockWrite(NULL);
7993 medium->LockRead(NULL);
7994 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7995 }
7996
7997 if (FAILED(rc)) throw rc;
7998
7999 alock.enter();
8000
8001 rc = diff->attachTo(mData->mUuid);
8002 AssertComRCThrowRC(rc);
8003
8004 /* add a new attachment */
8005 ComObjPtr<MediumAttachment> attachment;
8006 attachment.createObject();
8007 rc = attachment->init(this,
8008 diff,
8009 pAtt->getControllerName(),
8010 pAtt->getPort(),
8011 pAtt->getDevice(),
8012 DeviceType_HardDisk,
8013 true /* aImplicit */);
8014 if (FAILED(rc)) throw rc;
8015
8016 mMediaData->mAttachments.push_back(attachment);
8017 }
8018 }
8019 catch (HRESULT aRC) { rc = aRC; }
8020
8021 /* unlock all hard disks we locked */
8022 if (!aOnline)
8023 {
8024 ErrorInfoKeeper eik;
8025
8026 for (MediaList::const_iterator it = lockedMedia.begin();
8027 it != lockedMedia.end();
8028 ++it)
8029 {
8030 HRESULT rc2 = (*it)->UnlockRead(NULL);
8031 AssertComRC(rc2);
8032 }
8033 }
8034
8035 if (FAILED(rc))
8036 {
8037 MultiResultRef mrc(rc);
8038
8039 mrc = deleteImplicitDiffs(pfNeedsSaveSettings);
8040 }
8041
8042 return rc;
8043}
8044
8045/**
8046 * Deletes implicit differencing hard disks created either by
8047 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
8048 *
8049 * Note that to delete hard disks created by #AttachMedium() this method is
8050 * called from #fixupMedia() when the changes are rolled back.
8051 *
8052 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8053 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8054 *
8055 * @note Locks this object for writing.
8056 */
8057HRESULT Machine::deleteImplicitDiffs(bool *pfNeedsSaveSettings)
8058{
8059 AutoCaller autoCaller(this);
8060 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8061
8062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8063 LogFlowThisFuncEnter();
8064
8065 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
8066
8067 HRESULT rc = S_OK;
8068
8069 MediaData::AttachmentList implicitAtts;
8070
8071 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8072
8073 /* enumerate new attachments */
8074 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8075 it != mMediaData->mAttachments.end();
8076 ++it)
8077 {
8078 ComObjPtr<Medium> hd = (*it)->getMedium();
8079 if (hd.isNull())
8080 continue;
8081
8082 if ((*it)->isImplicit())
8083 {
8084 /* deassociate and mark for deletion */
8085 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
8086 rc = hd->detachFrom(mData->mUuid);
8087 AssertComRC(rc);
8088 implicitAtts.push_back(*it);
8089 continue;
8090 }
8091
8092 /* was this hard disk attached before? */
8093 if (!findAttachment(oldAtts, hd))
8094 {
8095 /* no: de-associate */
8096 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
8097 rc = hd->detachFrom(mData->mUuid);
8098 AssertComRC(rc);
8099 continue;
8100 }
8101 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
8102 }
8103
8104 /* rollback hard disk changes */
8105 mMediaData.rollback();
8106
8107 MultiResult mrc(S_OK);
8108
8109 /* delete unused implicit diffs */
8110 if (implicitAtts.size() != 0)
8111 {
8112 /* will leave the lock before the potentially lengthy
8113 * operation, so protect with the special state (unless already
8114 * protected) */
8115 MachineState_T oldState = mData->mMachineState;
8116 if ( oldState != MachineState_Saving
8117 && oldState != MachineState_LiveSnapshotting
8118 && oldState != MachineState_RestoringSnapshot
8119 && oldState != MachineState_DeletingSnapshot
8120 )
8121 setMachineState(MachineState_SettingUp);
8122
8123 alock.leave();
8124
8125 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
8126 it != implicitAtts.end();
8127 ++it)
8128 {
8129 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
8130 ComObjPtr<Medium> hd = (*it)->getMedium();
8131
8132 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
8133#if 1 /* HACK ALERT: Just make it kind of work */ /** @todo Fix this hack properly. The LockWrite / UnlockWrite / LockRead changes aren't undone! */
8134 if (rc == VBOX_E_INVALID_OBJECT_STATE)
8135 {
8136 LogFlowFunc(("Applying unlock hack on '%s'! FIXME!\n", (*it)->getLogName()));
8137 hd->UnlockWrite(NULL);
8138 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
8139 }
8140#endif
8141 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
8142 mrc = rc;
8143 }
8144
8145 alock.enter();
8146
8147 if (mData->mMachineState == MachineState_SettingUp)
8148 {
8149 setMachineState(oldState);
8150 }
8151 }
8152
8153 return mrc;
8154}
8155
8156/**
8157 * Looks through the given list of media attachments for one with the given parameters
8158 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8159 * can be searched as well if needed.
8160 *
8161 * @param list
8162 * @param aControllerName
8163 * @param aControllerPort
8164 * @param aDevice
8165 * @return
8166 */
8167MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8168 IN_BSTR aControllerName,
8169 LONG aControllerPort,
8170 LONG aDevice)
8171{
8172 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8173 it != ll.end();
8174 ++it)
8175 {
8176 MediumAttachment *pAttach = *it;
8177 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
8178 return pAttach;
8179 }
8180
8181 return NULL;
8182}
8183
8184/**
8185 * Looks through the given list of media attachments for one with the given parameters
8186 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8187 * can be searched as well if needed.
8188 *
8189 * @param list
8190 * @param aControllerName
8191 * @param aControllerPort
8192 * @param aDevice
8193 * @return
8194 */
8195MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8196 ComObjPtr<Medium> pMedium)
8197{
8198 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8199 it != ll.end();
8200 ++it)
8201 {
8202 MediumAttachment *pAttach = *it;
8203 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8204 if (pMediumThis.equalsTo(pMedium))
8205 return pAttach;
8206 }
8207
8208 return NULL;
8209}
8210
8211/**
8212 * Looks through the given list of media attachments for one with the given parameters
8213 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8214 * can be searched as well if needed.
8215 *
8216 * @param list
8217 * @param aControllerName
8218 * @param aControllerPort
8219 * @param aDevice
8220 * @return
8221 */
8222MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8223 Guid &id)
8224{
8225 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8226 it != ll.end();
8227 ++it)
8228 {
8229 MediumAttachment *pAttach = *it;
8230 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8231 if (pMediumThis->getId() == id)
8232 return pAttach;
8233 }
8234
8235 return NULL;
8236}
8237
8238/**
8239 * Perform deferred hard disk detachments.
8240 *
8241 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8242 * backed up).
8243 *
8244 * If @a aOnline is @c true then this method will also unlock the old hard disks
8245 * for which the new implicit diffs were created and will lock these new diffs for
8246 * writing.
8247 *
8248 * @param aOnline Whether the VM was online prior to this operation.
8249 *
8250 * @note Locks this object for writing!
8251 */
8252void Machine::commitMedia(bool aOnline /*= false*/)
8253{
8254 AutoCaller autoCaller(this);
8255 AssertComRCReturnVoid(autoCaller.rc());
8256
8257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8258
8259 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
8260
8261 HRESULT rc = S_OK;
8262
8263 /* no attach/detach operations -- nothing to do */
8264 if (!mMediaData.isBackedUp())
8265 return;
8266
8267 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8268
8269 /* enumerate new attachments */
8270 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8271 it != mMediaData->mAttachments.end();
8272 ++it)
8273 {
8274 MediumAttachment *pAttach = *it;
8275
8276 pAttach->commit();
8277
8278 Medium* pMedium = pAttach->getMedium();
8279 bool fImplicit = pAttach->isImplicit();
8280
8281 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
8282 (pMedium) ? pMedium->getName().raw() : "NULL",
8283 fImplicit));
8284
8285 /** @todo convert all this Machine-based voodoo to MediumAttachment
8286 * based commit logic. */
8287 if (fImplicit)
8288 {
8289 /* convert implicit attachment to normal */
8290 pAttach->setImplicit(false);
8291
8292 if ( aOnline
8293 && pMedium
8294 && pAttach->getType() == DeviceType_HardDisk
8295 )
8296 {
8297 rc = pMedium->LockWrite(NULL);
8298 AssertComRC(rc);
8299
8300 mData->mSession.mLockedMedia.push_back(
8301 Data::Session::LockedMedia::value_type(
8302 ComPtr<IMedium>(pMedium), true));
8303
8304 /* also, relock the old hard disk which is a base for the
8305 * new diff for reading if the VM is online */
8306
8307 ComObjPtr<Medium> parent = pMedium->getParent();
8308 /* make the relock atomic */
8309 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
8310 rc = parent->UnlockWrite(NULL);
8311 AssertComRC(rc);
8312 rc = parent->LockRead(NULL);
8313 AssertComRC(rc);
8314
8315 /* XXX actually we should replace the old entry in that
8316 * vector (write lock => read lock) but this would take
8317 * some effort. So lets just ignore the error code in
8318 * SessionMachine::unlockMedia(). */
8319 mData->mSession.mLockedMedia.push_back(
8320 Data::Session::LockedMedia::value_type (
8321 ComPtr<IMedium>(parent), false));
8322 }
8323
8324 continue;
8325 }
8326
8327 if (pMedium)
8328 {
8329 /* was this medium attached before? */
8330 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
8331 oldIt != oldAtts.end();
8332 ++oldIt)
8333 {
8334 MediumAttachment *pOldAttach = *oldIt;
8335 if (pOldAttach->getMedium().equalsTo(pMedium))
8336 {
8337 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().raw()));
8338
8339 /* yes: remove from old to avoid de-association */
8340 oldAtts.erase(oldIt);
8341 break;
8342 }
8343 }
8344 }
8345 }
8346
8347 /* enumerate remaining old attachments and de-associate from the
8348 * current machine state */
8349 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
8350 it != oldAtts.end();
8351 ++it)
8352 {
8353 MediumAttachment *pAttach = *it;
8354 Medium* pMedium = pAttach->getMedium();
8355
8356 /* Detach only hard disks, since DVD/floppy media is detached
8357 * instantly in MountMedium. */
8358 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
8359 {
8360 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().raw()));
8361
8362 /* now de-associate from the current machine state */
8363 rc = pMedium->detachFrom(mData->mUuid);
8364 AssertComRC(rc);
8365
8366 if ( aOnline
8367 && pAttach->getType() == DeviceType_HardDisk)
8368 {
8369 /* unlock since not used anymore */
8370 MediumState_T state;
8371 rc = pMedium->UnlockWrite(&state);
8372 /* the disk may be alredy relocked for reading above */
8373 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead);
8374 }
8375 }
8376 }
8377
8378 /* commit the hard disk changes */
8379 mMediaData.commit();
8380
8381 if (getClassID() == clsidSessionMachine)
8382 {
8383 /* attach new data to the primary machine and reshare it */
8384 mPeer->mMediaData.attach(mMediaData);
8385 }
8386
8387 return;
8388}
8389
8390/**
8391 * Perform deferred deletion of implicitly created diffs.
8392 *
8393 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8394 * backed up).
8395 *
8396 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8397 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8398 *
8399 * @note Locks this object for writing!
8400 */
8401void Machine::rollbackMedia()
8402{
8403 AutoCaller autoCaller(this);
8404 AssertComRCReturnVoid (autoCaller.rc());
8405
8406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8407
8408 LogFlowThisFunc(("Entering\n"));
8409
8410 HRESULT rc = S_OK;
8411
8412 /* no attach/detach operations -- nothing to do */
8413 if (!mMediaData.isBackedUp())
8414 return;
8415
8416 /* enumerate new attachments */
8417 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8418 it != mMediaData->mAttachments.end();
8419 ++it)
8420 {
8421 MediumAttachment *pAttach = *it;
8422 /* Fix up the backrefs for DVD/floppy media. */
8423 if (pAttach->getType() != DeviceType_HardDisk)
8424 {
8425 Medium* pMedium = pAttach->getMedium();
8426 if (pMedium)
8427 {
8428 rc = pMedium->detachFrom(mData->mUuid);
8429 AssertComRC(rc);
8430 }
8431 }
8432
8433 (*it)->rollback();
8434
8435 pAttach = *it;
8436 /* Fix up the backrefs for DVD/floppy media. */
8437 if (pAttach->getType() != DeviceType_HardDisk)
8438 {
8439 Medium* pMedium = pAttach->getMedium();
8440 if (pMedium)
8441 {
8442 rc = pMedium->attachTo(mData->mUuid);
8443 AssertComRC(rc);
8444 }
8445 }
8446 }
8447
8448 /** @todo convert all this Machine-based voodoo to MediumAttachment
8449 * based rollback logic. */
8450 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
8451 // which gets called if Machine::registeredInit() fails...
8452 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
8453
8454 return;
8455}
8456
8457/**
8458 * Returns true if the settings file is located in the directory named exactly
8459 * as the machine. This will be true if the machine settings structure was
8460 * created by default in #openConfigLoader().
8461 *
8462 * @param aSettingsDir if not NULL, the full machine settings file directory
8463 * name will be assigned there.
8464 *
8465 * @note Doesn't lock anything.
8466 * @note Not thread safe (must be called from this object's lock).
8467 */
8468bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
8469{
8470 Utf8Str settingsDir = mData->m_strConfigFileFull;
8471 settingsDir.stripFilename();
8472 char *dirName = RTPathFilename(settingsDir.c_str());
8473
8474 AssertReturn(dirName, false);
8475
8476 /* if we don't rename anything on name change, return false shorlty */
8477 if (!mUserData->mNameSync)
8478 return false;
8479
8480 if (aSettingsDir)
8481 *aSettingsDir = settingsDir;
8482
8483 return Bstr(dirName) == mUserData->mName;
8484}
8485
8486/**
8487 * Discards all changes to machine settings.
8488 *
8489 * @param aNotify Whether to notify the direct session about changes or not.
8490 *
8491 * @note Locks objects for writing!
8492 */
8493void Machine::rollback(bool aNotify)
8494{
8495 AutoCaller autoCaller(this);
8496 AssertComRCReturn(autoCaller.rc(), (void)0);
8497
8498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8499
8500 if (!mStorageControllers.isNull())
8501 {
8502 if (mStorageControllers.isBackedUp())
8503 {
8504 /* unitialize all new devices (absent in the backed up list). */
8505 StorageControllerList::const_iterator it = mStorageControllers->begin();
8506 StorageControllerList *backedList = mStorageControllers.backedUpData();
8507 while (it != mStorageControllers->end())
8508 {
8509 if ( std::find(backedList->begin(), backedList->end(), *it)
8510 == backedList->end()
8511 )
8512 {
8513 (*it)->uninit();
8514 }
8515 ++it;
8516 }
8517
8518 /* restore the list */
8519 mStorageControllers.rollback();
8520 }
8521
8522 /* rollback any changes to devices after restoring the list */
8523 if (m_flModifications & IsModified_Storage)
8524 {
8525 StorageControllerList::const_iterator it = mStorageControllers->begin();
8526 while (it != mStorageControllers->end())
8527 {
8528 (*it)->rollback();
8529 ++it;
8530 }
8531 }
8532 }
8533
8534 mUserData.rollback();
8535
8536 mHWData.rollback();
8537
8538 if (m_flModifications & IsModified_Storage)
8539 rollbackMedia();
8540
8541 if (mBIOSSettings)
8542 mBIOSSettings->rollback();
8543
8544#ifdef VBOX_WITH_VRDP
8545 if (mVRDPServer && (m_flModifications & IsModified_VRDPServer))
8546 mVRDPServer->rollback();
8547#endif
8548
8549 if (mAudioAdapter)
8550 mAudioAdapter->rollback();
8551
8552 if (mUSBController && (m_flModifications & IsModified_USB))
8553 mUSBController->rollback();
8554
8555 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
8556 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
8557 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
8558
8559 if (m_flModifications & IsModified_NetworkAdapters)
8560 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8561 if ( mNetworkAdapters[slot]
8562 && mNetworkAdapters[slot]->isModified())
8563 {
8564 mNetworkAdapters[slot]->rollback();
8565 networkAdapters[slot] = mNetworkAdapters[slot];
8566 }
8567
8568 if (m_flModifications & IsModified_SerialPorts)
8569 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8570 if ( mSerialPorts[slot]
8571 && mSerialPorts[slot]->isModified())
8572 {
8573 mSerialPorts[slot]->rollback();
8574 serialPorts[slot] = mSerialPorts[slot];
8575 }
8576
8577 if (m_flModifications & IsModified_ParallelPorts)
8578 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8579 if ( mParallelPorts[slot]
8580 && mParallelPorts[slot]->isModified())
8581 {
8582 mParallelPorts[slot]->rollback();
8583 parallelPorts[slot] = mParallelPorts[slot];
8584 }
8585
8586 if (aNotify)
8587 {
8588 /* inform the direct session about changes */
8589
8590 ComObjPtr<Machine> that = this;
8591 uint32_t flModifications = m_flModifications;
8592 alock.leave();
8593
8594 if (flModifications & IsModified_SharedFolders)
8595 that->onSharedFolderChange();
8596
8597 if (flModifications & IsModified_VRDPServer)
8598 that->onVRDPServerChange();
8599 if (flModifications & IsModified_USB)
8600 that->onUSBControllerChange();
8601
8602 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
8603 if (networkAdapters[slot])
8604 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
8605 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
8606 if (serialPorts[slot])
8607 that->onSerialPortChange(serialPorts[slot]);
8608 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
8609 if (parallelPorts[slot])
8610 that->onParallelPortChange(parallelPorts[slot]);
8611
8612 if (flModifications & IsModified_Storage)
8613 that->onStorageControllerChange();
8614 }
8615}
8616
8617/**
8618 * Commits all the changes to machine settings.
8619 *
8620 * Note that this operation is supposed to never fail.
8621 *
8622 * @note Locks this object and children for writing.
8623 */
8624void Machine::commit()
8625{
8626 AutoCaller autoCaller(this);
8627 AssertComRCReturnVoid(autoCaller.rc());
8628
8629 AutoCaller peerCaller(mPeer);
8630 AssertComRCReturnVoid(peerCaller.rc());
8631
8632 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
8633
8634 /*
8635 * use safe commit to ensure Snapshot machines (that share mUserData)
8636 * will still refer to a valid memory location
8637 */
8638 mUserData.commitCopy();
8639
8640 mHWData.commit();
8641
8642 if (mMediaData.isBackedUp())
8643 commitMedia();
8644
8645 mBIOSSettings->commit();
8646#ifdef VBOX_WITH_VRDP
8647 mVRDPServer->commit();
8648#endif
8649 mAudioAdapter->commit();
8650 mUSBController->commit();
8651
8652 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8653 mNetworkAdapters[slot]->commit();
8654 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8655 mSerialPorts[slot]->commit();
8656 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8657 mParallelPorts[slot]->commit();
8658
8659 bool commitStorageControllers = false;
8660
8661 if (mStorageControllers.isBackedUp())
8662 {
8663 mStorageControllers.commit();
8664
8665 if (mPeer)
8666 {
8667 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
8668
8669 /* Commit all changes to new controllers (this will reshare data with
8670 * peers for thos who have peers) */
8671 StorageControllerList *newList = new StorageControllerList();
8672 StorageControllerList::const_iterator it = mStorageControllers->begin();
8673 while (it != mStorageControllers->end())
8674 {
8675 (*it)->commit();
8676
8677 /* look if this controller has a peer device */
8678 ComObjPtr<StorageController> peer = (*it)->getPeer();
8679 if (!peer)
8680 {
8681 /* no peer means the device is a newly created one;
8682 * create a peer owning data this device share it with */
8683 peer.createObject();
8684 peer->init(mPeer, *it, true /* aReshare */);
8685 }
8686 else
8687 {
8688 /* remove peer from the old list */
8689 mPeer->mStorageControllers->remove(peer);
8690 }
8691 /* and add it to the new list */
8692 newList->push_back(peer);
8693
8694 ++it;
8695 }
8696
8697 /* uninit old peer's controllers that are left */
8698 it = mPeer->mStorageControllers->begin();
8699 while (it != mPeer->mStorageControllers->end())
8700 {
8701 (*it)->uninit();
8702 ++it;
8703 }
8704
8705 /* attach new list of controllers to our peer */
8706 mPeer->mStorageControllers.attach(newList);
8707 }
8708 else
8709 {
8710 /* we have no peer (our parent is the newly created machine);
8711 * just commit changes to devices */
8712 commitStorageControllers = true;
8713 }
8714 }
8715 else
8716 {
8717 /* the list of controllers itself is not changed,
8718 * just commit changes to controllers themselves */
8719 commitStorageControllers = true;
8720 }
8721
8722 if (commitStorageControllers)
8723 {
8724 StorageControllerList::const_iterator it = mStorageControllers->begin();
8725 while (it != mStorageControllers->end())
8726 {
8727 (*it)->commit();
8728 ++it;
8729 }
8730 }
8731
8732 if (getClassID() == clsidSessionMachine)
8733 {
8734 /* attach new data to the primary machine and reshare it */
8735 mPeer->mUserData.attach(mUserData);
8736 mPeer->mHWData.attach(mHWData);
8737 /* mMediaData is reshared by fixupMedia */
8738 // mPeer->mMediaData.attach(mMediaData);
8739 Assert(mPeer->mMediaData.data() == mMediaData.data());
8740 }
8741}
8742
8743/**
8744 * Copies all the hardware data from the given machine.
8745 *
8746 * Currently, only called when the VM is being restored from a snapshot. In
8747 * particular, this implies that the VM is not running during this method's
8748 * call.
8749 *
8750 * @note This method must be called from under this object's lock.
8751 *
8752 * @note This method doesn't call #commit(), so all data remains backed up and
8753 * unsaved.
8754 */
8755void Machine::copyFrom(Machine *aThat)
8756{
8757 AssertReturnVoid(getClassID() == clsidMachine || getClassID() == clsidSessionMachine);
8758 AssertReturnVoid(aThat->getClassID() == clsidSnapshotMachine);
8759
8760 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
8761
8762 mHWData.assignCopy(aThat->mHWData);
8763
8764 // create copies of all shared folders (mHWData after attiching a copy
8765 // contains just references to original objects)
8766 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
8767 it != mHWData->mSharedFolders.end();
8768 ++it)
8769 {
8770 ComObjPtr<SharedFolder> folder;
8771 folder.createObject();
8772 HRESULT rc = folder->initCopy(getMachine(), *it);
8773 AssertComRC(rc);
8774 *it = folder;
8775 }
8776
8777 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
8778#ifdef VBOX_WITH_VRDP
8779 mVRDPServer->copyFrom(aThat->mVRDPServer);
8780#endif
8781 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
8782 mUSBController->copyFrom(aThat->mUSBController);
8783
8784 /* create private copies of all controllers */
8785 mStorageControllers.backup();
8786 mStorageControllers->clear();
8787 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
8788 it != aThat->mStorageControllers->end();
8789 ++it)
8790 {
8791 ComObjPtr<StorageController> ctrl;
8792 ctrl.createObject();
8793 ctrl->initCopy(this, *it);
8794 mStorageControllers->push_back(ctrl);
8795 }
8796
8797 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8798 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
8799 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8800 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
8801 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8802 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
8803}
8804
8805#ifdef VBOX_WITH_RESOURCE_USAGE_API
8806void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
8807{
8808 pm::CollectorHAL *hal = aCollector->getHAL();
8809 /* Create sub metrics */
8810 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
8811 "Percentage of processor time spent in user mode by VM process.");
8812 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
8813 "Percentage of processor time spent in kernel mode by VM process.");
8814 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
8815 "Size of resident portion of VM process in memory.");
8816 /* Create and register base metrics */
8817 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
8818 cpuLoadUser, cpuLoadKernel);
8819 aCollector->registerBaseMetric(cpuLoad);
8820 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
8821 ramUsageUsed);
8822 aCollector->registerBaseMetric(ramUsage);
8823
8824 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
8825 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8826 new pm::AggregateAvg()));
8827 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8828 new pm::AggregateMin()));
8829 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8830 new pm::AggregateMax()));
8831 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
8832 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8833 new pm::AggregateAvg()));
8834 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8835 new pm::AggregateMin()));
8836 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8837 new pm::AggregateMax()));
8838
8839 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
8840 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8841 new pm::AggregateAvg()));
8842 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8843 new pm::AggregateMin()));
8844 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8845 new pm::AggregateMax()));
8846};
8847
8848void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
8849{
8850 aCollector->unregisterMetricsFor(aMachine);
8851 aCollector->unregisterBaseMetricsFor(aMachine);
8852};
8853#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8854
8855
8856////////////////////////////////////////////////////////////////////////////////
8857
8858DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
8859
8860HRESULT SessionMachine::FinalConstruct()
8861{
8862 LogFlowThisFunc(("\n"));
8863
8864#if defined(RT_OS_WINDOWS)
8865 mIPCSem = NULL;
8866#elif defined(RT_OS_OS2)
8867 mIPCSem = NULLHANDLE;
8868#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8869 mIPCSem = -1;
8870#else
8871# error "Port me!"
8872#endif
8873
8874 return S_OK;
8875}
8876
8877void SessionMachine::FinalRelease()
8878{
8879 LogFlowThisFunc(("\n"));
8880
8881 uninit(Uninit::Unexpected);
8882}
8883
8884/**
8885 * @note Must be called only by Machine::openSession() from its own write lock.
8886 */
8887HRESULT SessionMachine::init(Machine *aMachine)
8888{
8889 LogFlowThisFuncEnter();
8890 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8891
8892 AssertReturn(aMachine, E_INVALIDARG);
8893
8894 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
8895
8896 /* Enclose the state transition NotReady->InInit->Ready */
8897 AutoInitSpan autoInitSpan(this);
8898 AssertReturn(autoInitSpan.isOk(), E_FAIL);
8899
8900 /* create the interprocess semaphore */
8901#if defined(RT_OS_WINDOWS)
8902 mIPCSemName = aMachine->mData->m_strConfigFileFull;
8903 for (size_t i = 0; i < mIPCSemName.length(); i++)
8904 if (mIPCSemName[i] == '\\')
8905 mIPCSemName[i] = '/';
8906 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName);
8907 ComAssertMsgRet(mIPCSem,
8908 ("Cannot create IPC mutex '%ls', err=%d",
8909 mIPCSemName.raw(), ::GetLastError()),
8910 E_FAIL);
8911#elif defined(RT_OS_OS2)
8912 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
8913 aMachine->mData->mUuid.raw());
8914 mIPCSemName = ipcSem;
8915 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.raw(), &mIPCSem, 0, FALSE);
8916 ComAssertMsgRet(arc == NO_ERROR,
8917 ("Cannot create IPC mutex '%s', arc=%ld",
8918 ipcSem.raw(), arc),
8919 E_FAIL);
8920#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8921# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8922# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8923 /** @todo Check that this still works correctly. */
8924 AssertCompileSize(key_t, 8);
8925# else
8926 AssertCompileSize(key_t, 4);
8927# endif
8928 key_t key;
8929 mIPCSem = -1;
8930 mIPCKey = "0";
8931 for (uint32_t i = 0; i < 1 << 24; i++)
8932 {
8933 key = ((uint32_t)'V' << 24) | i;
8934 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8935 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8936 {
8937 mIPCSem = sem;
8938 if (sem >= 0)
8939 mIPCKey = BstrFmt("%u", key);
8940 break;
8941 }
8942 }
8943# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8944 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8945 char *pszSemName = NULL;
8946 RTStrUtf8ToCurrentCP(&pszSemName, semName);
8947 key_t key = ::ftok(pszSemName, 'V');
8948 RTStrFree(pszSemName);
8949
8950 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8951# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8952
8953 int errnoSave = errno;
8954 if (mIPCSem < 0 && errnoSave == ENOSYS)
8955 {
8956 setError(E_FAIL,
8957 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8958 "support for SysV IPC. Check the host kernel configuration for "
8959 "CONFIG_SYSVIPC=y"));
8960 return E_FAIL;
8961 }
8962 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8963 * the IPC semaphores */
8964 if (mIPCSem < 0 && errnoSave == ENOSPC)
8965 {
8966#ifdef RT_OS_LINUX
8967 setError(E_FAIL,
8968 tr("Cannot create IPC semaphore because the system limit for the "
8969 "maximum number of semaphore sets (SEMMNI), or the system wide "
8970 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8971 "current set of SysV IPC semaphores can be determined from "
8972 "the file /proc/sysvipc/sem"));
8973#else
8974 setError(E_FAIL,
8975 tr("Cannot create IPC semaphore because the system-imposed limit "
8976 "on the maximum number of allowed semaphores or semaphore "
8977 "identifiers system-wide would be exceeded"));
8978#endif
8979 return E_FAIL;
8980 }
8981 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8982 E_FAIL);
8983 /* set the initial value to 1 */
8984 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
8985 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8986 E_FAIL);
8987#else
8988# error "Port me!"
8989#endif
8990
8991 /* memorize the peer Machine */
8992 unconst(mPeer) = aMachine;
8993 /* share the parent pointer */
8994 unconst(mParent) = aMachine->mParent;
8995
8996 /* take the pointers to data to share */
8997 mData.share(aMachine->mData);
8998 mSSData.share(aMachine->mSSData);
8999
9000 mUserData.share(aMachine->mUserData);
9001 mHWData.share(aMachine->mHWData);
9002 mMediaData.share(aMachine->mMediaData);
9003
9004 mStorageControllers.allocate();
9005 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
9006 it != aMachine->mStorageControllers->end();
9007 ++it)
9008 {
9009 ComObjPtr<StorageController> ctl;
9010 ctl.createObject();
9011 ctl->init(this, *it);
9012 mStorageControllers->push_back(ctl);
9013 }
9014
9015 unconst(mBIOSSettings).createObject();
9016 mBIOSSettings->init(this, aMachine->mBIOSSettings);
9017#ifdef VBOX_WITH_VRDP
9018 /* create another VRDPServer object that will be mutable */
9019 unconst(mVRDPServer).createObject();
9020 mVRDPServer->init(this, aMachine->mVRDPServer);
9021#endif
9022 /* create another audio adapter object that will be mutable */
9023 unconst(mAudioAdapter).createObject();
9024 mAudioAdapter->init(this, aMachine->mAudioAdapter);
9025 /* create a list of serial ports that will be mutable */
9026 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9027 {
9028 unconst(mSerialPorts[slot]).createObject();
9029 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
9030 }
9031 /* create a list of parallel ports that will be mutable */
9032 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9033 {
9034 unconst(mParallelPorts[slot]).createObject();
9035 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
9036 }
9037 /* create another USB controller object that will be mutable */
9038 unconst(mUSBController).createObject();
9039 mUSBController->init(this, aMachine->mUSBController);
9040
9041 /* create a list of network adapters that will be mutable */
9042 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9043 {
9044 unconst(mNetworkAdapters[slot]).createObject();
9045 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
9046 }
9047
9048 /* default is to delete saved state on Saved -> PoweredOff transition */
9049 mRemoveSavedState = true;
9050
9051 /* Confirm a successful initialization when it's the case */
9052 autoInitSpan.setSucceeded();
9053
9054 LogFlowThisFuncLeave();
9055 return S_OK;
9056}
9057
9058/**
9059 * Uninitializes this session object. If the reason is other than
9060 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
9061 *
9062 * @param aReason uninitialization reason
9063 *
9064 * @note Locks mParent + this object for writing.
9065 */
9066void SessionMachine::uninit(Uninit::Reason aReason)
9067{
9068 LogFlowThisFuncEnter();
9069 LogFlowThisFunc(("reason=%d\n", aReason));
9070
9071 /*
9072 * Strongly reference ourselves to prevent this object deletion after
9073 * mData->mSession.mMachine.setNull() below (which can release the last
9074 * reference and call the destructor). Important: this must be done before
9075 * accessing any members (and before AutoUninitSpan that does it as well).
9076 * This self reference will be released as the very last step on return.
9077 */
9078 ComObjPtr<SessionMachine> selfRef = this;
9079
9080 /* Enclose the state transition Ready->InUninit->NotReady */
9081 AutoUninitSpan autoUninitSpan(this);
9082 if (autoUninitSpan.uninitDone())
9083 {
9084 LogFlowThisFunc(("Already uninitialized\n"));
9085 LogFlowThisFuncLeave();
9086 return;
9087 }
9088
9089 if (autoUninitSpan.initFailed())
9090 {
9091 /* We've been called by init() because it's failed. It's not really
9092 * necessary (nor it's safe) to perform the regular uninit sequense
9093 * below, the following is enough.
9094 */
9095 LogFlowThisFunc(("Initialization failed.\n"));
9096#if defined(RT_OS_WINDOWS)
9097 if (mIPCSem)
9098 ::CloseHandle(mIPCSem);
9099 mIPCSem = NULL;
9100#elif defined(RT_OS_OS2)
9101 if (mIPCSem != NULLHANDLE)
9102 ::DosCloseMutexSem(mIPCSem);
9103 mIPCSem = NULLHANDLE;
9104#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9105 if (mIPCSem >= 0)
9106 ::semctl(mIPCSem, 0, IPC_RMID);
9107 mIPCSem = -1;
9108# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9109 mIPCKey = "0";
9110# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9111#else
9112# error "Port me!"
9113#endif
9114 uninitDataAndChildObjects();
9115 mData.free();
9116 unconst(mParent) = NULL;
9117 unconst(mPeer) = NULL;
9118 LogFlowThisFuncLeave();
9119 return;
9120 }
9121
9122 /* We need to lock this object in uninit() because the lock is shared
9123 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
9124 * and others need mParent lock, and USB needs host lock. */
9125 AutoMultiWriteLock3 alock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
9126
9127#ifdef VBOX_WITH_RESOURCE_USAGE_API
9128 unregisterMetrics(mParent->performanceCollector(), mPeer);
9129#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9130
9131 MachineState_T lastState = mData->mMachineState;
9132 NOREF(lastState);
9133
9134 if (aReason == Uninit::Abnormal)
9135 {
9136 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
9137 Global::IsOnlineOrTransient(lastState)));
9138
9139 /* reset the state to Aborted */
9140 if (mData->mMachineState != MachineState_Aborted)
9141 setMachineState(MachineState_Aborted);
9142 }
9143
9144 // any machine settings modified?
9145 if (m_flModifications)
9146 {
9147 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
9148 rollback(false /* aNotify */);
9149 }
9150
9151 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
9152 if (!mSnapshotData.mStateFilePath.isEmpty())
9153 {
9154 LogWarningThisFunc(("canceling failed save state request!\n"));
9155 endSavingState(FALSE /* aSuccess */);
9156 }
9157 else if (!mSnapshotData.mSnapshot.isNull())
9158 {
9159 LogWarningThisFunc(("canceling untaken snapshot!\n"));
9160
9161 /* delete all differencing hard disks created (this will also attach
9162 * their parents back by rolling back mMediaData) */
9163 rollbackMedia();
9164 /* delete the saved state file (it might have been already created) */
9165 if (mSnapshotData.mSnapshot->stateFilePath().length())
9166 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
9167
9168 mSnapshotData.mSnapshot->uninit();
9169 }
9170
9171#ifdef VBOX_WITH_USB
9172 /* release all captured USB devices */
9173 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
9174 {
9175 /* Console::captureUSBDevices() is called in the VM process only after
9176 * setting the machine state to Starting or Restoring.
9177 * Console::detachAllUSBDevices() will be called upon successful
9178 * termination. So, we need to release USB devices only if there was
9179 * an abnormal termination of a running VM.
9180 *
9181 * This is identical to SessionMachine::DetachAllUSBDevices except
9182 * for the aAbnormal argument. */
9183 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9184 AssertComRC(rc);
9185 NOREF(rc);
9186
9187 USBProxyService *service = mParent->host()->usbProxyService();
9188 if (service)
9189 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
9190 }
9191#endif /* VBOX_WITH_USB */
9192
9193 if (!mData->mSession.mType.isEmpty())
9194 {
9195 /* mType is not null when this machine's process has been started by
9196 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
9197 * need to queue the PID to reap the process (and avoid zombies on
9198 * Linux). */
9199 Assert(mData->mSession.mPid != NIL_RTPROCESS);
9200 mParent->addProcessToReap(mData->mSession.mPid);
9201 }
9202
9203 mData->mSession.mPid = NIL_RTPROCESS;
9204
9205 if (aReason == Uninit::Unexpected)
9206 {
9207 /* Uninitialization didn't come from #checkForDeath(), so tell the
9208 * client watcher thread to update the set of machines that have open
9209 * sessions. */
9210 mParent->updateClientWatcher();
9211 }
9212
9213 /* uninitialize all remote controls */
9214 if (mData->mSession.mRemoteControls.size())
9215 {
9216 LogFlowThisFunc(("Closing remote sessions (%d):\n",
9217 mData->mSession.mRemoteControls.size()));
9218
9219 Data::Session::RemoteControlList::iterator it =
9220 mData->mSession.mRemoteControls.begin();
9221 while (it != mData->mSession.mRemoteControls.end())
9222 {
9223 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
9224 HRESULT rc = (*it)->Uninitialize();
9225 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
9226 if (FAILED(rc))
9227 LogWarningThisFunc(("Forgot to close the remote session?\n"));
9228 ++it;
9229 }
9230 mData->mSession.mRemoteControls.clear();
9231 }
9232
9233 /*
9234 * An expected uninitialization can come only from #checkForDeath().
9235 * Otherwise it means that something's got really wrong (for examlple,
9236 * the Session implementation has released the VirtualBox reference
9237 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
9238 * etc). However, it's also possible, that the client releases the IPC
9239 * semaphore correctly (i.e. before it releases the VirtualBox reference),
9240 * but the VirtualBox release event comes first to the server process.
9241 * This case is practically possible, so we should not assert on an
9242 * unexpected uninit, just log a warning.
9243 */
9244
9245 if ((aReason == Uninit::Unexpected))
9246 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
9247
9248 if (aReason != Uninit::Normal)
9249 {
9250 mData->mSession.mDirectControl.setNull();
9251 }
9252 else
9253 {
9254 /* this must be null here (see #OnSessionEnd()) */
9255 Assert(mData->mSession.mDirectControl.isNull());
9256 Assert(mData->mSession.mState == SessionState_Closing);
9257 Assert(!mData->mSession.mProgress.isNull());
9258 }
9259 if (mData->mSession.mProgress)
9260 {
9261 if (aReason == Uninit::Normal)
9262 mData->mSession.mProgress->notifyComplete(S_OK);
9263 else
9264 mData->mSession.mProgress->notifyComplete(E_FAIL,
9265 COM_IIDOF(ISession),
9266 getComponentName(),
9267 tr("The VM session was aborted"));
9268 mData->mSession.mProgress.setNull();
9269 }
9270
9271 /* remove the association between the peer machine and this session machine */
9272 Assert(mData->mSession.mMachine == this ||
9273 aReason == Uninit::Unexpected);
9274
9275 /* reset the rest of session data */
9276 mData->mSession.mMachine.setNull();
9277 mData->mSession.mState = SessionState_Closed;
9278 mData->mSession.mType.setNull();
9279
9280 /* close the interprocess semaphore before leaving the exclusive lock */
9281#if defined(RT_OS_WINDOWS)
9282 if (mIPCSem)
9283 ::CloseHandle(mIPCSem);
9284 mIPCSem = NULL;
9285#elif defined(RT_OS_OS2)
9286 if (mIPCSem != NULLHANDLE)
9287 ::DosCloseMutexSem(mIPCSem);
9288 mIPCSem = NULLHANDLE;
9289#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9290 if (mIPCSem >= 0)
9291 ::semctl(mIPCSem, 0, IPC_RMID);
9292 mIPCSem = -1;
9293# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9294 mIPCKey = "0";
9295# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9296#else
9297# error "Port me!"
9298#endif
9299
9300 /* fire an event */
9301 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
9302
9303 uninitDataAndChildObjects();
9304
9305 /* free the essential data structure last */
9306 mData.free();
9307
9308 /* leave the exclusive lock before setting the below two to NULL */
9309 alock.leave();
9310
9311 unconst(mParent) = NULL;
9312 unconst(mPeer) = NULL;
9313
9314 LogFlowThisFuncLeave();
9315}
9316
9317// util::Lockable interface
9318////////////////////////////////////////////////////////////////////////////////
9319
9320/**
9321 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
9322 * with the primary Machine instance (mPeer).
9323 */
9324RWLockHandle *SessionMachine::lockHandle() const
9325{
9326 AssertReturn(mPeer != NULL, NULL);
9327 return mPeer->lockHandle();
9328}
9329
9330// IInternalMachineControl methods
9331////////////////////////////////////////////////////////////////////////////////
9332
9333/**
9334 * @note Locks this object for writing.
9335 */
9336STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
9337{
9338 AutoCaller autoCaller(this);
9339 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9340
9341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9342
9343 mRemoveSavedState = aRemove;
9344
9345 return S_OK;
9346}
9347
9348/**
9349 * @note Locks the same as #setMachineState() does.
9350 */
9351STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
9352{
9353 return setMachineState(aMachineState);
9354}
9355
9356/**
9357 * @note Locks this object for reading.
9358 */
9359STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
9360{
9361 AutoCaller autoCaller(this);
9362 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9363
9364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9365
9366#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
9367 mIPCSemName.cloneTo(aId);
9368 return S_OK;
9369#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9370# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9371 mIPCKey.cloneTo(aId);
9372# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9373 mData->m_strConfigFileFull.cloneTo(aId);
9374# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9375 return S_OK;
9376#else
9377# error "Port me!"
9378#endif
9379}
9380
9381/**
9382 * @note Locks this object for writing.
9383 */
9384STDMETHODIMP SessionMachine::SetPowerUpInfo(IVirtualBoxErrorInfo *aError)
9385{
9386 AutoCaller autoCaller(this);
9387 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9388
9389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9390
9391 if ( mData->mSession.mState == SessionState_Open
9392 && mData->mSession.mProgress)
9393 {
9394 /* Finalize the progress, since the remote session has completed
9395 * power on (successful or not). */
9396 if (aError)
9397 {
9398 /* Transfer error information immediately, as the
9399 * IVirtualBoxErrorInfo object is most likely transient. */
9400 HRESULT rc;
9401 LONG rRc = S_OK;
9402 rc = aError->COMGETTER(ResultCode)(&rRc);
9403 AssertComRCReturnRC(rc);
9404 Bstr rIID;
9405 rc = aError->COMGETTER(InterfaceID)(rIID.asOutParam());
9406 AssertComRCReturnRC(rc);
9407 Bstr rComponent;
9408 rc = aError->COMGETTER(Component)(rComponent.asOutParam());
9409 AssertComRCReturnRC(rc);
9410 Bstr rText;
9411 rc = aError->COMGETTER(Text)(rText.asOutParam());
9412 AssertComRCReturnRC(rc);
9413 mData->mSession.mProgress->notifyComplete(rRc, Guid(rIID), rComponent, Utf8Str(rText).raw());
9414 }
9415 else
9416 mData->mSession.mProgress->notifyComplete(S_OK);
9417 mData->mSession.mProgress.setNull();
9418
9419 return S_OK;
9420 }
9421 else
9422 return VBOX_E_INVALID_OBJECT_STATE;
9423}
9424
9425/**
9426 * Goes through the USB filters of the given machine to see if the given
9427 * device matches any filter or not.
9428 *
9429 * @note Locks the same as USBController::hasMatchingFilter() does.
9430 */
9431STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
9432 BOOL *aMatched,
9433 ULONG *aMaskedIfs)
9434{
9435 LogFlowThisFunc(("\n"));
9436
9437 CheckComArgNotNull(aUSBDevice);
9438 CheckComArgOutPointerValid(aMatched);
9439
9440 AutoCaller autoCaller(this);
9441 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9442
9443#ifdef VBOX_WITH_USB
9444 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
9445#else
9446 NOREF(aUSBDevice);
9447 NOREF(aMaskedIfs);
9448 *aMatched = FALSE;
9449#endif
9450
9451 return S_OK;
9452}
9453
9454/**
9455 * @note Locks the same as Host::captureUSBDevice() does.
9456 */
9457STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
9458{
9459 LogFlowThisFunc(("\n"));
9460
9461 AutoCaller autoCaller(this);
9462 AssertComRCReturnRC(autoCaller.rc());
9463
9464#ifdef VBOX_WITH_USB
9465 /* if captureDeviceForVM() fails, it must have set extended error info */
9466 MultiResult rc = mParent->host()->checkUSBProxyService();
9467 if (FAILED(rc)) return rc;
9468
9469 USBProxyService *service = mParent->host()->usbProxyService();
9470 AssertReturn(service, E_FAIL);
9471 return service->captureDeviceForVM(this, Guid(aId));
9472#else
9473 NOREF(aId);
9474 return E_NOTIMPL;
9475#endif
9476}
9477
9478/**
9479 * @note Locks the same as Host::detachUSBDevice() does.
9480 */
9481STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
9482{
9483 LogFlowThisFunc(("\n"));
9484
9485 AutoCaller autoCaller(this);
9486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9487
9488#ifdef VBOX_WITH_USB
9489 USBProxyService *service = mParent->host()->usbProxyService();
9490 AssertReturn(service, E_FAIL);
9491 return service->detachDeviceFromVM(this, Guid(aId), !!aDone);
9492#else
9493 NOREF(aId);
9494 NOREF(aDone);
9495 return E_NOTIMPL;
9496#endif
9497}
9498
9499/**
9500 * Inserts all machine filters to the USB proxy service and then calls
9501 * Host::autoCaptureUSBDevices().
9502 *
9503 * Called by Console from the VM process upon VM startup.
9504 *
9505 * @note Locks what called methods lock.
9506 */
9507STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
9508{
9509 LogFlowThisFunc(("\n"));
9510
9511 AutoCaller autoCaller(this);
9512 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9513
9514#ifdef VBOX_WITH_USB
9515 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
9516 AssertComRC(rc);
9517 NOREF(rc);
9518
9519 USBProxyService *service = mParent->host()->usbProxyService();
9520 AssertReturn(service, E_FAIL);
9521 return service->autoCaptureDevicesForVM(this);
9522#else
9523 return S_OK;
9524#endif
9525}
9526
9527/**
9528 * Removes all machine filters from the USB proxy service and then calls
9529 * Host::detachAllUSBDevices().
9530 *
9531 * Called by Console from the VM process upon normal VM termination or by
9532 * SessionMachine::uninit() upon abnormal VM termination (from under the
9533 * Machine/SessionMachine lock).
9534 *
9535 * @note Locks what called methods lock.
9536 */
9537STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
9538{
9539 LogFlowThisFunc(("\n"));
9540
9541 AutoCaller autoCaller(this);
9542 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9543
9544#ifdef VBOX_WITH_USB
9545 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9546 AssertComRC(rc);
9547 NOREF(rc);
9548
9549 USBProxyService *service = mParent->host()->usbProxyService();
9550 AssertReturn(service, E_FAIL);
9551 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
9552#else
9553 NOREF(aDone);
9554 return S_OK;
9555#endif
9556}
9557
9558/**
9559 * @note Locks this object for writing.
9560 */
9561STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
9562 IProgress **aProgress)
9563{
9564 LogFlowThisFuncEnter();
9565
9566 AssertReturn(aSession, E_INVALIDARG);
9567 AssertReturn(aProgress, E_INVALIDARG);
9568
9569 AutoCaller autoCaller(this);
9570
9571 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
9572 /*
9573 * We don't assert below because it might happen that a non-direct session
9574 * informs us it is closed right after we've been uninitialized -- it's ok.
9575 */
9576 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9577
9578 /* get IInternalSessionControl interface */
9579 ComPtr<IInternalSessionControl> control(aSession);
9580
9581 ComAssertRet(!control.isNull(), E_INVALIDARG);
9582
9583 /* Creating a Progress object requires the VirtualBox lock, and
9584 * thus locking it here is required by the lock order rules. */
9585 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
9586
9587 if (control.equalsTo(mData->mSession.mDirectControl))
9588 {
9589 ComAssertRet(aProgress, E_POINTER);
9590
9591 /* The direct session is being normally closed by the client process
9592 * ----------------------------------------------------------------- */
9593
9594 /* go to the closing state (essential for all open*Session() calls and
9595 * for #checkForDeath()) */
9596 Assert(mData->mSession.mState == SessionState_Open);
9597 mData->mSession.mState = SessionState_Closing;
9598
9599 /* set direct control to NULL to release the remote instance */
9600 mData->mSession.mDirectControl.setNull();
9601 LogFlowThisFunc(("Direct control is set to NULL\n"));
9602
9603 if (mData->mSession.mProgress)
9604 {
9605 /* finalize the progress, someone might wait if a frontend
9606 * closes the session before powering on the VM. */
9607 mData->mSession.mProgress->notifyComplete(E_FAIL,
9608 COM_IIDOF(ISession),
9609 getComponentName(),
9610 tr("The VM session was closed before any attempt to power it on"));
9611 mData->mSession.mProgress.setNull();
9612 }
9613
9614 /* Create the progress object the client will use to wait until
9615 * #checkForDeath() is called to uninitialize this session object after
9616 * it releases the IPC semaphore. */
9617 Assert(mData->mSession.mProgress.isNull());
9618 ComObjPtr<Progress> progress;
9619 progress.createObject();
9620 ComPtr<IUnknown> pPeer(mPeer);
9621 progress->init(mParent, pPeer,
9622 Bstr(tr("Closing session")), FALSE /* aCancelable */);
9623 progress.queryInterfaceTo(aProgress);
9624 mData->mSession.mProgress = progress;
9625 }
9626 else
9627 {
9628 /* the remote session is being normally closed */
9629 Data::Session::RemoteControlList::iterator it =
9630 mData->mSession.mRemoteControls.begin();
9631 while (it != mData->mSession.mRemoteControls.end())
9632 {
9633 if (control.equalsTo(*it))
9634 break;
9635 ++it;
9636 }
9637 BOOL found = it != mData->mSession.mRemoteControls.end();
9638 ComAssertMsgRet(found, ("The session is not found in the session list!"),
9639 E_INVALIDARG);
9640 mData->mSession.mRemoteControls.remove(*it);
9641 }
9642
9643 LogFlowThisFuncLeave();
9644 return S_OK;
9645}
9646
9647/**
9648 * @note Locks this object for writing.
9649 */
9650STDMETHODIMP SessionMachine::BeginSavingState(IProgress *aProgress, BSTR *aStateFilePath)
9651{
9652 LogFlowThisFuncEnter();
9653
9654 AssertReturn(aProgress, E_INVALIDARG);
9655 AssertReturn(aStateFilePath, E_POINTER);
9656
9657 AutoCaller autoCaller(this);
9658 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9659
9660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9661
9662 AssertReturn( mData->mMachineState == MachineState_Paused
9663 && mSnapshotData.mLastState == MachineState_Null
9664 && mSnapshotData.mProgressId.isEmpty()
9665 && mSnapshotData.mStateFilePath.isEmpty(),
9666 E_FAIL);
9667
9668 /* memorize the progress ID and add it to the global collection */
9669 Bstr progressId;
9670 HRESULT rc = aProgress->COMGETTER(Id)(progressId.asOutParam());
9671 AssertComRCReturn(rc, rc);
9672 rc = mParent->addProgress(aProgress);
9673 AssertComRCReturn(rc, rc);
9674
9675 Bstr stateFilePath;
9676 /* stateFilePath is null when the machine is not running */
9677 if (mData->mMachineState == MachineState_Paused)
9678 {
9679 stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
9680 mUserData->mSnapshotFolderFull.raw(),
9681 RTPATH_DELIMITER, mData->mUuid.raw());
9682 }
9683
9684 /* fill in the snapshot data */
9685 mSnapshotData.mLastState = mData->mMachineState;
9686 mSnapshotData.mProgressId = Guid(progressId);
9687 mSnapshotData.mStateFilePath = stateFilePath;
9688
9689 /* set the state to Saving (this is expected by Console::SaveState()) */
9690 setMachineState(MachineState_Saving);
9691
9692 stateFilePath.cloneTo(aStateFilePath);
9693
9694 return S_OK;
9695}
9696
9697/**
9698 * @note Locks mParent + this object for writing.
9699 */
9700STDMETHODIMP SessionMachine::EndSavingState(BOOL aSuccess)
9701{
9702 LogFlowThisFunc(("\n"));
9703
9704 AutoCaller autoCaller(this);
9705 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9706
9707 /* endSavingState() need mParent lock */
9708 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
9709
9710 AssertReturn( mData->mMachineState == MachineState_Saving
9711 && mSnapshotData.mLastState != MachineState_Null
9712 && !mSnapshotData.mProgressId.isEmpty()
9713 && !mSnapshotData.mStateFilePath.isEmpty(),
9714 E_FAIL);
9715
9716 /*
9717 * on success, set the state to Saved;
9718 * on failure, set the state to the state we had when BeginSavingState() was
9719 * called (this is expected by Console::SaveState() and
9720 * Console::saveStateThread())
9721 */
9722 if (aSuccess)
9723 setMachineState(MachineState_Saved);
9724 else
9725 setMachineState(mSnapshotData.mLastState);
9726
9727 return endSavingState(aSuccess);
9728}
9729
9730/**
9731 * @note Locks this object for writing.
9732 */
9733STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
9734{
9735 LogFlowThisFunc(("\n"));
9736
9737 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
9738
9739 AutoCaller autoCaller(this);
9740 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9741
9742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9743
9744 AssertReturn( mData->mMachineState == MachineState_PoweredOff
9745 || mData->mMachineState == MachineState_Teleported
9746 || mData->mMachineState == MachineState_Aborted
9747 , E_FAIL); /** @todo setError. */
9748
9749 Utf8Str stateFilePathFull = aSavedStateFile;
9750 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9751 if (RT_FAILURE(vrc))
9752 return setError(VBOX_E_FILE_ERROR,
9753 tr("Invalid saved state file path '%ls' (%Rrc)"),
9754 aSavedStateFile,
9755 vrc);
9756
9757 mSSData->mStateFilePath = stateFilePathFull;
9758
9759 /* The below setMachineState() will detect the state transition and will
9760 * update the settings file */
9761
9762 return setMachineState(MachineState_Saved);
9763}
9764
9765STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
9766 ComSafeArrayOut(BSTR, aValues),
9767 ComSafeArrayOut(ULONG64, aTimestamps),
9768 ComSafeArrayOut(BSTR, aFlags))
9769{
9770 LogFlowThisFunc(("\n"));
9771
9772#ifdef VBOX_WITH_GUEST_PROPS
9773 using namespace guestProp;
9774
9775 AutoCaller autoCaller(this);
9776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9777
9778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9779
9780 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
9781 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
9782 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
9783 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
9784
9785 size_t cEntries = mHWData->mGuestProperties.size();
9786 com::SafeArray<BSTR> names(cEntries);
9787 com::SafeArray<BSTR> values(cEntries);
9788 com::SafeArray<ULONG64> timestamps(cEntries);
9789 com::SafeArray<BSTR> flags(cEntries);
9790 unsigned i = 0;
9791 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
9792 it != mHWData->mGuestProperties.end();
9793 ++it)
9794 {
9795 char szFlags[MAX_FLAGS_LEN + 1];
9796 it->strName.cloneTo(&names[i]);
9797 it->strValue.cloneTo(&values[i]);
9798 timestamps[i] = it->mTimestamp;
9799 /* If it is NULL, keep it NULL. */
9800 if (it->mFlags)
9801 {
9802 writeFlags(it->mFlags, szFlags);
9803 Bstr(szFlags).cloneTo(&flags[i]);
9804 }
9805 else
9806 flags[i] = NULL;
9807 ++i;
9808 }
9809 names.detachTo(ComSafeArrayOutArg(aNames));
9810 values.detachTo(ComSafeArrayOutArg(aValues));
9811 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
9812 flags.detachTo(ComSafeArrayOutArg(aFlags));
9813 mHWData->mPropertyServiceActive = true;
9814 return S_OK;
9815#else
9816 ReturnComNotImplemented();
9817#endif
9818}
9819
9820STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn(IN_BSTR, aNames),
9821 ComSafeArrayIn(IN_BSTR, aValues),
9822 ComSafeArrayIn(ULONG64, aTimestamps),
9823 ComSafeArrayIn(IN_BSTR, aFlags))
9824{
9825 LogFlowThisFunc(("\n"));
9826
9827#ifdef VBOX_WITH_GUEST_PROPS
9828 using namespace guestProp;
9829
9830 AssertReturn(!ComSafeArrayInIsNull(aNames), E_POINTER);
9831 AssertReturn(!ComSafeArrayInIsNull(aValues), E_POINTER);
9832 AssertReturn(!ComSafeArrayInIsNull(aTimestamps), E_POINTER);
9833 AssertReturn(!ComSafeArrayInIsNull(aFlags), E_POINTER);
9834
9835 AutoCaller autoCaller(this);
9836 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9837
9838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9839
9840 /*
9841 * Temporarily reset the registered flag, so that our machine state
9842 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in all
9843 * setters will return FALSE for a Machine instance if mRegistered is TRUE).
9844 *
9845 * This is copied from registeredInit(), and may or may not be the right
9846 * way to handle this.
9847 *
9848 * @todo r=dj review this, this gets called during machine power-down when
9849 * we have already saved the machine settings, there's no need to do this
9850 * twice.
9851 */
9852 Assert(mData->mRegistered);
9853 mData->mRegistered = FALSE;
9854
9855 HRESULT rc = checkStateDependency(MutableStateDep);
9856 AssertLogRelMsgReturn(SUCCEEDED(rc), ("%Rhrc\n", rc), rc);
9857
9858 com::SafeArray<IN_BSTR> names( ComSafeArrayInArg(aNames));
9859 com::SafeArray<IN_BSTR> values( ComSafeArrayInArg(aValues));
9860 com::SafeArray<ULONG64> timestamps(ComSafeArrayInArg(aTimestamps));
9861 com::SafeArray<IN_BSTR> flags( ComSafeArrayInArg(aFlags));
9862
9863 DiscardSettings();
9864 setModified(IsModified_MachineData);
9865 mHWData.backup();
9866
9867 mHWData->mGuestProperties.erase(mHWData->mGuestProperties.begin(),
9868 mHWData->mGuestProperties.end());
9869 for (unsigned i = 0; i < names.size(); ++i)
9870 {
9871 uint32_t fFlags = NILFLAG;
9872 validateFlags(Utf8Str(flags[i]).raw(), &fFlags);
9873 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
9874 mHWData->mGuestProperties.push_back(property);
9875 }
9876
9877 mHWData->mPropertyServiceActive = false;
9878
9879 alock.release();
9880 SaveSettings();
9881
9882 /* Restore the mRegistered flag. */
9883 alock.acquire();
9884 mData->mRegistered = TRUE;
9885
9886 return S_OK;
9887#else
9888 ReturnComNotImplemented();
9889#endif
9890}
9891
9892STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
9893 IN_BSTR aValue,
9894 ULONG64 aTimestamp,
9895 IN_BSTR aFlags)
9896{
9897 LogFlowThisFunc(("\n"));
9898
9899#ifdef VBOX_WITH_GUEST_PROPS
9900 using namespace guestProp;
9901
9902 CheckComArgStrNotEmptyOrNull(aName);
9903 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
9904 return E_POINTER; /* aValue can be NULL to indicate deletion */
9905
9906 try
9907 {
9908 /*
9909 * Convert input up front.
9910 */
9911 Utf8Str utf8Name(aName);
9912 uint32_t fFlags = NILFLAG;
9913 if (aFlags)
9914 {
9915 Utf8Str utf8Flags(aFlags);
9916 int vrc = validateFlags(utf8Flags.raw(), &fFlags);
9917 AssertRCReturn(vrc, E_INVALIDARG);
9918 }
9919
9920 /*
9921 * Now grab the object lock, validate the state and do the update.
9922 */
9923 AutoCaller autoCaller(this);
9924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9925
9926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9927
9928 AssertReturn(mHWData->mPropertyServiceActive, VBOX_E_INVALID_OBJECT_STATE);
9929 switch (mData->mMachineState)
9930 {
9931 case MachineState_Paused:
9932 case MachineState_Running:
9933 case MachineState_Teleporting:
9934 case MachineState_TeleportingPausedVM:
9935 case MachineState_LiveSnapshotting:
9936 case MachineState_Saving:
9937 break;
9938
9939 default:
9940 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
9941 VBOX_E_INVALID_VM_STATE);
9942 }
9943
9944 setModified(IsModified_MachineData);
9945 mHWData.backup();
9946
9947 /** @todo r=bird: The careful memory handling doesn't work out here because
9948 * the catch block won't undo any damange we've done. So, if push_back throws
9949 * bad_alloc then you've lost the value.
9950 *
9951 * Another thing. Doing a linear search here isn't extremely efficient, esp.
9952 * since values that changes actually bubbles to the end of the list. Using
9953 * something that has an efficient lookup and can tollerate a bit of updates
9954 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
9955 * combination of RTStrCache (for sharing names and getting uniqueness into
9956 * the bargain) and hash/tree is another. */
9957 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9958 iter != mHWData->mGuestProperties.end();
9959 ++iter)
9960 if (utf8Name == iter->strName)
9961 {
9962 mHWData->mGuestProperties.erase(iter);
9963 break;
9964 }
9965 if (aValue != NULL)
9966 {
9967 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9968 mHWData->mGuestProperties.push_back(property);
9969 }
9970
9971 /*
9972 * Send a callback notification if appropriate
9973 */
9974 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
9975 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
9976 RTSTR_MAX,
9977 utf8Name.raw(),
9978 RTSTR_MAX, NULL)
9979 )
9980 {
9981 alock.leave();
9982
9983 mParent->onGuestPropertyChange(mData->mUuid,
9984 aName,
9985 aValue,
9986 aFlags);
9987 }
9988 }
9989 catch (...)
9990 {
9991 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
9992 }
9993 return S_OK;
9994#else
9995 ReturnComNotImplemented();
9996#endif
9997}
9998
9999// public methods only for internal purposes
10000/////////////////////////////////////////////////////////////////////////////
10001
10002/**
10003 * Called from the client watcher thread to check for expected or unexpected
10004 * death of the client process that has a direct session to this machine.
10005 *
10006 * On Win32 and on OS/2, this method is called only when we've got the
10007 * mutex (i.e. the client has either died or terminated normally) so it always
10008 * returns @c true (the client is terminated, the session machine is
10009 * uninitialized).
10010 *
10011 * On other platforms, the method returns @c true if the client process has
10012 * terminated normally or abnormally and the session machine was uninitialized,
10013 * and @c false if the client process is still alive.
10014 *
10015 * @note Locks this object for writing.
10016 */
10017bool SessionMachine::checkForDeath()
10018{
10019 Uninit::Reason reason;
10020 bool terminated = false;
10021
10022 /* Enclose autoCaller with a block because calling uninit() from under it
10023 * will deadlock. */
10024 {
10025 AutoCaller autoCaller(this);
10026 if (!autoCaller.isOk())
10027 {
10028 /* return true if not ready, to cause the client watcher to exclude
10029 * the corresponding session from watching */
10030 LogFlowThisFunc(("Already uninitialized!\n"));
10031 return true;
10032 }
10033
10034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10035
10036 /* Determine the reason of death: if the session state is Closing here,
10037 * everything is fine. Otherwise it means that the client did not call
10038 * OnSessionEnd() before it released the IPC semaphore. This may happen
10039 * either because the client process has abnormally terminated, or
10040 * because it simply forgot to call ISession::Close() before exiting. We
10041 * threat the latter also as an abnormal termination (see
10042 * Session::uninit() for details). */
10043 reason = mData->mSession.mState == SessionState_Closing ?
10044 Uninit::Normal :
10045 Uninit::Abnormal;
10046
10047#if defined(RT_OS_WINDOWS)
10048
10049 AssertMsg(mIPCSem, ("semaphore must be created"));
10050
10051 /* release the IPC mutex */
10052 ::ReleaseMutex(mIPCSem);
10053
10054 terminated = true;
10055
10056#elif defined(RT_OS_OS2)
10057
10058 AssertMsg(mIPCSem, ("semaphore must be created"));
10059
10060 /* release the IPC mutex */
10061 ::DosReleaseMutexSem(mIPCSem);
10062
10063 terminated = true;
10064
10065#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10066
10067 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
10068
10069 int val = ::semctl(mIPCSem, 0, GETVAL);
10070 if (val > 0)
10071 {
10072 /* the semaphore is signaled, meaning the session is terminated */
10073 terminated = true;
10074 }
10075
10076#else
10077# error "Port me!"
10078#endif
10079
10080 } /* AutoCaller block */
10081
10082 if (terminated)
10083 uninit(reason);
10084
10085 return terminated;
10086}
10087
10088/**
10089 * @note Locks this object for reading.
10090 */
10091HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
10092{
10093 LogFlowThisFunc(("\n"));
10094
10095 AutoCaller autoCaller(this);
10096 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10097
10098 ComPtr<IInternalSessionControl> directControl;
10099 {
10100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10101 directControl = mData->mSession.mDirectControl;
10102 }
10103
10104 /* ignore notifications sent after #OnSessionEnd() is called */
10105 if (!directControl)
10106 return S_OK;
10107
10108 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
10109}
10110
10111/**
10112 * @note Locks this object for reading.
10113 */
10114HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
10115{
10116 LogFlowThisFunc(("\n"));
10117
10118 AutoCaller autoCaller(this);
10119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10120
10121 ComPtr<IInternalSessionControl> directControl;
10122 {
10123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10124 directControl = mData->mSession.mDirectControl;
10125 }
10126
10127 /* ignore notifications sent after #OnSessionEnd() is called */
10128 if (!directControl)
10129 return S_OK;
10130
10131 return directControl->OnSerialPortChange(serialPort);
10132}
10133
10134/**
10135 * @note Locks this object for reading.
10136 */
10137HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
10138{
10139 LogFlowThisFunc(("\n"));
10140
10141 AutoCaller autoCaller(this);
10142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10143
10144 ComPtr<IInternalSessionControl> directControl;
10145 {
10146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10147 directControl = mData->mSession.mDirectControl;
10148 }
10149
10150 /* ignore notifications sent after #OnSessionEnd() is called */
10151 if (!directControl)
10152 return S_OK;
10153
10154 return directControl->OnParallelPortChange(parallelPort);
10155}
10156
10157/**
10158 * @note Locks this object for reading.
10159 */
10160HRESULT SessionMachine::onStorageControllerChange()
10161{
10162 LogFlowThisFunc(("\n"));
10163
10164 AutoCaller autoCaller(this);
10165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10166
10167 ComPtr<IInternalSessionControl> directControl;
10168 {
10169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10170 directControl = mData->mSession.mDirectControl;
10171 }
10172
10173 /* ignore notifications sent after #OnSessionEnd() is called */
10174 if (!directControl)
10175 return S_OK;
10176
10177 return directControl->OnStorageControllerChange();
10178}
10179
10180/**
10181 * @note Locks this object for reading.
10182 */
10183HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
10184{
10185 LogFlowThisFunc(("\n"));
10186
10187 AutoCaller autoCaller(this);
10188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10189
10190 ComPtr<IInternalSessionControl> directControl;
10191 {
10192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10193 directControl = mData->mSession.mDirectControl;
10194 }
10195
10196 /* ignore notifications sent after #OnSessionEnd() is called */
10197 if (!directControl)
10198 return S_OK;
10199
10200 return directControl->OnMediumChange(aAttachment, aForce);
10201}
10202
10203/**
10204 * @note Locks this object for reading.
10205 */
10206HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
10207{
10208 LogFlowThisFunc(("\n"));
10209
10210 AutoCaller autoCaller(this);
10211 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10212
10213 ComPtr<IInternalSessionControl> directControl;
10214 {
10215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10216 directControl = mData->mSession.mDirectControl;
10217 }
10218
10219 /* ignore notifications sent after #OnSessionEnd() is called */
10220 if (!directControl)
10221 return S_OK;
10222
10223 return directControl->OnCPUChange(aCPU, aRemove);
10224}
10225
10226/**
10227 * @note Locks this object for reading.
10228 */
10229HRESULT SessionMachine::onVRDPServerChange()
10230{
10231 LogFlowThisFunc(("\n"));
10232
10233 AutoCaller autoCaller(this);
10234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10235
10236 ComPtr<IInternalSessionControl> directControl;
10237 {
10238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10239 directControl = mData->mSession.mDirectControl;
10240 }
10241
10242 /* ignore notifications sent after #OnSessionEnd() is called */
10243 if (!directControl)
10244 return S_OK;
10245
10246 return directControl->OnVRDPServerChange();
10247}
10248
10249/**
10250 * @note Locks this object for reading.
10251 */
10252HRESULT SessionMachine::onUSBControllerChange()
10253{
10254 LogFlowThisFunc(("\n"));
10255
10256 AutoCaller autoCaller(this);
10257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10258
10259 ComPtr<IInternalSessionControl> directControl;
10260 {
10261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10262 directControl = mData->mSession.mDirectControl;
10263 }
10264
10265 /* ignore notifications sent after #OnSessionEnd() is called */
10266 if (!directControl)
10267 return S_OK;
10268
10269 return directControl->OnUSBControllerChange();
10270}
10271
10272/**
10273 * @note Locks this object for reading.
10274 */
10275HRESULT SessionMachine::onSharedFolderChange()
10276{
10277 LogFlowThisFunc(("\n"));
10278
10279 AutoCaller autoCaller(this);
10280 AssertComRCReturnRC(autoCaller.rc());
10281
10282 ComPtr<IInternalSessionControl> directControl;
10283 {
10284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10285 directControl = mData->mSession.mDirectControl;
10286 }
10287
10288 /* ignore notifications sent after #OnSessionEnd() is called */
10289 if (!directControl)
10290 return S_OK;
10291
10292 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
10293}
10294
10295/**
10296 * Returns @c true if this machine's USB controller reports it has a matching
10297 * filter for the given USB device and @c false otherwise.
10298 *
10299 * @note Caller must have requested machine read lock.
10300 */
10301bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
10302{
10303 AutoCaller autoCaller(this);
10304 /* silently return if not ready -- this method may be called after the
10305 * direct machine session has been called */
10306 if (!autoCaller.isOk())
10307 return false;
10308
10309
10310#ifdef VBOX_WITH_USB
10311 switch (mData->mMachineState)
10312 {
10313 case MachineState_Starting:
10314 case MachineState_Restoring:
10315 case MachineState_TeleportingIn:
10316 case MachineState_Paused:
10317 case MachineState_Running:
10318 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
10319 * elsewhere... */
10320 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
10321 default: break;
10322 }
10323#else
10324 NOREF(aDevice);
10325 NOREF(aMaskedIfs);
10326#endif
10327 return false;
10328}
10329
10330/**
10331 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10332 */
10333HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
10334 IVirtualBoxErrorInfo *aError,
10335 ULONG aMaskedIfs)
10336{
10337 LogFlowThisFunc(("\n"));
10338
10339 AutoCaller autoCaller(this);
10340
10341 /* This notification may happen after the machine object has been
10342 * uninitialized (the session was closed), so don't assert. */
10343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10344
10345 ComPtr<IInternalSessionControl> directControl;
10346 {
10347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10348 directControl = mData->mSession.mDirectControl;
10349 }
10350
10351 /* fail on notifications sent after #OnSessionEnd() is called, it is
10352 * expected by the caller */
10353 if (!directControl)
10354 return E_FAIL;
10355
10356 /* No locks should be held at this point. */
10357 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10358 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10359
10360 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
10361}
10362
10363/**
10364 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10365 */
10366HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
10367 IVirtualBoxErrorInfo *aError)
10368{
10369 LogFlowThisFunc(("\n"));
10370
10371 AutoCaller autoCaller(this);
10372
10373 /* This notification may happen after the machine object has been
10374 * uninitialized (the session was closed), so don't assert. */
10375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10376
10377 ComPtr<IInternalSessionControl> directControl;
10378 {
10379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10380 directControl = mData->mSession.mDirectControl;
10381 }
10382
10383 /* fail on notifications sent after #OnSessionEnd() is called, it is
10384 * expected by the caller */
10385 if (!directControl)
10386 return E_FAIL;
10387
10388 /* No locks should be held at this point. */
10389 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10390 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10391
10392 return directControl->OnUSBDeviceDetach(aId, aError);
10393}
10394
10395// protected methods
10396/////////////////////////////////////////////////////////////////////////////
10397
10398/**
10399 * Helper method to finalize saving the state.
10400 *
10401 * @note Must be called from under this object's lock.
10402 *
10403 * @param aSuccess TRUE if the snapshot has been taken successfully
10404 *
10405 * @note Locks mParent + this objects for writing.
10406 */
10407HRESULT SessionMachine::endSavingState(BOOL aSuccess)
10408{
10409 LogFlowThisFuncEnter();
10410
10411 AutoCaller autoCaller(this);
10412 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10413
10414 /* saveSettings() needs mParent lock */
10415 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10416
10417 HRESULT rc = S_OK;
10418
10419 if (aSuccess)
10420 {
10421 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
10422
10423 /* save all VM settings */
10424 rc = saveSettings(NULL);
10425 // no need to check whether VirtualBox.xml needs saving also since
10426 // we can't have a name change pending at this point
10427 }
10428 else
10429 {
10430 /* delete the saved state file (it might have been already created) */
10431 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
10432 }
10433
10434 /* remove the completed progress object */
10435 mParent->removeProgress(mSnapshotData.mProgressId);
10436
10437 /* clear out the temporary saved state data */
10438 mSnapshotData.mLastState = MachineState_Null;
10439 mSnapshotData.mProgressId.clear();
10440 mSnapshotData.mStateFilePath.setNull();
10441
10442 LogFlowThisFuncLeave();
10443 return rc;
10444}
10445
10446/**
10447 * Locks the attached media.
10448 *
10449 * All attached hard disks are locked for writing and DVD/floppy are locked for
10450 * reading. Parents of attached hard disks (if any) are locked for reading.
10451 *
10452 * This method also performs accessibility check of all media it locks: if some
10453 * media is inaccessible, the method will return a failure and a bunch of
10454 * extended error info objects per each inaccessible medium.
10455 *
10456 * Note that this method is atomic: if it returns a success, all media are
10457 * locked as described above; on failure no media is locked at all (all
10458 * succeeded individual locks will be undone).
10459 *
10460 * This method is intended to be called when the machine is in Starting or
10461 * Restoring state and asserts otherwise.
10462 *
10463 * The locks made by this method must be undone by calling #unlockMedia() when
10464 * no more needed.
10465 */
10466HRESULT SessionMachine::lockMedia()
10467{
10468 AutoCaller autoCaller(this);
10469 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10470
10471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10472
10473 AssertReturn( mData->mMachineState == MachineState_Starting
10474 || mData->mMachineState == MachineState_Restoring
10475 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
10476
10477 try
10478 {
10479 HRESULT rc = S_OK;
10480
10481 ErrorInfoKeeper eik(true /* aIsNull */);
10482 MultiResult mrc(S_OK);
10483
10484 /* Lock all medium objects attached to the VM.
10485 * Get status for inaccessible media as well. */
10486 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10487 it != mMediaData->mAttachments.end();
10488 ++it)
10489 {
10490 DeviceType_T devType = (*it)->getType();
10491 ComObjPtr<Medium> medium = (*it)->getMedium();
10492
10493 bool first = true;
10494
10495 /** @todo split out the media locking, and put it into
10496 * MediumImpl.cpp, as it needs this functionality too. */
10497 while (!medium.isNull())
10498 {
10499 MediumState_T mediumState = medium->getState();
10500
10501 /* accessibility check must be first, otherwise locking
10502 * interferes with getting the medium state. */
10503 if (mediumState == MediumState_Inaccessible)
10504 {
10505 rc = medium->RefreshState(&mediumState);
10506 if (FAILED(rc)) throw rc;
10507
10508 if (mediumState == MediumState_Inaccessible)
10509 {
10510 Bstr error;
10511 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
10512 if (FAILED(rc)) throw rc;
10513
10514 Bstr loc;
10515 rc = medium->COMGETTER(Location)(loc.asOutParam());
10516 if (FAILED(rc)) throw rc;
10517
10518 /* collect multiple errors */
10519 eik.restore();
10520
10521 /* be in sync with MediumBase::setStateError() */
10522 Assert(!error.isEmpty());
10523 mrc = setError(E_FAIL,
10524 tr("Medium '%ls' is not accessible. %ls"),
10525 loc.raw(),
10526 error.raw());
10527
10528 eik.fetch();
10529 }
10530 }
10531
10532 if (first)
10533 {
10534 if (devType != DeviceType_DVD)
10535 {
10536 /* HardDisk and Floppy medium must be locked for writing */
10537 rc = medium->LockWrite(NULL);
10538 if (FAILED(rc)) throw rc;
10539 }
10540 else
10541 {
10542 /* DVD medium must be locked for reading */
10543 rc = medium->LockRead(NULL);
10544 if (FAILED(rc)) throw rc;
10545 }
10546
10547 mData->mSession.mLockedMedia.push_back(
10548 Data::Session::LockedMedia::value_type(
10549 ComPtr<IMedium>(medium), true));
10550
10551 first = false;
10552 }
10553 else
10554 {
10555 rc = medium->LockRead(NULL);
10556 if (FAILED(rc)) throw rc;
10557
10558 mData->mSession.mLockedMedia.push_back(
10559 Data::Session::LockedMedia::value_type(
10560 ComPtr<IMedium>(medium), false));
10561 }
10562
10563
10564 /* no locks or callers here since there should be no way to
10565 * change the hard disk parent at this point (as it is still
10566 * attached to the machine) */
10567 medium = medium->getParent();
10568 }
10569 }
10570
10571 /* @todo r=dj is this correct? first restoring the eik and then throwing? */
10572 eik.restore();
10573 HRESULT rc2 = (HRESULT)mrc;
10574 if (FAILED(rc2)) throw rc2;
10575 }
10576 catch (HRESULT aRC)
10577 {
10578 /* Unlock all locked media on failure */
10579 unlockMedia();
10580 return aRC;
10581 }
10582
10583 return S_OK;
10584}
10585
10586/**
10587 * Undoes the locks made by by #lockMedia().
10588 */
10589void SessionMachine::unlockMedia()
10590{
10591 AutoCaller autoCaller(this);
10592 AssertComRCReturnVoid(autoCaller.rc());
10593
10594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10595
10596 /* we may be holding important error info on the current thread;
10597 * preserve it */
10598 ErrorInfoKeeper eik;
10599
10600 HRESULT rc = S_OK;
10601
10602 for (Data::Session::LockedMedia::const_iterator
10603 it = mData->mSession.mLockedMedia.begin();
10604 it != mData->mSession.mLockedMedia.end(); ++it)
10605 {
10606 MediumState_T state;
10607 if (it->second)
10608 rc = it->first->UnlockWrite(&state);
10609 else
10610 rc = it->first->UnlockRead(&state);
10611
10612 /* The second can happen if an object was re-locked in
10613 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
10614 * image was unmounted at runtime. */
10615 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
10616 }
10617
10618 mData->mSession.mLockedMedia.clear();
10619}
10620
10621/**
10622 * Helper to change the machine state (reimplementation).
10623 *
10624 * @note Locks this object for writing.
10625 */
10626HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
10627{
10628 LogFlowThisFuncEnter();
10629 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
10630
10631 AutoCaller autoCaller(this);
10632 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10633
10634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10635
10636 MachineState_T oldMachineState = mData->mMachineState;
10637
10638 AssertMsgReturn(oldMachineState != aMachineState,
10639 ("oldMachineState=%s, aMachineState=%s\n",
10640 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
10641 E_FAIL);
10642
10643 HRESULT rc = S_OK;
10644
10645 int stsFlags = 0;
10646 bool deleteSavedState = false;
10647
10648 /* detect some state transitions */
10649
10650 if ( ( oldMachineState == MachineState_Saved
10651 && aMachineState == MachineState_Restoring)
10652 || ( ( oldMachineState == MachineState_PoweredOff
10653 || oldMachineState == MachineState_Teleported
10654 || oldMachineState == MachineState_Aborted
10655 )
10656 && ( aMachineState == MachineState_TeleportingIn
10657 || aMachineState == MachineState_Starting
10658 )
10659 )
10660 )
10661 {
10662 /* The EMT thread is about to start */
10663
10664 /* Nothing to do here for now... */
10665
10666 /// @todo NEWMEDIA don't let mDVDDrive and other children
10667 /// change anything when in the Starting/Restoring state
10668 }
10669 else if ( ( oldMachineState == MachineState_Running
10670 || oldMachineState == MachineState_Paused
10671 || oldMachineState == MachineState_Teleporting
10672 || oldMachineState == MachineState_LiveSnapshotting
10673 || oldMachineState == MachineState_Stuck
10674 || oldMachineState == MachineState_Starting
10675 || oldMachineState == MachineState_Stopping
10676 || oldMachineState == MachineState_Saving
10677 || oldMachineState == MachineState_Restoring
10678 || oldMachineState == MachineState_TeleportingPausedVM
10679 || oldMachineState == MachineState_TeleportingIn
10680 )
10681 && ( aMachineState == MachineState_PoweredOff
10682 || aMachineState == MachineState_Saved
10683 || aMachineState == MachineState_Teleported
10684 || aMachineState == MachineState_Aborted
10685 )
10686 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10687 * snapshot */
10688 && ( mSnapshotData.mSnapshot.isNull()
10689 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
10690 )
10691 )
10692 {
10693 /* The EMT thread has just stopped, unlock attached media. Note that as
10694 * opposed to locking that is done from Console, we do unlocking here
10695 * because the VM process may have aborted before having a chance to
10696 * properly unlock all media it locked. */
10697
10698 unlockMedia();
10699 }
10700
10701 if (oldMachineState == MachineState_Restoring)
10702 {
10703 if (aMachineState != MachineState_Saved)
10704 {
10705 /*
10706 * delete the saved state file once the machine has finished
10707 * restoring from it (note that Console sets the state from
10708 * Restoring to Saved if the VM couldn't restore successfully,
10709 * to give the user an ability to fix an error and retry --
10710 * we keep the saved state file in this case)
10711 */
10712 deleteSavedState = true;
10713 }
10714 }
10715 else if ( oldMachineState == MachineState_Saved
10716 && ( aMachineState == MachineState_PoweredOff
10717 || aMachineState == MachineState_Aborted
10718 || aMachineState == MachineState_Teleported
10719 )
10720 )
10721 {
10722 /*
10723 * delete the saved state after Console::DiscardSavedState() is called
10724 * or if the VM process (owning a direct VM session) crashed while the
10725 * VM was Saved
10726 */
10727
10728 /// @todo (dmik)
10729 // Not sure that deleting the saved state file just because of the
10730 // client death before it attempted to restore the VM is a good
10731 // thing. But when it crashes we need to go to the Aborted state
10732 // which cannot have the saved state file associated... The only
10733 // way to fix this is to make the Aborted condition not a VM state
10734 // but a bool flag: i.e., when a crash occurs, set it to true and
10735 // change the state to PoweredOff or Saved depending on the
10736 // saved state presence.
10737
10738 deleteSavedState = true;
10739 mData->mCurrentStateModified = TRUE;
10740 stsFlags |= SaveSTS_CurStateModified;
10741 }
10742
10743 if ( aMachineState == MachineState_Starting
10744 || aMachineState == MachineState_Restoring
10745 || aMachineState == MachineState_TeleportingIn
10746 )
10747 {
10748 /* set the current state modified flag to indicate that the current
10749 * state is no more identical to the state in the
10750 * current snapshot */
10751 if (!mData->mCurrentSnapshot.isNull())
10752 {
10753 mData->mCurrentStateModified = TRUE;
10754 stsFlags |= SaveSTS_CurStateModified;
10755 }
10756 }
10757
10758 if (deleteSavedState)
10759 {
10760 if (mRemoveSavedState)
10761 {
10762 Assert(!mSSData->mStateFilePath.isEmpty());
10763 RTFileDelete(mSSData->mStateFilePath.c_str());
10764 }
10765 mSSData->mStateFilePath.setNull();
10766 stsFlags |= SaveSTS_StateFilePath;
10767 }
10768
10769 /* redirect to the underlying peer machine */
10770 mPeer->setMachineState(aMachineState);
10771
10772 if ( aMachineState == MachineState_PoweredOff
10773 || aMachineState == MachineState_Teleported
10774 || aMachineState == MachineState_Aborted
10775 || aMachineState == MachineState_Saved)
10776 {
10777 /* the machine has stopped execution
10778 * (or the saved state file was adopted) */
10779 stsFlags |= SaveSTS_StateTimeStamp;
10780 }
10781
10782 if ( ( oldMachineState == MachineState_PoweredOff
10783 || oldMachineState == MachineState_Aborted
10784 || oldMachineState == MachineState_Teleported
10785 )
10786 && aMachineState == MachineState_Saved)
10787 {
10788 /* the saved state file was adopted */
10789 Assert(!mSSData->mStateFilePath.isEmpty());
10790 stsFlags |= SaveSTS_StateFilePath;
10791 }
10792
10793 rc = saveStateSettings(stsFlags);
10794
10795 if ( ( oldMachineState != MachineState_PoweredOff
10796 && oldMachineState != MachineState_Aborted
10797 && oldMachineState != MachineState_Teleported
10798 )
10799 && ( aMachineState == MachineState_PoweredOff
10800 || aMachineState == MachineState_Aborted
10801 || aMachineState == MachineState_Teleported
10802 )
10803 )
10804 {
10805 /* we've been shut down for any reason */
10806 /* no special action so far */
10807 }
10808
10809 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
10810 LogFlowThisFuncLeave();
10811 return rc;
10812}
10813
10814/**
10815 * Sends the current machine state value to the VM process.
10816 *
10817 * @note Locks this object for reading, then calls a client process.
10818 */
10819HRESULT SessionMachine::updateMachineStateOnClient()
10820{
10821 AutoCaller autoCaller(this);
10822 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10823
10824 ComPtr<IInternalSessionControl> directControl;
10825 {
10826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10827 AssertReturn(!!mData, E_FAIL);
10828 directControl = mData->mSession.mDirectControl;
10829
10830 /* directControl may be already set to NULL here in #OnSessionEnd()
10831 * called too early by the direct session process while there is still
10832 * some operation (like discarding the snapshot) in progress. The client
10833 * process in this case is waiting inside Session::close() for the
10834 * "end session" process object to complete, while #uninit() called by
10835 * #checkForDeath() on the Watcher thread is waiting for the pending
10836 * operation to complete. For now, we accept this inconsitent behavior
10837 * and simply do nothing here. */
10838
10839 if (mData->mSession.mState == SessionState_Closing)
10840 return S_OK;
10841
10842 AssertReturn(!directControl.isNull(), E_FAIL);
10843 }
10844
10845 return directControl->UpdateMachineState(mData->mMachineState);
10846}
Note: See TracBrowser for help on using the repository browser.

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