VirtualBox

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

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

Main/VirtualBox: new parameter for overriding VM file existence check on creation.

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