VirtualBox

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

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

Main: API consistency: method names start with lower case always; no camel case within acronyms in method names (Cpu -> CPU)

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

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