VirtualBox

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

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

Added interfaces to override cpuid leafs.

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