VirtualBox

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

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

MachineImpl: fixed instance number incrementing loop

  • 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 24323 2009-11-04 12:51:39Z 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 + 1;
3771 }
3772 }
3773
3774 rc = ctrl->init(this, aName, aConnectionType, ulInstance);
3775 CheckComRCReturnRC(rc);
3776
3777 mStorageControllers.backup();
3778 mStorageControllers->push_back (ctrl);
3779
3780 ctrl.queryInterfaceTo(controller);
3781
3782 /* inform the direct session if any */
3783 alock.leave();
3784 onStorageControllerChange();
3785
3786 return S_OK;
3787}
3788
3789STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
3790 IStorageController **aStorageController)
3791{
3792 CheckComArgStrNotEmptyOrNull(aName);
3793
3794 AutoCaller autoCaller(this);
3795 CheckComRCReturnRC(autoCaller.rc());
3796
3797 AutoReadLock alock(this);
3798
3799 ComObjPtr<StorageController> ctrl;
3800
3801 HRESULT rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3802 if (SUCCEEDED(rc))
3803 ctrl.queryInterfaceTo(aStorageController);
3804
3805 return rc;
3806}
3807
3808STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
3809 IStorageController **aStorageController)
3810{
3811 AutoCaller autoCaller(this);
3812 CheckComRCReturnRC(autoCaller.rc());
3813
3814 AutoReadLock alock(this);
3815
3816 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
3817 it != mStorageControllers->end();
3818 ++it)
3819 {
3820 if ((*it)->instance() == aInstance)
3821 {
3822 (*it).queryInterfaceTo(aStorageController);
3823 return S_OK;
3824 }
3825 }
3826
3827 return setError(VBOX_E_OBJECT_NOT_FOUND,
3828 tr("Could not find a storage controller with instance number '%lu'"),
3829 aInstance);
3830}
3831
3832STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
3833{
3834 CheckComArgStrNotEmptyOrNull(aName);
3835
3836 AutoCaller autoCaller(this);
3837 CheckComRCReturnRC(autoCaller.rc());
3838
3839 AutoWriteLock alock(this);
3840
3841 HRESULT rc = checkStateDependency(MutableStateDep);
3842 CheckComRCReturnRC(rc);
3843
3844 ComObjPtr<StorageController> ctrl;
3845 rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3846 CheckComRCReturnRC(rc);
3847
3848 /* We can remove the controller only if there is no device attached. */
3849 /* check if the device slot is already busy */
3850 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
3851 it != mMediaData->mAttachments.end();
3852 ++it)
3853 {
3854 if (Bstr((*it)->controllerName()) == aName)
3855 return setError(VBOX_E_OBJECT_IN_USE,
3856 tr("Storage controller named '%ls' has still devices attached"),
3857 aName);
3858 }
3859
3860 /* We can remove it now. */
3861 mStorageControllers.backup();
3862
3863 ctrl->unshare();
3864
3865 mStorageControllers->remove (ctrl);
3866
3867 /* inform the direct session if any */
3868 alock.leave();
3869 onStorageControllerChange();
3870
3871 return S_OK;
3872}
3873
3874// public methods for internal purposes
3875/////////////////////////////////////////////////////////////////////////////
3876
3877/**
3878 * Saves the registry entry of this machine to the given configuration node.
3879 *
3880 * @param aEntryNode Node to save the registry entry to.
3881 *
3882 * @note locks this object for reading.
3883 */
3884HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
3885{
3886 AutoLimitedCaller autoCaller(this);
3887 AssertComRCReturnRC(autoCaller.rc());
3888
3889 AutoReadLock alock(this);
3890
3891 data.uuid = mData->mUuid;
3892 data.strSettingsFile = mData->m_strConfigFile;
3893
3894 return S_OK;
3895}
3896
3897/**
3898 * Calculates the absolute path of the given path taking the directory of the
3899 * machine settings file as the current directory.
3900 *
3901 * @param aPath Path to calculate the absolute path for.
3902 * @param aResult Where to put the result (used only on success, can be the
3903 * same Utf8Str instance as passed in @a aPath).
3904 * @return IPRT result.
3905 *
3906 * @note Locks this object for reading.
3907 */
3908int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3909{
3910 AutoCaller autoCaller(this);
3911 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3912
3913 AutoReadLock alock(this);
3914
3915 AssertReturn (!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
3916
3917 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
3918
3919 strSettingsDir.stripFilename();
3920 char folder[RTPATH_MAX];
3921 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3922 if (RT_SUCCESS(vrc))
3923 aResult = folder;
3924
3925 return vrc;
3926}
3927
3928/**
3929 * Tries to calculate the relative path of the given absolute path using the
3930 * directory of the machine settings file as the base directory.
3931 *
3932 * @param aPath Absolute path to calculate the relative path for.
3933 * @param aResult Where to put the result (used only when it's possible to
3934 * make a relative path from the given absolute path; otherwise
3935 * left untouched).
3936 *
3937 * @note Locks this object for reading.
3938 */
3939void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3940{
3941 AutoCaller autoCaller(this);
3942 AssertComRCReturn (autoCaller.rc(), (void) 0);
3943
3944 AutoReadLock alock(this);
3945
3946 AssertReturnVoid (!mData->m_strConfigFileFull.isEmpty());
3947
3948 Utf8Str settingsDir = mData->m_strConfigFileFull;
3949
3950 settingsDir.stripFilename();
3951 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3952 {
3953 /* when assigning, we create a separate Utf8Str instance because both
3954 * aPath and aResult can point to the same memory location when this
3955 * func is called (if we just do aResult = aPath, aResult will be freed
3956 * first, and since its the same as aPath, an attempt to copy garbage
3957 * will be made. */
3958 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3959 }
3960}
3961
3962/**
3963 * Returns the full path to the machine's log folder in the
3964 * \a aLogFolder argument.
3965 */
3966void Machine::getLogFolder (Utf8Str &aLogFolder)
3967{
3968 AutoCaller autoCaller(this);
3969 AssertComRCReturnVoid (autoCaller.rc());
3970
3971 AutoReadLock alock(this);
3972
3973 Utf8Str settingsDir;
3974 if (isInOwnDir (&settingsDir))
3975 {
3976 /* Log folder is <Machines>/<VM_Name>/Logs */
3977 aLogFolder = Utf8StrFmt ("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
3978 }
3979 else
3980 {
3981 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
3982 Assert (!mUserData->mSnapshotFolderFull.isEmpty());
3983 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
3984 RTPATH_DELIMITER);
3985 }
3986}
3987
3988/**
3989 * @note Locks this object for writing, calls the client process (outside the
3990 * lock).
3991 */
3992HRESULT Machine::openSession(IInternalSessionControl *aControl)
3993{
3994 LogFlowThisFuncEnter();
3995
3996 AssertReturn(aControl, E_FAIL);
3997
3998 AutoCaller autoCaller(this);
3999 CheckComRCReturnRC(autoCaller.rc());
4000
4001 AutoWriteLock alock(this);
4002
4003 if (!mData->mRegistered)
4004 return setError(E_UNEXPECTED,
4005 tr("The machine '%ls' is not registered"),
4006 mUserData->mName.raw());
4007
4008 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4009
4010 /* Hack: in case the session is closing and there is a progress object
4011 * which allows waiting for the session to be closed, take the opportunity
4012 * and do a limited wait (max. 1 second). This helps a lot when the system
4013 * is busy and thus session closing can take a little while. */
4014 if ( mData->mSession.mState == SessionState_Closing
4015 && mData->mSession.mProgress)
4016 {
4017 alock.leave();
4018 mData->mSession.mProgress->WaitForCompletion(1000);
4019 alock.enter();
4020 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4021 }
4022
4023 if (mData->mSession.mState == SessionState_Open ||
4024 mData->mSession.mState == SessionState_Closing)
4025 return setError(VBOX_E_INVALID_OBJECT_STATE,
4026 tr("A session for the machine '%ls' is currently open (or being closed)"),
4027 mUserData->mName.raw());
4028
4029 /* may not be busy */
4030 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
4031
4032 /* get the session PID */
4033 RTPROCESS pid = NIL_RTPROCESS;
4034 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
4035 aControl->GetPID((ULONG *) &pid);
4036 Assert(pid != NIL_RTPROCESS);
4037
4038 if (mData->mSession.mState == SessionState_Spawning)
4039 {
4040 /* This machine is awaiting for a spawning session to be opened, so
4041 * reject any other open attempts from processes other than one
4042 * started by #openRemoteSession(). */
4043
4044 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
4045 mData->mSession.mPid, mData->mSession.mPid));
4046 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
4047
4048 if (mData->mSession.mPid != pid)
4049 return setError(E_ACCESSDENIED,
4050 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
4051 "session with the machine named '%ls', while only a process "
4052 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
4053 pid, mUserData->mName.raw(), mData->mSession.mPid);
4054 }
4055
4056 /* create a SessionMachine object */
4057 ComObjPtr<SessionMachine> sessionMachine;
4058 sessionMachine.createObject();
4059 HRESULT rc = sessionMachine->init(this);
4060 AssertComRC(rc);
4061
4062 /* NOTE: doing return from this function after this point but
4063 * before the end is forbidden since it may call SessionMachine::uninit()
4064 * (through the ComObjPtr's destructor) which requests the VirtualBox write
4065 * lock while still holding the Machine lock in alock so that a deadlock
4066 * is possible due to the wrong lock order. */
4067
4068 if (SUCCEEDED(rc))
4069 {
4070#ifdef VBOX_WITH_RESOURCE_USAGE_API
4071 registerMetrics(mParent->performanceCollector(), this, pid);
4072#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4073
4074 /*
4075 * Set the session state to Spawning to protect against subsequent
4076 * attempts to open a session and to unregister the machine after
4077 * we leave the lock.
4078 */
4079 SessionState_T origState = mData->mSession.mState;
4080 mData->mSession.mState = SessionState_Spawning;
4081
4082 /*
4083 * Leave the lock before calling the client process -- it will call
4084 * Machine/SessionMachine methods. Leaving the lock here is quite safe
4085 * because the state is Spawning, so that openRemotesession() and
4086 * openExistingSession() calls will fail. This method, called before we
4087 * enter the lock again, will fail because of the wrong PID.
4088 *
4089 * Note that mData->mSession.mRemoteControls accessed outside
4090 * the lock may not be modified when state is Spawning, so it's safe.
4091 */
4092 alock.leave();
4093
4094 LogFlowThisFunc(("Calling AssignMachine()...\n"));
4095 rc = aControl->AssignMachine(sessionMachine);
4096 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
4097
4098 /* The failure may occur w/o any error info (from RPC), so provide one */
4099 if (FAILED(rc))
4100 setError(VBOX_E_VM_ERROR,
4101 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4102
4103 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
4104 {
4105 /* complete the remote session initialization */
4106
4107 /* get the console from the direct session */
4108 ComPtr<IConsole> console;
4109 rc = aControl->GetRemoteConsole(console.asOutParam());
4110 ComAssertComRC(rc);
4111
4112 if (SUCCEEDED(rc) && !console)
4113 {
4114 ComAssert(!!console);
4115 rc = E_FAIL;
4116 }
4117
4118 /* assign machine & console to the remote session */
4119 if (SUCCEEDED(rc))
4120 {
4121 /*
4122 * after openRemoteSession(), the first and the only
4123 * entry in remoteControls is that remote session
4124 */
4125 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4126 rc = mData->mSession.mRemoteControls.front()->
4127 AssignRemoteMachine(sessionMachine, console);
4128 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4129
4130 /* The failure may occur w/o any error info (from RPC), so provide one */
4131 if (FAILED(rc))
4132 setError(VBOX_E_VM_ERROR,
4133 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
4134 }
4135
4136 if (FAILED(rc))
4137 aControl->Uninitialize();
4138 }
4139
4140 /* enter the lock again */
4141 alock.enter();
4142
4143 /* Restore the session state */
4144 mData->mSession.mState = origState;
4145 }
4146
4147 /* finalize spawning anyway (this is why we don't return on errors above) */
4148 if (mData->mSession.mState == SessionState_Spawning)
4149 {
4150 /* Note that the progress object is finalized later */
4151
4152 /* We don't reset mSession.mPid here because it is necessary for
4153 * SessionMachine::uninit() to reap the child process later. */
4154
4155 if (FAILED(rc))
4156 {
4157 /* Close the remote session, remove the remote control from the list
4158 * and reset session state to Closed (@note keep the code in sync
4159 * with the relevant part in openSession()). */
4160
4161 Assert (mData->mSession.mRemoteControls.size() == 1);
4162 if (mData->mSession.mRemoteControls.size() == 1)
4163 {
4164 ErrorInfoKeeper eik;
4165 mData->mSession.mRemoteControls.front()->Uninitialize();
4166 }
4167
4168 mData->mSession.mRemoteControls.clear();
4169 mData->mSession.mState = SessionState_Closed;
4170 }
4171 }
4172 else
4173 {
4174 /* memorize PID of the directly opened session */
4175 if (SUCCEEDED(rc))
4176 mData->mSession.mPid = pid;
4177 }
4178
4179 if (SUCCEEDED(rc))
4180 {
4181 /* memorize the direct session control and cache IUnknown for it */
4182 mData->mSession.mDirectControl = aControl;
4183 mData->mSession.mState = SessionState_Open;
4184 /* associate the SessionMachine with this Machine */
4185 mData->mSession.mMachine = sessionMachine;
4186
4187 /* request an IUnknown pointer early from the remote party for later
4188 * identity checks (it will be internally cached within mDirectControl
4189 * at least on XPCOM) */
4190 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
4191 NOREF(unk);
4192 }
4193
4194 if (mData->mSession.mProgress)
4195 {
4196 /* finalize the progress after setting the state, for consistency */
4197 mData->mSession.mProgress->notifyComplete(rc);
4198 mData->mSession.mProgress.setNull();
4199 }
4200
4201 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
4202 * would break the lock order */
4203 alock.leave();
4204
4205 /* uninitialize the created session machine on failure */
4206 if (FAILED(rc))
4207 sessionMachine->uninit();
4208
4209 LogFlowThisFunc(("rc=%08X\n", rc));
4210 LogFlowThisFuncLeave();
4211 return rc;
4212}
4213
4214/**
4215 * @note Locks this object for writing, calls the client process
4216 * (inside the lock).
4217 */
4218HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
4219 IN_BSTR aType,
4220 IN_BSTR aEnvironment,
4221 Progress *aProgress)
4222{
4223 LogFlowThisFuncEnter();
4224
4225 AssertReturn(aControl, E_FAIL);
4226 AssertReturn(aProgress, E_FAIL);
4227
4228 AutoCaller autoCaller(this);
4229 CheckComRCReturnRC(autoCaller.rc());
4230
4231 AutoWriteLock alock(this);
4232
4233 if (!mData->mRegistered)
4234 return setError(E_UNEXPECTED,
4235 tr("The machine '%ls' is not registered"),
4236 mUserData->mName.raw());
4237
4238 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4239
4240 if (mData->mSession.mState == SessionState_Open ||
4241 mData->mSession.mState == SessionState_Spawning ||
4242 mData->mSession.mState == SessionState_Closing)
4243 return setError(VBOX_E_INVALID_OBJECT_STATE,
4244 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
4245 mUserData->mName.raw());
4246
4247 /* may not be busy */
4248 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
4249
4250 /* get the path to the executable */
4251 char szPath[RTPATH_MAX];
4252 RTPathAppPrivateArch(szPath, RTPATH_MAX);
4253 size_t sz = strlen(szPath);
4254 szPath[sz++] = RTPATH_DELIMITER;
4255 szPath[sz] = 0;
4256 char *cmd = szPath + sz;
4257 sz = RTPATH_MAX - sz;
4258
4259 int vrc = VINF_SUCCESS;
4260 RTPROCESS pid = NIL_RTPROCESS;
4261
4262 RTENV env = RTENV_DEFAULT;
4263
4264 if (aEnvironment != NULL && *aEnvironment)
4265 {
4266 char *newEnvStr = NULL;
4267
4268 do
4269 {
4270 /* clone the current environment */
4271 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
4272 AssertRCBreakStmt(vrc2, vrc = vrc2);
4273
4274 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
4275 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
4276
4277 /* put new variables to the environment
4278 * (ignore empty variable names here since RTEnv API
4279 * intentionally doesn't do that) */
4280 char *var = newEnvStr;
4281 for (char *p = newEnvStr; *p; ++p)
4282 {
4283 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
4284 {
4285 *p = '\0';
4286 if (*var)
4287 {
4288 char *val = strchr (var, '=');
4289 if (val)
4290 {
4291 *val++ = '\0';
4292 vrc2 = RTEnvSetEx (env, var, val);
4293 }
4294 else
4295 vrc2 = RTEnvUnsetEx (env, var);
4296 if (RT_FAILURE(vrc2))
4297 break;
4298 }
4299 var = p + 1;
4300 }
4301 }
4302 if (RT_SUCCESS(vrc2) && *var)
4303 vrc2 = RTEnvPutEx (env, var);
4304
4305 AssertRCBreakStmt (vrc2, vrc = vrc2);
4306 }
4307 while (0);
4308
4309 if (newEnvStr != NULL)
4310 RTStrFree(newEnvStr);
4311 }
4312
4313 Bstr type (aType);
4314
4315 /* Qt is default */
4316#ifdef VBOX_WITH_QTGUI
4317 if (type == "gui" || type == "GUI/Qt")
4318 {
4319# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
4320 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
4321# else
4322 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
4323# endif
4324 Assert (sz >= sizeof (VirtualBox_exe));
4325 strcpy (cmd, VirtualBox_exe);
4326
4327 Utf8Str idStr = mData->mUuid.toString();
4328# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
4329 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4330# else
4331 Utf8Str name = mUserData->mName;
4332 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4333# endif
4334 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4335 }
4336#else /* !VBOX_WITH_QTGUI */
4337 if (0)
4338 ;
4339#endif /* VBOX_WITH_QTGUI */
4340
4341 else
4342
4343#ifdef VBOX_WITH_VBOXSDL
4344 if (type == "sdl" || type == "GUI/SDL")
4345 {
4346 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
4347 Assert (sz >= sizeof (VBoxSDL_exe));
4348 strcpy (cmd, VBoxSDL_exe);
4349
4350 Utf8Str idStr = mData->mUuid.toString();
4351# ifdef RT_OS_WINDOWS
4352 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4353# else
4354 Utf8Str name = mUserData->mName;
4355 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4356# endif
4357 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4358 }
4359#else /* !VBOX_WITH_VBOXSDL */
4360 if (0)
4361 ;
4362#endif /* !VBOX_WITH_VBOXSDL */
4363
4364 else
4365
4366#ifdef VBOX_WITH_HEADLESS
4367 if ( type == "headless"
4368 || type == "capture"
4369#ifdef VBOX_WITH_VRDP
4370 || type == "vrdp"
4371#endif
4372 )
4373 {
4374 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
4375 Assert (sz >= sizeof (VBoxHeadless_exe));
4376 strcpy (cmd, VBoxHeadless_exe);
4377
4378 Utf8Str idStr = mData->mUuid.toString();
4379 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
4380# ifdef RT_OS_WINDOWS
4381 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
4382# else
4383 Utf8Str name = mUserData->mName;
4384 const char * args[] ={szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
4385# endif
4386#ifdef VBOX_WITH_VRDP
4387 if (type == "headless")
4388 {
4389 unsigned pos = RT_ELEMENTS(args) - 3;
4390 args[pos++] = "--vrdp";
4391 args[pos] = "off";
4392 }
4393#endif
4394 if (type == "capture")
4395 {
4396 unsigned pos = RT_ELEMENTS(args) - 3;
4397 args[pos] = "--capture";
4398 }
4399 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4400 }
4401#else /* !VBOX_WITH_HEADLESS */
4402 if (0)
4403 ;
4404#endif /* !VBOX_WITH_HEADLESS */
4405 else
4406 {
4407 RTEnvDestroy (env);
4408 return setError (E_INVALIDARG,
4409 tr ("Invalid session type: '%ls'"), aType);
4410 }
4411
4412 RTEnvDestroy (env);
4413
4414 if (RT_FAILURE(vrc))
4415 return setError (VBOX_E_IPRT_ERROR,
4416 tr ("Could not launch a process for the machine '%ls' (%Rrc)"),
4417 mUserData->mName.raw(), vrc);
4418
4419 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
4420
4421 /*
4422 * Note that we don't leave the lock here before calling the client,
4423 * because it doesn't need to call us back if called with a NULL argument.
4424 * Leaving the lock herer is dangerous because we didn't prepare the
4425 * launch data yet, but the client we've just started may happen to be
4426 * too fast and call openSession() that will fail (because of PID, etc.),
4427 * so that the Machine will never get out of the Spawning session state.
4428 */
4429
4430 /* inform the session that it will be a remote one */
4431 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
4432 HRESULT rc = aControl->AssignMachine (NULL);
4433 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
4434
4435 if (FAILED(rc))
4436 {
4437 /* restore the session state */
4438 mData->mSession.mState = SessionState_Closed;
4439 /* The failure may occur w/o any error info (from RPC), so provide one */
4440 return setError(VBOX_E_VM_ERROR,
4441 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4442 }
4443
4444 /* attach launch data to the machine */
4445 Assert (mData->mSession.mPid == NIL_RTPROCESS);
4446 mData->mSession.mRemoteControls.push_back (aControl);
4447 mData->mSession.mProgress = aProgress;
4448 mData->mSession.mPid = pid;
4449 mData->mSession.mState = SessionState_Spawning;
4450 mData->mSession.mType = type;
4451
4452 LogFlowThisFuncLeave();
4453 return S_OK;
4454}
4455
4456/**
4457 * @note Locks this object for writing, calls the client process
4458 * (outside the lock).
4459 */
4460HRESULT Machine::openExistingSession (IInternalSessionControl *aControl)
4461{
4462 LogFlowThisFuncEnter();
4463
4464 AssertReturn(aControl, E_FAIL);
4465
4466 AutoCaller autoCaller(this);
4467 CheckComRCReturnRC(autoCaller.rc());
4468
4469 AutoWriteLock alock(this);
4470
4471 if (!mData->mRegistered)
4472 return setError (E_UNEXPECTED,
4473 tr ("The machine '%ls' is not registered"), mUserData->mName.raw());
4474
4475 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4476
4477 if (mData->mSession.mState != SessionState_Open)
4478 return setError (VBOX_E_INVALID_SESSION_STATE,
4479 tr ("The machine '%ls' does not have an open session"),
4480 mUserData->mName.raw());
4481
4482 ComAssertRet (!mData->mSession.mDirectControl.isNull(), E_FAIL);
4483
4484 /*
4485 * Get the console from the direct session (note that we don't leave the
4486 * lock here because GetRemoteConsole must not call us back).
4487 */
4488 ComPtr<IConsole> console;
4489 HRESULT rc = mData->mSession.mDirectControl->
4490 GetRemoteConsole (console.asOutParam());
4491 if (FAILED (rc))
4492 {
4493 /* The failure may occur w/o any error info (from RPC), so provide one */
4494 return setError (VBOX_E_VM_ERROR,
4495 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
4496 }
4497
4498 ComAssertRet (!console.isNull(), E_FAIL);
4499
4500 ComObjPtr<SessionMachine> sessionMachine = mData->mSession.mMachine;
4501 AssertReturn(!sessionMachine.isNull(), E_FAIL);
4502
4503 /*
4504 * Leave the lock before calling the client process. It's safe here
4505 * since the only thing to do after we get the lock again is to add
4506 * the remote control to the list (which doesn't directly influence
4507 * anything).
4508 */
4509 alock.leave();
4510
4511 /* attach the remote session to the machine */
4512 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4513 rc = aControl->AssignRemoteMachine (sessionMachine, console);
4514 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4515
4516 /* The failure may occur w/o any error info (from RPC), so provide one */
4517 if (FAILED(rc))
4518 return setError(VBOX_E_VM_ERROR,
4519 tr("Failed to assign the machine to the session (%Rrc)"),
4520 rc);
4521
4522 alock.enter();
4523
4524 /* need to revalidate the state after entering the lock again */
4525 if (mData->mSession.mState != SessionState_Open)
4526 {
4527 aControl->Uninitialize();
4528
4529 return setError(VBOX_E_INVALID_SESSION_STATE,
4530 tr("The machine '%ls' does not have an open session"),
4531 mUserData->mName.raw());
4532 }
4533
4534 /* store the control in the list */
4535 mData->mSession.mRemoteControls.push_back (aControl);
4536
4537 LogFlowThisFuncLeave();
4538 return S_OK;
4539}
4540
4541/**
4542 * Returns @c true if the given machine has an open direct session and returns
4543 * the session machine instance and additional session data (on some platforms)
4544 * if so.
4545 *
4546 * Note that when the method returns @c false, the arguments remain unchanged.
4547 *
4548 * @param aMachine Session machine object.
4549 * @param aControl Direct session control object (optional).
4550 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
4551 *
4552 * @note locks this object for reading.
4553 */
4554#if defined (RT_OS_WINDOWS)
4555bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4556 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4557 HANDLE *aIPCSem /*= NULL*/,
4558 bool aAllowClosing /*= false*/)
4559#elif defined (RT_OS_OS2)
4560bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4561 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4562 HMTX *aIPCSem /*= NULL*/,
4563 bool aAllowClosing /*= false*/)
4564#else
4565bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4566 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4567 bool aAllowClosing /*= false*/)
4568#endif
4569{
4570 AutoLimitedCaller autoCaller(this);
4571 AssertComRCReturn (autoCaller.rc(), false);
4572
4573 /* just return false for inaccessible machines */
4574 if (autoCaller.state() != Ready)
4575 return false;
4576
4577 AutoReadLock alock(this);
4578
4579 if (mData->mSession.mState == SessionState_Open ||
4580 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
4581 {
4582 AssertReturn(!mData->mSession.mMachine.isNull(), false);
4583
4584 aMachine = mData->mSession.mMachine;
4585
4586 if (aControl != NULL)
4587 *aControl = mData->mSession.mDirectControl;
4588
4589#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4590 /* Additional session data */
4591 if (aIPCSem != NULL)
4592 *aIPCSem = aMachine->mIPCSem;
4593#endif
4594 return true;
4595 }
4596
4597 return false;
4598}
4599
4600/**
4601 * Returns @c true if the given machine has an spawning direct session and
4602 * returns and additional session data (on some platforms) if so.
4603 *
4604 * Note that when the method returns @c false, the arguments remain unchanged.
4605 *
4606 * @param aPID PID of the spawned direct session process.
4607 *
4608 * @note locks this object for reading.
4609 */
4610#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4611bool Machine::isSessionSpawning (RTPROCESS *aPID /*= NULL*/)
4612#else
4613bool Machine::isSessionSpawning()
4614#endif
4615{
4616 AutoLimitedCaller autoCaller(this);
4617 AssertComRCReturn (autoCaller.rc(), false);
4618
4619 /* just return false for inaccessible machines */
4620 if (autoCaller.state() != Ready)
4621 return false;
4622
4623 AutoReadLock alock(this);
4624
4625 if (mData->mSession.mState == SessionState_Spawning)
4626 {
4627#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4628 /* Additional session data */
4629 if (aPID != NULL)
4630 {
4631 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
4632 *aPID = mData->mSession.mPid;
4633 }
4634#endif
4635 return true;
4636 }
4637
4638 return false;
4639}
4640
4641/**
4642 * Called from the client watcher thread to check for unexpected client process
4643 * death during Session_Spawning state (e.g. before it successfully opened a
4644 * direct session).
4645 *
4646 * On Win32 and on OS/2, this method is called only when we've got the
4647 * direct client's process termination notification, so it always returns @c
4648 * true.
4649 *
4650 * On other platforms, this method returns @c true if the client process is
4651 * terminated and @c false if it's still alive.
4652 *
4653 * @note Locks this object for writing.
4654 */
4655bool Machine::checkForSpawnFailure()
4656{
4657 AutoCaller autoCaller(this);
4658 if (!autoCaller.isOk())
4659 {
4660 /* nothing to do */
4661 LogFlowThisFunc(("Already uninitialized!\n"));
4662 return true;
4663 }
4664
4665 /* VirtualBox::addProcessToReap() needs a write lock */
4666 AutoMultiWriteLock2 alock(mParent, this);
4667
4668 if (mData->mSession.mState != SessionState_Spawning)
4669 {
4670 /* nothing to do */
4671 LogFlowThisFunc(("Not spawning any more!\n"));
4672 return true;
4673 }
4674
4675 HRESULT rc = S_OK;
4676
4677#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
4678
4679 /* the process was already unexpectedly terminated, we just need to set an
4680 * error and finalize session spawning */
4681 rc = setError(E_FAIL,
4682 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4683 name().raw());
4684#else
4685
4686 /* PID not yet initialized, skip check. */
4687 if (mData->mSession.mPid == NIL_RTPROCESS)
4688 return false;
4689
4690 RTPROCSTATUS status;
4691 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
4692 &status);
4693
4694 if (vrc != VERR_PROCESS_RUNNING)
4695 rc = setError(E_FAIL,
4696 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4697 name().raw());
4698#endif
4699
4700 if (FAILED(rc))
4701 {
4702 /* Close the remote session, remove the remote control from the list
4703 * and reset session state to Closed (@note keep the code in sync with
4704 * the relevant part in checkForSpawnFailure()). */
4705
4706 Assert(mData->mSession.mRemoteControls.size() == 1);
4707 if (mData->mSession.mRemoteControls.size() == 1)
4708 {
4709 ErrorInfoKeeper eik;
4710 mData->mSession.mRemoteControls.front()->Uninitialize();
4711 }
4712
4713 mData->mSession.mRemoteControls.clear();
4714 mData->mSession.mState = SessionState_Closed;
4715
4716 /* finalize the progress after setting the state, for consistency */
4717 if (!mData->mSession.mProgress.isNull())
4718 {
4719 mData->mSession.mProgress->notifyComplete(rc);
4720 mData->mSession.mProgress.setNull();
4721 }
4722
4723 mParent->addProcessToReap(mData->mSession.mPid);
4724 mData->mSession.mPid = NIL_RTPROCESS;
4725
4726 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
4727 return true;
4728 }
4729
4730 return false;
4731}
4732
4733/**
4734 * Checks that the registered flag of the machine can be set according to
4735 * the argument and sets it. On success, commits and saves all settings.
4736 *
4737 * @note When this machine is inaccessible, the only valid value for \a
4738 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
4739 * inaccessible machines are not currently supported. Note that unregistering
4740 * an inaccessible machine will \b uninitialize this machine object. Therefore,
4741 * the caller must make sure there are no active Machine::addCaller() calls
4742 * on the current thread because this will block Machine::uninit().
4743 *
4744 * @note Must be called from mParent's write lock. Locks this object and
4745 * children for writing.
4746 */
4747HRESULT Machine::trySetRegistered (BOOL aRegistered)
4748{
4749 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
4750
4751 AutoLimitedCaller autoCaller(this);
4752 AssertComRCReturnRC(autoCaller.rc());
4753
4754 AutoWriteLock alock(this);
4755
4756 /* wait for state dependants to drop to zero */
4757 ensureNoStateDependencies();
4758
4759 ComAssertRet (mData->mRegistered != aRegistered, E_FAIL);
4760
4761 if (!mData->mAccessible)
4762 {
4763 /* A special case: the machine is not accessible. */
4764
4765 /* inaccessible machines can only be unregistered */
4766 AssertReturn(!aRegistered, E_FAIL);
4767
4768 /* Uninitialize ourselves here because currently there may be no
4769 * unregistered that are inaccessible (this state combination is not
4770 * supported). Note releasing the caller and leaving the lock before
4771 * calling uninit() */
4772
4773 alock.leave();
4774 autoCaller.release();
4775
4776 uninit();
4777
4778 return S_OK;
4779 }
4780
4781 AssertReturn(autoCaller.state() == Ready, E_FAIL);
4782
4783 if (aRegistered)
4784 {
4785 if (mData->mRegistered)
4786 return setError(VBOX_E_INVALID_OBJECT_STATE,
4787 tr("The machine '%ls' with UUID {%s} is already registered"),
4788 mUserData->mName.raw(),
4789 mData->mUuid.toString().raw());
4790 }
4791 else
4792 {
4793 if (mData->mMachineState == MachineState_Saved)
4794 return setError(VBOX_E_INVALID_VM_STATE,
4795 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
4796 mUserData->mName.raw());
4797
4798 size_t snapshotCount = 0;
4799 if (mData->mFirstSnapshot)
4800 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4801 if (snapshotCount)
4802 return setError(VBOX_E_INVALID_OBJECT_STATE,
4803 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
4804 mUserData->mName.raw(), snapshotCount);
4805
4806 if (mData->mSession.mState != SessionState_Closed)
4807 return setError(VBOX_E_INVALID_OBJECT_STATE,
4808 tr("Cannot unregister the machine '%ls' because it has an open session"),
4809 mUserData->mName.raw());
4810
4811 if (mMediaData->mAttachments.size() != 0)
4812 return setError(VBOX_E_INVALID_OBJECT_STATE,
4813 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
4814 mUserData->mName.raw(),
4815 mMediaData->mAttachments.size());
4816
4817 /* Note that we do not prevent unregistration of a DVD or Floppy image
4818 * is attached: as opposed to hard disks detaching such an image
4819 * implicitly in this method (which we will do below) won't have any
4820 * side effects (like detached orphan base and diff hard disks etc).*/
4821 }
4822
4823 HRESULT rc = S_OK;
4824
4825 /* Ensure the settings are saved. If we are going to be registered and
4826 * isConfigLocked() is FALSE then it means that no config file exists yet,
4827 * so create it by calling saveSettings() too. */
4828 if ( isModified()
4829 || (aRegistered && !mData->m_pMachineConfigFile->fileExists())
4830 )
4831 {
4832 rc = saveSettings();
4833 CheckComRCReturnRC(rc);
4834 }
4835
4836 /* more config checking goes here */
4837
4838 if (SUCCEEDED(rc))
4839 {
4840 /* we may have had implicit modifications we want to fix on success */
4841 commit();
4842
4843 mData->mRegistered = aRegistered;
4844 }
4845 else
4846 {
4847 /* we may have had implicit modifications we want to cancel on failure*/
4848 rollback (false /* aNotify */);
4849 }
4850
4851 return rc;
4852}
4853
4854/**
4855 * Increases the number of objects dependent on the machine state or on the
4856 * registered state. Guarantees that these two states will not change at least
4857 * until #releaseStateDependency() is called.
4858 *
4859 * Depending on the @a aDepType value, additional state checks may be made.
4860 * These checks will set extended error info on failure. See
4861 * #checkStateDependency() for more info.
4862 *
4863 * If this method returns a failure, the dependency is not added and the caller
4864 * is not allowed to rely on any particular machine state or registration state
4865 * value and may return the failed result code to the upper level.
4866 *
4867 * @param aDepType Dependency type to add.
4868 * @param aState Current machine state (NULL if not interested).
4869 * @param aRegistered Current registered state (NULL if not interested).
4870 *
4871 * @note Locks this object for writing.
4872 */
4873HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
4874 MachineState_T *aState /* = NULL */,
4875 BOOL *aRegistered /* = NULL */)
4876{
4877 AutoCaller autoCaller(this);
4878 AssertComRCReturnRC(autoCaller.rc());
4879
4880 AutoWriteLock alock(this);
4881
4882 HRESULT rc = checkStateDependency(aDepType);
4883 CheckComRCReturnRC(rc);
4884
4885 {
4886 if (mData->mMachineStateChangePending != 0)
4887 {
4888 /* ensureNoStateDependencies() is waiting for state dependencies to
4889 * drop to zero so don't add more. It may make sense to wait a bit
4890 * and retry before reporting an error (since the pending state
4891 * transition should be really quick) but let's just assert for
4892 * now to see if it ever happens on practice. */
4893
4894 AssertFailed();
4895
4896 return setError(E_ACCESSDENIED,
4897 tr("Machine state change is in progress. Please retry the operation later."));
4898 }
4899
4900 ++mData->mMachineStateDeps;
4901 Assert (mData->mMachineStateDeps != 0 /* overflow */);
4902 }
4903
4904 if (aState)
4905 *aState = mData->mMachineState;
4906 if (aRegistered)
4907 *aRegistered = mData->mRegistered;
4908
4909 return S_OK;
4910}
4911
4912/**
4913 * Decreases the number of objects dependent on the machine state.
4914 * Must always complete the #addStateDependency() call after the state
4915 * dependency is no more necessary.
4916 */
4917void Machine::releaseStateDependency()
4918{
4919 AutoCaller autoCaller(this);
4920 AssertComRCReturnVoid (autoCaller.rc());
4921
4922 AutoWriteLock alock(this);
4923
4924 AssertReturnVoid (mData->mMachineStateDeps != 0
4925 /* releaseStateDependency() w/o addStateDependency()? */);
4926 -- mData->mMachineStateDeps;
4927
4928 if (mData->mMachineStateDeps == 0)
4929 {
4930 /* inform ensureNoStateDependencies() that there are no more deps */
4931 if (mData->mMachineStateChangePending != 0)
4932 {
4933 Assert (mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
4934 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
4935 }
4936 }
4937}
4938
4939// protected methods
4940/////////////////////////////////////////////////////////////////////////////
4941
4942/**
4943 * Performs machine state checks based on the @a aDepType value. If a check
4944 * fails, this method will set extended error info, otherwise it will return
4945 * S_OK. It is supposed, that on failure, the caller will immedieately return
4946 * the return value of this method to the upper level.
4947 *
4948 * When @a aDepType is AnyStateDep, this method always returns S_OK.
4949 *
4950 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
4951 * current state of this machine object allows to change settings of the
4952 * machine (i.e. the machine is not registered, or registered but not running
4953 * and not saved). It is useful to call this method from Machine setters
4954 * before performing any change.
4955 *
4956 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
4957 * as for MutableStateDep except that if the machine is saved, S_OK is also
4958 * returned. This is useful in setters which allow changing machine
4959 * properties when it is in the saved state.
4960 *
4961 * @param aDepType Dependency type to check.
4962 *
4963 * @note Non Machine based classes should use #addStateDependency() and
4964 * #releaseStateDependency() methods or the smart AutoStateDependency
4965 * template.
4966 *
4967 * @note This method must be called from under this object's read or write
4968 * lock.
4969 */
4970HRESULT Machine::checkStateDependency(StateDependency aDepType)
4971{
4972 switch (aDepType)
4973 {
4974 case AnyStateDep:
4975 {
4976 break;
4977 }
4978 case MutableStateDep:
4979 {
4980 if ( mData->mRegistered
4981 && ( mType != IsSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
4982 || ( mData->mMachineState != MachineState_Paused
4983 && mData->mMachineState != MachineState_Running
4984 && mData->mMachineState != MachineState_Aborted
4985 && mData->mMachineState != MachineState_Teleported
4986 && mData->mMachineState != MachineState_PoweredOff
4987 )
4988 )
4989 )
4990 return setError(VBOX_E_INVALID_VM_STATE,
4991 tr("The machine is not mutable (state is %s)"),
4992 Global::stringifyMachineState(mData->mMachineState));
4993 break;
4994 }
4995 case MutableOrSavedStateDep:
4996 {
4997 if ( mData->mRegistered
4998 && ( mType != IsSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
4999 || ( mData->mMachineState != MachineState_Paused
5000 && mData->mMachineState != MachineState_Running
5001 && mData->mMachineState != MachineState_Aborted
5002 && mData->mMachineState != MachineState_Teleported
5003 && mData->mMachineState != MachineState_Saved
5004 && mData->mMachineState != MachineState_PoweredOff
5005 )
5006 )
5007 )
5008 return setError(VBOX_E_INVALID_VM_STATE,
5009 tr("The machine is not mutable (state is %s)"),
5010 Global::stringifyMachineState(mData->mMachineState));
5011 break;
5012 }
5013 }
5014
5015 return S_OK;
5016}
5017
5018/**
5019 * Helper to initialize all associated child objects and allocate data
5020 * structures.
5021 *
5022 * This method must be called as a part of the object's initialization procedure
5023 * (usually done in the #init() method).
5024 *
5025 * @note Must be called only from #init() or from #registeredInit().
5026 */
5027HRESULT Machine::initDataAndChildObjects()
5028{
5029 AutoCaller autoCaller(this);
5030 AssertComRCReturnRC(autoCaller.rc());
5031 AssertComRCReturn (autoCaller.state() == InInit ||
5032 autoCaller.state() == Limited, E_FAIL);
5033
5034 AssertReturn(!mData->mAccessible, E_FAIL);
5035
5036 /* allocate data structures */
5037 mSSData.allocate();
5038 mUserData.allocate();
5039 mHWData.allocate();
5040 mMediaData.allocate();
5041 mStorageControllers.allocate();
5042
5043 /* initialize mOSTypeId */
5044 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
5045
5046 /* create associated BIOS settings object */
5047 unconst(mBIOSSettings).createObject();
5048 mBIOSSettings->init (this);
5049
5050#ifdef VBOX_WITH_VRDP
5051 /* create an associated VRDPServer object (default is disabled) */
5052 unconst(mVRDPServer).createObject();
5053 mVRDPServer->init (this);
5054#endif
5055
5056 /* create associated serial port objects */
5057 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5058 {
5059 unconst(mSerialPorts [slot]).createObject();
5060 mSerialPorts [slot]->init (this, slot);
5061 }
5062
5063 /* create associated parallel port objects */
5064 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5065 {
5066 unconst(mParallelPorts [slot]).createObject();
5067 mParallelPorts [slot]->init (this, slot);
5068 }
5069
5070 /* create the audio adapter object (always present, default is disabled) */
5071 unconst(mAudioAdapter).createObject();
5072 mAudioAdapter->init (this);
5073
5074 /* create the USB controller object (always present, default is disabled) */
5075 unconst(mUSBController).createObject();
5076 mUSBController->init (this);
5077
5078 /* create associated network adapter objects */
5079 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5080 {
5081 unconst(mNetworkAdapters [slot]).createObject();
5082 mNetworkAdapters [slot]->init (this, slot);
5083 }
5084
5085 return S_OK;
5086}
5087
5088/**
5089 * Helper to uninitialize all associated child objects and to free all data
5090 * structures.
5091 *
5092 * This method must be called as a part of the object's uninitialization
5093 * procedure (usually done in the #uninit() method).
5094 *
5095 * @note Must be called only from #uninit() or from #registeredInit().
5096 */
5097void Machine::uninitDataAndChildObjects()
5098{
5099 AutoCaller autoCaller(this);
5100 AssertComRCReturnVoid (autoCaller.rc());
5101 AssertComRCReturnVoid (autoCaller.state() == InUninit ||
5102 autoCaller.state() == Limited);
5103
5104 /* uninit all children using addDependentChild()/removeDependentChild()
5105 * in their init()/uninit() methods */
5106 uninitDependentChildren();
5107
5108 /* tell all our other child objects we've been uninitialized */
5109
5110 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5111 {
5112 if (mNetworkAdapters [slot])
5113 {
5114 mNetworkAdapters [slot]->uninit();
5115 unconst(mNetworkAdapters [slot]).setNull();
5116 }
5117 }
5118
5119 if (mUSBController)
5120 {
5121 mUSBController->uninit();
5122 unconst(mUSBController).setNull();
5123 }
5124
5125 if (mAudioAdapter)
5126 {
5127 mAudioAdapter->uninit();
5128 unconst(mAudioAdapter).setNull();
5129 }
5130
5131 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5132 {
5133 if (mParallelPorts [slot])
5134 {
5135 mParallelPorts [slot]->uninit();
5136 unconst(mParallelPorts [slot]).setNull();
5137 }
5138 }
5139
5140 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5141 {
5142 if (mSerialPorts [slot])
5143 {
5144 mSerialPorts [slot]->uninit();
5145 unconst(mSerialPorts [slot]).setNull();
5146 }
5147 }
5148
5149#ifdef VBOX_WITH_VRDP
5150 if (mVRDPServer)
5151 {
5152 mVRDPServer->uninit();
5153 unconst(mVRDPServer).setNull();
5154 }
5155#endif
5156
5157 if (mBIOSSettings)
5158 {
5159 mBIOSSettings->uninit();
5160 unconst(mBIOSSettings).setNull();
5161 }
5162
5163 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
5164 * instance is uninitialized; SessionMachine instances refer to real
5165 * Machine hard disks). This is necessary for a clean re-initialization of
5166 * the VM after successfully re-checking the accessibility state. Note
5167 * that in case of normal Machine or SnapshotMachine uninitialization (as
5168 * a result of unregistering or discarding the snapshot), outdated hard
5169 * disk attachments will already be uninitialized and deleted, so this
5170 * code will not affect them. */
5171 if (!!mMediaData && (mType == IsMachine || mType == IsSnapshotMachine))
5172 {
5173 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5174 it != mMediaData->mAttachments.end();
5175 ++it)
5176 {
5177 ComObjPtr<Medium> hd = (*it)->medium();
5178 if (hd.isNull() || (*it)->type() != DeviceType_HardDisk)
5179 continue;
5180 HRESULT rc = hd->detachFrom(mData->mUuid, snapshotId());
5181 AssertComRC (rc);
5182 }
5183 }
5184
5185 if (mType == IsMachine)
5186 {
5187 /* reset some important fields of mData */
5188 mData->mCurrentSnapshot.setNull();
5189 mData->mFirstSnapshot.setNull();
5190 }
5191
5192 /* free data structures (the essential mData structure is not freed here
5193 * since it may be still in use) */
5194 mMediaData.free();
5195 mStorageControllers.free();
5196 mHWData.free();
5197 mUserData.free();
5198 mSSData.free();
5199}
5200
5201/**
5202 * Makes sure that there are no machine state dependants. If necessary, waits
5203 * for the number of dependants to drop to zero.
5204 *
5205 * Make sure this method is called from under this object's write lock to
5206 * guarantee that no new dependants may be added when this method returns
5207 * control to the caller.
5208 *
5209 * @note Locks this object for writing. The lock will be released while waiting
5210 * (if necessary).
5211 *
5212 * @warning To be used only in methods that change the machine state!
5213 */
5214void Machine::ensureNoStateDependencies()
5215{
5216 AssertReturnVoid (isWriteLockOnCurrentThread());
5217
5218 AutoWriteLock alock(this);
5219
5220 /* Wait for all state dependants if necessary */
5221 if (mData->mMachineStateDeps != 0)
5222 {
5223 /* lazy semaphore creation */
5224 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
5225 RTSemEventMultiCreate (&mData->mMachineStateDepsSem);
5226
5227 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
5228 mData->mMachineStateDeps));
5229
5230 ++mData->mMachineStateChangePending;
5231
5232 /* reset the semaphore before waiting, the last dependant will signal
5233 * it */
5234 RTSemEventMultiReset (mData->mMachineStateDepsSem);
5235
5236 alock.leave();
5237
5238 RTSemEventMultiWait (mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
5239
5240 alock.enter();
5241
5242 -- mData->mMachineStateChangePending;
5243 }
5244}
5245
5246/**
5247 * Changes the machine state and informs callbacks.
5248 *
5249 * This method is not intended to fail so it either returns S_OK or asserts (and
5250 * returns a failure).
5251 *
5252 * @note Locks this object for writing.
5253 */
5254HRESULT Machine::setMachineState (MachineState_T aMachineState)
5255{
5256 LogFlowThisFuncEnter();
5257 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
5258
5259 AutoCaller autoCaller(this);
5260 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
5261
5262 AutoWriteLock alock(this);
5263
5264 /* wait for state dependants to drop to zero */
5265 ensureNoStateDependencies();
5266
5267 if (mData->mMachineState != aMachineState)
5268 {
5269 mData->mMachineState = aMachineState;
5270
5271 RTTimeNow (&mData->mLastStateChange);
5272
5273 mParent->onMachineStateChange(mData->mUuid, aMachineState);
5274 }
5275
5276 LogFlowThisFuncLeave();
5277 return S_OK;
5278}
5279
5280/**
5281 * Searches for a shared folder with the given logical name
5282 * in the collection of shared folders.
5283 *
5284 * @param aName logical name of the shared folder
5285 * @param aSharedFolder where to return the found object
5286 * @param aSetError whether to set the error info if the folder is
5287 * not found
5288 * @return
5289 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
5290 *
5291 * @note
5292 * must be called from under the object's lock!
5293 */
5294HRESULT Machine::findSharedFolder (CBSTR aName,
5295 ComObjPtr<SharedFolder> &aSharedFolder,
5296 bool aSetError /* = false */)
5297{
5298 bool found = false;
5299 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
5300 !found && it != mHWData->mSharedFolders.end();
5301 ++it)
5302 {
5303 AutoWriteLock alock(*it);
5304 found = (*it)->name() == aName;
5305 if (found)
5306 aSharedFolder = *it;
5307 }
5308
5309 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
5310
5311 if (aSetError && !found)
5312 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
5313
5314 return rc;
5315}
5316
5317/**
5318 * Loads all the VM settings by walking down the <Machine> node.
5319 *
5320 * @param aRegistered true when the machine is being loaded on VirtualBox
5321 * startup
5322 *
5323 * @note This method is intended to be called only from init(), so it assumes
5324 * all machine data fields have appropriate default values when it is called.
5325 *
5326 * @note Doesn't lock any objects.
5327 */
5328HRESULT Machine::loadSettings(bool aRegistered)
5329{
5330 LogFlowThisFuncEnter();
5331 AssertReturn(mType == IsMachine, E_FAIL);
5332
5333 AutoCaller autoCaller(this);
5334 AssertReturn(autoCaller.state() == InInit, E_FAIL);
5335
5336 HRESULT rc = S_OK;
5337
5338 try
5339 {
5340 Assert(mData->m_pMachineConfigFile == NULL);
5341
5342 // load and parse machine XML; this will throw on XML or logic errors
5343 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
5344
5345 /* If the stored UUID is not empty, it means the registered machine
5346 * is being loaded. Compare the loaded UUID with the stored one taken
5347 * from the global registry. */
5348 if (!mData->mUuid.isEmpty())
5349 {
5350 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
5351 {
5352 throw setError(E_FAIL,
5353 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
5354 mData->m_pMachineConfigFile->uuid.raw(),
5355 mData->m_strConfigFileFull.raw(),
5356 mData->mUuid.toString().raw(),
5357 mParent->settingsFilePath().raw());
5358 }
5359 }
5360 else
5361 unconst (mData->mUuid) = mData->m_pMachineConfigFile->uuid;
5362
5363 /* name (required) */
5364 mUserData->mName = mData->m_pMachineConfigFile->strName;
5365
5366 /* nameSync (optional, default is true) */
5367 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
5368
5369 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
5370
5371 // guest OS type
5372 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
5373 /* look up the object by Id to check it is valid */
5374 ComPtr<IGuestOSType> guestOSType;
5375 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
5376 guestOSType.asOutParam());
5377 CheckComRCThrowRC(rc);
5378
5379 // stateFile (optional)
5380 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
5381 mSSData->mStateFilePath.setNull();
5382 else
5383 {
5384 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
5385 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
5386 if (RT_FAILURE(vrc))
5387 throw setError(E_FAIL,
5388 tr("Invalid saved state file path '%s' (%Rrc)"),
5389 mData->m_pMachineConfigFile->strStateFile.raw(),
5390 vrc);
5391 mSSData->mStateFilePath = stateFilePathFull;
5392 }
5393
5394 /* snapshotFolder (optional) */
5395 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
5396 CheckComRCThrowRC(rc);
5397
5398 /* currentStateModified (optional, default is true) */
5399 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
5400
5401 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
5402
5403 /* teleportation */
5404 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
5405 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
5406 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
5407 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
5408
5409 /*
5410 * note: all mUserData members must be assigned prior this point because
5411 * we need to commit changes in order to let mUserData be shared by all
5412 * snapshot machine instances.
5413 */
5414 mUserData.commitCopy();
5415
5416 /* Snapshot node (optional) */
5417 if (mData->m_pMachineConfigFile->llFirstSnapshot.size())
5418 {
5419 // there can only be one root snapshot
5420 Assert(mData->m_pMachineConfigFile->llFirstSnapshot.size() == 1);
5421
5422 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
5423
5424 rc = loadSnapshot(snap,
5425 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
5426 NULL); // no parent == first snapshot
5427 CheckComRCThrowRC(rc);
5428 }
5429
5430 /* Hardware node (required) */
5431 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
5432 CheckComRCThrowRC(rc);
5433
5434 /* Load storage controllers */
5435 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
5436 CheckComRCThrowRC(rc);
5437
5438 /*
5439 * NOTE: the assignment below must be the last thing to do,
5440 * otherwise it will be not possible to change the settings
5441 * somewehere in the code above because all setters will be
5442 * blocked by checkStateDependency(MutableStateDep).
5443 */
5444
5445 /* set the machine state to Aborted or Saved when appropriate */
5446 if (mData->m_pMachineConfigFile->fAborted)
5447 {
5448 Assert(!mSSData->mStateFilePath.isEmpty());
5449 mSSData->mStateFilePath.setNull();
5450
5451 /* no need to use setMachineState() during init() */
5452 mData->mMachineState = MachineState_Aborted;
5453 }
5454 else if (!mSSData->mStateFilePath.isEmpty())
5455 {
5456 /* no need to use setMachineState() during init() */
5457 mData->mMachineState = MachineState_Saved;
5458 }
5459 }
5460 catch (HRESULT err)
5461 {
5462 /* we assume that error info is set by the thrower */
5463 rc = err;
5464 }
5465 catch (...)
5466 {
5467 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
5468 }
5469
5470 LogFlowThisFuncLeave();
5471 return rc;
5472}
5473
5474/**
5475 * Recursively loads all snapshots starting from the given.
5476 *
5477 * @param aNode <Snapshot> node.
5478 * @param aCurSnapshotId Current snapshot ID from the settings file.
5479 * @param aParentSnapshot Parent snapshot.
5480 */
5481HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
5482 const Guid &aCurSnapshotId,
5483 Snapshot *aParentSnapshot)
5484{
5485 AssertReturn (mType == IsMachine, E_FAIL);
5486
5487 HRESULT rc = S_OK;
5488
5489 Utf8Str strStateFile;
5490 if (!data.strStateFile.isEmpty())
5491 {
5492 /* optional */
5493 strStateFile = data.strStateFile;
5494 int vrc = calculateFullPath(strStateFile, strStateFile);
5495 if (RT_FAILURE(vrc))
5496 return setError(E_FAIL,
5497 tr("Invalid saved state file path '%s' (%Rrc)"),
5498 strStateFile.raw(),
5499 vrc);
5500 }
5501
5502 /* create a snapshot machine object */
5503 ComObjPtr<SnapshotMachine> pSnapshotMachine;
5504 pSnapshotMachine.createObject();
5505 rc = pSnapshotMachine->init(this,
5506 data.hardware,
5507 data.storage,
5508 data.uuid,
5509 strStateFile);
5510 CheckComRCReturnRC (rc);
5511
5512 /* create a snapshot object */
5513 ComObjPtr<Snapshot> pSnapshot;
5514 pSnapshot.createObject();
5515 /* initialize the snapshot */
5516 rc = pSnapshot->init(mParent, // VirtualBox object
5517 data.uuid,
5518 data.strName,
5519 data.strDescription,
5520 data.timestamp,
5521 pSnapshotMachine,
5522 aParentSnapshot);
5523 CheckComRCReturnRC (rc);
5524
5525 /* memorize the first snapshot if necessary */
5526 if (!mData->mFirstSnapshot)
5527 mData->mFirstSnapshot = pSnapshot;
5528
5529 /* memorize the current snapshot when appropriate */
5530 if ( !mData->mCurrentSnapshot
5531 && pSnapshot->getId() == aCurSnapshotId
5532 )
5533 mData->mCurrentSnapshot = pSnapshot;
5534
5535 // now create the children
5536 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
5537 it != data.llChildSnapshots.end();
5538 ++it)
5539 {
5540 const settings::Snapshot &childData = *it;
5541 // recurse
5542 rc = loadSnapshot(childData,
5543 aCurSnapshotId,
5544 pSnapshot); // parent = the one we created above
5545 CheckComRCBreakRC(rc);
5546 }
5547
5548 return rc;
5549}
5550
5551/**
5552 * @param aNode <Hardware> node.
5553 */
5554HRESULT Machine::loadHardware(const settings::Hardware &data)
5555{
5556 AssertReturn(mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5557
5558 HRESULT rc = S_OK;
5559
5560 try
5561 {
5562 /* The hardware version attribute (optional). */
5563 mHWData->mHWVersion = data.strVersion;
5564 mHWData->mHardwareUUID = data.uuid;
5565
5566 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
5567 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
5568 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
5569 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
5570 mHWData->mPAEEnabled = data.fPAE;
5571 mHWData->mSyntheticCpu = data.fSyntheticCpu;
5572
5573 mHWData->mCPUCount = data.cCPUs;
5574
5575 // cpuid leafs
5576 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
5577 it != data.llCpuIdLeafs.end();
5578 ++it)
5579 {
5580 const settings::CpuIdLeaf &leaf = *it;
5581
5582 switch (leaf.ulId)
5583 {
5584 case 0x0:
5585 case 0x1:
5586 case 0x2:
5587 case 0x3:
5588 case 0x4:
5589 case 0x5:
5590 case 0x6:
5591 case 0x7:
5592 case 0x8:
5593 case 0x9:
5594 case 0xA:
5595 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
5596 break;
5597
5598 case 0x80000000:
5599 case 0x80000001:
5600 case 0x80000002:
5601 case 0x80000003:
5602 case 0x80000004:
5603 case 0x80000005:
5604 case 0x80000006:
5605 case 0x80000007:
5606 case 0x80000008:
5607 case 0x80000009:
5608 case 0x8000000A:
5609 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
5610 break;
5611
5612 default:
5613 /* just ignore */
5614 break;
5615 }
5616 }
5617
5618 mHWData->mMemorySize = data.ulMemorySizeMB;
5619
5620 // boot order
5621 for (size_t i = 0;
5622 i < RT_ELEMENTS(mHWData->mBootOrder);
5623 i++)
5624 {
5625 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
5626 if (it == data.mapBootOrder.end())
5627 mHWData->mBootOrder[i] = DeviceType_Null;
5628 else
5629 mHWData->mBootOrder[i] = it->second;
5630 }
5631
5632 mHWData->mVRAMSize = data.ulVRAMSizeMB;
5633 mHWData->mMonitorCount = data.cMonitors;
5634 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
5635 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
5636 mHWData->mFirmwareType = data.firmwareType;
5637
5638#ifdef VBOX_WITH_VRDP
5639 /* RemoteDisplay */
5640 rc = mVRDPServer->loadSettings(data.vrdpSettings);
5641 CheckComRCReturnRC (rc);
5642#endif
5643
5644 /* BIOS */
5645 rc = mBIOSSettings->loadSettings(data.biosSettings);
5646 CheckComRCReturnRC (rc);
5647
5648 /* USB Controller */
5649 rc = mUSBController->loadSettings(data.usbController);
5650 CheckComRCReturnRC (rc);
5651
5652 // network adapters
5653 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
5654 it != data.llNetworkAdapters.end();
5655 ++it)
5656 {
5657 const settings::NetworkAdapter &nic = *it;
5658
5659 /* slot unicity is guaranteed by XML Schema */
5660 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
5661 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
5662 CheckComRCReturnRC (rc);
5663 }
5664
5665 // serial ports
5666 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
5667 it != data.llSerialPorts.end();
5668 ++it)
5669 {
5670 const settings::SerialPort &s = *it;
5671
5672 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
5673 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
5674 CheckComRCReturnRC (rc);
5675 }
5676
5677 // parallel ports (optional)
5678 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
5679 it != data.llParallelPorts.end();
5680 ++it)
5681 {
5682 const settings::ParallelPort &p = *it;
5683
5684 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
5685 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
5686 CheckComRCReturnRC (rc);
5687 }
5688
5689 /* AudioAdapter */
5690 rc = mAudioAdapter->loadSettings(data.audioAdapter);
5691 CheckComRCReturnRC (rc);
5692
5693 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
5694 it != data.llSharedFolders.end();
5695 ++it)
5696 {
5697 const settings::SharedFolder &sf = *it;
5698 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
5699 CheckComRCReturnRC (rc);
5700 }
5701
5702 // Clipboard
5703 mHWData->mClipboardMode = data.clipboardMode;
5704
5705 // guest settings
5706 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
5707 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
5708
5709#ifdef VBOX_WITH_GUEST_PROPS
5710 /* Guest properties (optional) */
5711 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
5712 it != data.llGuestProperties.end();
5713 ++it)
5714 {
5715 const settings::GuestProperty &prop = *it;
5716 uint32_t fFlags = guestProp::NILFLAG;
5717 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
5718 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
5719 mHWData->mGuestProperties.push_back(property);
5720 }
5721
5722 mHWData->mPropertyServiceActive = false;
5723 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
5724#endif /* VBOX_WITH_GUEST_PROPS defined */
5725 }
5726 catch(std::bad_alloc &)
5727 {
5728 return E_OUTOFMEMORY;
5729 }
5730
5731 AssertComRC(rc);
5732 return rc;
5733}
5734
5735 /**
5736 * @param aNode <StorageControllers> node.
5737 */
5738HRESULT Machine::loadStorageControllers(const settings::Storage &data,
5739 bool aRegistered,
5740 const Guid *aSnapshotId /* = NULL */)
5741{
5742 AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5743
5744 HRESULT rc = S_OK;
5745
5746 /* Make sure the attached hard disks don't get unregistered until we
5747 * associate them with tis machine (important for VMs loaded (opened) after
5748 * VirtualBox startup) */
5749 AutoReadLock vboxLock(mParent);
5750
5751 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
5752 it != data.llStorageControllers.end();
5753 ++it)
5754 {
5755 const settings::StorageController &ctlData = *it;
5756
5757 ComObjPtr<StorageController> pCtl;
5758 /* Try to find one with the name first. */
5759 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
5760 if (SUCCEEDED(rc))
5761 return setError(VBOX_E_OBJECT_IN_USE,
5762 tr("Storage controller named '%s' already exists"),
5763 ctlData.strName.raw());
5764
5765 pCtl.createObject();
5766 rc = pCtl->init(this,
5767 ctlData.strName,
5768 ctlData.storageBus,
5769 ctlData.ulInstance);
5770 CheckComRCReturnRC (rc);
5771
5772 mStorageControllers->push_back(pCtl);
5773
5774 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
5775 CheckComRCReturnRC (rc);
5776
5777 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
5778 CheckComRCReturnRC (rc);
5779
5780 /* Set IDE emulation settings (only for AHCI controller). */
5781 if (ctlData.controllerType == StorageControllerType_IntelAhci)
5782 {
5783 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
5784 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
5785 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
5786 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
5787 )
5788 return rc;
5789 }
5790
5791 /* Load the attached devices now. */
5792 rc = loadStorageDevices(pCtl,
5793 ctlData,
5794 aRegistered,
5795 aSnapshotId);
5796 CheckComRCReturnRC (rc);
5797 }
5798
5799 return S_OK;
5800}
5801
5802/**
5803 * @param aNode <HardDiskAttachments> node.
5804 * @param aRegistered true when the machine is being loaded on VirtualBox
5805 * startup, or when a snapshot is being loaded (wchich
5806 * currently can happen on startup only)
5807 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
5808 *
5809 * @note Lock mParent for reading and hard disks for writing before calling.
5810 */
5811HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
5812 const settings::StorageController &data,
5813 bool aRegistered,
5814 const Guid *aSnapshotId /*= NULL*/)
5815{
5816 AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
5817 (mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
5818
5819 HRESULT rc = S_OK;
5820
5821 if (!aRegistered && data.llAttachedDevices.size() > 0)
5822 /* when the machine is being loaded (opened) from a file, it cannot
5823 * have hard disks attached (this should not happen normally,
5824 * because we don't allow to attach hard disks to an unregistered
5825 * VM at all */
5826 return setError(E_FAIL,
5827 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
5828 mUserData->mName.raw(),
5829 data.llAttachedDevices.size());
5830
5831 /* paranoia: detect duplicate attachments */
5832 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5833 it != data.llAttachedDevices.end();
5834 ++it)
5835 {
5836 for (settings::AttachedDevicesList::const_iterator it2 = it;
5837 it2 != data.llAttachedDevices.end();
5838 ++it2)
5839 {
5840 if (it == it2)
5841 continue;
5842
5843 if ( (*it).lPort == (*it2).lPort
5844 && (*it).lDevice == (*it2).lDevice)
5845 {
5846 return setError(E_FAIL,
5847 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
5848 aStorageController->name().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
5849 }
5850 }
5851 }
5852
5853 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5854 it != data.llAttachedDevices.end();
5855 ++it)
5856 {
5857 const settings::AttachedDevice &dev = *it;
5858 ComObjPtr<Medium> medium;
5859
5860 switch (dev.deviceType)
5861 {
5862 case DeviceType_Floppy:
5863 /* find a floppy by UUID */
5864 if (!dev.uuid.isEmpty())
5865 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5866 /* find a floppy by host device name */
5867 else if (!dev.strHostDriveSrc.isEmpty())
5868 {
5869 SafeIfaceArray<IMedium> drivevec;
5870 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
5871 if (SUCCEEDED(rc))
5872 {
5873 for (size_t i = 0; i < drivevec.size(); ++i)
5874 {
5875 /// @todo eliminate this conversion
5876 ComObjPtr<Medium> med = (Medium *)drivevec[i];
5877 if ( dev.strHostDriveSrc == med->name()
5878 || dev.strHostDriveSrc == med->location())
5879 {
5880 medium = med;
5881 break;
5882 }
5883 }
5884 }
5885 }
5886 break;
5887
5888 case DeviceType_DVD:
5889 /* find a DVD by UUID */
5890 if (!dev.uuid.isEmpty())
5891 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5892 /* find a DVD by host device name */
5893 else if (!dev.strHostDriveSrc.isEmpty())
5894 {
5895 SafeIfaceArray<IMedium> drivevec;
5896 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
5897 if (SUCCEEDED(rc))
5898 {
5899 for (size_t i = 0; i < drivevec.size(); ++i)
5900 {
5901 Bstr hostDriveSrc(dev.strHostDriveSrc);
5902 /// @todo eliminate this conversion
5903 ComObjPtr<Medium> med = (Medium *)drivevec[i];
5904 if ( hostDriveSrc == med->name()
5905 || hostDriveSrc == med->location())
5906 {
5907 medium = med;
5908 break;
5909 }
5910 }
5911 }
5912 }
5913 break;
5914
5915 case DeviceType_HardDisk:
5916 {
5917 /* find a hard disk by UUID */
5918 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5919 CheckComRCReturnRC(rc);
5920
5921 AutoWriteLock hdLock(medium);
5922
5923 if (medium->type() == MediumType_Immutable)
5924 {
5925 if (mType == IsSnapshotMachine)
5926 return setError(E_FAIL,
5927 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
5928 "of the virtual machine '%ls' ('%s')"),
5929 medium->locationFull().raw(),
5930 dev.uuid.raw(),
5931 aSnapshotId->raw(),
5932 mUserData->mName.raw(),
5933 mData->m_strConfigFileFull.raw());
5934
5935 return setError(E_FAIL,
5936 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
5937 medium->locationFull().raw(),
5938 dev.uuid.raw(),
5939 mUserData->mName.raw(),
5940 mData->m_strConfigFileFull.raw());
5941 }
5942
5943 if ( mType != IsSnapshotMachine
5944 && medium->children().size() != 0
5945 )
5946 return setError(E_FAIL,
5947 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
5948 "because it has %d differencing child hard disks"),
5949 medium->locationFull().raw(),
5950 dev.uuid.raw(),
5951 mUserData->mName.raw(),
5952 mData->m_strConfigFileFull.raw(),
5953 medium->children().size());
5954
5955 if (findAttachment(mMediaData->mAttachments,
5956 medium))
5957 return setError(E_FAIL,
5958 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
5959 medium->locationFull().raw(),
5960 dev.uuid.raw(),
5961 mUserData->mName.raw(),
5962 mData->m_strConfigFileFull.raw());
5963
5964 break;
5965 }
5966
5967 default:
5968 return setError(E_FAIL,
5969 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
5970 medium->locationFull().raw(),
5971 mUserData->mName.raw(),
5972 mData->m_strConfigFileFull.raw());
5973 }
5974
5975 if (rc)
5976 break;
5977
5978 ComObjPtr<MediumAttachment> pAttachment;
5979 pAttachment.createObject();
5980 rc = pAttachment->init(this,
5981 medium,
5982 aStorageController->name(),
5983 dev.lPort,
5984 dev.lDevice,
5985 dev.deviceType);
5986 CheckComRCBreakRC(rc);
5987
5988 /* associate the medium with this machine and snapshot */
5989 if (!medium.isNull())
5990 {
5991 if (mType == IsSnapshotMachine)
5992 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
5993 else
5994 rc = medium->attachTo(mData->mUuid);
5995 }
5996 AssertComRCBreakRC (rc);
5997
5998 /* backup mMediaData to let registeredInit() properly rollback on failure
5999 * (= limited accessibility) */
6000
6001 mMediaData.backup();
6002 mMediaData->mAttachments.push_back(pAttachment);
6003 }
6004
6005 return rc;
6006}
6007
6008/**
6009 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
6010 *
6011 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
6012 * @param aSnapshot where to return the found snapshot
6013 * @param aSetError true to set extended error info on failure
6014 */
6015HRESULT Machine::findSnapshot(const Guid &aId,
6016 ComObjPtr<Snapshot> &aSnapshot,
6017 bool aSetError /* = false */)
6018{
6019 AutoReadLock chlock(snapshotsTreeLockHandle());
6020
6021 if (!mData->mFirstSnapshot)
6022 {
6023 if (aSetError)
6024 return setError(E_FAIL,
6025 tr("This machine does not have any snapshots"));
6026 return E_FAIL;
6027 }
6028
6029 if (aId.isEmpty())
6030 aSnapshot = mData->mFirstSnapshot;
6031 else
6032 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
6033
6034 if (!aSnapshot)
6035 {
6036 if (aSetError)
6037 return setError(E_FAIL,
6038 tr("Could not find a snapshot with UUID {%s}"),
6039 aId.toString().raw());
6040 return E_FAIL;
6041 }
6042
6043 return S_OK;
6044}
6045
6046/**
6047 * Returns the snapshot with the given name or fails of no such snapshot.
6048 *
6049 * @param aName snapshot name to find
6050 * @param aSnapshot where to return the found snapshot
6051 * @param aSetError true to set extended error info on failure
6052 */
6053HRESULT Machine::findSnapshot(IN_BSTR aName,
6054 ComObjPtr<Snapshot> &aSnapshot,
6055 bool aSetError /* = false */)
6056{
6057 AssertReturn(aName, E_INVALIDARG);
6058
6059 AutoReadLock chlock(snapshotsTreeLockHandle());
6060
6061 if (!mData->mFirstSnapshot)
6062 {
6063 if (aSetError)
6064 return setError(VBOX_E_OBJECT_NOT_FOUND,
6065 tr("This machine does not have any snapshots"));
6066 return VBOX_E_OBJECT_NOT_FOUND;
6067 }
6068
6069 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
6070
6071 if (!aSnapshot)
6072 {
6073 if (aSetError)
6074 return setError(VBOX_E_OBJECT_NOT_FOUND,
6075 tr("Could not find a snapshot named '%ls'"), aName);
6076 return VBOX_E_OBJECT_NOT_FOUND;
6077 }
6078
6079 return S_OK;
6080}
6081
6082/**
6083 * Returns a storage controller object with the given name.
6084 *
6085 * @param aName storage controller name to find
6086 * @param aStorageController where to return the found storage controller
6087 * @param aSetError true to set extended error info on failure
6088 */
6089HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
6090 ComObjPtr<StorageController> &aStorageController,
6091 bool aSetError /* = false */)
6092{
6093 AssertReturn (!aName.isEmpty(), E_INVALIDARG);
6094
6095 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6096 it != mStorageControllers->end();
6097 ++it)
6098 {
6099 if ((*it)->name() == aName)
6100 {
6101 aStorageController = (*it);
6102 return S_OK;
6103 }
6104 }
6105
6106 if (aSetError)
6107 return setError(VBOX_E_OBJECT_NOT_FOUND,
6108 tr("Could not find a storage controller named '%s'"),
6109 aName.raw());
6110 return VBOX_E_OBJECT_NOT_FOUND;
6111}
6112
6113HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
6114 MediaData::AttachmentList &atts)
6115{
6116 AutoCaller autoCaller(this);
6117 CheckComRCReturnRC(autoCaller.rc());
6118
6119 AutoReadLock alock(this);
6120
6121 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
6122 it != mMediaData->mAttachments.end();
6123 ++it)
6124 {
6125 if (Bstr((*it)->controllerName()) == aName)
6126 atts.push_back(*it);
6127 }
6128
6129 return S_OK;
6130}
6131
6132/**
6133 * Helper for #saveSettings. Cares about renaming the settings directory and
6134 * file if the machine name was changed and about creating a new settings file
6135 * if this is a new machine.
6136 *
6137 * @note Must be never called directly but only from #saveSettings().
6138 *
6139 * @param aRenamed receives |true| if the name was changed and the settings
6140 * file was renamed as a result, or |false| otherwise. The
6141 * value makes sense only on success.
6142 * @param aNew receives |true| if a virgin settings file was created.
6143 */
6144HRESULT Machine::prepareSaveSettings(bool &aRenamed,
6145 bool &aNew)
6146{
6147 /* Note: tecnhically, mParent needs to be locked only when the machine is
6148 * registered (see prepareSaveSettings() for details) but we don't
6149 * currently differentiate it in callers of saveSettings() so we don't
6150 * make difference here too. */
6151 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6152 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6153
6154 HRESULT rc = S_OK;
6155
6156 aRenamed = false;
6157
6158 /* if we're ready and isConfigLocked() is FALSE then it means
6159 * that no config file exists yet (we will create a virgin one) */
6160 aNew = !mData->m_pMachineConfigFile->fileExists();
6161
6162 /* attempt to rename the settings file if machine name is changed */
6163 if ( mUserData->mNameSync
6164 && mUserData.isBackedUp()
6165 && mUserData.backedUpData()->mName != mUserData->mName
6166 )
6167 {
6168 aRenamed = true;
6169
6170 bool dirRenamed = false;
6171 bool fileRenamed = false;
6172
6173 Utf8Str configFile, newConfigFile;
6174 Utf8Str configDir, newConfigDir;
6175
6176 do
6177 {
6178 int vrc = VINF_SUCCESS;
6179
6180 Utf8Str name = mUserData.backedUpData()->mName;
6181 Utf8Str newName = mUserData->mName;
6182
6183 configFile = mData->m_strConfigFileFull;
6184
6185 /* first, rename the directory if it matches the machine name */
6186 configDir = configFile;
6187 configDir.stripFilename();
6188 newConfigDir = configDir;
6189 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
6190 {
6191 newConfigDir.stripFilename();
6192 newConfigDir = Utf8StrFmt ("%s%c%s",
6193 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6194 /* new dir and old dir cannot be equal here because of 'if'
6195 * above and because name != newName */
6196 Assert (configDir != newConfigDir);
6197 if (!aNew)
6198 {
6199 /* perform real rename only if the machine is not new */
6200 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
6201 if (RT_FAILURE(vrc))
6202 {
6203 rc = setError(E_FAIL,
6204 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
6205 configDir.raw(),
6206 newConfigDir.raw(),
6207 vrc);
6208 break;
6209 }
6210 dirRenamed = true;
6211 }
6212 }
6213
6214 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
6215 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6216
6217 /* then try to rename the settings file itself */
6218 if (newConfigFile != configFile)
6219 {
6220 /* get the path to old settings file in renamed directory */
6221 configFile = Utf8StrFmt("%s%c%s",
6222 newConfigDir.raw(),
6223 RTPATH_DELIMITER,
6224 RTPathFilename(configFile.c_str()));
6225 if (!aNew)
6226 {
6227 /* perform real rename only if the machine is not new */
6228 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
6229 if (RT_FAILURE(vrc))
6230 {
6231 rc = setError(E_FAIL,
6232 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
6233 configFile.raw(),
6234 newConfigFile.raw(),
6235 vrc);
6236 break;
6237 }
6238 fileRenamed = true;
6239 }
6240 }
6241
6242 /* update m_strConfigFileFull amd mConfigFile */
6243 Utf8Str oldConfigFileFull = mData->m_strConfigFileFull;
6244 Utf8Str oldConfigFile = mData->m_strConfigFile;
6245 mData->m_strConfigFileFull = newConfigFile;
6246 /* try to get the relative path for mConfigFile */
6247 Utf8Str path = newConfigFile;
6248 mParent->calculateRelativePath (path, path);
6249 mData->m_strConfigFile = path;
6250
6251 /* last, try to update the global settings with the new path */
6252 if (mData->mRegistered)
6253 {
6254 rc = mParent->updateSettings(configDir.c_str(), newConfigDir.c_str());
6255 if (FAILED(rc))
6256 {
6257 /* revert to old values */
6258 mData->m_strConfigFileFull = oldConfigFileFull;
6259 mData->m_strConfigFile = oldConfigFile;
6260 break;
6261 }
6262 }
6263
6264 /* update the snapshot folder */
6265 path = mUserData->mSnapshotFolderFull;
6266 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6267 {
6268 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6269 path.raw() + configDir.length());
6270 mUserData->mSnapshotFolderFull = path;
6271 calculateRelativePath (path, path);
6272 mUserData->mSnapshotFolder = path;
6273 }
6274
6275 /* update the saved state file path */
6276 path = mSSData->mStateFilePath;
6277 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6278 {
6279 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6280 path.raw() + configDir.length());
6281 mSSData->mStateFilePath = path;
6282 }
6283
6284 /* Update saved state file paths of all online snapshots.
6285 * Note that saveSettings() will recognize name change
6286 * and will save all snapshots in this case. */
6287 if (mData->mFirstSnapshot)
6288 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
6289 newConfigDir.c_str());
6290 }
6291 while (0);
6292
6293 if (FAILED(rc))
6294 {
6295 /* silently try to rename everything back */
6296 if (fileRenamed)
6297 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
6298 if (dirRenamed)
6299 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
6300 }
6301
6302 CheckComRCReturnRC(rc);
6303 }
6304
6305 if (aNew)
6306 {
6307 /* create a virgin config file */
6308 int vrc = VINF_SUCCESS;
6309
6310 /* ensure the settings directory exists */
6311 Utf8Str path(mData->m_strConfigFileFull);
6312 path.stripFilename();
6313 if (!RTDirExists(path.c_str()))
6314 {
6315 vrc = RTDirCreateFullPath(path.c_str(), 0777);
6316 if (RT_FAILURE(vrc))
6317 {
6318 return setError(E_FAIL,
6319 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
6320 path.raw(),
6321 vrc);
6322 }
6323 }
6324
6325 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
6326 path = Utf8Str(mData->m_strConfigFileFull);
6327 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
6328 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
6329 if (RT_FAILURE(vrc))
6330 {
6331 mData->mHandleCfgFile = NIL_RTFILE;
6332 return setError(E_FAIL,
6333 tr("Could not create the settings file '%s' (%Rrc)"),
6334 path.raw(),
6335 vrc);
6336 }
6337 RTFileClose(mData->mHandleCfgFile);
6338 }
6339
6340 return rc;
6341}
6342
6343/**
6344 * Saves and commits machine data, user data and hardware data.
6345 *
6346 * Note that on failure, the data remains uncommitted.
6347 *
6348 * @a aFlags may combine the following flags:
6349 *
6350 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
6351 * Used when saving settings after an operation that makes them 100%
6352 * correspond to the settings from the current snapshot.
6353 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
6354 * #isReallyModified() returns false. This is necessary for cases when we
6355 * change machine data diectly, not through the backup()/commit() mechanism.
6356 *
6357 * @note Must be called from under mParent write lock (sometimes needed by
6358 * #prepareSaveSettings()) and this object's write lock. Locks children for
6359 * writing. There is one exception when mParent is unused and therefore may be
6360 * left unlocked: if this machine is an unregistered one.
6361 */
6362HRESULT Machine::saveSettings(int aFlags /*= 0*/)
6363{
6364 LogFlowThisFuncEnter();
6365
6366 /* Note: tecnhically, mParent needs to be locked only when the machine is
6367 * registered (see prepareSaveSettings() for details) but we don't
6368 * currently differentiate it in callers of saveSettings() so we don't
6369 * make difference here too. */
6370 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6371 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6372
6373 /* make sure child objects are unable to modify the settings while we are
6374 * saving them */
6375 ensureNoStateDependencies();
6376
6377 AssertReturn(mType == IsMachine || mType == IsSessionMachine, E_FAIL);
6378
6379 BOOL currentStateModified = mData->mCurrentStateModified;
6380 bool settingsModified;
6381
6382 if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
6383 {
6384 /* We ignore changes to user data when setting mCurrentStateModified
6385 * because the current state will not differ from the current snapshot
6386 * if only user data has been changed (user data is shared by all
6387 * snapshots). */
6388 currentStateModified = isReallyModified (true /* aIgnoreUserData */);
6389 settingsModified = mUserData.hasActualChanges() || currentStateModified;
6390 }
6391 else
6392 {
6393 if (aFlags & SaveS_ResetCurStateModified)
6394 currentStateModified = FALSE;
6395 settingsModified = isReallyModified();
6396 }
6397
6398 HRESULT rc = S_OK;
6399
6400 /* First, prepare to save settings. It will care about renaming the
6401 * settings directory and file if the machine name was changed and about
6402 * creating a new settings file if this is a new machine. */
6403 bool isRenamed = false;
6404 bool isNew = false;
6405 rc = prepareSaveSettings(isRenamed, isNew);
6406 CheckComRCReturnRC(rc);
6407
6408 try
6409 {
6410 mData->m_pMachineConfigFile->uuid = mData->mUuid;
6411 mData->m_pMachineConfigFile->strName = mUserData->mName;
6412 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
6413 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
6414 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
6415
6416 if ( mData->mMachineState == MachineState_Saved
6417 || mData->mMachineState == MachineState_Restoring
6418 )
6419 {
6420 Assert(!mSSData->mStateFilePath.isEmpty());
6421 /* try to make the file name relative to the settings file dir */
6422 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
6423 }
6424 else
6425 {
6426 Assert(mSSData->mStateFilePath.isEmpty());
6427 mData->m_pMachineConfigFile->strStateFile.setNull();
6428 }
6429
6430 if (mData->mCurrentSnapshot)
6431 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
6432 else
6433 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
6434
6435 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
6436 mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
6437 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6438 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6439/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
6440
6441 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
6442 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
6443 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
6444 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
6445
6446 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
6447 CheckComRCThrowRC(rc);
6448
6449 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
6450 CheckComRCThrowRC(rc);
6451
6452 // save snapshots
6453 rc = saveAllSnapshots();
6454 CheckComRCThrowRC(rc);
6455
6456 // now spit it all out
6457 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6458 }
6459 catch (HRESULT err)
6460 {
6461 /* we assume that error info is set by the thrower */
6462 rc = err;
6463 }
6464 catch (...)
6465 {
6466 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6467 }
6468
6469 if (SUCCEEDED(rc))
6470 {
6471 commit();
6472
6473 /* memorize the new modified state */
6474 mData->mCurrentStateModified = currentStateModified;
6475 }
6476
6477 if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
6478 {
6479 /* Fire the data change event, even on failure (since we've already
6480 * committed all data). This is done only for SessionMachines because
6481 * mutable Machine instances are always not registered (i.e. private
6482 * to the client process that creates them) and thus don't need to
6483 * inform callbacks. */
6484 if (mType == IsSessionMachine)
6485 mParent->onMachineDataChange(mData->mUuid);
6486 }
6487
6488 LogFlowThisFunc(("rc=%08X\n", rc));
6489 LogFlowThisFuncLeave();
6490 return rc;
6491}
6492
6493HRESULT Machine::saveAllSnapshots()
6494{
6495 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
6496
6497 HRESULT rc = S_OK;
6498
6499 try
6500 {
6501 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
6502
6503 if (mData->mFirstSnapshot)
6504 {
6505 settings::Snapshot snapNew;
6506 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
6507
6508 // get reference to the fresh copy of the snapshot on the list and
6509 // work on that copy directly to avoid excessive copying later
6510 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6511
6512 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
6513 CheckComRCThrowRC(rc);
6514 }
6515
6516// if (mType == IsSessionMachine)
6517// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
6518
6519 }
6520 catch (HRESULT err)
6521 {
6522 /* we assume that error info is set by the thrower */
6523 rc = err;
6524 }
6525 catch (...)
6526 {
6527 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6528 }
6529
6530 return rc;
6531}
6532
6533/**
6534 * Saves the VM hardware configuration. It is assumed that the
6535 * given node is empty.
6536 *
6537 * @param aNode <Hardware> node to save the VM hardware confguration to.
6538 */
6539HRESULT Machine::saveHardware(settings::Hardware &data)
6540{
6541 HRESULT rc = S_OK;
6542
6543 try
6544 {
6545 /* The hardware version attribute (optional).
6546 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
6547 if ( mHWData->mHWVersion == "1"
6548 && mSSData->mStateFilePath.isEmpty()
6549 )
6550 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. */
6551
6552 data.strVersion = mHWData->mHWVersion;
6553 data.uuid = mHWData->mHardwareUUID;
6554
6555 // CPU
6556 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
6557 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
6558 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
6559 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
6560 data.fPAE = !!mHWData->mPAEEnabled;
6561 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
6562
6563 /* Standard and Extended CPUID leafs. */
6564 data.llCpuIdLeafs.clear();
6565 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
6566 {
6567 if (mHWData->mCpuIdStdLeafs[idx].ulId != -1)
6568 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
6569 }
6570 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
6571 {
6572 if (mHWData->mCpuIdExtLeafs[idx].ulId != -1)
6573 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
6574 }
6575
6576 data.cCPUs = mHWData->mCPUCount;
6577
6578 // memory
6579 data.ulMemorySizeMB = mHWData->mMemorySize;
6580
6581 // firmware
6582 data.firmwareType = mHWData->mFirmwareType;
6583
6584 // boot order
6585 data.mapBootOrder.clear();
6586 for (size_t i = 0;
6587 i < RT_ELEMENTS(mHWData->mBootOrder);
6588 ++i)
6589 data.mapBootOrder[i] = mHWData->mBootOrder[i];
6590
6591 // display
6592 data.ulVRAMSizeMB = mHWData->mVRAMSize;
6593 data.cMonitors = mHWData->mMonitorCount;
6594 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
6595 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
6596
6597#ifdef VBOX_WITH_VRDP
6598 /* VRDP settings (optional) */
6599 rc = mVRDPServer->saveSettings(data.vrdpSettings);
6600 CheckComRCThrowRC(rc);
6601#endif
6602
6603 /* BIOS (required) */
6604 rc = mBIOSSettings->saveSettings(data.biosSettings);
6605 CheckComRCThrowRC(rc);
6606
6607 /* USB Controller (required) */
6608 rc = mUSBController->saveSettings(data.usbController);
6609 CheckComRCThrowRC(rc);
6610
6611 /* Network adapters (required) */
6612 data.llNetworkAdapters.clear();
6613 for (ULONG slot = 0;
6614 slot < RT_ELEMENTS(mNetworkAdapters);
6615 ++slot)
6616 {
6617 settings::NetworkAdapter nic;
6618 nic.ulSlot = slot;
6619 rc = mNetworkAdapters[slot]->saveSettings(nic);
6620 CheckComRCThrowRC(rc);
6621
6622 data.llNetworkAdapters.push_back(nic);
6623 }
6624
6625 /* Serial ports */
6626 data.llSerialPorts.clear();
6627 for (ULONG slot = 0;
6628 slot < RT_ELEMENTS(mSerialPorts);
6629 ++slot)
6630 {
6631 settings::SerialPort s;
6632 s.ulSlot = slot;
6633 rc = mSerialPorts[slot]->saveSettings(s);
6634 CheckComRCReturnRC (rc);
6635
6636 data.llSerialPorts.push_back(s);
6637 }
6638
6639 /* Parallel ports */
6640 data.llParallelPorts.clear();
6641 for (ULONG slot = 0;
6642 slot < RT_ELEMENTS(mParallelPorts);
6643 ++slot)
6644 {
6645 settings::ParallelPort p;
6646 p.ulSlot = slot;
6647 rc = mParallelPorts[slot]->saveSettings(p);
6648 CheckComRCReturnRC (rc);
6649
6650 data.llParallelPorts.push_back(p);
6651 }
6652
6653 /* Audio adapter */
6654 rc = mAudioAdapter->saveSettings(data.audioAdapter);
6655 CheckComRCReturnRC (rc);
6656
6657 /* Shared folders */
6658 data.llSharedFolders.clear();
6659 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6660 it != mHWData->mSharedFolders.end();
6661 ++it)
6662 {
6663 ComObjPtr<SharedFolder> pFolder = *it;
6664 settings::SharedFolder sf;
6665 sf.strName = pFolder->name();
6666 sf.strHostPath = pFolder->hostPath();
6667 sf.fWritable = !!pFolder->writable();
6668
6669 data.llSharedFolders.push_back(sf);
6670 }
6671
6672 // clipboard
6673 data.clipboardMode = mHWData->mClipboardMode;
6674
6675 /* Guest */
6676 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
6677 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
6678
6679 // guest properties
6680 data.llGuestProperties.clear();
6681#ifdef VBOX_WITH_GUEST_PROPS
6682 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
6683 it != mHWData->mGuestProperties.end();
6684 ++it)
6685 {
6686 HWData::GuestProperty property = *it;
6687
6688 settings::GuestProperty prop;
6689 prop.strName = property.strName;
6690 prop.strValue = property.strValue;
6691 prop.timestamp = property.mTimestamp;
6692 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
6693 guestProp::writeFlags(property.mFlags, szFlags);
6694 prop.strFlags = szFlags;
6695
6696 data.llGuestProperties.push_back(prop);
6697 }
6698
6699 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
6700#endif /* VBOX_WITH_GUEST_PROPS defined */
6701 }
6702 catch(std::bad_alloc &)
6703 {
6704 return E_OUTOFMEMORY;
6705 }
6706
6707 AssertComRC(rc);
6708 return rc;
6709}
6710
6711/**
6712 * Saves the storage controller configuration.
6713 *
6714 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
6715 */
6716HRESULT Machine::saveStorageControllers(settings::Storage &data)
6717{
6718 data.llStorageControllers.clear();
6719
6720 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6721 it != mStorageControllers->end();
6722 ++it)
6723 {
6724 HRESULT rc;
6725 ComObjPtr<StorageController> pCtl = *it;
6726
6727 settings::StorageController ctl;
6728 ctl.strName = pCtl->name();
6729 ctl.controllerType = pCtl->controllerType();
6730 ctl.storageBus = pCtl->storageBus();
6731 ctl.ulInstance = pCtl->instance();
6732
6733 /* Save the port count. */
6734 ULONG portCount;
6735 rc = pCtl->COMGETTER(PortCount)(&portCount);
6736 ComAssertComRCRet(rc, rc);
6737 ctl.ulPortCount = portCount;
6738
6739 /* Save IDE emulation settings. */
6740 if (ctl.controllerType == StorageControllerType_IntelAhci)
6741 {
6742 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
6743 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
6744 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
6745 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
6746 )
6747 ComAssertComRCRet(rc, rc);
6748 }
6749
6750 /* save the devices now. */
6751 rc = saveStorageDevices(pCtl, ctl);
6752 ComAssertComRCRet(rc, rc);
6753
6754 data.llStorageControllers.push_back(ctl);
6755 }
6756
6757 return S_OK;
6758}
6759
6760/**
6761 * Saves the hard disk confguration.
6762 */
6763HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
6764 settings::StorageController &data)
6765{
6766 MediaData::AttachmentList atts;
6767
6768 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->name()), atts);
6769 CheckComRCReturnRC (rc);
6770
6771 data.llAttachedDevices.clear();
6772 for (MediaData::AttachmentList::const_iterator it = atts.begin();
6773 it != atts.end();
6774 ++it)
6775 {
6776 settings::AttachedDevice dev;
6777
6778 MediumAttachment *pAttach = *it;
6779 Medium *pMedium = pAttach->medium();
6780
6781 dev.deviceType = pAttach->type();
6782 dev.lPort = pAttach->port();
6783 dev.lDevice = pAttach->device();
6784 if (pMedium)
6785 {
6786 BOOL fHostDrive = false;
6787 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
6788 if (FAILED(rc))
6789 return rc;
6790 if (fHostDrive)
6791 dev.strHostDriveSrc = pMedium->location();
6792 else
6793 dev.uuid = pMedium->id();
6794 dev.fPassThrough = pAttach->passthrough();
6795 }
6796
6797 data.llAttachedDevices.push_back(dev);
6798 }
6799
6800 return S_OK;
6801}
6802
6803/**
6804 * Saves machine state settings as defined by aFlags
6805 * (SaveSTS_* values).
6806 *
6807 * @param aFlags Combination of SaveSTS_* flags.
6808 *
6809 * @note Locks objects for writing.
6810 */
6811HRESULT Machine::saveStateSettings(int aFlags)
6812{
6813 if (aFlags == 0)
6814 return S_OK;
6815
6816 AutoCaller autoCaller (this);
6817 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6818
6819 /* This object's write lock is also necessary to serialize file access
6820 * (prevent concurrent reads and writes) */
6821 AutoWriteLock alock(this);
6822
6823 HRESULT rc = S_OK;
6824
6825 Assert(mData->m_pMachineConfigFile);
6826
6827 try
6828 {
6829 if (aFlags & SaveSTS_CurStateModified)
6830 mData->m_pMachineConfigFile->fCurrentStateModified = true;
6831
6832 if (aFlags & SaveSTS_StateFilePath)
6833 {
6834 if (!mSSData->mStateFilePath.isEmpty())
6835 /* try to make the file name relative to the settings file dir */
6836 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
6837 else
6838 mData->m_pMachineConfigFile->strStateFile.setNull();
6839 }
6840
6841 if (aFlags & SaveSTS_StateTimeStamp)
6842 {
6843 Assert( mData->mMachineState != MachineState_Aborted
6844 || mSSData->mStateFilePath.isEmpty());
6845
6846 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6847
6848 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6849//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
6850 }
6851
6852 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6853 }
6854 catch (...)
6855 {
6856 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6857 }
6858
6859 return rc;
6860}
6861
6862/**
6863 * Creates differencing hard disks for all normal hard disks attached to this
6864 * machine and a new set of attachments to refer to created disks.
6865 *
6866 * Used when taking a snapshot or when discarding the current state.
6867 *
6868 * This method assumes that mMediaData contains the original hard disk attachments
6869 * it needs to create diffs for. On success, these attachments will be replaced
6870 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
6871 * called to delete created diffs which will also rollback mMediaData and restore
6872 * whatever was backed up before calling this method.
6873 *
6874 * Attachments with non-normal hard disks are left as is.
6875 *
6876 * If @a aOnline is @c false then the original hard disks that require implicit
6877 * diffs will be locked for reading. Otherwise it is assumed that they are
6878 * already locked for writing (when the VM was started). Note that in the latter
6879 * case it is responsibility of the caller to lock the newly created diffs for
6880 * writing if this method succeeds.
6881 *
6882 * @param aFolder Folder where to create diff hard disks.
6883 * @param aProgress Progress object to run (must contain at least as
6884 * many operations left as the number of hard disks
6885 * attached).
6886 * @param aOnline Whether the VM was online prior to this operation.
6887 *
6888 * @note The progress object is not marked as completed, neither on success nor
6889 * on failure. This is a responsibility of the caller.
6890 *
6891 * @note Locks this object for writing.
6892 */
6893HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
6894 IProgress *aProgress,
6895 ULONG aWeight,
6896 bool aOnline)
6897{
6898 AssertReturn(!aFolder.isEmpty(), E_FAIL);
6899
6900 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
6901
6902 AutoCaller autoCaller(this);
6903 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6904
6905 AutoWriteLock alock(this);
6906
6907 /* must be in a protective state because we leave the lock below */
6908 AssertReturn( mData->mMachineState == MachineState_Saving
6909 || mData->mMachineState == MachineState_LiveSnapshotting
6910 || mData->mMachineState == MachineState_RestoringSnapshot
6911 || mData->mMachineState == MachineState_DeletingSnapshot
6912 , E_FAIL);
6913
6914 HRESULT rc = S_OK;
6915
6916 typedef std::list< ComObjPtr<Medium> > LockedMedia;
6917 LockedMedia lockedMedia;
6918
6919 try
6920 {
6921 if (!aOnline)
6922 {
6923 /* lock all attached hard disks early to detect "in use"
6924 * situations before creating actual diffs */
6925 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6926 it != mMediaData->mAttachments.end();
6927 ++it)
6928 {
6929 MediumAttachment* pAtt = *it;
6930 if (pAtt->type() == DeviceType_HardDisk)
6931 {
6932 Medium* pHD = pAtt->medium();
6933 Assert(pHD);
6934 rc = pHD->LockRead (NULL);
6935 CheckComRCThrowRC(rc);
6936 lockedMedia.push_back(pHD);
6937 }
6938 }
6939 }
6940
6941 /* remember the current list (note that we don't use backup() since
6942 * mMediaData may be already backed up) */
6943 MediaData::AttachmentList atts = mMediaData->mAttachments;
6944
6945 /* start from scratch */
6946 mMediaData->mAttachments.clear();
6947
6948 /* go through remembered attachments and create diffs for normal hard
6949 * disks and attach them */
6950 for (MediaData::AttachmentList::const_iterator it = atts.begin();
6951 it != atts.end();
6952 ++it)
6953 {
6954 MediumAttachment* pAtt = *it;
6955
6956 DeviceType_T devType = pAtt->type();
6957 Medium* medium = pAtt->medium();
6958
6959 if ( devType != DeviceType_HardDisk
6960 || medium == NULL
6961 || medium->type() != MediumType_Normal)
6962 {
6963 /* copy the attachment as is */
6964
6965 /** @todo the progress object created in Console::TakeSnaphot
6966 * only expects operations for hard disks. Later other
6967 * device types need to show up in the progress as well. */
6968 if (devType == DeviceType_HardDisk)
6969 {
6970 if (medium == NULL)
6971 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
6972 aWeight); // weight
6973 else
6974 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
6975 medium->base()->name().raw()),
6976 aWeight); // weight
6977 }
6978
6979 mMediaData->mAttachments.push_back(pAtt);
6980 continue;
6981 }
6982
6983 /* need a diff */
6984 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
6985 medium->base()->name().raw()),
6986 aWeight); // weight
6987
6988 ComObjPtr<Medium> diff;
6989 diff.createObject();
6990 rc = diff->init(mParent,
6991 medium->preferredDiffFormat().raw(),
6992 BstrFmt("%ls"RTPATH_SLASH_STR,
6993 mUserData->mSnapshotFolderFull.raw()).raw());
6994 CheckComRCThrowRC(rc);
6995
6996 /* leave the lock before the potentially lengthy operation */
6997 alock.leave();
6998
6999 rc = medium->createDiffStorageAndWait(diff,
7000 MediumVariant_Standard,
7001 NULL);
7002
7003 // at this point, the old image is still locked for writing, but instead
7004 // we need the new diff image locked for writing and lock the previously
7005 // current one for reading only
7006 if (aOnline)
7007 {
7008 diff->LockWrite(NULL);
7009 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7010 medium->UnlockWrite(NULL);
7011 medium->LockRead(NULL);
7012 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7013 }
7014
7015 alock.enter();
7016
7017 CheckComRCThrowRC(rc);
7018
7019 rc = diff->attachTo(mData->mUuid);
7020 AssertComRCThrowRC(rc);
7021
7022 /* add a new attachment */
7023 ComObjPtr<MediumAttachment> attachment;
7024 attachment.createObject();
7025 rc = attachment->init(this,
7026 diff,
7027 pAtt->controllerName(),
7028 pAtt->port(),
7029 pAtt->device(),
7030 DeviceType_HardDisk,
7031 true /* aImplicit */);
7032 CheckComRCThrowRC(rc);
7033
7034 mMediaData->mAttachments.push_back(attachment);
7035 }
7036 }
7037 catch (HRESULT aRC) { rc = aRC; }
7038
7039 /* unlock all hard disks we locked */
7040 if (!aOnline)
7041 {
7042 ErrorInfoKeeper eik;
7043
7044 for (LockedMedia::const_iterator it = lockedMedia.begin();
7045 it != lockedMedia.end();
7046 ++it)
7047 {
7048 HRESULT rc2 = (*it)->UnlockRead(NULL);
7049 AssertComRC(rc2);
7050 }
7051 }
7052
7053 if (FAILED(rc))
7054 {
7055 MultiResultRef mrc (rc);
7056
7057 mrc = deleteImplicitDiffs();
7058 }
7059
7060 return rc;
7061}
7062
7063/**
7064 * Deletes implicit differencing hard disks created either by
7065 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
7066 *
7067 * Note that to delete hard disks created by #AttachMedium() this method is
7068 * called from #fixupMedia() when the changes are rolled back.
7069 *
7070 * @note Locks this object for writing.
7071 */
7072HRESULT Machine::deleteImplicitDiffs()
7073{
7074 AutoCaller autoCaller(this);
7075 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7076
7077 AutoWriteLock alock(this);
7078
7079 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
7080
7081 HRESULT rc = S_OK;
7082
7083 MediaData::AttachmentList implicitAtts;
7084
7085 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7086
7087 /* enumerate new attachments */
7088 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7089 it != mMediaData->mAttachments.end();
7090 ++it)
7091 {
7092 ComObjPtr<Medium> hd = (*it)->medium();
7093 if (hd.isNull())
7094 continue;
7095
7096 if ((*it)->isImplicit())
7097 {
7098 /* deassociate and mark for deletion */
7099 rc = hd->detachFrom(mData->mUuid);
7100 AssertComRC(rc);
7101 implicitAtts.push_back (*it);
7102 continue;
7103 }
7104
7105 /* was this hard disk attached before? */
7106 if (!findAttachment(oldAtts, hd))
7107 {
7108 /* no: de-associate */
7109 rc = hd->detachFrom(mData->mUuid);
7110 AssertComRC(rc);
7111 continue;
7112 }
7113 }
7114
7115 /* rollback hard disk changes */
7116 mMediaData.rollback();
7117
7118 MultiResult mrc (S_OK);
7119
7120 /* delete unused implicit diffs */
7121 if (implicitAtts.size() != 0)
7122 {
7123 /* will leave the lock before the potentially lengthy
7124 * operation, so protect with the special state (unless already
7125 * protected) */
7126 MachineState_T oldState = mData->mMachineState;
7127 if ( oldState != MachineState_Saving
7128 && oldState != MachineState_LiveSnapshotting
7129 && oldState != MachineState_RestoringSnapshot
7130 && oldState != MachineState_DeletingSnapshot
7131 )
7132 setMachineState (MachineState_SettingUp);
7133
7134 alock.leave();
7135
7136 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
7137 it != implicitAtts.end();
7138 ++it)
7139 {
7140 ComObjPtr<Medium> hd = (*it)->medium();
7141 mrc = hd->deleteStorageAndWait();
7142 }
7143
7144 alock.enter();
7145
7146 if (mData->mMachineState == MachineState_SettingUp)
7147 {
7148 setMachineState (oldState);
7149 }
7150 }
7151
7152 return mrc;
7153}
7154
7155/**
7156 * Looks through the given list of media attachments for one with the given parameters
7157 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7158 * can be searched as well if needed.
7159 *
7160 * @param list
7161 * @param aControllerName
7162 * @param aControllerPort
7163 * @param aDevice
7164 * @return
7165 */
7166MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7167 IN_BSTR aControllerName,
7168 LONG aControllerPort,
7169 LONG aDevice)
7170{
7171 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7172 it != ll.end();
7173 ++it)
7174 {
7175 MediumAttachment *pAttach = *it;
7176 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
7177 return pAttach;
7178 }
7179
7180 return NULL;
7181}
7182
7183/**
7184 * Looks through the given list of media attachments for one with the given parameters
7185 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7186 * can be searched as well if needed.
7187 *
7188 * @param list
7189 * @param aControllerName
7190 * @param aControllerPort
7191 * @param aDevice
7192 * @return
7193 */
7194MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7195 ComObjPtr<Medium> pMedium)
7196{
7197 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7198 it != ll.end();
7199 ++it)
7200 {
7201 MediumAttachment *pAttach = *it;
7202 ComObjPtr<Medium> pMediumThis = pAttach->medium();
7203 if (pMediumThis.equalsTo(pMedium))
7204 return pAttach;
7205 }
7206
7207 return NULL;
7208}
7209
7210/**
7211 * Looks through the given list of media attachments for one with the given parameters
7212 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7213 * can be searched as well if needed.
7214 *
7215 * @param list
7216 * @param aControllerName
7217 * @param aControllerPort
7218 * @param aDevice
7219 * @return
7220 */
7221MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7222 Guid &id)
7223{
7224 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7225 it != ll.end();
7226 ++it)
7227 {
7228 MediumAttachment *pAttach = *it;
7229 ComObjPtr<Medium> pMediumThis = pAttach->medium();
7230 if (pMediumThis->id() == id)
7231 return pAttach;
7232 }
7233
7234 return NULL;
7235}
7236
7237/**
7238 * Perform deferred hard disk detachments on success and deletion of implicitly
7239 * created diffs on failure.
7240 *
7241 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
7242 * backed up).
7243 *
7244 * When the data is backed up, this method will commit mMediaData if @a aCommit is
7245 * @c true and rollback it otherwise before returning.
7246 *
7247 * If @a aOnline is @c true then this method called with @a aCommit = @c true
7248 * will also unlock the old hard disks for which the new implicit diffs were
7249 * created and will lock these new diffs for writing. When @a aCommit is @c
7250 * false, this argument is ignored.
7251 *
7252 * @param aCommit @c true if called on success.
7253 * @param aOnline Whether the VM was online prior to this operation.
7254 *
7255 * @note Locks this object for writing!
7256 */
7257void Machine::fixupMedia(bool aCommit, bool aOnline /*= false*/)
7258{
7259 AutoCaller autoCaller(this);
7260 AssertComRCReturnVoid (autoCaller.rc());
7261
7262 AutoWriteLock alock(this);
7263
7264 LogFlowThisFunc(("Entering, aCommit=%d, aOnline=%d\n", aCommit, aOnline));
7265
7266 HRESULT rc = S_OK;
7267
7268 /* no attach/detach operations -- nothing to do */
7269 if (!mMediaData.isBackedUp())
7270 return;
7271
7272 if (aCommit)
7273 {
7274 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7275
7276 /* enumerate new attachments */
7277 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7278 it != mMediaData->mAttachments.end();
7279 ++it)
7280 {
7281 MediumAttachment *pAttach = *it;
7282
7283 pAttach->commit();
7284
7285 Medium* pMedium = pAttach->medium();
7286 bool fImplicit = pAttach->isImplicit();
7287
7288 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
7289 (pMedium) ? pMedium->name().raw() : "NULL",
7290 fImplicit));
7291
7292 /** @todo convert all this Machine-based voodoo to MediumAttachment
7293 * based commit logic. */
7294 if (fImplicit)
7295 {
7296 /* convert implicit attachment to normal */
7297 pAttach->setImplicit(false);
7298
7299 if ( aOnline
7300 && pMedium
7301 && pAttach->type() == DeviceType_HardDisk
7302 )
7303 {
7304 rc = pMedium->LockWrite(NULL);
7305 AssertComRC(rc);
7306
7307 mData->mSession.mLockedMedia.push_back(
7308 Data::Session::LockedMedia::value_type(
7309 ComPtr<IMedium>(pMedium), true));
7310
7311 /* also, relock the old hard disk which is a base for the
7312 * new diff for reading if the VM is online */
7313
7314 ComObjPtr<Medium> parent = pMedium->parent();
7315 /* make the relock atomic */
7316 AutoWriteLock parentLock (parent);
7317 rc = parent->UnlockWrite(NULL);
7318 AssertComRC(rc);
7319 rc = parent->LockRead(NULL);
7320 AssertComRC(rc);
7321
7322 /* XXX actually we should replace the old entry in that
7323 * vector (write lock => read lock) but this would take
7324 * some effort. So lets just ignore the error code in
7325 * SessionMachine::unlockMedia(). */
7326 mData->mSession.mLockedMedia.push_back(
7327 Data::Session::LockedMedia::value_type (
7328 ComPtr<IMedium>(parent), false));
7329 }
7330
7331 continue;
7332 }
7333
7334 if (pMedium)
7335 {
7336 /* was this medium attached before? */
7337 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
7338 oldIt != oldAtts.end();
7339 ++oldIt)
7340 {
7341 MediumAttachment *pOldAttach = *it;
7342 if (pOldAttach->medium().equalsTo(pMedium))
7343 {
7344 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->name().raw()));
7345
7346 /* yes: remove from old to avoid de-association */
7347 oldAtts.erase(oldIt);
7348 break;
7349 }
7350 }
7351 }
7352 }
7353
7354 /* enumerate remaining old attachments and de-associate from the
7355 * current machine state */
7356 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
7357 it != oldAtts.end();
7358 ++it)
7359 {
7360 MediumAttachment *pAttach = *it;
7361
7362 Medium* pMedium = pAttach->medium();
7363
7364 if (pMedium)
7365 {
7366 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->name().raw()));
7367
7368 /* now de-associate from the current machine state */
7369 rc = pMedium->detachFrom(mData->mUuid);
7370 AssertComRC(rc);
7371
7372 if ( aOnline
7373 && pAttach->type() == DeviceType_HardDisk)
7374 {
7375 /* unlock since not used anymore */
7376 MediumState_T state;
7377 rc = pMedium->UnlockWrite(&state);
7378 /* the disk may be alredy relocked for reading above */
7379 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead);
7380 }
7381 }
7382 }
7383
7384 /* commit the hard disk changes */
7385 mMediaData.commit();
7386
7387 if (mType == IsSessionMachine)
7388 {
7389 /* attach new data to the primary machine and reshare it */
7390 mPeer->mMediaData.attach(mMediaData);
7391 }
7392 }
7393 else
7394 {
7395 /* enumerate new attachments */
7396 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7397 it != mMediaData->mAttachments.end();
7398 ++it)
7399 {
7400 (*it)->rollback();
7401 }
7402
7403 /** @todo convert all this Machine-based voodoo to MediumAttachment
7404 * based rollback logic. */
7405 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
7406 // which gets called if Machine::registeredInit() fails...
7407 deleteImplicitDiffs();
7408 }
7409
7410 return;
7411}
7412
7413/**
7414 * Returns true if the settings file is located in the directory named exactly
7415 * as the machine. This will be true if the machine settings structure was
7416 * created by default in #openConfigLoader().
7417 *
7418 * @param aSettingsDir if not NULL, the full machine settings file directory
7419 * name will be assigned there.
7420 *
7421 * @note Doesn't lock anything.
7422 * @note Not thread safe (must be called from this object's lock).
7423 */
7424bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
7425{
7426 Utf8Str settingsDir = mData->m_strConfigFileFull;
7427 settingsDir.stripFilename();
7428 char *dirName = RTPathFilename(settingsDir.c_str());
7429
7430 AssertReturn(dirName, false);
7431
7432 /* if we don't rename anything on name change, return false shorlty */
7433 if (!mUserData->mNameSync)
7434 return false;
7435
7436 if (aSettingsDir)
7437 *aSettingsDir = settingsDir;
7438
7439 return Bstr (dirName) == mUserData->mName;
7440}
7441
7442/**
7443 * @note Locks objects for reading!
7444 */
7445bool Machine::isModified()
7446{
7447 AutoCaller autoCaller(this);
7448 AssertComRCReturn (autoCaller.rc(), false);
7449
7450 AutoReadLock alock(this);
7451
7452 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7453 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
7454 return true;
7455
7456 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7457 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
7458 return true;
7459
7460 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7461 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
7462 return true;
7463
7464 if (!mStorageControllers.isNull())
7465 {
7466 for (StorageControllerList::const_iterator it =
7467 mStorageControllers->begin();
7468 it != mStorageControllers->end();
7469 ++it)
7470 {
7471 if ((*it)->isModified())
7472 return true;
7473 }
7474 }
7475
7476 return
7477 mUserData.isBackedUp() ||
7478 mHWData.isBackedUp() ||
7479 mMediaData.isBackedUp() ||
7480 mStorageControllers.isBackedUp() ||
7481#ifdef VBOX_WITH_VRDP
7482 (mVRDPServer && mVRDPServer->isModified()) ||
7483#endif
7484 (mAudioAdapter && mAudioAdapter->isModified()) ||
7485 (mUSBController && mUSBController->isModified()) ||
7486 (mBIOSSettings && mBIOSSettings->isModified());
7487}
7488
7489/**
7490 * Returns the logical OR of data.hasActualChanges() of this and all child
7491 * objects.
7492 *
7493 * @param aIgnoreUserData @c true to ignore changes to mUserData
7494 *
7495 * @note Locks objects for reading!
7496 */
7497bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
7498{
7499 AutoCaller autoCaller(this);
7500 AssertComRCReturn (autoCaller.rc(), false);
7501
7502 AutoReadLock alock(this);
7503
7504 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7505 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
7506 return true;
7507
7508 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7509 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
7510 return true;
7511
7512 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7513 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
7514 return true;
7515
7516 if (!mStorageControllers.isBackedUp())
7517 {
7518 /* see whether any of the devices has changed its data */
7519 for (StorageControllerList::const_iterator
7520 it = mStorageControllers->begin();
7521 it != mStorageControllers->end();
7522 ++it)
7523 {
7524 if ((*it)->isReallyModified())
7525 return true;
7526 }
7527 }
7528 else
7529 {
7530 if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
7531 return true;
7532 }
7533
7534 return
7535 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
7536 mHWData.hasActualChanges() ||
7537 mMediaData.hasActualChanges() ||
7538 mStorageControllers.hasActualChanges() ||
7539#ifdef VBOX_WITH_VRDP
7540 (mVRDPServer && mVRDPServer->isReallyModified()) ||
7541#endif
7542 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
7543 (mUSBController && mUSBController->isReallyModified()) ||
7544 (mBIOSSettings && mBIOSSettings->isReallyModified());
7545}
7546
7547/**
7548 * Discards all changes to machine settings.
7549 *
7550 * @param aNotify Whether to notify the direct session about changes or not.
7551 *
7552 * @note Locks objects for writing!
7553 */
7554void Machine::rollback (bool aNotify)
7555{
7556 AutoCaller autoCaller(this);
7557 AssertComRCReturn (autoCaller.rc(), (void) 0);
7558
7559 AutoWriteLock alock(this);
7560
7561 /* check for changes in own data */
7562
7563 bool sharedFoldersChanged = false, storageChanged = false;
7564
7565 if (aNotify && mHWData.isBackedUp())
7566 {
7567 if (mHWData->mSharedFolders.size() !=
7568 mHWData.backedUpData()->mSharedFolders.size())
7569 sharedFoldersChanged = true;
7570 else
7571 {
7572 for (HWData::SharedFolderList::iterator rit =
7573 mHWData->mSharedFolders.begin();
7574 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
7575 ++rit)
7576 {
7577 for (HWData::SharedFolderList::iterator cit =
7578 mHWData.backedUpData()->mSharedFolders.begin();
7579 cit != mHWData.backedUpData()->mSharedFolders.end();
7580 ++cit)
7581 {
7582 if ((*cit)->name() != (*rit)->name() ||
7583 (*cit)->hostPath() != (*rit)->hostPath())
7584 {
7585 sharedFoldersChanged = true;
7586 break;
7587 }
7588 }
7589 }
7590 }
7591 }
7592
7593 if (!mStorageControllers.isNull())
7594 {
7595 if (mStorageControllers.isBackedUp())
7596 {
7597 /* unitialize all new devices (absent in the backed up list). */
7598 StorageControllerList::const_iterator it = mStorageControllers->begin();
7599 StorageControllerList *backedList = mStorageControllers.backedUpData();
7600 while (it != mStorageControllers->end())
7601 {
7602 if (std::find (backedList->begin(), backedList->end(), *it ) ==
7603 backedList->end())
7604 {
7605 (*it)->uninit();
7606 }
7607 ++it;
7608 }
7609
7610 /* restore the list */
7611 mStorageControllers.rollback();
7612 }
7613
7614 /* rollback any changes to devices after restoring the list */
7615 StorageControllerList::const_iterator it = mStorageControllers->begin();
7616 while (it != mStorageControllers->end())
7617 {
7618 if ((*it)->isModified())
7619 (*it)->rollback();
7620
7621 ++it;
7622 }
7623 }
7624
7625 mUserData.rollback();
7626
7627 mHWData.rollback();
7628
7629 if (mMediaData.isBackedUp())
7630 fixupMedia(false /* aCommit */);
7631
7632 /* check for changes in child objects */
7633
7634 bool vrdpChanged = false, usbChanged = false;
7635
7636 ComPtr<INetworkAdapter> networkAdapters [RT_ELEMENTS (mNetworkAdapters)];
7637 ComPtr<ISerialPort> serialPorts [RT_ELEMENTS (mSerialPorts)];
7638 ComPtr<IParallelPort> parallelPorts [RT_ELEMENTS (mParallelPorts)];
7639
7640 if (mBIOSSettings)
7641 mBIOSSettings->rollback();
7642
7643#ifdef VBOX_WITH_VRDP
7644 if (mVRDPServer)
7645 vrdpChanged = mVRDPServer->rollback();
7646#endif
7647
7648 if (mAudioAdapter)
7649 mAudioAdapter->rollback();
7650
7651 if (mUSBController)
7652 usbChanged = mUSBController->rollback();
7653
7654 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7655 if (mNetworkAdapters [slot])
7656 if (mNetworkAdapters [slot]->rollback())
7657 networkAdapters [slot] = mNetworkAdapters [slot];
7658
7659 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7660 if (mSerialPorts [slot])
7661 if (mSerialPorts [slot]->rollback())
7662 serialPorts [slot] = mSerialPorts [slot];
7663
7664 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7665 if (mParallelPorts [slot])
7666 if (mParallelPorts [slot]->rollback())
7667 parallelPorts [slot] = mParallelPorts [slot];
7668
7669 if (aNotify)
7670 {
7671 /* inform the direct session about changes */
7672
7673 ComObjPtr<Machine> that = this;
7674 alock.leave();
7675
7676 if (sharedFoldersChanged)
7677 that->onSharedFolderChange();
7678
7679 if (vrdpChanged)
7680 that->onVRDPServerChange();
7681 if (usbChanged)
7682 that->onUSBControllerChange();
7683
7684 for (ULONG slot = 0; slot < RT_ELEMENTS (networkAdapters); slot ++)
7685 if (networkAdapters [slot])
7686 that->onNetworkAdapterChange (networkAdapters [slot], FALSE);
7687 for (ULONG slot = 0; slot < RT_ELEMENTS (serialPorts); slot ++)
7688 if (serialPorts [slot])
7689 that->onSerialPortChange (serialPorts [slot]);
7690 for (ULONG slot = 0; slot < RT_ELEMENTS (parallelPorts); slot ++)
7691 if (parallelPorts [slot])
7692 that->onParallelPortChange (parallelPorts [slot]);
7693
7694 if (storageChanged)
7695 that->onStorageControllerChange();
7696 }
7697}
7698
7699/**
7700 * Commits all the changes to machine settings.
7701 *
7702 * Note that this operation is supposed to never fail.
7703 *
7704 * @note Locks this object and children for writing.
7705 */
7706void Machine::commit()
7707{
7708 AutoCaller autoCaller(this);
7709 AssertComRCReturnVoid (autoCaller.rc());
7710
7711 AutoCaller peerCaller (mPeer);
7712 AssertComRCReturnVoid (peerCaller.rc());
7713
7714 AutoMultiWriteLock2 alock (mPeer, this);
7715
7716 /*
7717 * use safe commit to ensure Snapshot machines (that share mUserData)
7718 * will still refer to a valid memory location
7719 */
7720 mUserData.commitCopy();
7721
7722 mHWData.commit();
7723
7724 if (mMediaData.isBackedUp())
7725 fixupMedia(true /* aCommit */);
7726
7727 mBIOSSettings->commit();
7728#ifdef VBOX_WITH_VRDP
7729 mVRDPServer->commit();
7730#endif
7731 mAudioAdapter->commit();
7732 mUSBController->commit();
7733
7734 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7735 mNetworkAdapters [slot]->commit();
7736 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7737 mSerialPorts [slot]->commit();
7738 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7739 mParallelPorts [slot]->commit();
7740
7741 bool commitStorageControllers = false;
7742
7743 if (mStorageControllers.isBackedUp())
7744 {
7745 mStorageControllers.commit();
7746
7747 if (mPeer)
7748 {
7749 AutoWriteLock peerlock (mPeer);
7750
7751 /* Commit all changes to new controllers (this will reshare data with
7752 * peers for thos who have peers) */
7753 StorageControllerList *newList = new StorageControllerList();
7754 StorageControllerList::const_iterator it = mStorageControllers->begin();
7755 while (it != mStorageControllers->end())
7756 {
7757 (*it)->commit();
7758
7759 /* look if this controller has a peer device */
7760 ComObjPtr<StorageController> peer = (*it)->peer();
7761 if (!peer)
7762 {
7763 /* no peer means the device is a newly created one;
7764 * create a peer owning data this device share it with */
7765 peer.createObject();
7766 peer->init (mPeer, *it, true /* aReshare */);
7767 }
7768 else
7769 {
7770 /* remove peer from the old list */
7771 mPeer->mStorageControllers->remove (peer);
7772 }
7773 /* and add it to the new list */
7774 newList->push_back(peer);
7775
7776 ++it;
7777 }
7778
7779 /* uninit old peer's controllers that are left */
7780 it = mPeer->mStorageControllers->begin();
7781 while (it != mPeer->mStorageControllers->end())
7782 {
7783 (*it)->uninit();
7784 ++it;
7785 }
7786
7787 /* attach new list of controllers to our peer */
7788 mPeer->mStorageControllers.attach (newList);
7789 }
7790 else
7791 {
7792 /* we have no peer (our parent is the newly created machine);
7793 * just commit changes to devices */
7794 commitStorageControllers = true;
7795 }
7796 }
7797 else
7798 {
7799 /* the list of controllers itself is not changed,
7800 * just commit changes to controllers themselves */
7801 commitStorageControllers = true;
7802 }
7803
7804 if (commitStorageControllers)
7805 {
7806 StorageControllerList::const_iterator it = mStorageControllers->begin();
7807 while (it != mStorageControllers->end())
7808 {
7809 (*it)->commit();
7810 ++it;
7811 }
7812 }
7813
7814 if (mType == IsSessionMachine)
7815 {
7816 /* attach new data to the primary machine and reshare it */
7817 mPeer->mUserData.attach (mUserData);
7818 mPeer->mHWData.attach (mHWData);
7819 /* mMediaData is reshared by fixupMedia */
7820 // mPeer->mMediaData.attach(mMediaData);
7821 Assert(mPeer->mMediaData.data() == mMediaData.data());
7822 }
7823}
7824
7825/**
7826 * Copies all the hardware data from the given machine.
7827 *
7828 * Currently, only called when the VM is being restored from a snapshot. In
7829 * particular, this implies that the VM is not running during this method's
7830 * call.
7831 *
7832 * @note This method must be called from under this object's lock.
7833 *
7834 * @note This method doesn't call #commit(), so all data remains backed up and
7835 * unsaved.
7836 */
7837void Machine::copyFrom (Machine *aThat)
7838{
7839 AssertReturnVoid (mType == IsMachine || mType == IsSessionMachine);
7840 AssertReturnVoid (aThat->mType == IsSnapshotMachine);
7841
7842 AssertReturnVoid (!Global::IsOnline (mData->mMachineState));
7843
7844 mHWData.assignCopy (aThat->mHWData);
7845
7846 // create copies of all shared folders (mHWData after attiching a copy
7847 // contains just references to original objects)
7848 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
7849 it != mHWData->mSharedFolders.end();
7850 ++it)
7851 {
7852 ComObjPtr<SharedFolder> folder;
7853 folder.createObject();
7854 HRESULT rc = folder->initCopy (machine(), *it);
7855 AssertComRC (rc);
7856 *it = folder;
7857 }
7858
7859 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
7860#ifdef VBOX_WITH_VRDP
7861 mVRDPServer->copyFrom (aThat->mVRDPServer);
7862#endif
7863 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
7864 mUSBController->copyFrom (aThat->mUSBController);
7865
7866 /* create private copies of all controllers */
7867 mStorageControllers.backup();
7868 mStorageControllers->clear();
7869 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
7870 it != aThat->mStorageControllers->end();
7871 ++it)
7872 {
7873 ComObjPtr<StorageController> ctrl;
7874 ctrl.createObject();
7875 ctrl->initCopy (this, *it);
7876 mStorageControllers->push_back(ctrl);
7877 }
7878
7879 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7880 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
7881 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7882 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
7883 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7884 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
7885}
7886
7887#ifdef VBOX_WITH_RESOURCE_USAGE_API
7888void Machine::registerMetrics (PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
7889{
7890 pm::CollectorHAL *hal = aCollector->getHAL();
7891 /* Create sub metrics */
7892 pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User",
7893 "Percentage of processor time spent in user mode by VM process.");
7894 pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel",
7895 "Percentage of processor time spent in kernel mode by VM process.");
7896 pm::SubMetric *ramUsageUsed = new pm::SubMetric ("RAM/Usage/Used",
7897 "Size of resident portion of VM process in memory.");
7898 /* Create and register base metrics */
7899 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw (hal, aMachine, pid,
7900 cpuLoadUser, cpuLoadKernel);
7901 aCollector->registerBaseMetric (cpuLoad);
7902 pm::BaseMetric *ramUsage = new pm::MachineRamUsage (hal, aMachine, pid,
7903 ramUsageUsed);
7904 aCollector->registerBaseMetric (ramUsage);
7905
7906 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser, 0));
7907 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7908 new pm::AggregateAvg()));
7909 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7910 new pm::AggregateMin()));
7911 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7912 new pm::AggregateMax()));
7913 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel, 0));
7914 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7915 new pm::AggregateAvg()));
7916 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7917 new pm::AggregateMin()));
7918 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7919 new pm::AggregateMax()));
7920
7921 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed, 0));
7922 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7923 new pm::AggregateAvg()));
7924 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7925 new pm::AggregateMin()));
7926 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7927 new pm::AggregateMax()));
7928};
7929
7930void Machine::unregisterMetrics (PerformanceCollector *aCollector, Machine *aMachine)
7931{
7932 aCollector->unregisterMetricsFor (aMachine);
7933 aCollector->unregisterBaseMetricsFor (aMachine);
7934};
7935#endif /* VBOX_WITH_RESOURCE_USAGE_API */
7936
7937
7938////////////////////////////////////////////////////////////////////////////////
7939
7940DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
7941
7942HRESULT SessionMachine::FinalConstruct()
7943{
7944 LogFlowThisFunc(("\n"));
7945
7946 /* set the proper type to indicate we're the SessionMachine instance */
7947 unconst(mType) = IsSessionMachine;
7948
7949#if defined(RT_OS_WINDOWS)
7950 mIPCSem = NULL;
7951#elif defined(RT_OS_OS2)
7952 mIPCSem = NULLHANDLE;
7953#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7954 mIPCSem = -1;
7955#else
7956# error "Port me!"
7957#endif
7958
7959 return S_OK;
7960}
7961
7962void SessionMachine::FinalRelease()
7963{
7964 LogFlowThisFunc(("\n"));
7965
7966 uninit (Uninit::Unexpected);
7967}
7968
7969/**
7970 * @note Must be called only by Machine::openSession() from its own write lock.
7971 */
7972HRESULT SessionMachine::init (Machine *aMachine)
7973{
7974 LogFlowThisFuncEnter();
7975 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
7976
7977 AssertReturn(aMachine, E_INVALIDARG);
7978
7979 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
7980
7981 /* Enclose the state transition NotReady->InInit->Ready */
7982 AutoInitSpan autoInitSpan(this);
7983 AssertReturn(autoInitSpan.isOk(), E_FAIL);
7984
7985 /* create the interprocess semaphore */
7986#if defined(RT_OS_WINDOWS)
7987 mIPCSemName = aMachine->mData->m_strConfigFileFull;
7988 for (size_t i = 0; i < mIPCSemName.length(); i++)
7989 if (mIPCSemName[i] == '\\')
7990 mIPCSemName[i] = '/';
7991 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
7992 ComAssertMsgRet (mIPCSem,
7993 ("Cannot create IPC mutex '%ls', err=%d",
7994 mIPCSemName.raw(), ::GetLastError()),
7995 E_FAIL);
7996#elif defined(RT_OS_OS2)
7997 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%RTuuid}",
7998 aMachine->mData->mUuid.raw());
7999 mIPCSemName = ipcSem;
8000 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
8001 ComAssertMsgRet (arc == NO_ERROR,
8002 ("Cannot create IPC mutex '%s', arc=%ld",
8003 ipcSem.raw(), arc),
8004 E_FAIL);
8005#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8006# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8007# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8008 /** @todo Check that this still works correctly. */
8009 AssertCompileSize(key_t, 8);
8010# else
8011 AssertCompileSize(key_t, 4);
8012# endif
8013 key_t key;
8014 mIPCSem = -1;
8015 mIPCKey = "0";
8016 for (uint32_t i = 0; i < 1 << 24; i++)
8017 {
8018 key = ((uint32_t)'V' << 24) | i;
8019 int sem = ::semget (key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8020 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8021 {
8022 mIPCSem = sem;
8023 if (sem >= 0)
8024 mIPCKey = BstrFmt ("%u", key);
8025 break;
8026 }
8027 }
8028# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8029 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8030 char *pszSemName = NULL;
8031 RTStrUtf8ToCurrentCP (&pszSemName, semName);
8032 key_t key = ::ftok (pszSemName, 'V');
8033 RTStrFree (pszSemName);
8034
8035 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8036# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8037
8038 int errnoSave = errno;
8039 if (mIPCSem < 0 && errnoSave == ENOSYS)
8040 {
8041 setError(E_FAIL,
8042 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8043 "support for SysV IPC. Check the host kernel configuration for "
8044 "CONFIG_SYSVIPC=y"));
8045 return E_FAIL;
8046 }
8047 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8048 * the IPC semaphores */
8049 if (mIPCSem < 0 && errnoSave == ENOSPC)
8050 {
8051#ifdef RT_OS_LINUX
8052 setError(E_FAIL,
8053 tr("Cannot create IPC semaphore because the system limit for the "
8054 "maximum number of semaphore sets (SEMMNI), or the system wide "
8055 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8056 "current set of SysV IPC semaphores can be determined from "
8057 "the file /proc/sysvipc/sem"));
8058#else
8059 setError(E_FAIL,
8060 tr("Cannot create IPC semaphore because the system-imposed limit "
8061 "on the maximum number of allowed semaphores or semaphore "
8062 "identifiers system-wide would be exceeded"));
8063#endif
8064 return E_FAIL;
8065 }
8066 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8067 E_FAIL);
8068 /* set the initial value to 1 */
8069 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
8070 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8071 E_FAIL);
8072#else
8073# error "Port me!"
8074#endif
8075
8076 /* memorize the peer Machine */
8077 unconst(mPeer) = aMachine;
8078 /* share the parent pointer */
8079 unconst(mParent) = aMachine->mParent;
8080
8081 /* take the pointers to data to share */
8082 mData.share (aMachine->mData);
8083 mSSData.share (aMachine->mSSData);
8084
8085 mUserData.share (aMachine->mUserData);
8086 mHWData.share (aMachine->mHWData);
8087 mMediaData.share(aMachine->mMediaData);
8088
8089 mStorageControllers.allocate();
8090 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
8091 it != aMachine->mStorageControllers->end();
8092 ++it)
8093 {
8094 ComObjPtr<StorageController> ctl;
8095 ctl.createObject();
8096 ctl->init(this, *it);
8097 mStorageControllers->push_back (ctl);
8098 }
8099
8100 unconst(mBIOSSettings).createObject();
8101 mBIOSSettings->init (this, aMachine->mBIOSSettings);
8102#ifdef VBOX_WITH_VRDP
8103 /* create another VRDPServer object that will be mutable */
8104 unconst(mVRDPServer).createObject();
8105 mVRDPServer->init (this, aMachine->mVRDPServer);
8106#endif
8107 /* create another audio adapter object that will be mutable */
8108 unconst(mAudioAdapter).createObject();
8109 mAudioAdapter->init (this, aMachine->mAudioAdapter);
8110 /* create a list of serial ports that will be mutable */
8111 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8112 {
8113 unconst(mSerialPorts [slot]).createObject();
8114 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
8115 }
8116 /* create a list of parallel ports that will be mutable */
8117 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8118 {
8119 unconst(mParallelPorts [slot]).createObject();
8120 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
8121 }
8122 /* create another USB controller object that will be mutable */
8123 unconst(mUSBController).createObject();
8124 mUSBController->init (this, aMachine->mUSBController);
8125
8126 /* create a list of network adapters that will be mutable */
8127 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8128 {
8129 unconst(mNetworkAdapters [slot]).createObject();
8130 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
8131 }
8132
8133 /* default is to delete saved state on Saved -> PoweredOff transition */
8134 mRemoveSavedState = true;
8135
8136 /* Confirm a successful initialization when it's the case */
8137 autoInitSpan.setSucceeded();
8138
8139 LogFlowThisFuncLeave();
8140 return S_OK;
8141}
8142
8143/**
8144 * Uninitializes this session object. If the reason is other than
8145 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
8146 *
8147 * @param aReason uninitialization reason
8148 *
8149 * @note Locks mParent + this object for writing.
8150 */
8151void SessionMachine::uninit (Uninit::Reason aReason)
8152{
8153 LogFlowThisFuncEnter();
8154 LogFlowThisFunc(("reason=%d\n", aReason));
8155
8156 /*
8157 * Strongly reference ourselves to prevent this object deletion after
8158 * mData->mSession.mMachine.setNull() below (which can release the last
8159 * reference and call the destructor). Important: this must be done before
8160 * accessing any members (and before AutoUninitSpan that does it as well).
8161 * This self reference will be released as the very last step on return.
8162 */
8163 ComObjPtr<SessionMachine> selfRef = this;
8164
8165 /* Enclose the state transition Ready->InUninit->NotReady */
8166 AutoUninitSpan autoUninitSpan(this);
8167 if (autoUninitSpan.uninitDone())
8168 {
8169 LogFlowThisFunc(("Already uninitialized\n"));
8170 LogFlowThisFuncLeave();
8171 return;
8172 }
8173
8174 if (autoUninitSpan.initFailed())
8175 {
8176 /* We've been called by init() because it's failed. It's not really
8177 * necessary (nor it's safe) to perform the regular uninit sequense
8178 * below, the following is enough.
8179 */
8180 LogFlowThisFunc(("Initialization failed.\n"));
8181#if defined(RT_OS_WINDOWS)
8182 if (mIPCSem)
8183 ::CloseHandle (mIPCSem);
8184 mIPCSem = NULL;
8185#elif defined(RT_OS_OS2)
8186 if (mIPCSem != NULLHANDLE)
8187 ::DosCloseMutexSem (mIPCSem);
8188 mIPCSem = NULLHANDLE;
8189#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8190 if (mIPCSem >= 0)
8191 ::semctl (mIPCSem, 0, IPC_RMID);
8192 mIPCSem = -1;
8193# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8194 mIPCKey = "0";
8195# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8196#else
8197# error "Port me!"
8198#endif
8199 uninitDataAndChildObjects();
8200 mData.free();
8201 unconst(mParent).setNull();
8202 unconst(mPeer).setNull();
8203 LogFlowThisFuncLeave();
8204 return;
8205 }
8206
8207 /* We need to lock this object in uninit() because the lock is shared
8208 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
8209 * and others need mParent lock. */
8210 AutoMultiWriteLock2 alock (mParent, this);
8211
8212#ifdef VBOX_WITH_RESOURCE_USAGE_API
8213 unregisterMetrics (mParent->performanceCollector(), mPeer);
8214#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8215
8216 MachineState_T lastState = mData->mMachineState;
8217 NOREF(lastState);
8218
8219 if (aReason == Uninit::Abnormal)
8220 {
8221 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
8222 Global::IsOnlineOrTransient (lastState)));
8223
8224 /* reset the state to Aborted */
8225 if (mData->mMachineState != MachineState_Aborted)
8226 setMachineState (MachineState_Aborted);
8227 }
8228
8229 if (isModified())
8230 {
8231 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
8232 rollback (false /* aNotify */);
8233 }
8234
8235 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
8236 if (!mSnapshotData.mStateFilePath.isEmpty())
8237 {
8238 LogWarningThisFunc(("canceling failed save state request!\n"));
8239 endSavingState (FALSE /* aSuccess */);
8240 }
8241 else if (!mSnapshotData.mSnapshot.isNull())
8242 {
8243 LogWarningThisFunc(("canceling untaken snapshot!\n"));
8244 endTakingSnapshot (FALSE /* aSuccess */);
8245 }
8246
8247#ifdef VBOX_WITH_USB
8248 /* release all captured USB devices */
8249 if (aReason == Uninit::Abnormal && Global::IsOnline (lastState))
8250 {
8251 /* Console::captureUSBDevices() is called in the VM process only after
8252 * setting the machine state to Starting or Restoring.
8253 * Console::detachAllUSBDevices() will be called upon successful
8254 * termination. So, we need to release USB devices only if there was
8255 * an abnormal termination of a running VM.
8256 *
8257 * This is identical to SessionMachine::DetachAllUSBDevices except
8258 * for the aAbnormal argument. */
8259 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8260 AssertComRC(rc);
8261 NOREF (rc);
8262
8263 USBProxyService *service = mParent->host()->usbProxyService();
8264 if (service)
8265 service->detachAllDevicesFromVM (this, true /* aDone */, true /* aAbnormal */);
8266 }
8267#endif /* VBOX_WITH_USB */
8268
8269 if (!mData->mSession.mType.isNull())
8270 {
8271 /* mType is not null when this machine's process has been started by
8272 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
8273 * need to queue the PID to reap the process (and avoid zombies on
8274 * Linux). */
8275 Assert (mData->mSession.mPid != NIL_RTPROCESS);
8276 mParent->addProcessToReap (mData->mSession.mPid);
8277 }
8278
8279 mData->mSession.mPid = NIL_RTPROCESS;
8280
8281 if (aReason == Uninit::Unexpected)
8282 {
8283 /* Uninitialization didn't come from #checkForDeath(), so tell the
8284 * client watcher thread to update the set of machines that have open
8285 * sessions. */
8286 mParent->updateClientWatcher();
8287 }
8288
8289 /* uninitialize all remote controls */
8290 if (mData->mSession.mRemoteControls.size())
8291 {
8292 LogFlowThisFunc(("Closing remote sessions (%d):\n",
8293 mData->mSession.mRemoteControls.size()));
8294
8295 Data::Session::RemoteControlList::iterator it =
8296 mData->mSession.mRemoteControls.begin();
8297 while (it != mData->mSession.mRemoteControls.end())
8298 {
8299 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
8300 HRESULT rc = (*it)->Uninitialize();
8301 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
8302 if (FAILED (rc))
8303 LogWarningThisFunc(("Forgot to close the remote session?\n"));
8304 ++it;
8305 }
8306 mData->mSession.mRemoteControls.clear();
8307 }
8308
8309 /*
8310 * An expected uninitialization can come only from #checkForDeath().
8311 * Otherwise it means that something's got really wrong (for examlple,
8312 * the Session implementation has released the VirtualBox reference
8313 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
8314 * etc). However, it's also possible, that the client releases the IPC
8315 * semaphore correctly (i.e. before it releases the VirtualBox reference),
8316 * but the VirtualBox release event comes first to the server process.
8317 * This case is practically possible, so we should not assert on an
8318 * unexpected uninit, just log a warning.
8319 */
8320
8321 if ((aReason == Uninit::Unexpected))
8322 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
8323
8324 if (aReason != Uninit::Normal)
8325 {
8326 mData->mSession.mDirectControl.setNull();
8327 }
8328 else
8329 {
8330 /* this must be null here (see #OnSessionEnd()) */
8331 Assert (mData->mSession.mDirectControl.isNull());
8332 Assert (mData->mSession.mState == SessionState_Closing);
8333 Assert (!mData->mSession.mProgress.isNull());
8334
8335 mData->mSession.mProgress->notifyComplete (S_OK);
8336 mData->mSession.mProgress.setNull();
8337 }
8338
8339 /* remove the association between the peer machine and this session machine */
8340 Assert (mData->mSession.mMachine == this ||
8341 aReason == Uninit::Unexpected);
8342
8343 /* reset the rest of session data */
8344 mData->mSession.mMachine.setNull();
8345 mData->mSession.mState = SessionState_Closed;
8346 mData->mSession.mType.setNull();
8347
8348 /* close the interprocess semaphore before leaving the exclusive lock */
8349#if defined(RT_OS_WINDOWS)
8350 if (mIPCSem)
8351 ::CloseHandle (mIPCSem);
8352 mIPCSem = NULL;
8353#elif defined(RT_OS_OS2)
8354 if (mIPCSem != NULLHANDLE)
8355 ::DosCloseMutexSem (mIPCSem);
8356 mIPCSem = NULLHANDLE;
8357#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8358 if (mIPCSem >= 0)
8359 ::semctl (mIPCSem, 0, IPC_RMID);
8360 mIPCSem = -1;
8361# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8362 mIPCKey = "0";
8363# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8364#else
8365# error "Port me!"
8366#endif
8367
8368 /* fire an event */
8369 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
8370
8371 uninitDataAndChildObjects();
8372
8373 /* free the essential data structure last */
8374 mData.free();
8375
8376 /* leave the exclusive lock before setting the below two to NULL */
8377 alock.leave();
8378
8379 unconst(mParent).setNull();
8380 unconst(mPeer).setNull();
8381
8382 LogFlowThisFuncLeave();
8383}
8384
8385// util::Lockable interface
8386////////////////////////////////////////////////////////////////////////////////
8387
8388/**
8389 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
8390 * with the primary Machine instance (mPeer).
8391 */
8392RWLockHandle *SessionMachine::lockHandle() const
8393{
8394 AssertReturn(!mPeer.isNull(), NULL);
8395 return mPeer->lockHandle();
8396}
8397
8398// IInternalMachineControl methods
8399////////////////////////////////////////////////////////////////////////////////
8400
8401/**
8402 * @note Locks this object for writing.
8403 */
8404STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
8405{
8406 AutoCaller autoCaller(this);
8407 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8408
8409 AutoWriteLock alock(this);
8410
8411 mRemoveSavedState = aRemove;
8412
8413 return S_OK;
8414}
8415
8416/**
8417 * @note Locks the same as #setMachineState() does.
8418 */
8419STDMETHODIMP SessionMachine::UpdateState (MachineState_T aMachineState)
8420{
8421 return setMachineState (aMachineState);
8422}
8423
8424/**
8425 * @note Locks this object for reading.
8426 */
8427STDMETHODIMP SessionMachine::GetIPCId (BSTR *aId)
8428{
8429 AutoCaller autoCaller(this);
8430 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8431
8432 AutoReadLock alock(this);
8433
8434#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8435 mIPCSemName.cloneTo(aId);
8436 return S_OK;
8437#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8438# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8439 mIPCKey.cloneTo(aId);
8440# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8441 mData->m_strConfigFileFull.cloneTo(aId);
8442# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8443 return S_OK;
8444#else
8445# error "Port me!"
8446#endif
8447}
8448
8449/**
8450 * Goes through the USB filters of the given machine to see if the given
8451 * device matches any filter or not.
8452 *
8453 * @note Locks the same as USBController::hasMatchingFilter() does.
8454 */
8455STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
8456 BOOL *aMatched,
8457 ULONG *aMaskedIfs)
8458{
8459 LogFlowThisFunc(("\n"));
8460
8461 CheckComArgNotNull (aUSBDevice);
8462 CheckComArgOutPointerValid(aMatched);
8463
8464 AutoCaller autoCaller(this);
8465 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8466
8467#ifdef VBOX_WITH_USB
8468 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice, aMaskedIfs);
8469#else
8470 NOREF(aUSBDevice);
8471 NOREF(aMaskedIfs);
8472 *aMatched = FALSE;
8473#endif
8474
8475 return S_OK;
8476}
8477
8478/**
8479 * @note Locks the same as Host::captureUSBDevice() does.
8480 */
8481STDMETHODIMP SessionMachine::CaptureUSBDevice (IN_BSTR aId)
8482{
8483 LogFlowThisFunc(("\n"));
8484
8485 AutoCaller autoCaller(this);
8486 AssertComRCReturnRC(autoCaller.rc());
8487
8488#ifdef VBOX_WITH_USB
8489 /* if captureDeviceForVM() fails, it must have set extended error info */
8490 MultiResult rc = mParent->host()->checkUSBProxyService();
8491 CheckComRCReturnRC(rc);
8492
8493 USBProxyService *service = mParent->host()->usbProxyService();
8494 AssertReturn(service, E_FAIL);
8495 return service->captureDeviceForVM (this, Guid(aId));
8496#else
8497 NOREF(aId);
8498 return E_NOTIMPL;
8499#endif
8500}
8501
8502/**
8503 * @note Locks the same as Host::detachUSBDevice() does.
8504 */
8505STDMETHODIMP SessionMachine::DetachUSBDevice (IN_BSTR aId, BOOL aDone)
8506{
8507 LogFlowThisFunc(("\n"));
8508
8509 AutoCaller autoCaller(this);
8510 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8511
8512#ifdef VBOX_WITH_USB
8513 USBProxyService *service = mParent->host()->usbProxyService();
8514 AssertReturn(service, E_FAIL);
8515 return service->detachDeviceFromVM (this, Guid(aId), !!aDone);
8516#else
8517 NOREF(aId);
8518 NOREF(aDone);
8519 return E_NOTIMPL;
8520#endif
8521}
8522
8523/**
8524 * Inserts all machine filters to the USB proxy service and then calls
8525 * Host::autoCaptureUSBDevices().
8526 *
8527 * Called by Console from the VM process upon VM startup.
8528 *
8529 * @note Locks what called methods lock.
8530 */
8531STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
8532{
8533 LogFlowThisFunc(("\n"));
8534
8535 AutoCaller autoCaller(this);
8536 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8537
8538#ifdef VBOX_WITH_USB
8539 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
8540 AssertComRC(rc);
8541 NOREF (rc);
8542
8543 USBProxyService *service = mParent->host()->usbProxyService();
8544 AssertReturn(service, E_FAIL);
8545 return service->autoCaptureDevicesForVM (this);
8546#else
8547 return S_OK;
8548#endif
8549}
8550
8551/**
8552 * Removes all machine filters from the USB proxy service and then calls
8553 * Host::detachAllUSBDevices().
8554 *
8555 * Called by Console from the VM process upon normal VM termination or by
8556 * SessionMachine::uninit() upon abnormal VM termination (from under the
8557 * Machine/SessionMachine lock).
8558 *
8559 * @note Locks what called methods lock.
8560 */
8561STDMETHODIMP SessionMachine::DetachAllUSBDevices (BOOL aDone)
8562{
8563 LogFlowThisFunc(("\n"));
8564
8565 AutoCaller autoCaller(this);
8566 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8567
8568#ifdef VBOX_WITH_USB
8569 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8570 AssertComRC(rc);
8571 NOREF (rc);
8572
8573 USBProxyService *service = mParent->host()->usbProxyService();
8574 AssertReturn(service, E_FAIL);
8575 return service->detachAllDevicesFromVM (this, !!aDone, false /* aAbnormal */);
8576#else
8577 NOREF(aDone);
8578 return S_OK;
8579#endif
8580}
8581
8582/**
8583 * @note Locks this object for writing.
8584 */
8585STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
8586 IProgress **aProgress)
8587{
8588 LogFlowThisFuncEnter();
8589
8590 AssertReturn(aSession, E_INVALIDARG);
8591 AssertReturn(aProgress, E_INVALIDARG);
8592
8593 AutoCaller autoCaller(this);
8594
8595 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
8596 /*
8597 * We don't assert below because it might happen that a non-direct session
8598 * informs us it is closed right after we've been uninitialized -- it's ok.
8599 */
8600 CheckComRCReturnRC(autoCaller.rc());
8601
8602 /* get IInternalSessionControl interface */
8603 ComPtr<IInternalSessionControl> control (aSession);
8604
8605 ComAssertRet (!control.isNull(), E_INVALIDARG);
8606
8607 AutoWriteLock alock(this);
8608
8609 if (control.equalsTo (mData->mSession.mDirectControl))
8610 {
8611 ComAssertRet (aProgress, E_POINTER);
8612
8613 /* The direct session is being normally closed by the client process
8614 * ----------------------------------------------------------------- */
8615
8616 /* go to the closing state (essential for all open*Session() calls and
8617 * for #checkForDeath()) */
8618 Assert (mData->mSession.mState == SessionState_Open);
8619 mData->mSession.mState = SessionState_Closing;
8620
8621 /* set direct control to NULL to release the remote instance */
8622 mData->mSession.mDirectControl.setNull();
8623 LogFlowThisFunc(("Direct control is set to NULL\n"));
8624
8625 /* Create the progress object the client will use to wait until
8626 * #checkForDeath() is called to uninitialize this session object after
8627 * it releases the IPC semaphore. */
8628 ComObjPtr<Progress> progress;
8629 progress.createObject();
8630 progress->init (mParent, static_cast <IMachine *> (mPeer),
8631 Bstr (tr ("Closing session")), FALSE /* aCancelable */);
8632 progress.queryInterfaceTo(aProgress);
8633 mData->mSession.mProgress = progress;
8634 }
8635 else
8636 {
8637 /* the remote session is being normally closed */
8638 Data::Session::RemoteControlList::iterator it =
8639 mData->mSession.mRemoteControls.begin();
8640 while (it != mData->mSession.mRemoteControls.end())
8641 {
8642 if (control.equalsTo (*it))
8643 break;
8644 ++it;
8645 }
8646 BOOL found = it != mData->mSession.mRemoteControls.end();
8647 ComAssertMsgRet (found, ("The session is not found in the session list!"),
8648 E_INVALIDARG);
8649 mData->mSession.mRemoteControls.remove (*it);
8650 }
8651
8652 LogFlowThisFuncLeave();
8653 return S_OK;
8654}
8655
8656/**
8657 * @note Locks this object for writing.
8658 */
8659STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
8660{
8661 LogFlowThisFuncEnter();
8662
8663 AssertReturn(aProgress, E_INVALIDARG);
8664 AssertReturn(aStateFilePath, E_POINTER);
8665
8666 AutoCaller autoCaller(this);
8667 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8668
8669 AutoWriteLock alock(this);
8670
8671 AssertReturn( mData->mMachineState == MachineState_Paused
8672 && mSnapshotData.mLastState == MachineState_Null
8673 && mSnapshotData.mProgressId.isEmpty()
8674 && mSnapshotData.mStateFilePath.isEmpty(),
8675 E_FAIL);
8676
8677 /* memorize the progress ID and add it to the global collection */
8678 Bstr progressId;
8679 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
8680 AssertComRCReturn (rc, rc);
8681 rc = mParent->addProgress (aProgress);
8682 AssertComRCReturn (rc, rc);
8683
8684 Bstr stateFilePath;
8685 /* stateFilePath is null when the machine is not running */
8686 if (mData->mMachineState == MachineState_Paused)
8687 {
8688 stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
8689 mUserData->mSnapshotFolderFull.raw(),
8690 RTPATH_DELIMITER, mData->mUuid.raw());
8691 }
8692
8693 /* fill in the snapshot data */
8694 mSnapshotData.mLastState = mData->mMachineState;
8695 mSnapshotData.mProgressId = Guid(progressId);
8696 mSnapshotData.mStateFilePath = stateFilePath;
8697
8698 /* set the state to Saving (this is expected by Console::SaveState()) */
8699 setMachineState (MachineState_Saving);
8700
8701 stateFilePath.cloneTo(aStateFilePath);
8702
8703 return S_OK;
8704}
8705
8706/**
8707 * @note Locks mParent + this object for writing.
8708 */
8709STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
8710{
8711 LogFlowThisFunc(("\n"));
8712
8713 AutoCaller autoCaller(this);
8714 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8715
8716 /* endSavingState() need mParent lock */
8717 AutoMultiWriteLock2 alock (mParent, this);
8718
8719 AssertReturn( mData->mMachineState == MachineState_Saving
8720 && mSnapshotData.mLastState != MachineState_Null
8721 && !mSnapshotData.mProgressId.isEmpty()
8722 && !mSnapshotData.mStateFilePath.isEmpty(),
8723 E_FAIL);
8724
8725 /*
8726 * on success, set the state to Saved;
8727 * on failure, set the state to the state we had when BeginSavingState() was
8728 * called (this is expected by Console::SaveState() and
8729 * Console::saveStateThread())
8730 */
8731 if (aSuccess)
8732 setMachineState (MachineState_Saved);
8733 else
8734 setMachineState (mSnapshotData.mLastState);
8735
8736 return endSavingState (aSuccess);
8737}
8738
8739/**
8740 * @note Locks this object for writing.
8741 */
8742STDMETHODIMP SessionMachine::AdoptSavedState (IN_BSTR aSavedStateFile)
8743{
8744 LogFlowThisFunc(("\n"));
8745
8746 AssertReturn(aSavedStateFile, E_INVALIDARG);
8747
8748 AutoCaller autoCaller(this);
8749 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8750
8751 AutoWriteLock alock(this);
8752
8753 AssertReturn( mData->mMachineState == MachineState_PoweredOff
8754 || mData->mMachineState == MachineState_Teleported
8755 || mData->mMachineState == MachineState_Aborted
8756 , E_FAIL); /** @todo setError. */
8757
8758 Utf8Str stateFilePathFull = aSavedStateFile;
8759 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8760 if (RT_FAILURE(vrc))
8761 return setError(VBOX_E_FILE_ERROR,
8762 tr("Invalid saved state file path '%ls' (%Rrc)"),
8763 aSavedStateFile,
8764 vrc);
8765
8766 mSSData->mStateFilePath = stateFilePathFull;
8767
8768 /* The below setMachineState() will detect the state transition and will
8769 * update the settings file */
8770
8771 return setMachineState (MachineState_Saved);
8772}
8773
8774STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
8775 ComSafeArrayOut(BSTR, aValues),
8776 ComSafeArrayOut(ULONG64, aTimestamps),
8777 ComSafeArrayOut(BSTR, aFlags))
8778{
8779 LogFlowThisFunc(("\n"));
8780
8781#ifdef VBOX_WITH_GUEST_PROPS
8782 using namespace guestProp;
8783
8784 AutoCaller autoCaller(this);
8785 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8786
8787 AutoReadLock alock(this);
8788
8789 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
8790 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
8791 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
8792 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
8793
8794 size_t cEntries = mHWData->mGuestProperties.size();
8795 com::SafeArray<BSTR> names (cEntries);
8796 com::SafeArray<BSTR> values (cEntries);
8797 com::SafeArray<ULONG64> timestamps (cEntries);
8798 com::SafeArray<BSTR> flags (cEntries);
8799 unsigned i = 0;
8800 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
8801 it != mHWData->mGuestProperties.end();
8802 ++it)
8803 {
8804 char szFlags[MAX_FLAGS_LEN + 1];
8805 it->strName.cloneTo(&names[i]);
8806 it->strValue.cloneTo(&values[i]);
8807 timestamps[i] = it->mTimestamp;
8808 /* If it is NULL, keep it NULL. */
8809 if (it->mFlags)
8810 {
8811 writeFlags(it->mFlags, szFlags);
8812 Bstr(szFlags).cloneTo(&flags[i]);
8813 }
8814 else
8815 flags[i] = NULL;
8816 ++i;
8817 }
8818 names.detachTo(ComSafeArrayOutArg(aNames));
8819 values.detachTo(ComSafeArrayOutArg(aValues));
8820 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
8821 flags.detachTo(ComSafeArrayOutArg(aFlags));
8822 mHWData->mPropertyServiceActive = true;
8823 return S_OK;
8824#else
8825 ReturnComNotImplemented();
8826#endif
8827}
8828
8829STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn (IN_BSTR, aNames),
8830 ComSafeArrayIn (IN_BSTR, aValues),
8831 ComSafeArrayIn (ULONG64, aTimestamps),
8832 ComSafeArrayIn (IN_BSTR, aFlags))
8833{
8834 LogFlowThisFunc(("\n"));
8835
8836#ifdef VBOX_WITH_GUEST_PROPS
8837 using namespace guestProp;
8838
8839 AutoCaller autoCaller(this);
8840 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8841
8842 AutoWriteLock alock(this);
8843
8844 /* Temporarily reset the registered flag, so that our machine state
8845 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in
8846 * all setters will return FALSE for a Machine instance if mRegistered
8847 * is TRUE). This is copied from registeredInit(), and may or may not be
8848 * the right way to handle this. */
8849 mData->mRegistered = FALSE;
8850 HRESULT rc = checkStateDependency(MutableStateDep);
8851 LogRel (("checkStateDependency(MutableStateDep) returned 0x%x\n", rc));
8852 CheckComRCReturnRC(rc);
8853
8854 // ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
8855
8856 AssertReturn(!ComSafeArrayInIsNull (aNames), E_POINTER);
8857 AssertReturn(!ComSafeArrayInIsNull (aValues), E_POINTER);
8858 AssertReturn(!ComSafeArrayInIsNull (aTimestamps), E_POINTER);
8859 AssertReturn(!ComSafeArrayInIsNull (aFlags), E_POINTER);
8860
8861 com::SafeArray<IN_BSTR> names (ComSafeArrayInArg (aNames));
8862 com::SafeArray<IN_BSTR> values (ComSafeArrayInArg (aValues));
8863 com::SafeArray<ULONG64> timestamps (ComSafeArrayInArg (aTimestamps));
8864 com::SafeArray<IN_BSTR> flags (ComSafeArrayInArg (aFlags));
8865 DiscardSettings();
8866 mHWData.backup();
8867 mHWData->mGuestProperties.erase (mHWData->mGuestProperties.begin(),
8868 mHWData->mGuestProperties.end());
8869 for (unsigned i = 0; i < names.size(); ++i)
8870 {
8871 uint32_t fFlags = NILFLAG;
8872 validateFlags (Utf8Str (flags[i]).raw(), &fFlags);
8873 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
8874 mHWData->mGuestProperties.push_back (property);
8875 }
8876 mHWData->mPropertyServiceActive = false;
8877 alock.unlock();
8878 SaveSettings();
8879 /* Restore the mRegistered flag. */
8880 mData->mRegistered = TRUE;
8881 return S_OK;
8882#else
8883 ReturnComNotImplemented();
8884#endif
8885}
8886
8887STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
8888 IN_BSTR aValue,
8889 ULONG64 aTimestamp,
8890 IN_BSTR aFlags)
8891{
8892 LogFlowThisFunc(("\n"));
8893
8894#ifdef VBOX_WITH_GUEST_PROPS
8895 using namespace guestProp;
8896
8897 CheckComArgNotNull (aName);
8898 if ((aValue != NULL) && (!VALID_PTR (aValue) || !VALID_PTR (aFlags)))
8899 return E_POINTER; /* aValue can be NULL to indicate deletion */
8900
8901 try
8902 {
8903 Utf8Str utf8Name(aName);
8904 Utf8Str utf8Flags(aFlags);
8905 Utf8Str utf8Patterns(mHWData->mGuestPropertyNotificationPatterns);
8906
8907 uint32_t fFlags = NILFLAG;
8908 if ((aFlags != NULL) && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags)))
8909 return E_INVALIDARG;
8910
8911 bool matchAll = false;
8912 if (utf8Patterns.isEmpty())
8913 matchAll = true;
8914
8915 AutoCaller autoCaller(this);
8916 CheckComRCReturnRC(autoCaller.rc());
8917
8918 AutoWriteLock alock(this);
8919
8920 HRESULT rc = checkStateDependency(MutableStateDep);
8921 CheckComRCReturnRC(rc);
8922
8923 mHWData.backup();
8924 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
8925 iter != mHWData->mGuestProperties.end();
8926 ++iter)
8927 if (utf8Name == iter->strName)
8928 {
8929 mHWData->mGuestProperties.erase(iter);
8930 break;
8931 }
8932 if (aValue != NULL)
8933 {
8934 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
8935 mHWData->mGuestProperties.push_back (property);
8936 }
8937
8938 /* send a callback notification if appropriate */
8939 alock.leave();
8940 if ( matchAll
8941 || RTStrSimplePatternMultiMatch(utf8Patterns.raw(),
8942 RTSTR_MAX,
8943 utf8Name.raw(),
8944 RTSTR_MAX, NULL)
8945 )
8946 mParent->onGuestPropertyChange(mData->mUuid,
8947 aName,
8948 aValue,
8949 aFlags);
8950 }
8951 catch(std::bad_alloc &)
8952 {
8953 return E_OUTOFMEMORY;
8954 }
8955 return S_OK;
8956#else
8957 ReturnComNotImplemented();
8958#endif
8959}
8960
8961// public methods only for internal purposes
8962/////////////////////////////////////////////////////////////////////////////
8963
8964/**
8965 * Called from the client watcher thread to check for expected or unexpected
8966 * death of the client process that has a direct session to this machine.
8967 *
8968 * On Win32 and on OS/2, this method is called only when we've got the
8969 * mutex (i.e. the client has either died or terminated normally) so it always
8970 * returns @c true (the client is terminated, the session machine is
8971 * uninitialized).
8972 *
8973 * On other platforms, the method returns @c true if the client process has
8974 * terminated normally or abnormally and the session machine was uninitialized,
8975 * and @c false if the client process is still alive.
8976 *
8977 * @note Locks this object for writing.
8978 */
8979bool SessionMachine::checkForDeath()
8980{
8981 Uninit::Reason reason;
8982 bool terminated = false;
8983
8984 /* Enclose autoCaller with a block because calling uninit() from under it
8985 * will deadlock. */
8986 {
8987 AutoCaller autoCaller(this);
8988 if (!autoCaller.isOk())
8989 {
8990 /* return true if not ready, to cause the client watcher to exclude
8991 * the corresponding session from watching */
8992 LogFlowThisFunc(("Already uninitialized!\n"));
8993 return true;
8994 }
8995
8996 AutoWriteLock alock(this);
8997
8998 /* Determine the reason of death: if the session state is Closing here,
8999 * everything is fine. Otherwise it means that the client did not call
9000 * OnSessionEnd() before it released the IPC semaphore. This may happen
9001 * either because the client process has abnormally terminated, or
9002 * because it simply forgot to call ISession::Close() before exiting. We
9003 * threat the latter also as an abnormal termination (see
9004 * Session::uninit() for details). */
9005 reason = mData->mSession.mState == SessionState_Closing ?
9006 Uninit::Normal :
9007 Uninit::Abnormal;
9008
9009#if defined(RT_OS_WINDOWS)
9010
9011 AssertMsg (mIPCSem, ("semaphore must be created"));
9012
9013 /* release the IPC mutex */
9014 ::ReleaseMutex (mIPCSem);
9015
9016 terminated = true;
9017
9018#elif defined(RT_OS_OS2)
9019
9020 AssertMsg (mIPCSem, ("semaphore must be created"));
9021
9022 /* release the IPC mutex */
9023 ::DosReleaseMutexSem (mIPCSem);
9024
9025 terminated = true;
9026
9027#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9028
9029 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9030
9031 int val = ::semctl (mIPCSem, 0, GETVAL);
9032 if (val > 0)
9033 {
9034 /* the semaphore is signaled, meaning the session is terminated */
9035 terminated = true;
9036 }
9037
9038#else
9039# error "Port me!"
9040#endif
9041
9042 } /* AutoCaller block */
9043
9044 if (terminated)
9045 uninit (reason);
9046
9047 return terminated;
9048}
9049
9050/**
9051 * @note Locks this object for reading.
9052 */
9053HRESULT SessionMachine::onNetworkAdapterChange (INetworkAdapter *networkAdapter, BOOL changeAdapter)
9054{
9055 LogFlowThisFunc(("\n"));
9056
9057 AutoCaller autoCaller(this);
9058 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9059
9060 ComPtr<IInternalSessionControl> directControl;
9061 {
9062 AutoReadLock alock(this);
9063 directControl = mData->mSession.mDirectControl;
9064 }
9065
9066 /* ignore notifications sent after #OnSessionEnd() is called */
9067 if (!directControl)
9068 return S_OK;
9069
9070 return directControl->OnNetworkAdapterChange (networkAdapter, changeAdapter);
9071}
9072
9073/**
9074 * @note Locks this object for reading.
9075 */
9076HRESULT SessionMachine::onSerialPortChange (ISerialPort *serialPort)
9077{
9078 LogFlowThisFunc(("\n"));
9079
9080 AutoCaller autoCaller(this);
9081 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9082
9083 ComPtr<IInternalSessionControl> directControl;
9084 {
9085 AutoReadLock alock(this);
9086 directControl = mData->mSession.mDirectControl;
9087 }
9088
9089 /* ignore notifications sent after #OnSessionEnd() is called */
9090 if (!directControl)
9091 return S_OK;
9092
9093 return directControl->OnSerialPortChange (serialPort);
9094}
9095
9096/**
9097 * @note Locks this object for reading.
9098 */
9099HRESULT SessionMachine::onParallelPortChange (IParallelPort *parallelPort)
9100{
9101 LogFlowThisFunc(("\n"));
9102
9103 AutoCaller autoCaller(this);
9104 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9105
9106 ComPtr<IInternalSessionControl> directControl;
9107 {
9108 AutoReadLock alock(this);
9109 directControl = mData->mSession.mDirectControl;
9110 }
9111
9112 /* ignore notifications sent after #OnSessionEnd() is called */
9113 if (!directControl)
9114 return S_OK;
9115
9116 return directControl->OnParallelPortChange (parallelPort);
9117}
9118
9119/**
9120 * @note Locks this object for reading.
9121 */
9122HRESULT SessionMachine::onStorageControllerChange ()
9123{
9124 LogFlowThisFunc(("\n"));
9125
9126 AutoCaller autoCaller(this);
9127 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9128
9129 ComPtr<IInternalSessionControl> directControl;
9130 {
9131 AutoReadLock alock(this);
9132 directControl = mData->mSession.mDirectControl;
9133 }
9134
9135 /* ignore notifications sent after #OnSessionEnd() is called */
9136 if (!directControl)
9137 return S_OK;
9138
9139 return directControl->OnStorageControllerChange ();
9140}
9141
9142/**
9143 * @note Locks this object for reading.
9144 */
9145HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment)
9146{
9147 LogFlowThisFunc(("\n"));
9148
9149 AutoCaller autoCaller(this);
9150 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9151
9152 ComPtr<IInternalSessionControl> directControl;
9153 {
9154 AutoReadLock alock(this);
9155 directControl = mData->mSession.mDirectControl;
9156 }
9157
9158 /* ignore notifications sent after #OnSessionEnd() is called */
9159 if (!directControl)
9160 return S_OK;
9161
9162 return directControl->OnMediumChange(aAttachment);
9163}
9164
9165/**
9166 * @note Locks this object for reading.
9167 */
9168HRESULT SessionMachine::onVRDPServerChange()
9169{
9170 LogFlowThisFunc(("\n"));
9171
9172 AutoCaller autoCaller(this);
9173 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9174
9175 ComPtr<IInternalSessionControl> directControl;
9176 {
9177 AutoReadLock alock(this);
9178 directControl = mData->mSession.mDirectControl;
9179 }
9180
9181 /* ignore notifications sent after #OnSessionEnd() is called */
9182 if (!directControl)
9183 return S_OK;
9184
9185 return directControl->OnVRDPServerChange();
9186}
9187
9188/**
9189 * @note Locks this object for reading.
9190 */
9191HRESULT SessionMachine::onUSBControllerChange()
9192{
9193 LogFlowThisFunc(("\n"));
9194
9195 AutoCaller autoCaller(this);
9196 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9197
9198 ComPtr<IInternalSessionControl> directControl;
9199 {
9200 AutoReadLock alock(this);
9201 directControl = mData->mSession.mDirectControl;
9202 }
9203
9204 /* ignore notifications sent after #OnSessionEnd() is called */
9205 if (!directControl)
9206 return S_OK;
9207
9208 return directControl->OnUSBControllerChange();
9209}
9210
9211/**
9212 * @note Locks this object for reading.
9213 */
9214HRESULT SessionMachine::onSharedFolderChange()
9215{
9216 LogFlowThisFunc(("\n"));
9217
9218 AutoCaller autoCaller(this);
9219 AssertComRCReturnRC(autoCaller.rc());
9220
9221 ComPtr<IInternalSessionControl> directControl;
9222 {
9223 AutoReadLock alock(this);
9224 directControl = mData->mSession.mDirectControl;
9225 }
9226
9227 /* ignore notifications sent after #OnSessionEnd() is called */
9228 if (!directControl)
9229 return S_OK;
9230
9231 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
9232}
9233
9234/**
9235 * Returns @c true if this machine's USB controller reports it has a matching
9236 * filter for the given USB device and @c false otherwise.
9237 *
9238 * @note Locks this object for reading.
9239 */
9240bool SessionMachine::hasMatchingUSBFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
9241{
9242 AutoCaller autoCaller(this);
9243 /* silently return if not ready -- this method may be called after the
9244 * direct machine session has been called */
9245 if (!autoCaller.isOk())
9246 return false;
9247
9248 AutoReadLock alock(this);
9249
9250#ifdef VBOX_WITH_USB
9251 switch (mData->mMachineState)
9252 {
9253 case MachineState_Starting:
9254 case MachineState_Restoring:
9255 case MachineState_TeleportingIn:
9256 case MachineState_Paused:
9257 case MachineState_Running:
9258 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
9259 * elsewhere... */
9260 return mUSBController->hasMatchingFilter (aDevice, aMaskedIfs);
9261 default: break;
9262 }
9263#else
9264 NOREF(aDevice);
9265 NOREF(aMaskedIfs);
9266#endif
9267 return false;
9268}
9269
9270/**
9271 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9272 */
9273HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
9274 IVirtualBoxErrorInfo *aError,
9275 ULONG aMaskedIfs)
9276{
9277 LogFlowThisFunc(("\n"));
9278
9279 AutoCaller autoCaller(this);
9280
9281 /* This notification may happen after the machine object has been
9282 * uninitialized (the session was closed), so don't assert. */
9283 CheckComRCReturnRC(autoCaller.rc());
9284
9285 ComPtr<IInternalSessionControl> directControl;
9286 {
9287 AutoReadLock alock(this);
9288 directControl = mData->mSession.mDirectControl;
9289 }
9290
9291 /* fail on notifications sent after #OnSessionEnd() is called, it is
9292 * expected by the caller */
9293 if (!directControl)
9294 return E_FAIL;
9295
9296 /* No locks should be held at this point. */
9297 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9298 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9299
9300 return directControl->OnUSBDeviceAttach (aDevice, aError, aMaskedIfs);
9301}
9302
9303/**
9304 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9305 */
9306HRESULT SessionMachine::onUSBDeviceDetach (IN_BSTR aId,
9307 IVirtualBoxErrorInfo *aError)
9308{
9309 LogFlowThisFunc(("\n"));
9310
9311 AutoCaller autoCaller(this);
9312
9313 /* This notification may happen after the machine object has been
9314 * uninitialized (the session was closed), so don't assert. */
9315 CheckComRCReturnRC(autoCaller.rc());
9316
9317 ComPtr<IInternalSessionControl> directControl;
9318 {
9319 AutoReadLock alock(this);
9320 directControl = mData->mSession.mDirectControl;
9321 }
9322
9323 /* fail on notifications sent after #OnSessionEnd() is called, it is
9324 * expected by the caller */
9325 if (!directControl)
9326 return E_FAIL;
9327
9328 /* No locks should be held at this point. */
9329 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9330 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9331
9332 return directControl->OnUSBDeviceDetach (aId, aError);
9333}
9334
9335// protected methods
9336/////////////////////////////////////////////////////////////////////////////
9337
9338/**
9339 * Helper method to finalize saving the state.
9340 *
9341 * @note Must be called from under this object's lock.
9342 *
9343 * @param aSuccess TRUE if the snapshot has been taken successfully
9344 *
9345 * @note Locks mParent + this objects for writing.
9346 */
9347HRESULT SessionMachine::endSavingState (BOOL aSuccess)
9348{
9349 LogFlowThisFuncEnter();
9350
9351 AutoCaller autoCaller(this);
9352 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9353
9354 /* saveSettings() needs mParent lock */
9355 AutoMultiWriteLock2 alock (mParent, this);
9356
9357 HRESULT rc = S_OK;
9358
9359 if (aSuccess)
9360 {
9361 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
9362
9363 /* save all VM settings */
9364 rc = saveSettings();
9365 }
9366 else
9367 {
9368 /* delete the saved state file (it might have been already created) */
9369 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
9370 }
9371
9372 /* remove the completed progress object */
9373 mParent->removeProgress(mSnapshotData.mProgressId);
9374
9375 /* clear out the temporary saved state data */
9376 mSnapshotData.mLastState = MachineState_Null;
9377 mSnapshotData.mProgressId.clear();
9378 mSnapshotData.mStateFilePath.setNull();
9379
9380 LogFlowThisFuncLeave();
9381 return rc;
9382}
9383
9384/**
9385 * Locks the attached media.
9386 *
9387 * All attached hard disks are locked for writing and DVD/floppy are locked for
9388 * reading. Parents of attached hard disks (if any) are locked for reading.
9389 *
9390 * This method also performs accessibility check of all media it locks: if some
9391 * media is inaccessible, the method will return a failure and a bunch of
9392 * extended error info objects per each inaccessible medium.
9393 *
9394 * Note that this method is atomic: if it returns a success, all media are
9395 * locked as described above; on failure no media is locked at all (all
9396 * succeeded individual locks will be undone).
9397 *
9398 * This method is intended to be called when the machine is in Starting or
9399 * Restoring state and asserts otherwise.
9400 *
9401 * The locks made by this method must be undone by calling #unlockMedia() when
9402 * no more needed.
9403 */
9404HRESULT SessionMachine::lockMedia()
9405{
9406 AutoCaller autoCaller(this);
9407 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9408
9409 AutoWriteLock alock(this);
9410
9411 AssertReturn( mData->mMachineState == MachineState_Starting
9412 || mData->mMachineState == MachineState_Restoring
9413 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
9414
9415 typedef std::list <ComPtr<IMedium> > MediaList;
9416
9417 try
9418 {
9419 HRESULT rc = S_OK;
9420
9421 ErrorInfoKeeper eik(true /* aIsNull */);
9422 MultiResult mrc(S_OK);
9423
9424 /* Lock all medium objects attached to the VM.
9425 * Get status for inaccessible media as well. */
9426 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9427 it != mMediaData->mAttachments.end();
9428 ++it)
9429 {
9430 DeviceType_T devType = (*it)->type();
9431 ComObjPtr<Medium> medium = (*it)->medium();
9432
9433 bool first = true;
9434
9435 /** @todo split out the media locking, and put it into
9436 * MediumImpl.cpp, as it needs this functionality too. */
9437 while (!medium.isNull())
9438 {
9439 MediumState_T mediumState = medium->state();
9440
9441 /* accessibility check must be first, otherwise locking
9442 * interferes with getting the medium state. */
9443 if (mediumState == MediumState_Inaccessible)
9444 {
9445 rc = medium->RefreshState(&mediumState);
9446 CheckComRCThrowRC(rc);
9447
9448 if (mediumState == MediumState_Inaccessible)
9449 {
9450 Bstr error;
9451 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
9452 CheckComRCThrowRC(rc);
9453
9454 Bstr loc;
9455 rc = medium->COMGETTER(Location)(loc.asOutParam());
9456 CheckComRCThrowRC(rc);
9457
9458 /* collect multiple errors */
9459 eik.restore();
9460
9461 /* be in sync with MediumBase::setStateError() */
9462 Assert(!error.isEmpty());
9463 mrc = setError(E_FAIL,
9464 tr("Medium '%ls' is not accessible. %ls"),
9465 loc.raw(),
9466 error.raw());
9467
9468 eik.fetch();
9469 }
9470 }
9471
9472 if (first)
9473 {
9474 if (devType != DeviceType_DVD)
9475 {
9476 /* HardDisk and Floppy medium must be locked for writing */
9477 rc = medium->LockWrite(NULL);
9478 CheckComRCThrowRC(rc);
9479 }
9480 else
9481 {
9482 /* DVD medium must be locked for reading */
9483 rc = medium->LockRead(NULL);
9484 CheckComRCThrowRC(rc);
9485 }
9486
9487 mData->mSession.mLockedMedia.push_back(
9488 Data::Session::LockedMedia::value_type(
9489 ComPtr<IMedium>(medium), true));
9490
9491 first = false;
9492 }
9493 else
9494 {
9495 rc = medium->LockRead(NULL);
9496 CheckComRCThrowRC(rc);
9497
9498 mData->mSession.mLockedMedia.push_back(
9499 Data::Session::LockedMedia::value_type(
9500 ComPtr<IMedium>(medium), false));
9501 }
9502
9503
9504 /* no locks or callers here since there should be no way to
9505 * change the hard disk parent at this point (as it is still
9506 * attached to the machine) */
9507 medium = medium->parent();
9508 }
9509 }
9510
9511 eik.restore();
9512 CheckComRCThrowRC((HRESULT)mrc);
9513 }
9514 catch (HRESULT aRC)
9515 {
9516 /* Unlock all locked media on failure */
9517 unlockMedia();
9518 return aRC;
9519 }
9520
9521 return S_OK;
9522}
9523
9524/**
9525 * Undoes the locks made by by #lockMedia().
9526 */
9527void SessionMachine::unlockMedia()
9528{
9529 AutoCaller autoCaller(this);
9530 AssertComRCReturnVoid (autoCaller.rc());
9531
9532 AutoWriteLock alock(this);
9533
9534 /* we may be holding important error info on the current thread;
9535 * preserve it */
9536 ErrorInfoKeeper eik;
9537
9538 HRESULT rc = S_OK;
9539
9540 for (Data::Session::LockedMedia::const_iterator
9541 it = mData->mSession.mLockedMedia.begin();
9542 it != mData->mSession.mLockedMedia.end(); ++it)
9543 {
9544 MediumState_T state;
9545 if (it->second)
9546 rc = it->first->UnlockWrite (&state);
9547 else
9548 rc = it->first->UnlockRead (&state);
9549
9550 /* The second can happen if an object was re-locked in
9551 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
9552 * image was unmounted at runtime. */
9553 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
9554 }
9555
9556 mData->mSession.mLockedMedia.clear();
9557}
9558
9559/**
9560 * Helper to change the machine state (reimplementation).
9561 *
9562 * @note Locks this object for writing.
9563 */
9564HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
9565{
9566 LogFlowThisFuncEnter();
9567 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
9568
9569 AutoCaller autoCaller(this);
9570 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9571
9572 AutoWriteLock alock(this);
9573
9574 MachineState_T oldMachineState = mData->mMachineState;
9575
9576 AssertMsgReturn (oldMachineState != aMachineState,
9577 ("oldMachineState=%d, aMachineState=%d\n",
9578 oldMachineState, aMachineState), E_FAIL);
9579
9580 HRESULT rc = S_OK;
9581
9582 int stsFlags = 0;
9583 bool deleteSavedState = false;
9584
9585 /* detect some state transitions */
9586
9587 if ( ( oldMachineState == MachineState_Saved
9588 && aMachineState == MachineState_Restoring)
9589 || ( ( oldMachineState == MachineState_PoweredOff
9590 || oldMachineState == MachineState_Teleported
9591 || oldMachineState == MachineState_Aborted
9592 )
9593 && ( aMachineState == MachineState_TeleportingIn
9594 || aMachineState == MachineState_Starting
9595 )
9596 )
9597 )
9598 {
9599 /* The EMT thread is about to start */
9600
9601 /* Nothing to do here for now... */
9602
9603 /// @todo NEWMEDIA don't let mDVDDrive and other children
9604 /// change anything when in the Starting/Restoring state
9605 }
9606 else if ( ( oldMachineState == MachineState_Running
9607 || oldMachineState == MachineState_Paused
9608 || oldMachineState == MachineState_Teleporting
9609 || oldMachineState == MachineState_LiveSnapshotting
9610 || oldMachineState == MachineState_Stuck
9611 || oldMachineState == MachineState_Starting
9612 || oldMachineState == MachineState_Stopping
9613 || oldMachineState == MachineState_Saving
9614 || oldMachineState == MachineState_Restoring
9615 || oldMachineState == MachineState_TeleportingPausedVM
9616 || oldMachineState == MachineState_TeleportingIn
9617 )
9618 && ( aMachineState == MachineState_PoweredOff
9619 || aMachineState == MachineState_Saved
9620 || aMachineState == MachineState_Teleported
9621 || aMachineState == MachineState_Aborted
9622 )
9623 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
9624 * snapshot */
9625 && ( mSnapshotData.mSnapshot.isNull()
9626 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
9627 )
9628 )
9629 {
9630 /* The EMT thread has just stopped, unlock attached media. Note that as
9631 * opposed to locking that is done from Console, we do unlocking here
9632 * because the VM process may have aborted before having a chance to
9633 * properly unlock all media it locked. */
9634
9635 unlockMedia();
9636 }
9637
9638 if (oldMachineState == MachineState_Restoring)
9639 {
9640 if (aMachineState != MachineState_Saved)
9641 {
9642 /*
9643 * delete the saved state file once the machine has finished
9644 * restoring from it (note that Console sets the state from
9645 * Restoring to Saved if the VM couldn't restore successfully,
9646 * to give the user an ability to fix an error and retry --
9647 * we keep the saved state file in this case)
9648 */
9649 deleteSavedState = true;
9650 }
9651 }
9652 else if ( oldMachineState == MachineState_Saved
9653 && ( aMachineState == MachineState_PoweredOff
9654 || aMachineState == MachineState_Aborted
9655 || aMachineState == MachineState_Teleported
9656 )
9657 )
9658 {
9659 /*
9660 * delete the saved state after Console::DiscardSavedState() is called
9661 * or if the VM process (owning a direct VM session) crashed while the
9662 * VM was Saved
9663 */
9664
9665 /// @todo (dmik)
9666 // Not sure that deleting the saved state file just because of the
9667 // client death before it attempted to restore the VM is a good
9668 // thing. But when it crashes we need to go to the Aborted state
9669 // which cannot have the saved state file associated... The only
9670 // way to fix this is to make the Aborted condition not a VM state
9671 // but a bool flag: i.e., when a crash occurs, set it to true and
9672 // change the state to PoweredOff or Saved depending on the
9673 // saved state presence.
9674
9675 deleteSavedState = true;
9676 mData->mCurrentStateModified = TRUE;
9677 stsFlags |= SaveSTS_CurStateModified;
9678 }
9679
9680 if ( aMachineState == MachineState_Starting
9681 || aMachineState == MachineState_Restoring
9682 || aMachineState == MachineState_TeleportingIn
9683 )
9684 {
9685 /* set the current state modified flag to indicate that the current
9686 * state is no more identical to the state in the
9687 * current snapshot */
9688 if (!mData->mCurrentSnapshot.isNull())
9689 {
9690 mData->mCurrentStateModified = TRUE;
9691 stsFlags |= SaveSTS_CurStateModified;
9692 }
9693 }
9694
9695 if (deleteSavedState)
9696 {
9697 if (mRemoveSavedState)
9698 {
9699 Assert(!mSSData->mStateFilePath.isEmpty());
9700 RTFileDelete(mSSData->mStateFilePath.c_str());
9701 }
9702 mSSData->mStateFilePath.setNull();
9703 stsFlags |= SaveSTS_StateFilePath;
9704 }
9705
9706 /* redirect to the underlying peer machine */
9707 mPeer->setMachineState (aMachineState);
9708
9709 if ( aMachineState == MachineState_PoweredOff
9710 || aMachineState == MachineState_Teleported
9711 || aMachineState == MachineState_Aborted
9712 || aMachineState == MachineState_Saved)
9713 {
9714 /* the machine has stopped execution
9715 * (or the saved state file was adopted) */
9716 stsFlags |= SaveSTS_StateTimeStamp;
9717 }
9718
9719 if ( ( oldMachineState == MachineState_PoweredOff
9720 || oldMachineState == MachineState_Aborted
9721 || oldMachineState == MachineState_Teleported
9722 )
9723 && aMachineState == MachineState_Saved)
9724 {
9725 /* the saved state file was adopted */
9726 Assert(!mSSData->mStateFilePath.isEmpty());
9727 stsFlags |= SaveSTS_StateFilePath;
9728 }
9729
9730 rc = saveStateSettings (stsFlags);
9731
9732 if ( ( oldMachineState != MachineState_PoweredOff
9733 && oldMachineState != MachineState_Aborted
9734 && oldMachineState != MachineState_Teleported
9735 )
9736 && ( aMachineState == MachineState_PoweredOff
9737 || aMachineState == MachineState_Aborted
9738 || aMachineState == MachineState_Teleported
9739 )
9740 )
9741 {
9742 /* we've been shut down for any reason */
9743 /* no special action so far */
9744 }
9745
9746 LogFlowThisFunc(("rc=%08X\n", rc));
9747 LogFlowThisFuncLeave();
9748 return rc;
9749}
9750
9751/**
9752 * Sends the current machine state value to the VM process.
9753 *
9754 * @note Locks this object for reading, then calls a client process.
9755 */
9756HRESULT SessionMachine::updateMachineStateOnClient()
9757{
9758 AutoCaller autoCaller(this);
9759 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9760
9761 ComPtr<IInternalSessionControl> directControl;
9762 {
9763 AutoReadLock alock(this);
9764 AssertReturn(!!mData, E_FAIL);
9765 directControl = mData->mSession.mDirectControl;
9766
9767 /* directControl may be already set to NULL here in #OnSessionEnd()
9768 * called too early by the direct session process while there is still
9769 * some operation (like discarding the snapshot) in progress. The client
9770 * process in this case is waiting inside Session::close() for the
9771 * "end session" process object to complete, while #uninit() called by
9772 * #checkForDeath() on the Watcher thread is waiting for the pending
9773 * operation to complete. For now, we accept this inconsitent behavior
9774 * and simply do nothing here. */
9775
9776 if (mData->mSession.mState == SessionState_Closing)
9777 return S_OK;
9778
9779 AssertReturn(!directControl.isNull(), E_FAIL);
9780 }
9781
9782 return directControl->UpdateMachineState (mData->mMachineState);
9783}
Note: See TracBrowser for help on using the repository browser.

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