VirtualBox

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

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

Main: addendum to r56387 (properly initialze mRTCUseUTC)

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

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