VirtualBox

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

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

Compile fixes

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

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