VirtualBox

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

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

Main/MediumAttachment: change return value of Controller getter to return a reference to the StorageController instead of just the name

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