VirtualBox

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

Last change on this file since 23938 was 23931, checked in by vboxsync, 15 years ago

Main/MachineImpl: add a parameter validity check

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 346.8 KB
Line 
1/* $Id: MachineImpl.cpp 23931 2009-10-21 12:55:11Z vboxsync $ */
2
3/** @file
4 * Implementation of IMachine in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/* Make sure all the stdint.h macros are included - must come first! */
24#ifndef __STDC_LIMIT_MACROS
25# define __STDC_LIMIT_MACROS
26#endif
27#ifndef __STDC_CONSTANT_MACROS
28# define __STDC_CONSTANT_MACROS
29#endif
30
31#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
32# include <errno.h>
33# include <sys/types.h>
34# include <sys/stat.h>
35# include <sys/ipc.h>
36# include <sys/sem.h>
37#endif
38
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "ProgressImpl.h"
42#include "MediumAttachmentImpl.h"
43#include "MediumImpl.h"
44#include "USBControllerImpl.h"
45#include "HostImpl.h"
46#include "SharedFolderImpl.h"
47#include "GuestOSTypeImpl.h"
48#include "VirtualBoxErrorInfoImpl.h"
49#include "GuestImpl.h"
50#include "StorageControllerImpl.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "Logging.h"
57#include "Performance.h"
58
59#include <stdio.h>
60#include <stdlib.h>
61
62#include <iprt/path.h>
63#include <iprt/dir.h>
64#include <iprt/asm.h>
65#include <iprt/process.h>
66#include <iprt/cpputils.h>
67#include <iprt/env.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71
72#include <VBox/err.h>
73#include <VBox/param.h>
74#include <VBox/settings.h>
75
76#ifdef VBOX_WITH_GUEST_PROPS
77# include <VBox/HostServices/GuestPropertySvc.h>
78# include <VBox/com/array.h>
79#endif
80
81#include <algorithm>
82
83#include <typeinfo>
84
85#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
86#define HOSTSUFF_EXE ".exe"
87#else /* !RT_OS_WINDOWS */
88#define HOSTSUFF_EXE ""
89#endif /* !RT_OS_WINDOWS */
90
91// defines / prototypes
92/////////////////////////////////////////////////////////////////////////////
93
94// 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)->matches((*thatIt)->controllerName(),
312 (*thatIt)->port(),
313 (*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, aControllerName, 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::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
2525 LONG aDevice, BOOL aPassthrough)
2526{
2527 CheckComArgNotNull(aControllerName);
2528
2529 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
2530 aControllerName, aControllerPort, aDevice, aPassthrough));
2531
2532 AutoCaller autoCaller(this);
2533 CheckComRCReturnRC(autoCaller.rc());
2534
2535 AutoWriteLock alock(this);
2536
2537 HRESULT rc = checkStateDependency(MutableStateDep);
2538 CheckComRCReturnRC(rc);
2539
2540 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2541
2542 if (Global::IsOnlineOrTransient(mData->mMachineState))
2543 return setError(VBOX_E_INVALID_VM_STATE,
2544 tr("Invalid machine state: %s"),
2545 Global::stringifyMachineState(mData->mMachineState));
2546
2547 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2548 aControllerName,
2549 aControllerPort,
2550 aDevice);
2551 if (!pAttach)
2552 return setError(VBOX_E_OBJECT_NOT_FOUND,
2553 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2554 aDevice, aControllerPort, aControllerName);
2555
2556
2557 mMediaData.backup();
2558
2559 AutoWriteLock attLock(pAttach);
2560
2561 if (pAttach->type() != DeviceType_DVD)
2562 return setError(E_INVALIDARG,
2563 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
2564 aDevice, aControllerPort, aControllerName);
2565 pAttach->updatePassthrough(aPassthrough);
2566
2567 return S_OK;
2568}
2569
2570STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
2571 LONG aControllerPort,
2572 LONG aDevice,
2573 IN_BSTR aId)
2574{
2575 int rc = S_OK;
2576 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2577 aControllerName, aControllerPort, aDevice));
2578
2579 CheckComArgNotNull(aControllerName);
2580 CheckComArgNotNull(aId);
2581
2582 AutoCaller autoCaller(this);
2583 CheckComRCReturnRC(autoCaller.rc());
2584
2585 AutoWriteLock alock(this);
2586
2587 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2588 aControllerName,
2589 aControllerPort,
2590 aDevice);
2591 if (pAttach.isNull())
2592 return setError(VBOX_E_OBJECT_NOT_FOUND,
2593 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
2594 aDevice, aControllerPort, aControllerName);
2595
2596 Guid id(aId);
2597 ComObjPtr<Medium> medium;
2598 switch (pAttach->type())
2599 {
2600 case DeviceType_DVD:
2601 if (!id.isEmpty())
2602 {
2603 /* find a DVD by host device UUID */
2604 SafeIfaceArray<IMedium> drivevec;
2605 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2606 if (SUCCEEDED(rc))
2607 {
2608 for (size_t i = 0; i < drivevec.size(); ++i)
2609 {
2610 /// @todo eliminate this conversion
2611 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2612 if (id == med->id())
2613 {
2614 medium = med;
2615 break;
2616 }
2617 }
2618 }
2619 /* find a DVD by UUID */
2620 if (medium.isNull())
2621 rc = mParent->findDVDImage(&id, NULL, true /* aDoSetError */, &medium);
2622 }
2623 CheckComRCReturnRC(rc);
2624 break;
2625 case DeviceType_Floppy:
2626 if (!id.isEmpty())
2627 {
2628 /* find a Floppy by host device UUID */
2629 SafeIfaceArray<IMedium> drivevec;
2630 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2631 if (SUCCEEDED(rc))
2632 {
2633 for (size_t i = 0; i < drivevec.size(); ++i)
2634 {
2635 /// @todo eliminate this conversion
2636 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2637 if (id == med->id())
2638 {
2639 medium = med;
2640 break;
2641 }
2642 }
2643 }
2644 /* find a Floppy by UUID */
2645 if (medium.isNull())
2646 rc = mParent->findFloppyImage(&id, NULL, true /* aDoSetError */, &medium);
2647 }
2648 CheckComRCReturnRC(rc);
2649 break;
2650 default:
2651 return setError(VBOX_E_INVALID_OBJECT_STATE,
2652 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
2653 aDevice, aControllerPort, aControllerName);
2654 }
2655
2656 if (SUCCEEDED(rc))
2657 {
2658
2659 mMediaData.backup();
2660 /* The backup operation makes the pAttach reference point to the
2661 * old settings. Re-get the correct reference. */
2662 pAttach = findAttachment(mMediaData->mAttachments,
2663 aControllerName,
2664 aControllerPort,
2665 aDevice);
2666 AutoWriteLock attLock(pAttach);
2667 if (!medium.isNull())
2668 medium->attachTo(mData->mUuid);
2669 pAttach->updateMedium(medium, false /* aImplicit */);
2670 }
2671
2672 alock.unlock();
2673 onMediumChange(pAttach);
2674
2675 return rc;
2676}
2677
2678STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
2679 LONG aControllerPort,
2680 LONG aDevice,
2681 IMedium **aMedium)
2682{
2683 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2684 aControllerName, aControllerPort, aDevice));
2685
2686 CheckComArgNotNull(aControllerName);
2687 CheckComArgOutPointerValid(aMedium);
2688
2689 AutoCaller autoCaller(this);
2690 CheckComRCReturnRC(autoCaller.rc());
2691
2692 AutoReadLock alock(this);
2693
2694 *aMedium = NULL;
2695
2696 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2697 aControllerName,
2698 aControllerPort,
2699 aDevice);
2700 if (pAttach.isNull())
2701 return setError(VBOX_E_OBJECT_NOT_FOUND,
2702 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2703 aDevice, aControllerPort, aControllerName);
2704
2705 pAttach->medium().queryInterfaceTo(aMedium);
2706
2707 return S_OK;
2708}
2709
2710STDMETHODIMP Machine::GetSerialPort (ULONG slot, ISerialPort **port)
2711{
2712 CheckComArgOutPointerValid(port);
2713 CheckComArgExpr (slot, slot < RT_ELEMENTS (mSerialPorts));
2714
2715 AutoCaller autoCaller(this);
2716 CheckComRCReturnRC(autoCaller.rc());
2717
2718 AutoReadLock alock(this);
2719
2720 mSerialPorts [slot].queryInterfaceTo(port);
2721
2722 return S_OK;
2723}
2724
2725STDMETHODIMP Machine::GetParallelPort (ULONG slot, IParallelPort **port)
2726{
2727 CheckComArgOutPointerValid(port);
2728 CheckComArgExpr (slot, slot < RT_ELEMENTS (mParallelPorts));
2729
2730 AutoCaller autoCaller(this);
2731 CheckComRCReturnRC(autoCaller.rc());
2732
2733 AutoReadLock alock(this);
2734
2735 mParallelPorts [slot].queryInterfaceTo(port);
2736
2737 return S_OK;
2738}
2739
2740STDMETHODIMP Machine::GetNetworkAdapter (ULONG slot, INetworkAdapter **adapter)
2741{
2742 CheckComArgOutPointerValid(adapter);
2743 CheckComArgExpr (slot, slot < RT_ELEMENTS (mNetworkAdapters));
2744
2745 AutoCaller autoCaller(this);
2746 CheckComRCReturnRC(autoCaller.rc());
2747
2748 AutoReadLock alock(this);
2749
2750 mNetworkAdapters[slot].queryInterfaceTo(adapter);
2751
2752 return S_OK;
2753}
2754
2755STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
2756{
2757 if (ComSafeArrayOutIsNull(aKeys))
2758 return E_POINTER;
2759
2760 AutoCaller autoCaller (this);
2761 CheckComRCReturnRC (autoCaller.rc());
2762
2763 AutoReadLock alock (this);
2764
2765 com::SafeArray<BSTR> saKeys(mData->m_pMachineConfigFile->mapExtraDataItems.size());
2766 int i = 0;
2767 for (settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.begin();
2768 it != mData->m_pMachineConfigFile->mapExtraDataItems.end();
2769 ++it, ++i)
2770 {
2771 const Utf8Str &strKey = it->first;
2772 strKey.cloneTo(&saKeys[i]);
2773 }
2774 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
2775
2776 return S_OK;
2777 }
2778
2779 /**
2780 * @note Locks this object for reading.
2781 */
2782STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
2783 BSTR *aValue)
2784{
2785 CheckComArgNotNull(aKey);
2786 CheckComArgOutPointerValid(aValue);
2787
2788 AutoCaller autoCaller (this);
2789 CheckComRCReturnRC (autoCaller.rc());
2790
2791 /* start with nothing found */
2792 Bstr bstrResult("");
2793
2794 AutoReadLock alock (this);
2795
2796 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
2797 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
2798 // found:
2799 bstrResult = it->second; // source is a Utf8Str
2800
2801 /* return the result to caller (may be empty) */
2802 bstrResult.cloneTo(aValue);
2803
2804 return S_OK;
2805}
2806
2807 /**
2808 * @note Locks mParent for writing + this object for writing.
2809 */
2810STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
2811{
2812 CheckComArgNotNull(aKey);
2813
2814 AutoCaller autoCaller(this);
2815 CheckComRCReturnRC (autoCaller.rc());
2816
2817 Utf8Str strKey(aKey);
2818 Utf8Str strValue(aValue);
2819 Utf8Str strOldValue; // empty
2820
2821 // locking note: we only hold the read lock briefly to look up the old value,
2822 // then release it and call the onExtraCanChange callbacks. There is a small
2823 // chance of a race insofar as the callback might be called twice if two callers
2824 // change the same key at the same time, but that's a much better solution
2825 // than the deadlock we had here before. The actual changing of the extradata
2826 // is then performed under the write lock and race-free.
2827
2828 // look up the old value first; if nothing's changed then we need not do anything
2829 {
2830 AutoReadLock alock(this); // hold read lock only while looking up
2831 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(strKey);
2832 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
2833 strOldValue = it->second;
2834 }
2835
2836 bool fChanged;
2837 if ((fChanged = (strOldValue != strValue)))
2838 {
2839 // ask for permission from all listeners outside the locks;
2840 // onExtraDataCanChange() only briefly requests the VirtualBox
2841 // lock to copy the list of callbacks to invoke
2842 Bstr error;
2843 Bstr bstrValue;
2844 if (aValue)
2845 bstrValue = aValue;
2846 else
2847 bstrValue = (const char *)"";
2848
2849 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
2850 {
2851 const char *sep = error.isEmpty() ? "" : ": ";
2852 CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
2853 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
2854 sep, err));
2855 return setError(E_ACCESSDENIED,
2856 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
2857 aKey,
2858 bstrValue.raw(),
2859 sep,
2860 err);
2861 }
2862
2863 // data is changing and change not vetoed: then write it out under the locks
2864
2865 // saveSettings() needs VirtualBox write lock
2866 AutoMultiWriteLock2 alock(mParent, this);
2867
2868 if (mType == IsSnapshotMachine)
2869 {
2870 HRESULT rc = checkStateDependency(MutableStateDep);
2871 CheckComRCReturnRC (rc);
2872 }
2873
2874 if (strValue.isEmpty())
2875 mData->m_pMachineConfigFile->mapExtraDataItems.erase(strKey);
2876 else
2877 mData->m_pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
2878 // creates a new key if needed
2879
2880 /* save settings on success */
2881 HRESULT rc = saveSettings();
2882 CheckComRCReturnRC (rc);
2883 }
2884
2885 // fire notification outside the lock
2886 if (fChanged)
2887 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
2888
2889 return S_OK;
2890}
2891
2892STDMETHODIMP Machine::SaveSettings()
2893{
2894 AutoCaller autoCaller(this);
2895 CheckComRCReturnRC(autoCaller.rc());
2896
2897 /* saveSettings() needs mParent lock */
2898 AutoMultiWriteLock2 alock(mParent, this);
2899
2900 /* when there was auto-conversion, we want to save the file even if
2901 * the VM is saved */
2902 HRESULT rc = checkStateDependency(MutableStateDep);
2903 CheckComRCReturnRC(rc);
2904
2905 /* the settings file path may never be null */
2906 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
2907
2908 /* save all VM data excluding snapshots */
2909 return saveSettings();
2910}
2911
2912STDMETHODIMP Machine::DiscardSettings()
2913{
2914 AutoCaller autoCaller(this);
2915 CheckComRCReturnRC(autoCaller.rc());
2916
2917 AutoWriteLock alock(this);
2918
2919 HRESULT rc = checkStateDependency(MutableStateDep);
2920 CheckComRCReturnRC(rc);
2921
2922 /*
2923 * during this rollback, the session will be notified if data has
2924 * been actually changed
2925 */
2926 rollback (true /* aNotify */);
2927
2928 return S_OK;
2929}
2930
2931STDMETHODIMP Machine::DeleteSettings()
2932{
2933 AutoCaller autoCaller(this);
2934 CheckComRCReturnRC(autoCaller.rc());
2935
2936 AutoWriteLock alock(this);
2937
2938 HRESULT rc = checkStateDependency(MutableStateDep);
2939 CheckComRCReturnRC(rc);
2940
2941 if (mData->mRegistered)
2942 return setError(VBOX_E_INVALID_VM_STATE,
2943 tr("Cannot delete settings of a registered machine"));
2944
2945 /* delete the settings only when the file actually exists */
2946 if (mData->m_pMachineConfigFile->fileExists())
2947 {
2948 int vrc = RTFileDelete(mData->m_strConfigFileFull.c_str());
2949 if (RT_FAILURE(vrc))
2950 return setError(VBOX_E_IPRT_ERROR,
2951 tr("Could not delete the settings file '%s' (%Rrc)"),
2952 mData->m_strConfigFileFull.raw(),
2953 vrc);
2954
2955 /* delete the Logs folder, nothing important should be left
2956 * there (we don't check for errors because the user might have
2957 * some private files there that we don't want to delete) */
2958 Utf8Str logFolder;
2959 getLogFolder(logFolder);
2960 Assert(logFolder.length());
2961 if (RTDirExists(logFolder.c_str()))
2962 {
2963 /* Delete all VBox.log[.N] files from the Logs folder
2964 * (this must be in sync with the rotation logic in
2965 * Console::powerUpThread()). Also, delete the VBox.png[.N]
2966 * files that may have been created by the GUI. */
2967 Utf8Str log = Utf8StrFmt("%s/VBox.log", logFolder.raw());
2968 RTFileDelete(log.c_str());
2969 log = Utf8StrFmt("%s/VBox.png", logFolder.raw());
2970 RTFileDelete(log.c_str());
2971 for (int i = 3; i >= 0; i--)
2972 {
2973 log = Utf8StrFmt("%s/VBox.log.%d", logFolder.raw(), i);
2974 RTFileDelete(log.c_str());
2975 log = Utf8StrFmt("%s/VBox.png.%d", logFolder.raw(), i);
2976 RTFileDelete(log.c_str());
2977 }
2978
2979 RTDirRemove(logFolder.c_str());
2980 }
2981
2982 /* delete the Snapshots folder, nothing important should be left
2983 * there (we don't check for errors because the user might have
2984 * some private files there that we don't want to delete) */
2985 Utf8Str snapshotFolder(mUserData->mSnapshotFolderFull);
2986 Assert(snapshotFolder.length());
2987 if (RTDirExists(snapshotFolder.c_str()))
2988 RTDirRemove(snapshotFolder.c_str());
2989
2990 /* delete the directory that contains the settings file, but only
2991 * if it matches the VM name (i.e. a structure created by default in
2992 * prepareSaveSettings()) */
2993 {
2994 Utf8Str settingsDir;
2995 if (isInOwnDir(&settingsDir))
2996 RTDirRemove(settingsDir.c_str());
2997 }
2998 }
2999
3000 return S_OK;
3001}
3002
3003STDMETHODIMP Machine::GetSnapshot (IN_BSTR aId, ISnapshot **aSnapshot)
3004{
3005 CheckComArgOutPointerValid(aSnapshot);
3006
3007 AutoCaller autoCaller(this);
3008 CheckComRCReturnRC(autoCaller.rc());
3009
3010 AutoReadLock alock(this);
3011
3012 Guid id(aId);
3013 ComObjPtr<Snapshot> snapshot;
3014
3015 HRESULT rc = findSnapshot (id, snapshot, true /* aSetError */);
3016 snapshot.queryInterfaceTo(aSnapshot);
3017
3018 return rc;
3019}
3020
3021STDMETHODIMP Machine::FindSnapshot (IN_BSTR aName, ISnapshot **aSnapshot)
3022{
3023 CheckComArgNotNull (aName);
3024 CheckComArgOutPointerValid(aSnapshot);
3025
3026 AutoCaller autoCaller(this);
3027 CheckComRCReturnRC(autoCaller.rc());
3028
3029 AutoReadLock alock(this);
3030
3031 ComObjPtr<Snapshot> snapshot;
3032
3033 HRESULT rc = findSnapshot (aName, snapshot, true /* aSetError */);
3034 snapshot.queryInterfaceTo(aSnapshot);
3035
3036 return rc;
3037}
3038
3039STDMETHODIMP Machine::SetCurrentSnapshot (IN_BSTR /* aId */)
3040{
3041 /// @todo (dmik) don't forget to set
3042 // mData->mCurrentStateModified to FALSE
3043
3044 return setError (E_NOTIMPL, "Not implemented");
3045}
3046
3047STDMETHODIMP Machine::CreateSharedFolder (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
3048{
3049 CheckComArgNotNull(aName);
3050 CheckComArgNotNull(aHostPath);
3051
3052 AutoCaller autoCaller(this);
3053 CheckComRCReturnRC(autoCaller.rc());
3054
3055 AutoWriteLock alock(this);
3056
3057 HRESULT rc = checkStateDependency(MutableStateDep);
3058 CheckComRCReturnRC(rc);
3059
3060 ComObjPtr<SharedFolder> sharedFolder;
3061 rc = findSharedFolder (aName, sharedFolder, false /* aSetError */);
3062 if (SUCCEEDED(rc))
3063 return setError(VBOX_E_OBJECT_IN_USE,
3064 tr("Shared folder named '%ls' already exists"),
3065 aName);
3066
3067 sharedFolder.createObject();
3068 rc = sharedFolder->init (machine(), aName, aHostPath, aWritable);
3069 CheckComRCReturnRC(rc);
3070
3071 mHWData.backup();
3072 mHWData->mSharedFolders.push_back (sharedFolder);
3073
3074 /* inform the direct session if any */
3075 alock.leave();
3076 onSharedFolderChange();
3077
3078 return S_OK;
3079}
3080
3081STDMETHODIMP Machine::RemoveSharedFolder (IN_BSTR aName)
3082{
3083 CheckComArgNotNull (aName);
3084
3085 AutoCaller autoCaller(this);
3086 CheckComRCReturnRC(autoCaller.rc());
3087
3088 AutoWriteLock alock(this);
3089
3090 HRESULT rc = checkStateDependency(MutableStateDep);
3091 CheckComRCReturnRC(rc);
3092
3093 ComObjPtr<SharedFolder> sharedFolder;
3094 rc = findSharedFolder (aName, sharedFolder, true /* aSetError */);
3095 CheckComRCReturnRC(rc);
3096
3097 mHWData.backup();
3098 mHWData->mSharedFolders.remove (sharedFolder);
3099
3100 /* inform the direct session if any */
3101 alock.leave();
3102 onSharedFolderChange();
3103
3104 return S_OK;
3105}
3106
3107STDMETHODIMP Machine::CanShowConsoleWindow (BOOL *aCanShow)
3108{
3109 CheckComArgOutPointerValid(aCanShow);
3110
3111 /* start with No */
3112 *aCanShow = FALSE;
3113
3114 AutoCaller autoCaller(this);
3115 AssertComRCReturnRC(autoCaller.rc());
3116
3117 ComPtr<IInternalSessionControl> directControl;
3118 {
3119 AutoReadLock alock(this);
3120
3121 if (mData->mSession.mState != SessionState_Open)
3122 return setError(VBOX_E_INVALID_VM_STATE,
3123 tr("Machine session is not open (session state: %s)"),
3124 Global::stringifySessionState(mData->mSession.mState));
3125
3126 directControl = mData->mSession.mDirectControl;
3127 }
3128
3129 /* ignore calls made after #OnSessionEnd() is called */
3130 if (!directControl)
3131 return S_OK;
3132
3133 ULONG64 dummy;
3134 return directControl->OnShowWindow (TRUE /* aCheck */, aCanShow, &dummy);
3135}
3136
3137STDMETHODIMP Machine::ShowConsoleWindow (ULONG64 *aWinId)
3138{
3139 CheckComArgOutPointerValid(aWinId);
3140
3141 AutoCaller autoCaller(this);
3142 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3143
3144 ComPtr<IInternalSessionControl> directControl;
3145 {
3146 AutoReadLock alock(this);
3147
3148 if (mData->mSession.mState != SessionState_Open)
3149 return setError(E_FAIL,
3150 tr("Machine session is not open (session state: %s)"),
3151 Global::stringifySessionState(mData->mSession.mState));
3152
3153 directControl = mData->mSession.mDirectControl;
3154 }
3155
3156 /* ignore calls made after #OnSessionEnd() is called */
3157 if (!directControl)
3158 return S_OK;
3159
3160 BOOL dummy;
3161 return directControl->OnShowWindow (FALSE /* aCheck */, &dummy, aWinId);
3162}
3163
3164STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
3165 BSTR *aValue,
3166 ULONG64 *aTimestamp,
3167 BSTR *aFlags)
3168{
3169#if !defined (VBOX_WITH_GUEST_PROPS)
3170 ReturnComNotImplemented();
3171#else
3172 CheckComArgNotNull(aName);
3173 CheckComArgOutPointerValid(aValue);
3174 CheckComArgOutPointerValid(aTimestamp);
3175 CheckComArgOutPointerValid(aFlags);
3176
3177 AutoCaller autoCaller(this);
3178 CheckComRCReturnRC(autoCaller.rc());
3179
3180 AutoReadLock alock(this);
3181
3182 using namespace guestProp;
3183 HRESULT rc = E_FAIL;
3184
3185 Utf8Str strName(aName);
3186
3187 if (!mHWData->mPropertyServiceActive)
3188 {
3189 bool found = false;
3190 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
3191 (it != mHWData->mGuestProperties.end()) && !found;
3192 ++it)
3193 {
3194 if (it->strName == strName)
3195 {
3196 char szFlags[MAX_FLAGS_LEN + 1];
3197 it->strValue.cloneTo(aValue);
3198 *aTimestamp = it->mTimestamp;
3199 writeFlags(it->mFlags, szFlags);
3200 Bstr(szFlags).cloneTo(aFlags);
3201 found = true;
3202 }
3203 }
3204 rc = S_OK;
3205 }
3206 else
3207 {
3208 ComPtr<IInternalSessionControl> directControl =
3209 mData->mSession.mDirectControl;
3210
3211 /* just be on the safe side when calling another process */
3212 alock.unlock();
3213
3214 /* fail if we were called after #OnSessionEnd() is called. This is a
3215 * silly race condition. */
3216
3217 if (!directControl)
3218 rc = E_FAIL;
3219 else
3220 rc = directControl->AccessGuestProperty (aName, NULL, NULL,
3221 false /* isSetter */,
3222 aValue, aTimestamp, aFlags);
3223 }
3224 return rc;
3225#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3226}
3227
3228STDMETHODIMP Machine::GetGuestPropertyValue (IN_BSTR aName, BSTR *aValue)
3229{
3230 ULONG64 dummyTimestamp;
3231 BSTR dummyFlags;
3232 return GetGuestProperty (aName, aValue, &dummyTimestamp, &dummyFlags);
3233}
3234
3235STDMETHODIMP Machine::GetGuestPropertyTimestamp (IN_BSTR aName, ULONG64 *aTimestamp)
3236{
3237 BSTR dummyValue;
3238 BSTR dummyFlags;
3239 return GetGuestProperty (aName, &dummyValue, aTimestamp, &dummyFlags);
3240}
3241
3242STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName,
3243 IN_BSTR aValue,
3244 IN_BSTR aFlags)
3245{
3246#if !defined (VBOX_WITH_GUEST_PROPS)
3247 ReturnComNotImplemented();
3248#else
3249 using namespace guestProp;
3250
3251 CheckComArgNotNull(aName);
3252 CheckComArgNotNull(aValue);
3253 if ((aFlags != NULL) && !VALID_PTR (aFlags))
3254 return E_INVALIDARG;
3255
3256 HRESULT rc = S_OK;
3257
3258 try
3259 {
3260 Utf8Str utf8Name(aName);
3261 Utf8Str utf8Flags(aFlags);
3262 Utf8Str utf8Patterns(mHWData->mGuestPropertyNotificationPatterns);
3263
3264 bool matchAll = false;
3265 if (utf8Patterns.isEmpty())
3266 matchAll = true;
3267
3268 uint32_t fFlags = NILFLAG;
3269 if ( (aFlags != NULL)
3270 && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags))
3271 )
3272 return setError(E_INVALIDARG,
3273 tr("Invalid flag values: '%ls'"),
3274 aFlags);
3275
3276 AutoCaller autoCaller(this);
3277 CheckComRCReturnRC(autoCaller.rc());
3278
3279 AutoWriteLock alock(this);
3280
3281 rc = checkStateDependency(MutableStateDep);
3282 CheckComRCReturnRC(rc);
3283
3284 rc = S_OK;
3285
3286 if (!mHWData->mPropertyServiceActive)
3287 {
3288 bool found = false;
3289 HWData::GuestProperty property;
3290 property.mFlags = NILFLAG;
3291
3292 if (SUCCEEDED(rc))
3293 {
3294 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3295 it != mHWData->mGuestProperties.end();
3296 ++it)
3297 if (it->strName == utf8Name)
3298 {
3299 property = *it;
3300 if (it->mFlags & (RDONLYHOST))
3301 rc = setError(E_ACCESSDENIED,
3302 tr("The property '%ls' cannot be changed by the host"),
3303 aName);
3304 else
3305 {
3306 mHWData.backup();
3307 /* The backup() operation invalidates our iterator, so
3308 * get a new one. */
3309 for (it = mHWData->mGuestProperties.begin();
3310 it->strName != utf8Name;
3311 ++it)
3312 ;
3313 mHWData->mGuestProperties.erase (it);
3314 }
3315 found = true;
3316 break;
3317 }
3318 }
3319 if (found && SUCCEEDED(rc))
3320 {
3321 if (*aValue)
3322 {
3323 RTTIMESPEC time;
3324 property.strValue = aValue;
3325 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3326 if (aFlags != NULL)
3327 property.mFlags = fFlags;
3328 mHWData->mGuestProperties.push_back (property);
3329 }
3330 }
3331 else if (SUCCEEDED(rc) && *aValue)
3332 {
3333 RTTIMESPEC time;
3334 mHWData.backup();
3335 property.strName = aName;
3336 property.strValue = aValue;
3337 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3338 property.mFlags = fFlags;
3339 mHWData->mGuestProperties.push_back (property);
3340 }
3341 if ( SUCCEEDED(rc)
3342 && ( matchAll
3343 || RTStrSimplePatternMultiMatch (utf8Patterns.raw(), RTSTR_MAX,
3344 utf8Name.raw(), RTSTR_MAX, NULL)
3345 )
3346 )
3347 mParent->onGuestPropertyChange (mData->mUuid, aName, aValue, aFlags);
3348 }
3349 else
3350 {
3351 ComPtr<IInternalSessionControl> directControl =
3352 mData->mSession.mDirectControl;
3353
3354 /* just be on the safe side when calling another process */
3355 alock.leave();
3356
3357 BSTR dummy = NULL;
3358 ULONG64 dummy64;
3359 if (!directControl)
3360 rc = E_FAIL;
3361 else
3362 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
3363 true /* isSetter */,
3364 &dummy, &dummy64, &dummy);
3365 }
3366 }
3367 catch (std::bad_alloc &)
3368 {
3369 rc = E_OUTOFMEMORY;
3370 }
3371
3372 return rc;
3373#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3374}
3375
3376STDMETHODIMP Machine::SetGuestPropertyValue (IN_BSTR aName, IN_BSTR aValue)
3377{
3378 return SetGuestProperty (aName, aValue, NULL);
3379}
3380
3381STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
3382 ComSafeArrayOut(BSTR, aNames),
3383 ComSafeArrayOut(BSTR, aValues),
3384 ComSafeArrayOut(ULONG64, aTimestamps),
3385 ComSafeArrayOut(BSTR, aFlags))
3386{
3387#if !defined (VBOX_WITH_GUEST_PROPS)
3388 ReturnComNotImplemented();
3389#else
3390 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
3391 return E_POINTER;
3392
3393 CheckComArgOutSafeArrayPointerValid(aNames);
3394 CheckComArgOutSafeArrayPointerValid(aValues);
3395 CheckComArgOutSafeArrayPointerValid(aTimestamps);
3396 CheckComArgOutSafeArrayPointerValid(aFlags);
3397
3398 AutoCaller autoCaller(this);
3399 CheckComRCReturnRC(autoCaller.rc());
3400
3401 AutoReadLock alock(this);
3402
3403 using namespace guestProp;
3404 HRESULT rc = E_FAIL;
3405
3406 Utf8Str strPatterns(aPatterns);
3407
3408 bool matchAll = false;
3409 if ((NULL == aPatterns) || (0 == aPatterns[0]))
3410 matchAll = true;
3411 if (!mHWData->mPropertyServiceActive)
3412 {
3413
3414 /*
3415 * Look for matching patterns and build up a list.
3416 */
3417 HWData::GuestPropertyList propList;
3418 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3419 it != mHWData->mGuestProperties.end();
3420 ++it)
3421 if ( matchAll
3422 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
3423 RTSTR_MAX,
3424 it->strName.raw(),
3425 RTSTR_MAX, NULL)
3426 )
3427 propList.push_back(*it);
3428
3429 /*
3430 * And build up the arrays for returning the property information.
3431 */
3432 size_t cEntries = propList.size();
3433 SafeArray<BSTR> names (cEntries);
3434 SafeArray<BSTR> values (cEntries);
3435 SafeArray<ULONG64> timestamps (cEntries);
3436 SafeArray<BSTR> flags (cEntries);
3437 size_t iProp = 0;
3438 for (HWData::GuestPropertyList::iterator it = propList.begin();
3439 it != propList.end();
3440 ++it)
3441 {
3442 char szFlags[MAX_FLAGS_LEN + 1];
3443 it->strName.cloneTo(&names[iProp]);
3444 it->strValue.cloneTo(&values[iProp]);
3445 timestamps[iProp] = it->mTimestamp;
3446 writeFlags(it->mFlags, szFlags);
3447 Bstr(szFlags).cloneTo(&flags[iProp]);
3448 ++iProp;
3449 }
3450 names.detachTo(ComSafeArrayOutArg(aNames));
3451 values.detachTo(ComSafeArrayOutArg(aValues));
3452 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
3453 flags.detachTo(ComSafeArrayOutArg(aFlags));
3454 rc = S_OK;
3455 }
3456 else
3457 {
3458 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
3459
3460 /* just be on the safe side when calling another process */
3461 alock.unlock();
3462
3463 if (!directControl)
3464 rc = E_FAIL;
3465 else
3466 rc = directControl->EnumerateGuestProperties(aPatterns,
3467 ComSafeArrayOutArg(aNames),
3468 ComSafeArrayOutArg(aValues),
3469 ComSafeArrayOutArg(aTimestamps),
3470 ComSafeArrayOutArg(aFlags));
3471 }
3472 return rc;
3473#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3474}
3475
3476STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
3477 ComSafeArrayOut(IMediumAttachment*, aAttachments))
3478{
3479 MediaData::AttachmentList atts;
3480
3481 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
3482 CheckComRCReturnRC(rc);
3483
3484 SafeIfaceArray<IMediumAttachment> attachments(atts);
3485 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
3486
3487 return S_OK;
3488}
3489
3490STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
3491 LONG aControllerPort,
3492 LONG aDevice,
3493 IMediumAttachment **aAttachment)
3494{
3495 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
3496 aControllerName, aControllerPort, aDevice));
3497
3498 CheckComArgNotNull(aControllerName);
3499 CheckComArgOutPointerValid(aAttachment);
3500
3501 AutoCaller autoCaller(this);
3502 CheckComRCReturnRC(autoCaller.rc());
3503
3504 AutoReadLock alock(this);
3505
3506 *aAttachment = NULL;
3507
3508 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3509 aControllerName,
3510 aControllerPort,
3511 aDevice);
3512 if (pAttach.isNull())
3513 return setError(VBOX_E_OBJECT_NOT_FOUND,
3514 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3515 aDevice, aControllerPort, aControllerName);
3516
3517 pAttach.queryInterfaceTo(aAttachment);
3518
3519 return S_OK;
3520}
3521
3522STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
3523 StorageBus_T aConnectionType,
3524 IStorageController **controller)
3525{
3526 CheckComArgStrNotEmptyOrNull(aName);
3527
3528 if ( (aConnectionType <= StorageBus_Null)
3529 || (aConnectionType > StorageBus_Floppy))
3530 return setError (E_INVALIDARG,
3531 tr ("Invalid connection type: %d"),
3532 aConnectionType);
3533
3534 AutoCaller autoCaller(this);
3535 CheckComRCReturnRC(autoCaller.rc());
3536
3537 AutoWriteLock alock(this);
3538
3539 HRESULT rc = checkStateDependency(MutableStateDep);
3540 CheckComRCReturnRC(rc);
3541
3542 /* try to find one with the name first. */
3543 ComObjPtr<StorageController> ctrl;
3544
3545 rc = getStorageControllerByName (aName, ctrl, false /* aSetError */);
3546 if (SUCCEEDED(rc))
3547 return setError (VBOX_E_OBJECT_IN_USE,
3548 tr ("Storage controller named '%ls' already exists"), aName);
3549
3550 ctrl.createObject();
3551 rc = ctrl->init (this, aName, aConnectionType);
3552 CheckComRCReturnRC(rc);
3553
3554 mStorageControllers.backup();
3555 mStorageControllers->push_back (ctrl);
3556
3557 ctrl.queryInterfaceTo(controller);
3558
3559 /* inform the direct session if any */
3560 alock.leave();
3561 onStorageControllerChange();
3562
3563 return S_OK;
3564}
3565
3566STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
3567 IStorageController **aStorageController)
3568{
3569 CheckComArgStrNotEmptyOrNull(aName);
3570
3571 AutoCaller autoCaller(this);
3572 CheckComRCReturnRC(autoCaller.rc());
3573
3574 AutoReadLock alock(this);
3575
3576 ComObjPtr<StorageController> ctrl;
3577
3578 HRESULT rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3579 if (SUCCEEDED(rc))
3580 ctrl.queryInterfaceTo(aStorageController);
3581
3582 return rc;
3583}
3584
3585STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
3586{
3587 CheckComArgStrNotEmptyOrNull(aName);
3588
3589 AutoCaller autoCaller(this);
3590 CheckComRCReturnRC(autoCaller.rc());
3591
3592 AutoWriteLock alock(this);
3593
3594 HRESULT rc = checkStateDependency(MutableStateDep);
3595 CheckComRCReturnRC(rc);
3596
3597 ComObjPtr<StorageController> ctrl;
3598 rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3599 CheckComRCReturnRC(rc);
3600
3601 /* We can remove the controller only if there is no device attached. */
3602 /* check if the device slot is already busy */
3603 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
3604 it != mMediaData->mAttachments.end();
3605 ++it)
3606 {
3607 if (Bstr((*it)->controllerName()) == aName)
3608 return setError(VBOX_E_OBJECT_IN_USE,
3609 tr("Storage controller named '%ls' has still devices attached"),
3610 aName);
3611 }
3612
3613 /* We can remove it now. */
3614 mStorageControllers.backup();
3615
3616 ctrl->unshare();
3617
3618 mStorageControllers->remove (ctrl);
3619
3620 /* inform the direct session if any */
3621 alock.leave();
3622 onStorageControllerChange();
3623
3624 return S_OK;
3625}
3626
3627// public methods for internal purposes
3628/////////////////////////////////////////////////////////////////////////////
3629
3630/**
3631 * Saves the registry entry of this machine to the given configuration node.
3632 *
3633 * @param aEntryNode Node to save the registry entry to.
3634 *
3635 * @note locks this object for reading.
3636 */
3637HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
3638{
3639 AutoLimitedCaller autoCaller(this);
3640 AssertComRCReturnRC(autoCaller.rc());
3641
3642 AutoReadLock alock(this);
3643
3644 data.uuid = mData->mUuid;
3645 data.strSettingsFile = mData->m_strConfigFile;
3646
3647 return S_OK;
3648}
3649
3650/**
3651 * Calculates the absolute path of the given path taking the directory of the
3652 * machine settings file as the current directory.
3653 *
3654 * @param aPath Path to calculate the absolute path for.
3655 * @param aResult Where to put the result (used only on success, can be the
3656 * same Utf8Str instance as passed in @a aPath).
3657 * @return IPRT result.
3658 *
3659 * @note Locks this object for reading.
3660 */
3661int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3662{
3663 AutoCaller autoCaller(this);
3664 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3665
3666 AutoReadLock alock(this);
3667
3668 AssertReturn (!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
3669
3670 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
3671
3672 strSettingsDir.stripFilename();
3673 char folder[RTPATH_MAX];
3674 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3675 if (RT_SUCCESS(vrc))
3676 aResult = folder;
3677
3678 return vrc;
3679}
3680
3681/**
3682 * Tries to calculate the relative path of the given absolute path using the
3683 * directory of the machine settings file as the base directory.
3684 *
3685 * @param aPath Absolute path to calculate the relative path for.
3686 * @param aResult Where to put the result (used only when it's possible to
3687 * make a relative path from the given absolute path; otherwise
3688 * left untouched).
3689 *
3690 * @note Locks this object for reading.
3691 */
3692void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3693{
3694 AutoCaller autoCaller(this);
3695 AssertComRCReturn (autoCaller.rc(), (void) 0);
3696
3697 AutoReadLock alock(this);
3698
3699 AssertReturnVoid (!mData->m_strConfigFileFull.isEmpty());
3700
3701 Utf8Str settingsDir = mData->m_strConfigFileFull;
3702
3703 settingsDir.stripFilename();
3704 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3705 {
3706 /* when assigning, we create a separate Utf8Str instance because both
3707 * aPath and aResult can point to the same memory location when this
3708 * func is called (if we just do aResult = aPath, aResult will be freed
3709 * first, and since its the same as aPath, an attempt to copy garbage
3710 * will be made. */
3711 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3712 }
3713}
3714
3715/**
3716 * Returns the full path to the machine's log folder in the
3717 * \a aLogFolder argument.
3718 */
3719void Machine::getLogFolder (Utf8Str &aLogFolder)
3720{
3721 AutoCaller autoCaller(this);
3722 AssertComRCReturnVoid (autoCaller.rc());
3723
3724 AutoReadLock alock(this);
3725
3726 Utf8Str settingsDir;
3727 if (isInOwnDir (&settingsDir))
3728 {
3729 /* Log folder is <Machines>/<VM_Name>/Logs */
3730 aLogFolder = Utf8StrFmt ("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
3731 }
3732 else
3733 {
3734 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
3735 Assert (!mUserData->mSnapshotFolderFull.isEmpty());
3736 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
3737 RTPATH_DELIMITER);
3738 }
3739}
3740
3741/**
3742 * @note Locks this object for writing, calls the client process (outside the
3743 * lock).
3744 */
3745HRESULT Machine::openSession(IInternalSessionControl *aControl)
3746{
3747 LogFlowThisFuncEnter();
3748
3749 AssertReturn(aControl, E_FAIL);
3750
3751 AutoCaller autoCaller(this);
3752 CheckComRCReturnRC(autoCaller.rc());
3753
3754 AutoWriteLock alock(this);
3755
3756 if (!mData->mRegistered)
3757 return setError(E_UNEXPECTED,
3758 tr("The machine '%ls' is not registered"),
3759 mUserData->mName.raw());
3760
3761 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3762
3763 if (mData->mSession.mState == SessionState_Open ||
3764 mData->mSession.mState == SessionState_Closing)
3765 return setError(VBOX_E_INVALID_OBJECT_STATE,
3766 tr("A session for the machine '%ls' is currently open (or being closed)"),
3767 mUserData->mName.raw());
3768
3769 /* may not be busy */
3770 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
3771
3772 /* get the session PID */
3773 RTPROCESS pid = NIL_RTPROCESS;
3774 AssertCompile (sizeof (ULONG) == sizeof (RTPROCESS));
3775 aControl->GetPID ((ULONG *) &pid);
3776 Assert (pid != NIL_RTPROCESS);
3777
3778 if (mData->mSession.mState == SessionState_Spawning)
3779 {
3780 /* This machine is awaiting for a spawning session to be opened, so
3781 * reject any other open attempts from processes other than one
3782 * started by #openRemoteSession(). */
3783
3784 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
3785 mData->mSession.mPid, mData->mSession.mPid));
3786 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3787
3788 if (mData->mSession.mPid != pid)
3789 return setError(E_ACCESSDENIED,
3790 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
3791 "session with the machine named '%ls', while only a process "
3792 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
3793 pid, mUserData->mName.raw(), mData->mSession.mPid);
3794 }
3795
3796 /* create a SessionMachine object */
3797 ComObjPtr<SessionMachine> sessionMachine;
3798 sessionMachine.createObject();
3799 HRESULT rc = sessionMachine->init (this);
3800 AssertComRC (rc);
3801
3802 /* NOTE: doing return from this function after this point but
3803 * before the end is forbidden since it may call SessionMachine::uninit()
3804 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3805 * lock while still holding the Machine lock in alock so that a deadlock
3806 * is possible due to the wrong lock order. */
3807
3808 if (SUCCEEDED(rc))
3809 {
3810#ifdef VBOX_WITH_RESOURCE_USAGE_API
3811 registerMetrics (mParent->performanceCollector(), this, pid);
3812#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3813
3814 /*
3815 * Set the session state to Spawning to protect against subsequent
3816 * attempts to open a session and to unregister the machine after
3817 * we leave the lock.
3818 */
3819 SessionState_T origState = mData->mSession.mState;
3820 mData->mSession.mState = SessionState_Spawning;
3821
3822 /*
3823 * Leave the lock before calling the client process -- it will call
3824 * Machine/SessionMachine methods. Leaving the lock here is quite safe
3825 * because the state is Spawning, so that openRemotesession() and
3826 * openExistingSession() calls will fail. This method, called before we
3827 * enter the lock again, will fail because of the wrong PID.
3828 *
3829 * Note that mData->mSession.mRemoteControls accessed outside
3830 * the lock may not be modified when state is Spawning, so it's safe.
3831 */
3832 alock.leave();
3833
3834 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3835 rc = aControl->AssignMachine (sessionMachine);
3836 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3837
3838 /* The failure may occur w/o any error info (from RPC), so provide one */
3839 if (FAILED (rc))
3840 setError (VBOX_E_VM_ERROR,
3841 tr ("Failed to assign the machine to the session (%Rrc)"), rc);
3842
3843 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
3844 {
3845 /* complete the remote session initialization */
3846
3847 /* get the console from the direct session */
3848 ComPtr<IConsole> console;
3849 rc = aControl->GetRemoteConsole (console.asOutParam());
3850 ComAssertComRC (rc);
3851
3852 if (SUCCEEDED(rc) && !console)
3853 {
3854 ComAssert (!!console);
3855 rc = E_FAIL;
3856 }
3857
3858 /* assign machine & console to the remote session */
3859 if (SUCCEEDED(rc))
3860 {
3861 /*
3862 * after openRemoteSession(), the first and the only
3863 * entry in remoteControls is that remote session
3864 */
3865 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3866 rc = mData->mSession.mRemoteControls.front()->
3867 AssignRemoteMachine (sessionMachine, console);
3868 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3869
3870 /* The failure may occur w/o any error info (from RPC), so provide one */
3871 if (FAILED(rc))
3872 setError(VBOX_E_VM_ERROR,
3873 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3874 }
3875
3876 if (FAILED(rc))
3877 aControl->Uninitialize();
3878 }
3879
3880 /* enter the lock again */
3881 alock.enter();
3882
3883 /* Restore the session state */
3884 mData->mSession.mState = origState;
3885 }
3886
3887 /* finalize spawning anyway (this is why we don't return on errors above) */
3888 if (mData->mSession.mState == SessionState_Spawning)
3889 {
3890 /* Note that the progress object is finalized later */
3891
3892 /* We don't reset mSession.mPid here because it is necessary for
3893 * SessionMachine::uninit() to reap the child process later. */
3894
3895 if (FAILED(rc))
3896 {
3897 /* Close the remote session, remove the remote control from the list
3898 * and reset session state to Closed (@note keep the code in sync
3899 * with the relevant part in openSession()). */
3900
3901 Assert (mData->mSession.mRemoteControls.size() == 1);
3902 if (mData->mSession.mRemoteControls.size() == 1)
3903 {
3904 ErrorInfoKeeper eik;
3905 mData->mSession.mRemoteControls.front()->Uninitialize();
3906 }
3907
3908 mData->mSession.mRemoteControls.clear();
3909 mData->mSession.mState = SessionState_Closed;
3910 }
3911 }
3912 else
3913 {
3914 /* memorize PID of the directly opened session */
3915 if (SUCCEEDED(rc))
3916 mData->mSession.mPid = pid;
3917 }
3918
3919 if (SUCCEEDED(rc))
3920 {
3921 /* memorize the direct session control and cache IUnknown for it */
3922 mData->mSession.mDirectControl = aControl;
3923 mData->mSession.mState = SessionState_Open;
3924 /* associate the SessionMachine with this Machine */
3925 mData->mSession.mMachine = sessionMachine;
3926
3927 /* request an IUnknown pointer early from the remote party for later
3928 * identity checks (it will be internally cached within mDirectControl
3929 * at least on XPCOM) */
3930 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3931 NOREF (unk);
3932 }
3933
3934 if (mData->mSession.mProgress)
3935 {
3936 /* finalize the progress after setting the state, for consistency */
3937 mData->mSession.mProgress->notifyComplete (rc);
3938 mData->mSession.mProgress.setNull();
3939 }
3940
3941 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
3942 * would break the lock order */
3943 alock.leave();
3944
3945 /* uninitialize the created session machine on failure */
3946 if (FAILED(rc))
3947 sessionMachine->uninit();
3948
3949 LogFlowThisFunc(("rc=%08X\n", rc));
3950 LogFlowThisFuncLeave();
3951 return rc;
3952}
3953
3954/**
3955 * @note Locks this object for writing, calls the client process
3956 * (inside the lock).
3957 */
3958HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
3959 IN_BSTR aType,
3960 IN_BSTR aEnvironment,
3961 Progress *aProgress)
3962{
3963 LogFlowThisFuncEnter();
3964
3965 AssertReturn(aControl, E_FAIL);
3966 AssertReturn(aProgress, E_FAIL);
3967
3968 AutoCaller autoCaller(this);
3969 CheckComRCReturnRC(autoCaller.rc());
3970
3971 AutoWriteLock alock(this);
3972
3973 if (!mData->mRegistered)
3974 return setError(E_UNEXPECTED,
3975 tr("The machine '%ls' is not registered"),
3976 mUserData->mName.raw());
3977
3978 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3979
3980 if (mData->mSession.mState == SessionState_Open ||
3981 mData->mSession.mState == SessionState_Spawning ||
3982 mData->mSession.mState == SessionState_Closing)
3983 return setError(VBOX_E_INVALID_OBJECT_STATE,
3984 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
3985 mUserData->mName.raw());
3986
3987 /* may not be busy */
3988 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
3989
3990 /* get the path to the executable */
3991 char szPath[RTPATH_MAX];
3992 RTPathAppPrivateArch(szPath, RTPATH_MAX);
3993 size_t sz = strlen(szPath);
3994 szPath[sz++] = RTPATH_DELIMITER;
3995 szPath[sz] = 0;
3996 char *cmd = szPath + sz;
3997 sz = RTPATH_MAX - sz;
3998
3999 int vrc = VINF_SUCCESS;
4000 RTPROCESS pid = NIL_RTPROCESS;
4001
4002 RTENV env = RTENV_DEFAULT;
4003
4004 if (aEnvironment != NULL && *aEnvironment)
4005 {
4006 char *newEnvStr = NULL;
4007
4008 do
4009 {
4010 /* clone the current environment */
4011 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
4012 AssertRCBreakStmt(vrc2, vrc = vrc2);
4013
4014 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
4015 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
4016
4017 /* put new variables to the environment
4018 * (ignore empty variable names here since RTEnv API
4019 * intentionally doesn't do that) */
4020 char *var = newEnvStr;
4021 for (char *p = newEnvStr; *p; ++p)
4022 {
4023 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
4024 {
4025 *p = '\0';
4026 if (*var)
4027 {
4028 char *val = strchr (var, '=');
4029 if (val)
4030 {
4031 *val++ = '\0';
4032 vrc2 = RTEnvSetEx (env, var, val);
4033 }
4034 else
4035 vrc2 = RTEnvUnsetEx (env, var);
4036 if (RT_FAILURE(vrc2))
4037 break;
4038 }
4039 var = p + 1;
4040 }
4041 }
4042 if (RT_SUCCESS(vrc2) && *var)
4043 vrc2 = RTEnvPutEx (env, var);
4044
4045 AssertRCBreakStmt (vrc2, vrc = vrc2);
4046 }
4047 while (0);
4048
4049 if (newEnvStr != NULL)
4050 RTStrFree(newEnvStr);
4051 }
4052
4053 Bstr type (aType);
4054
4055 /* Qt is default */
4056#ifdef VBOX_WITH_QTGUI
4057 if (type == "gui" || type == "GUI/Qt")
4058 {
4059# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
4060 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
4061# else
4062 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
4063# endif
4064 Assert (sz >= sizeof (VirtualBox_exe));
4065 strcpy (cmd, VirtualBox_exe);
4066
4067 Utf8Str idStr = mData->mUuid.toString();
4068# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
4069 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4070# else
4071 Utf8Str name = mUserData->mName;
4072 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4073# endif
4074 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4075 }
4076#else /* !VBOX_WITH_QTGUI */
4077 if (0)
4078 ;
4079#endif /* VBOX_WITH_QTGUI */
4080
4081 else
4082
4083#ifdef VBOX_WITH_VBOXSDL
4084 if (type == "sdl" || type == "GUI/SDL")
4085 {
4086 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
4087 Assert (sz >= sizeof (VBoxSDL_exe));
4088 strcpy (cmd, VBoxSDL_exe);
4089
4090 Utf8Str idStr = mData->mUuid.toString();
4091# ifdef RT_OS_WINDOWS
4092 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4093# else
4094 Utf8Str name = mUserData->mName;
4095 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4096# endif
4097 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4098 }
4099#else /* !VBOX_WITH_VBOXSDL */
4100 if (0)
4101 ;
4102#endif /* !VBOX_WITH_VBOXSDL */
4103
4104 else
4105
4106#ifdef VBOX_WITH_HEADLESS
4107 if ( type == "headless"
4108 || type == "capture"
4109#ifdef VBOX_WITH_VRDP
4110 || type == "vrdp"
4111#endif
4112 )
4113 {
4114 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
4115 Assert (sz >= sizeof (VBoxHeadless_exe));
4116 strcpy (cmd, VBoxHeadless_exe);
4117
4118 Utf8Str idStr = mData->mUuid.toString();
4119 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
4120# ifdef RT_OS_WINDOWS
4121 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
4122# else
4123 Utf8Str name = mUserData->mName;
4124 const char * args[] ={szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
4125# endif
4126#ifdef VBOX_WITH_VRDP
4127 if (type == "headless")
4128 {
4129 unsigned pos = RT_ELEMENTS(args) - 3;
4130 args[pos++] = "--vrdp";
4131 args[pos] = "off";
4132 }
4133#endif
4134 if (type == "capture")
4135 {
4136 unsigned pos = RT_ELEMENTS(args) - 3;
4137 args[pos] = "--capture";
4138 }
4139 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4140 }
4141#else /* !VBOX_WITH_HEADLESS */
4142 if (0)
4143 ;
4144#endif /* !VBOX_WITH_HEADLESS */
4145 else
4146 {
4147 RTEnvDestroy (env);
4148 return setError (E_INVALIDARG,
4149 tr ("Invalid session type: '%ls'"), aType);
4150 }
4151
4152 RTEnvDestroy (env);
4153
4154 if (RT_FAILURE(vrc))
4155 return setError (VBOX_E_IPRT_ERROR,
4156 tr ("Could not launch a process for the machine '%ls' (%Rrc)"),
4157 mUserData->mName.raw(), vrc);
4158
4159 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
4160
4161 /*
4162 * Note that we don't leave the lock here before calling the client,
4163 * because it doesn't need to call us back if called with a NULL argument.
4164 * Leaving the lock herer is dangerous because we didn't prepare the
4165 * launch data yet, but the client we've just started may happen to be
4166 * too fast and call openSession() that will fail (because of PID, etc.),
4167 * so that the Machine will never get out of the Spawning session state.
4168 */
4169
4170 /* inform the session that it will be a remote one */
4171 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
4172 HRESULT rc = aControl->AssignMachine (NULL);
4173 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
4174
4175 if (FAILED(rc))
4176 {
4177 /* restore the session state */
4178 mData->mSession.mState = SessionState_Closed;
4179 /* The failure may occur w/o any error info (from RPC), so provide one */
4180 return setError(VBOX_E_VM_ERROR,
4181 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4182 }
4183
4184 /* attach launch data to the machine */
4185 Assert (mData->mSession.mPid == NIL_RTPROCESS);
4186 mData->mSession.mRemoteControls.push_back (aControl);
4187 mData->mSession.mProgress = aProgress;
4188 mData->mSession.mPid = pid;
4189 mData->mSession.mState = SessionState_Spawning;
4190 mData->mSession.mType = type;
4191
4192 LogFlowThisFuncLeave();
4193 return S_OK;
4194}
4195
4196/**
4197 * @note Locks this object for writing, calls the client process
4198 * (outside the lock).
4199 */
4200HRESULT Machine::openExistingSession (IInternalSessionControl *aControl)
4201{
4202 LogFlowThisFuncEnter();
4203
4204 AssertReturn(aControl, E_FAIL);
4205
4206 AutoCaller autoCaller(this);
4207 CheckComRCReturnRC(autoCaller.rc());
4208
4209 AutoWriteLock alock(this);
4210
4211 if (!mData->mRegistered)
4212 return setError (E_UNEXPECTED,
4213 tr ("The machine '%ls' is not registered"), mUserData->mName.raw());
4214
4215 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4216
4217 if (mData->mSession.mState != SessionState_Open)
4218 return setError (VBOX_E_INVALID_SESSION_STATE,
4219 tr ("The machine '%ls' does not have an open session"),
4220 mUserData->mName.raw());
4221
4222 ComAssertRet (!mData->mSession.mDirectControl.isNull(), E_FAIL);
4223
4224 /*
4225 * Get the console from the direct session (note that we don't leave the
4226 * lock here because GetRemoteConsole must not call us back).
4227 */
4228 ComPtr<IConsole> console;
4229 HRESULT rc = mData->mSession.mDirectControl->
4230 GetRemoteConsole (console.asOutParam());
4231 if (FAILED (rc))
4232 {
4233 /* The failure may occur w/o any error info (from RPC), so provide one */
4234 return setError (VBOX_E_VM_ERROR,
4235 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
4236 }
4237
4238 ComAssertRet (!console.isNull(), E_FAIL);
4239
4240 ComObjPtr<SessionMachine> sessionMachine = mData->mSession.mMachine;
4241 AssertReturn(!sessionMachine.isNull(), E_FAIL);
4242
4243 /*
4244 * Leave the lock before calling the client process. It's safe here
4245 * since the only thing to do after we get the lock again is to add
4246 * the remote control to the list (which doesn't directly influence
4247 * anything).
4248 */
4249 alock.leave();
4250
4251 /* attach the remote session to the machine */
4252 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4253 rc = aControl->AssignRemoteMachine (sessionMachine, console);
4254 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4255
4256 /* The failure may occur w/o any error info (from RPC), so provide one */
4257 if (FAILED(rc))
4258 return setError(VBOX_E_VM_ERROR,
4259 tr("Failed to assign the machine to the session (%Rrc)"),
4260 rc);
4261
4262 alock.enter();
4263
4264 /* need to revalidate the state after entering the lock again */
4265 if (mData->mSession.mState != SessionState_Open)
4266 {
4267 aControl->Uninitialize();
4268
4269 return setError(VBOX_E_INVALID_SESSION_STATE,
4270 tr("The machine '%ls' does not have an open session"),
4271 mUserData->mName.raw());
4272 }
4273
4274 /* store the control in the list */
4275 mData->mSession.mRemoteControls.push_back (aControl);
4276
4277 LogFlowThisFuncLeave();
4278 return S_OK;
4279}
4280
4281/**
4282 * Returns @c true if the given machine has an open direct session and returns
4283 * the session machine instance and additional session data (on some platforms)
4284 * if so.
4285 *
4286 * Note that when the method returns @c false, the arguments remain unchanged.
4287 *
4288 * @param aMachine Session machine object.
4289 * @param aControl Direct session control object (optional).
4290 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
4291 *
4292 * @note locks this object for reading.
4293 */
4294#if defined (RT_OS_WINDOWS)
4295bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4296 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4297 HANDLE *aIPCSem /*= NULL*/,
4298 bool aAllowClosing /*= false*/)
4299#elif defined (RT_OS_OS2)
4300bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4301 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4302 HMTX *aIPCSem /*= NULL*/,
4303 bool aAllowClosing /*= false*/)
4304#else
4305bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4306 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4307 bool aAllowClosing /*= false*/)
4308#endif
4309{
4310 AutoLimitedCaller autoCaller(this);
4311 AssertComRCReturn (autoCaller.rc(), false);
4312
4313 /* just return false for inaccessible machines */
4314 if (autoCaller.state() != Ready)
4315 return false;
4316
4317 AutoReadLock alock(this);
4318
4319 if (mData->mSession.mState == SessionState_Open ||
4320 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
4321 {
4322 AssertReturn(!mData->mSession.mMachine.isNull(), false);
4323
4324 aMachine = mData->mSession.mMachine;
4325
4326 if (aControl != NULL)
4327 *aControl = mData->mSession.mDirectControl;
4328
4329#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4330 /* Additional session data */
4331 if (aIPCSem != NULL)
4332 *aIPCSem = aMachine->mIPCSem;
4333#endif
4334 return true;
4335 }
4336
4337 return false;
4338}
4339
4340/**
4341 * Returns @c true if the given machine has an spawning direct session and
4342 * returns and additional session data (on some platforms) if so.
4343 *
4344 * Note that when the method returns @c false, the arguments remain unchanged.
4345 *
4346 * @param aPID PID of the spawned direct session process.
4347 *
4348 * @note locks this object for reading.
4349 */
4350#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4351bool Machine::isSessionSpawning (RTPROCESS *aPID /*= NULL*/)
4352#else
4353bool Machine::isSessionSpawning()
4354#endif
4355{
4356 AutoLimitedCaller autoCaller(this);
4357 AssertComRCReturn (autoCaller.rc(), false);
4358
4359 /* just return false for inaccessible machines */
4360 if (autoCaller.state() != Ready)
4361 return false;
4362
4363 AutoReadLock alock(this);
4364
4365 if (mData->mSession.mState == SessionState_Spawning)
4366 {
4367#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4368 /* Additional session data */
4369 if (aPID != NULL)
4370 {
4371 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
4372 *aPID = mData->mSession.mPid;
4373 }
4374#endif
4375 return true;
4376 }
4377
4378 return false;
4379}
4380
4381/**
4382 * Called from the client watcher thread to check for unexpected client process
4383 * death during Session_Spawning state (e.g. before it successfully opened a
4384 * direct session).
4385 *
4386 * On Win32 and on OS/2, this method is called only when we've got the
4387 * direct client's process termination notification, so it always returns @c
4388 * true.
4389 *
4390 * On other platforms, this method returns @c true if the client process is
4391 * terminated and @c false if it's still alive.
4392 *
4393 * @note Locks this object for writing.
4394 */
4395bool Machine::checkForSpawnFailure()
4396{
4397 AutoCaller autoCaller(this);
4398 if (!autoCaller.isOk())
4399 {
4400 /* nothing to do */
4401 LogFlowThisFunc(("Already uninitialized!"));
4402 return true;
4403 }
4404
4405 /* VirtualBox::addProcessToReap() needs a write lock */
4406 AutoMultiWriteLock2 alock (mParent, this);
4407
4408 if (mData->mSession.mState != SessionState_Spawning)
4409 {
4410 /* nothing to do */
4411 LogFlowThisFunc(("Not spawning any more!"));
4412 return true;
4413 }
4414
4415 HRESULT rc = S_OK;
4416
4417#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4418
4419 /* the process was already unexpectedly terminated, we just need to set an
4420 * error and finalize session spawning */
4421 rc = setError(E_FAIL,
4422 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4423 name().raw());
4424#else
4425
4426 RTPROCSTATUS status;
4427 int vrc = ::RTProcWait (mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
4428 &status);
4429
4430 if (vrc != VERR_PROCESS_RUNNING)
4431 rc = setError(E_FAIL,
4432 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4433 name().raw());
4434#endif
4435
4436 if (FAILED(rc))
4437 {
4438 /* Close the remote session, remove the remote control from the list
4439 * and reset session state to Closed (@note keep the code in sync with
4440 * the relevant part in checkForSpawnFailure()). */
4441
4442 Assert (mData->mSession.mRemoteControls.size() == 1);
4443 if (mData->mSession.mRemoteControls.size() == 1)
4444 {
4445 ErrorInfoKeeper eik;
4446 mData->mSession.mRemoteControls.front()->Uninitialize();
4447 }
4448
4449 mData->mSession.mRemoteControls.clear();
4450 mData->mSession.mState = SessionState_Closed;
4451
4452 /* finalize the progress after setting the state, for consistency */
4453 mData->mSession.mProgress->notifyComplete (rc);
4454 mData->mSession.mProgress.setNull();
4455
4456 mParent->addProcessToReap (mData->mSession.mPid);
4457 mData->mSession.mPid = NIL_RTPROCESS;
4458
4459 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
4460 return true;
4461 }
4462
4463 return false;
4464}
4465
4466/**
4467 * Checks that the registered flag of the machine can be set according to
4468 * the argument and sets it. On success, commits and saves all settings.
4469 *
4470 * @note When this machine is inaccessible, the only valid value for \a
4471 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
4472 * inaccessible machines are not currently supported. Note that unregistering
4473 * an inaccessible machine will \b uninitialize this machine object. Therefore,
4474 * the caller must make sure there are no active Machine::addCaller() calls
4475 * on the current thread because this will block Machine::uninit().
4476 *
4477 * @note Must be called from mParent's write lock. Locks this object and
4478 * children for writing.
4479 */
4480HRESULT Machine::trySetRegistered (BOOL aRegistered)
4481{
4482 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
4483
4484 AutoLimitedCaller autoCaller(this);
4485 AssertComRCReturnRC(autoCaller.rc());
4486
4487 AutoWriteLock alock(this);
4488
4489 /* wait for state dependants to drop to zero */
4490 ensureNoStateDependencies();
4491
4492 ComAssertRet (mData->mRegistered != aRegistered, E_FAIL);
4493
4494 if (!mData->mAccessible)
4495 {
4496 /* A special case: the machine is not accessible. */
4497
4498 /* inaccessible machines can only be unregistered */
4499 AssertReturn(!aRegistered, E_FAIL);
4500
4501 /* Uninitialize ourselves here because currently there may be no
4502 * unregistered that are inaccessible (this state combination is not
4503 * supported). Note releasing the caller and leaving the lock before
4504 * calling uninit() */
4505
4506 alock.leave();
4507 autoCaller.release();
4508
4509 uninit();
4510
4511 return S_OK;
4512 }
4513
4514 AssertReturn(autoCaller.state() == Ready, E_FAIL);
4515
4516 if (aRegistered)
4517 {
4518 if (mData->mRegistered)
4519 return setError(VBOX_E_INVALID_OBJECT_STATE,
4520 tr("The machine '%ls' with UUID {%s} is already registered"),
4521 mUserData->mName.raw(),
4522 mData->mUuid.toString().raw());
4523 }
4524 else
4525 {
4526 if (mData->mMachineState == MachineState_Saved)
4527 return setError(VBOX_E_INVALID_VM_STATE,
4528 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
4529 mUserData->mName.raw());
4530
4531 size_t snapshotCount = 0;
4532 if (mData->mFirstSnapshot)
4533 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4534 if (snapshotCount)
4535 return setError(VBOX_E_INVALID_OBJECT_STATE,
4536 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
4537 mUserData->mName.raw(), snapshotCount);
4538
4539 if (mData->mSession.mState != SessionState_Closed)
4540 return setError(VBOX_E_INVALID_OBJECT_STATE,
4541 tr("Cannot unregister the machine '%ls' because it has an open session"),
4542 mUserData->mName.raw());
4543
4544 if (mMediaData->mAttachments.size() != 0)
4545 return setError(VBOX_E_INVALID_OBJECT_STATE,
4546 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
4547 mUserData->mName.raw(),
4548 mMediaData->mAttachments.size());
4549
4550 /* Note that we do not prevent unregistration of a DVD or Floppy image
4551 * is attached: as opposed to hard disks detaching such an image
4552 * implicitly in this method (which we will do below) won't have any
4553 * side effects (like detached orphan base and diff hard disks etc).*/
4554 }
4555
4556 HRESULT rc = S_OK;
4557
4558 /* Ensure the settings are saved. If we are going to be registered and
4559 * isConfigLocked() is FALSE then it means that no config file exists yet,
4560 * so create it by calling saveSettings() too. */
4561 if ( isModified()
4562 || (aRegistered && !mData->m_pMachineConfigFile->fileExists())
4563 )
4564 {
4565 rc = saveSettings();
4566 CheckComRCReturnRC(rc);
4567 }
4568
4569 /* more config checking goes here */
4570
4571 if (SUCCEEDED(rc))
4572 {
4573 /* we may have had implicit modifications we want to fix on success */
4574 commit();
4575
4576 mData->mRegistered = aRegistered;
4577 }
4578 else
4579 {
4580 /* we may have had implicit modifications we want to cancel on failure*/
4581 rollback (false /* aNotify */);
4582 }
4583
4584 return rc;
4585}
4586
4587/**
4588 * Increases the number of objects dependent on the machine state or on the
4589 * registered state. Guarantees that these two states will not change at least
4590 * until #releaseStateDependency() is called.
4591 *
4592 * Depending on the @a aDepType value, additional state checks may be made.
4593 * These checks will set extended error info on failure. See
4594 * #checkStateDependency() for more info.
4595 *
4596 * If this method returns a failure, the dependency is not added and the caller
4597 * is not allowed to rely on any particular machine state or registration state
4598 * value and may return the failed result code to the upper level.
4599 *
4600 * @param aDepType Dependency type to add.
4601 * @param aState Current machine state (NULL if not interested).
4602 * @param aRegistered Current registered state (NULL if not interested).
4603 *
4604 * @note Locks this object for writing.
4605 */
4606HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
4607 MachineState_T *aState /* = NULL */,
4608 BOOL *aRegistered /* = NULL */)
4609{
4610 AutoCaller autoCaller(this);
4611 AssertComRCReturnRC(autoCaller.rc());
4612
4613 AutoWriteLock alock(this);
4614
4615 HRESULT rc = checkStateDependency(aDepType);
4616 CheckComRCReturnRC(rc);
4617
4618 {
4619 if (mData->mMachineStateChangePending != 0)
4620 {
4621 /* ensureNoStateDependencies() is waiting for state dependencies to
4622 * drop to zero so don't add more. It may make sense to wait a bit
4623 * and retry before reporting an error (since the pending state
4624 * transition should be really quick) but let's just assert for
4625 * now to see if it ever happens on practice. */
4626
4627 AssertFailed();
4628
4629 return setError(E_ACCESSDENIED,
4630 tr("Machine state change is in progress. Please retry the operation later."));
4631 }
4632
4633 ++mData->mMachineStateDeps;
4634 Assert (mData->mMachineStateDeps != 0 /* overflow */);
4635 }
4636
4637 if (aState)
4638 *aState = mData->mMachineState;
4639 if (aRegistered)
4640 *aRegistered = mData->mRegistered;
4641
4642 return S_OK;
4643}
4644
4645/**
4646 * Decreases the number of objects dependent on the machine state.
4647 * Must always complete the #addStateDependency() call after the state
4648 * dependency is no more necessary.
4649 */
4650void Machine::releaseStateDependency()
4651{
4652 AutoCaller autoCaller(this);
4653 AssertComRCReturnVoid (autoCaller.rc());
4654
4655 AutoWriteLock alock(this);
4656
4657 AssertReturnVoid (mData->mMachineStateDeps != 0
4658 /* releaseStateDependency() w/o addStateDependency()? */);
4659 -- mData->mMachineStateDeps;
4660
4661 if (mData->mMachineStateDeps == 0)
4662 {
4663 /* inform ensureNoStateDependencies() that there are no more deps */
4664 if (mData->mMachineStateChangePending != 0)
4665 {
4666 Assert (mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
4667 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
4668 }
4669 }
4670}
4671
4672// protected methods
4673/////////////////////////////////////////////////////////////////////////////
4674
4675/**
4676 * Performs machine state checks based on the @a aDepType value. If a check
4677 * fails, this method will set extended error info, otherwise it will return
4678 * S_OK. It is supposed, that on failure, the caller will immedieately return
4679 * the return value of this method to the upper level.
4680 *
4681 * When @a aDepType is AnyStateDep, this method always returns S_OK.
4682 *
4683 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
4684 * current state of this machine object allows to change settings of the
4685 * machine (i.e. the machine is not registered, or registered but not running
4686 * and not saved). It is useful to call this method from Machine setters
4687 * before performing any change.
4688 *
4689 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
4690 * as for MutableStateDep except that if the machine is saved, S_OK is also
4691 * returned. This is useful in setters which allow changing machine
4692 * properties when it is in the saved state.
4693 *
4694 * @param aDepType Dependency type to check.
4695 *
4696 * @note Non Machine based classes should use #addStateDependency() and
4697 * #releaseStateDependency() methods or the smart AutoStateDependency
4698 * template.
4699 *
4700 * @note This method must be called from under this object's read or write
4701 * lock.
4702 */
4703HRESULT Machine::checkStateDependency(StateDependency aDepType)
4704{
4705 switch (aDepType)
4706 {
4707 case AnyStateDep:
4708 {
4709 break;
4710 }
4711 case MutableStateDep:
4712 {
4713 if (mData->mRegistered &&
4714 (mType != IsSessionMachine ||
4715 mData->mMachineState > MachineState_Paused ||
4716 mData->mMachineState == MachineState_Saved))
4717 return setError(VBOX_E_INVALID_VM_STATE,
4718 tr("The machine is not mutable (state is %s)"),
4719 Global::stringifyMachineState(mData->mMachineState));
4720 break;
4721 }
4722 case MutableOrSavedStateDep:
4723 {
4724 if (mData->mRegistered &&
4725 (mType != IsSessionMachine ||
4726 mData->mMachineState > MachineState_Paused))
4727 return setError(VBOX_E_INVALID_VM_STATE,
4728 tr("The machine is not mutable (state is %s)"),
4729 Global::stringifyMachineState(mData->mMachineState));
4730 break;
4731 }
4732 }
4733
4734 return S_OK;
4735}
4736
4737/**
4738 * Helper to initialize all associated child objects and allocate data
4739 * structures.
4740 *
4741 * This method must be called as a part of the object's initialization procedure
4742 * (usually done in the #init() method).
4743 *
4744 * @note Must be called only from #init() or from #registeredInit().
4745 */
4746HRESULT Machine::initDataAndChildObjects()
4747{
4748 AutoCaller autoCaller(this);
4749 AssertComRCReturnRC(autoCaller.rc());
4750 AssertComRCReturn (autoCaller.state() == InInit ||
4751 autoCaller.state() == Limited, E_FAIL);
4752
4753 AssertReturn(!mData->mAccessible, E_FAIL);
4754
4755 /* allocate data structures */
4756 mSSData.allocate();
4757 mUserData.allocate();
4758 mHWData.allocate();
4759 mMediaData.allocate();
4760 mStorageControllers.allocate();
4761
4762 /* initialize mOSTypeId */
4763 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
4764
4765 /* create associated BIOS settings object */
4766 unconst(mBIOSSettings).createObject();
4767 mBIOSSettings->init (this);
4768
4769#ifdef VBOX_WITH_VRDP
4770 /* create an associated VRDPServer object (default is disabled) */
4771 unconst(mVRDPServer).createObject();
4772 mVRDPServer->init (this);
4773#endif
4774
4775 /* create associated serial port objects */
4776 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
4777 {
4778 unconst(mSerialPorts [slot]).createObject();
4779 mSerialPorts [slot]->init (this, slot);
4780 }
4781
4782 /* create associated parallel port objects */
4783 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
4784 {
4785 unconst(mParallelPorts [slot]).createObject();
4786 mParallelPorts [slot]->init (this, slot);
4787 }
4788
4789 /* create the audio adapter object (always present, default is disabled) */
4790 unconst(mAudioAdapter).createObject();
4791 mAudioAdapter->init (this);
4792
4793 /* create the USB controller object (always present, default is disabled) */
4794 unconst(mUSBController).createObject();
4795 mUSBController->init (this);
4796
4797 /* create associated network adapter objects */
4798 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
4799 {
4800 unconst(mNetworkAdapters [slot]).createObject();
4801 mNetworkAdapters [slot]->init (this, slot);
4802 }
4803
4804 return S_OK;
4805}
4806
4807/**
4808 * Helper to uninitialize all associated child objects and to free all data
4809 * structures.
4810 *
4811 * This method must be called as a part of the object's uninitialization
4812 * procedure (usually done in the #uninit() method).
4813 *
4814 * @note Must be called only from #uninit() or from #registeredInit().
4815 */
4816void Machine::uninitDataAndChildObjects()
4817{
4818 AutoCaller autoCaller(this);
4819 AssertComRCReturnVoid (autoCaller.rc());
4820 AssertComRCReturnVoid (autoCaller.state() == InUninit ||
4821 autoCaller.state() == Limited);
4822
4823 /* uninit all children using addDependentChild()/removeDependentChild()
4824 * in their init()/uninit() methods */
4825 uninitDependentChildren();
4826
4827 /* tell all our other child objects we've been uninitialized */
4828
4829 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
4830 {
4831 if (mNetworkAdapters [slot])
4832 {
4833 mNetworkAdapters [slot]->uninit();
4834 unconst(mNetworkAdapters [slot]).setNull();
4835 }
4836 }
4837
4838 if (mUSBController)
4839 {
4840 mUSBController->uninit();
4841 unconst(mUSBController).setNull();
4842 }
4843
4844 if (mAudioAdapter)
4845 {
4846 mAudioAdapter->uninit();
4847 unconst(mAudioAdapter).setNull();
4848 }
4849
4850 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
4851 {
4852 if (mParallelPorts [slot])
4853 {
4854 mParallelPorts [slot]->uninit();
4855 unconst(mParallelPorts [slot]).setNull();
4856 }
4857 }
4858
4859 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
4860 {
4861 if (mSerialPorts [slot])
4862 {
4863 mSerialPorts [slot]->uninit();
4864 unconst(mSerialPorts [slot]).setNull();
4865 }
4866 }
4867
4868#ifdef VBOX_WITH_VRDP
4869 if (mVRDPServer)
4870 {
4871 mVRDPServer->uninit();
4872 unconst(mVRDPServer).setNull();
4873 }
4874#endif
4875
4876 if (mBIOSSettings)
4877 {
4878 mBIOSSettings->uninit();
4879 unconst(mBIOSSettings).setNull();
4880 }
4881
4882 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
4883 * instance is uninitialized; SessionMachine instances refer to real
4884 * Machine hard disks). This is necessary for a clean re-initialization of
4885 * the VM after successfully re-checking the accessibility state. Note
4886 * that in case of normal Machine or SnapshotMachine uninitialization (as
4887 * a result of unregistering or discarding the snapshot), outdated hard
4888 * disk attachments will already be uninitialized and deleted, so this
4889 * code will not affect them. */
4890 if (!!mMediaData && (mType == IsMachine || mType == IsSnapshotMachine))
4891 {
4892 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
4893 it != mMediaData->mAttachments.end();
4894 ++it)
4895 {
4896 ComObjPtr<Medium> hd = (*it)->medium();
4897 if (hd.isNull() || (*it)->type() != DeviceType_HardDisk)
4898 continue;
4899 HRESULT rc = hd->detachFrom(mData->mUuid, snapshotId());
4900 AssertComRC (rc);
4901 }
4902 }
4903
4904 if (mType == IsMachine)
4905 {
4906 /* reset some important fields of mData */
4907 mData->mCurrentSnapshot.setNull();
4908 mData->mFirstSnapshot.setNull();
4909 }
4910
4911 /* free data structures (the essential mData structure is not freed here
4912 * since it may be still in use) */
4913 mMediaData.free();
4914 mStorageControllers.free();
4915 mHWData.free();
4916 mUserData.free();
4917 mSSData.free();
4918}
4919
4920/**
4921 * Makes sure that there are no machine state dependants. If necessary, waits
4922 * for the number of dependants to drop to zero.
4923 *
4924 * Make sure this method is called from under this object's write lock to
4925 * guarantee that no new dependants may be added when this method returns
4926 * control to the caller.
4927 *
4928 * @note Locks this object for writing. The lock will be released while waiting
4929 * (if necessary).
4930 *
4931 * @warning To be used only in methods that change the machine state!
4932 */
4933void Machine::ensureNoStateDependencies()
4934{
4935 AssertReturnVoid (isWriteLockOnCurrentThread());
4936
4937 AutoWriteLock alock(this);
4938
4939 /* Wait for all state dependants if necessary */
4940 if (mData->mMachineStateDeps != 0)
4941 {
4942 /* lazy semaphore creation */
4943 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
4944 RTSemEventMultiCreate (&mData->mMachineStateDepsSem);
4945
4946 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
4947 mData->mMachineStateDeps));
4948
4949 ++mData->mMachineStateChangePending;
4950
4951 /* reset the semaphore before waiting, the last dependant will signal
4952 * it */
4953 RTSemEventMultiReset (mData->mMachineStateDepsSem);
4954
4955 alock.leave();
4956
4957 RTSemEventMultiWait (mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
4958
4959 alock.enter();
4960
4961 -- mData->mMachineStateChangePending;
4962 }
4963}
4964
4965/**
4966 * Changes the machine state and informs callbacks.
4967 *
4968 * This method is not intended to fail so it either returns S_OK or asserts (and
4969 * returns a failure).
4970 *
4971 * @note Locks this object for writing.
4972 */
4973HRESULT Machine::setMachineState (MachineState_T aMachineState)
4974{
4975 LogFlowThisFuncEnter();
4976 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
4977
4978 AutoCaller autoCaller(this);
4979 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4980
4981 AutoWriteLock alock(this);
4982
4983 /* wait for state dependants to drop to zero */
4984 ensureNoStateDependencies();
4985
4986 if (mData->mMachineState != aMachineState)
4987 {
4988 mData->mMachineState = aMachineState;
4989
4990 RTTimeNow (&mData->mLastStateChange);
4991
4992 mParent->onMachineStateChange(mData->mUuid, aMachineState);
4993 }
4994
4995 LogFlowThisFuncLeave();
4996 return S_OK;
4997}
4998
4999/**
5000 * Searches for a shared folder with the given logical name
5001 * in the collection of shared folders.
5002 *
5003 * @param aName logical name of the shared folder
5004 * @param aSharedFolder where to return the found object
5005 * @param aSetError whether to set the error info if the folder is
5006 * not found
5007 * @return
5008 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
5009 *
5010 * @note
5011 * must be called from under the object's lock!
5012 */
5013HRESULT Machine::findSharedFolder (CBSTR aName,
5014 ComObjPtr<SharedFolder> &aSharedFolder,
5015 bool aSetError /* = false */)
5016{
5017 bool found = false;
5018 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
5019 !found && it != mHWData->mSharedFolders.end();
5020 ++it)
5021 {
5022 AutoWriteLock alock(*it);
5023 found = (*it)->name() == aName;
5024 if (found)
5025 aSharedFolder = *it;
5026 }
5027
5028 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
5029
5030 if (aSetError && !found)
5031 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
5032
5033 return rc;
5034}
5035
5036/**
5037 * Loads all the VM settings by walking down the <Machine> node.
5038 *
5039 * @param aRegistered true when the machine is being loaded on VirtualBox
5040 * startup
5041 *
5042 * @note This method is intended to be called only from init(), so it assumes
5043 * all machine data fields have appropriate default values when it is called.
5044 *
5045 * @note Doesn't lock any objects.
5046 */
5047HRESULT Machine::loadSettings(bool aRegistered)
5048{
5049 LogFlowThisFuncEnter();
5050 AssertReturn(mType == IsMachine, E_FAIL);
5051
5052 AutoCaller autoCaller(this);
5053 AssertReturn(autoCaller.state() == InInit, E_FAIL);
5054
5055 HRESULT rc = S_OK;
5056
5057 try
5058 {
5059 Assert(mData->m_pMachineConfigFile == NULL);
5060
5061 // load and parse machine XML; this will throw on XML or logic errors
5062 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
5063
5064 /* If the stored UUID is not empty, it means the registered machine
5065 * is being loaded. Compare the loaded UUID with the stored one taken
5066 * from the global registry. */
5067 if (!mData->mUuid.isEmpty())
5068 {
5069 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
5070 {
5071 throw setError(E_FAIL,
5072 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
5073 mData->m_pMachineConfigFile->uuid.raw(),
5074 mData->m_strConfigFileFull.raw(),
5075 mData->mUuid.toString().raw(),
5076 mParent->settingsFilePath().raw());
5077 }
5078 }
5079 else
5080 unconst (mData->mUuid) = mData->m_pMachineConfigFile->uuid;
5081
5082 /* name (required) */
5083 mUserData->mName = mData->m_pMachineConfigFile->strName;
5084
5085 /* nameSync (optional, default is true) */
5086 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
5087
5088 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
5089
5090 // guest OS type
5091 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
5092 /* look up the object by Id to check it is valid */
5093 ComPtr<IGuestOSType> guestOSType;
5094 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
5095 guestOSType.asOutParam());
5096 CheckComRCThrowRC(rc);
5097
5098 // stateFile (optional)
5099 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
5100 mSSData->mStateFilePath.setNull();
5101 else
5102 {
5103 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
5104 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
5105 if (RT_FAILURE(vrc))
5106 throw setError(E_FAIL,
5107 tr("Invalid saved state file path '%s' (%Rrc)"),
5108 mData->m_pMachineConfigFile->strStateFile.raw(),
5109 vrc);
5110 mSSData->mStateFilePath = stateFilePathFull;
5111 }
5112
5113 /* snapshotFolder (optional) */
5114 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
5115 CheckComRCThrowRC(rc);
5116
5117 /* currentStateModified (optional, default is true) */
5118 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
5119
5120 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
5121
5122 /* teleportation */
5123 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
5124 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
5125 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
5126 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
5127
5128 /*
5129 * note: all mUserData members must be assigned prior this point because
5130 * we need to commit changes in order to let mUserData be shared by all
5131 * snapshot machine instances.
5132 */
5133 mUserData.commitCopy();
5134
5135 /* Snapshot node (optional) */
5136 if (mData->m_pMachineConfigFile->llFirstSnapshot.size())
5137 {
5138 // there can only be one root snapshot
5139 Assert(mData->m_pMachineConfigFile->llFirstSnapshot.size() == 1);
5140
5141 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
5142
5143 rc = loadSnapshot(snap,
5144 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
5145 NULL); // no parent == first snapshot
5146 CheckComRCThrowRC(rc);
5147 }
5148
5149 /* Hardware node (required) */
5150 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
5151 CheckComRCThrowRC(rc);
5152
5153 /* Load storage controllers */
5154 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
5155 CheckComRCThrowRC(rc);
5156
5157 /*
5158 * NOTE: the assignment below must be the last thing to do,
5159 * otherwise it will be not possible to change the settings
5160 * somewehere in the code above because all setters will be
5161 * blocked by checkStateDependency(MutableStateDep).
5162 */
5163
5164 /* set the machine state to Aborted or Saved when appropriate */
5165 if (mData->m_pMachineConfigFile->fAborted)
5166 {
5167 Assert(!mSSData->mStateFilePath);
5168 mSSData->mStateFilePath.setNull();
5169
5170 /* no need to use setMachineState() during init() */
5171 mData->mMachineState = MachineState_Aborted;
5172 }
5173 else if (mSSData->mStateFilePath)
5174 {
5175 /* no need to use setMachineState() during init() */
5176 mData->mMachineState = MachineState_Saved;
5177 }
5178 }
5179 catch (HRESULT err)
5180 {
5181 /* we assume that error info is set by the thrower */
5182 rc = err;
5183 }
5184 catch (...)
5185 {
5186 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
5187 }
5188
5189 LogFlowThisFuncLeave();
5190 return rc;
5191}
5192
5193/**
5194 * Recursively loads all snapshots starting from the given.
5195 *
5196 * @param aNode <Snapshot> node.
5197 * @param aCurSnapshotId Current snapshot ID from the settings file.
5198 * @param aParentSnapshot Parent snapshot.
5199 */
5200HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
5201 const Guid &aCurSnapshotId,
5202 Snapshot *aParentSnapshot)
5203{
5204 AssertReturn (mType == IsMachine, E_FAIL);
5205
5206 HRESULT rc = S_OK;
5207
5208 Utf8Str strStateFile;
5209 if (!data.strStateFile.isEmpty())
5210 {
5211 /* optional */
5212 strStateFile = data.strStateFile;
5213 int vrc = calculateFullPath(strStateFile, strStateFile);
5214 if (RT_FAILURE(vrc))
5215 return setError(E_FAIL,
5216 tr("Invalid saved state file path '%s' (%Rrc)"),
5217 strStateFile.raw(),
5218 vrc);
5219 }
5220
5221 /* create a snapshot machine object */
5222 ComObjPtr<SnapshotMachine> pSnapshotMachine;
5223 pSnapshotMachine.createObject();
5224 rc = pSnapshotMachine->init(this,
5225 data.hardware,
5226 data.storage,
5227 data.uuid,
5228 strStateFile);
5229 CheckComRCReturnRC (rc);
5230
5231 /* create a snapshot object */
5232 ComObjPtr<Snapshot> pSnapshot;
5233 pSnapshot.createObject();
5234 /* initialize the snapshot */
5235 rc = pSnapshot->init(mParent, // VirtualBox object
5236 data.uuid,
5237 data.strName,
5238 data.strDescription,
5239 data.timestamp,
5240 pSnapshotMachine,
5241 aParentSnapshot);
5242 CheckComRCReturnRC (rc);
5243
5244 /* memorize the first snapshot if necessary */
5245 if (!mData->mFirstSnapshot)
5246 mData->mFirstSnapshot = pSnapshot;
5247
5248 /* memorize the current snapshot when appropriate */
5249 if ( !mData->mCurrentSnapshot
5250 && pSnapshot->getId() == aCurSnapshotId
5251 )
5252 mData->mCurrentSnapshot = pSnapshot;
5253
5254 // now create the children
5255 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
5256 it != data.llChildSnapshots.end();
5257 ++it)
5258 {
5259 const settings::Snapshot &childData = *it;
5260 // recurse
5261 rc = loadSnapshot(childData,
5262 aCurSnapshotId,
5263 pSnapshot); // parent = the one we created above
5264 CheckComRCBreakRC(rc);
5265 }
5266
5267 return rc;
5268}
5269
5270/**
5271 * @param aNode <Hardware> node.
5272 */
5273HRESULT Machine::loadHardware(const settings::Hardware &data)
5274{
5275 AssertReturn(mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5276
5277 HRESULT rc = S_OK;
5278
5279 try
5280 {
5281 /* The hardware version attribute (optional). */
5282 mHWData->mHWVersion = data.strVersion;
5283
5284 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
5285 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
5286 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
5287 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
5288 mHWData->mPAEEnabled = data.fPAE;
5289 mHWData->mSyntheticCpu = data.fSyntheticCpu;
5290
5291 mHWData->mCPUCount = data.cCPUs;
5292
5293 mHWData->mMemorySize = data.ulMemorySizeMB;
5294
5295 // boot order
5296 for (size_t i = 0;
5297 i < RT_ELEMENTS(mHWData->mBootOrder);
5298 i++)
5299 {
5300 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
5301 if (it == data.mapBootOrder.end())
5302 mHWData->mBootOrder[i] = DeviceType_Null;
5303 else
5304 mHWData->mBootOrder[i] = it->second;
5305 }
5306
5307 mHWData->mVRAMSize = data.ulVRAMSizeMB;
5308 mHWData->mMonitorCount = data.cMonitors;
5309 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
5310 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
5311 mHWData->mFirmwareType = data.firmwareType;
5312
5313#ifdef VBOX_WITH_VRDP
5314 /* RemoteDisplay */
5315 rc = mVRDPServer->loadSettings(data.vrdpSettings);
5316 CheckComRCReturnRC (rc);
5317#endif
5318
5319 /* BIOS */
5320 rc = mBIOSSettings->loadSettings(data.biosSettings);
5321 CheckComRCReturnRC (rc);
5322
5323 /* USB Controller */
5324 rc = mUSBController->loadSettings(data.usbController);
5325 CheckComRCReturnRC (rc);
5326
5327 // network adapters
5328 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
5329 it != data.llNetworkAdapters.end();
5330 ++it)
5331 {
5332 const settings::NetworkAdapter &nic = *it;
5333
5334 /* slot unicity is guaranteed by XML Schema */
5335 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
5336 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
5337 CheckComRCReturnRC (rc);
5338 }
5339
5340 // serial ports
5341 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
5342 it != data.llSerialPorts.end();
5343 ++it)
5344 {
5345 const settings::SerialPort &s = *it;
5346
5347 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
5348 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
5349 CheckComRCReturnRC (rc);
5350 }
5351
5352 // parallel ports (optional)
5353 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
5354 it != data.llParallelPorts.end();
5355 ++it)
5356 {
5357 const settings::ParallelPort &p = *it;
5358
5359 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
5360 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
5361 CheckComRCReturnRC (rc);
5362 }
5363
5364 /* AudioAdapter */
5365 rc = mAudioAdapter->loadSettings(data.audioAdapter);
5366 CheckComRCReturnRC (rc);
5367
5368 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
5369 it != data.llSharedFolders.end();
5370 ++it)
5371 {
5372 const settings::SharedFolder &sf = *it;
5373 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
5374 CheckComRCReturnRC (rc);
5375 }
5376
5377 // Clipboard
5378 mHWData->mClipboardMode = data.clipboardMode;
5379
5380 // guest settings
5381 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
5382 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
5383
5384#ifdef VBOX_WITH_GUEST_PROPS
5385 /* Guest properties (optional) */
5386 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
5387 it != data.llGuestProperties.end();
5388 ++it)
5389 {
5390 const settings::GuestProperty &prop = *it;
5391 uint32_t fFlags = guestProp::NILFLAG;
5392 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
5393 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
5394 mHWData->mGuestProperties.push_back(property);
5395 }
5396
5397 mHWData->mPropertyServiceActive = false;
5398 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
5399#endif /* VBOX_WITH_GUEST_PROPS defined */
5400 }
5401 catch(std::bad_alloc &)
5402 {
5403 return E_OUTOFMEMORY;
5404 }
5405
5406 AssertComRC(rc);
5407 return rc;
5408}
5409
5410 /**
5411 * @param aNode <StorageControllers> node.
5412 */
5413HRESULT Machine::loadStorageControllers(const settings::Storage &data,
5414 bool aRegistered,
5415 const Guid *aSnapshotId /* = NULL */)
5416{
5417 AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5418
5419 HRESULT rc = S_OK;
5420
5421 /* Make sure the attached hard disks don't get unregistered until we
5422 * associate them with tis machine (important for VMs loaded (opened) after
5423 * VirtualBox startup) */
5424 AutoReadLock vboxLock(mParent);
5425
5426 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
5427 it != data.llStorageControllers.end();
5428 ++it)
5429 {
5430 const settings::StorageController &ctlData = *it;
5431
5432 ComObjPtr<StorageController> pCtl;
5433 /* Try to find one with the name first. */
5434 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
5435 if (SUCCEEDED(rc))
5436 return setError(VBOX_E_OBJECT_IN_USE,
5437 tr("Storage controller named '%s' already exists"),
5438 ctlData.strName.raw());
5439
5440 pCtl.createObject();
5441 rc = pCtl->init(this,
5442 ctlData.strName,
5443 ctlData.storageBus);
5444 CheckComRCReturnRC (rc);
5445
5446 mStorageControllers->push_back(pCtl);
5447
5448 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
5449 CheckComRCReturnRC (rc);
5450
5451 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
5452 CheckComRCReturnRC (rc);
5453
5454 /* Set IDE emulation settings (only for AHCI controller). */
5455 if (ctlData.controllerType == StorageControllerType_IntelAhci)
5456 {
5457 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
5458 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
5459 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
5460 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
5461 )
5462 return rc;
5463 }
5464
5465 /* Load the attached devices now. */
5466 rc = loadStorageDevices(pCtl,
5467 ctlData,
5468 aRegistered,
5469 aSnapshotId);
5470 CheckComRCReturnRC (rc);
5471 }
5472
5473 return S_OK;
5474}
5475
5476/**
5477 * @param aNode <HardDiskAttachments> node.
5478 * @param aRegistered true when the machine is being loaded on VirtualBox
5479 * startup, or when a snapshot is being loaded (wchich
5480 * currently can happen on startup only)
5481 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
5482 *
5483 * @note Lock mParent for reading and hard disks for writing before calling.
5484 */
5485HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
5486 const settings::StorageController &data,
5487 bool aRegistered,
5488 const Guid *aSnapshotId /*= NULL*/)
5489{
5490 AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
5491 (mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
5492
5493 HRESULT rc = S_OK;
5494
5495 if (!aRegistered && data.llAttachedDevices.size() > 0)
5496 /* when the machine is being loaded (opened) from a file, it cannot
5497 * have hard disks attached (this should not happen normally,
5498 * because we don't allow to attach hard disks to an unregistered
5499 * VM at all */
5500 return setError(E_FAIL,
5501 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
5502 mUserData->mName.raw(),
5503 data.llAttachedDevices.size());
5504
5505 /* paranoia: detect duplicate attachments */
5506 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5507 it != data.llAttachedDevices.end();
5508 ++it)
5509 {
5510 for (settings::AttachedDevicesList::const_iterator it2 = it;
5511 it2 != data.llAttachedDevices.end();
5512 ++it2)
5513 {
5514 if (it == it2)
5515 continue;
5516
5517 if ( (*it).lPort == (*it2).lPort
5518 && (*it).lDevice == (*it2).lDevice)
5519 {
5520 return setError(E_FAIL,
5521 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
5522 aStorageController->name().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
5523 }
5524 }
5525 }
5526
5527 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5528 it != data.llAttachedDevices.end();
5529 ++it)
5530 {
5531 const settings::AttachedDevice &dev = *it;
5532 ComObjPtr<Medium> medium;
5533
5534 switch (dev.deviceType)
5535 {
5536 case DeviceType_Floppy:
5537 /* find a floppy by UUID */
5538 if (!dev.uuid.isEmpty())
5539 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5540 /* find a floppy by host device name */
5541 else if (!dev.strHostDriveSrc.isEmpty())
5542 {
5543 SafeIfaceArray<IMedium> drivevec;
5544 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
5545 if (SUCCEEDED(rc))
5546 {
5547 for (size_t i = 0; i < drivevec.size(); ++i)
5548 {
5549 Bstr hostDriveSrc(dev.strHostDriveSrc);
5550 /// @todo eliminate this conversion
5551 ComObjPtr<Medium> med = (Medium *)drivevec[i];
5552 if ( hostDriveSrc == med->name()
5553 || hostDriveSrc == med->location())
5554 {
5555 medium = med;
5556 break;
5557 }
5558 }
5559 }
5560 }
5561 break;
5562
5563 case DeviceType_DVD:
5564 /* find a DVD by UUID */
5565 if (!dev.uuid.isEmpty())
5566 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5567 /* find a DVD by host device name */
5568 else if (!dev.strHostDriveSrc.isEmpty())
5569 {
5570 SafeIfaceArray<IMedium> drivevec;
5571 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
5572 if (SUCCEEDED(rc))
5573 {
5574 for (size_t i = 0; i < drivevec.size(); ++i)
5575 {
5576 Bstr hostDriveSrc(dev.strHostDriveSrc);
5577 /// @todo eliminate this conversion
5578 ComObjPtr<Medium> med = (Medium *)drivevec[i];
5579 if ( hostDriveSrc == med->name()
5580 || hostDriveSrc == med->location())
5581 {
5582 medium = med;
5583 break;
5584 }
5585 }
5586 }
5587 }
5588 break;
5589
5590 case DeviceType_HardDisk:
5591 {
5592 /* find a hard disk by UUID */
5593 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5594 CheckComRCReturnRC(rc);
5595
5596 AutoWriteLock hdLock(medium);
5597
5598 if (medium->type() == MediumType_Immutable)
5599 {
5600 if (mType == IsSnapshotMachine)
5601 return setError(E_FAIL,
5602 tr("Immutable hard disk '%ls' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
5603 "of the virtual machine '%ls' ('%s')"),
5604 medium->locationFull().raw(),
5605 dev.uuid.raw(),
5606 aSnapshotId->raw(),
5607 mUserData->mName.raw(),
5608 mData->m_strConfigFileFull.raw());
5609
5610 return setError(E_FAIL,
5611 tr("Immutable hard disk '%ls' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
5612 medium->locationFull().raw(),
5613 dev.uuid.raw(),
5614 mUserData->mName.raw(),
5615 mData->m_strConfigFileFull.raw());
5616 }
5617
5618 if ( mType != IsSnapshotMachine
5619 && medium->children().size() != 0
5620 )
5621 return setError(E_FAIL,
5622 tr("Hard disk '%ls' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
5623 "because it has %d differencing child hard disks"),
5624 medium->locationFull().raw(),
5625 dev.uuid.raw(),
5626 mUserData->mName.raw(),
5627 mData->m_strConfigFileFull.raw(),
5628 medium->children().size());
5629
5630 if (findAttachment(mMediaData->mAttachments,
5631 medium))
5632 return setError(E_FAIL,
5633 tr("Hard disk '%ls' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
5634 medium->locationFull().raw(),
5635 dev.uuid.raw(),
5636 mUserData->mName.raw(),
5637 mData->m_strConfigFileFull.raw());
5638
5639 break;
5640 }
5641
5642 default:
5643 return setError(E_FAIL,
5644 tr("Device with unknown type is attached to the virtual machine '%ls' ('%s')"),
5645 medium->locationFull().raw(),
5646 mUserData->mName.raw(),
5647 mData->m_strConfigFileFull.raw());
5648 }
5649
5650 if (rc)
5651 break;
5652
5653 ComObjPtr<MediumAttachment> pAttachment;
5654 pAttachment.createObject();
5655 rc = pAttachment->init(this,
5656 medium,
5657 aStorageController->name(),
5658 dev.lPort,
5659 dev.lDevice,
5660 dev.deviceType);
5661 CheckComRCBreakRC(rc);
5662
5663 /* associate the medium with this machine and snapshot */
5664 if (!medium.isNull())
5665 {
5666 if (mType == IsSnapshotMachine)
5667 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
5668 else
5669 rc = medium->attachTo(mData->mUuid);
5670 }
5671 AssertComRCBreakRC (rc);
5672
5673 /* backup mMediaData to let registeredInit() properly rollback on failure
5674 * (= limited accessibility) */
5675
5676 mMediaData.backup();
5677 mMediaData->mAttachments.push_back(pAttachment);
5678 }
5679
5680 return rc;
5681}
5682
5683/**
5684 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
5685 *
5686 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
5687 * @param aSnapshot where to return the found snapshot
5688 * @param aSetError true to set extended error info on failure
5689 */
5690HRESULT Machine::findSnapshot(const Guid &aId,
5691 ComObjPtr<Snapshot> &aSnapshot,
5692 bool aSetError /* = false */)
5693{
5694 AutoReadLock chlock(snapshotsTreeLockHandle());
5695
5696 if (!mData->mFirstSnapshot)
5697 {
5698 if (aSetError)
5699 return setError(E_FAIL,
5700 tr("This machine does not have any snapshots"));
5701 return E_FAIL;
5702 }
5703
5704 if (aId.isEmpty())
5705 aSnapshot = mData->mFirstSnapshot;
5706 else
5707 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
5708
5709 if (!aSnapshot)
5710 {
5711 if (aSetError)
5712 return setError(E_FAIL,
5713 tr("Could not find a snapshot with UUID {%s}"),
5714 aId.toString().raw());
5715 return E_FAIL;
5716 }
5717
5718 return S_OK;
5719}
5720
5721/**
5722 * Returns the snapshot with the given name or fails of no such snapshot.
5723 *
5724 * @param aName snapshot name to find
5725 * @param aSnapshot where to return the found snapshot
5726 * @param aSetError true to set extended error info on failure
5727 */
5728HRESULT Machine::findSnapshot(IN_BSTR aName,
5729 ComObjPtr<Snapshot> &aSnapshot,
5730 bool aSetError /* = false */)
5731{
5732 AssertReturn(aName, E_INVALIDARG);
5733
5734 AutoReadLock chlock(snapshotsTreeLockHandle());
5735
5736 if (!mData->mFirstSnapshot)
5737 {
5738 if (aSetError)
5739 return setError(VBOX_E_OBJECT_NOT_FOUND,
5740 tr("This machine does not have any snapshots"));
5741 return VBOX_E_OBJECT_NOT_FOUND;
5742 }
5743
5744 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
5745
5746 if (!aSnapshot)
5747 {
5748 if (aSetError)
5749 return setError(VBOX_E_OBJECT_NOT_FOUND,
5750 tr("Could not find a snapshot named '%ls'"), aName);
5751 return VBOX_E_OBJECT_NOT_FOUND;
5752 }
5753
5754 return S_OK;
5755}
5756
5757/**
5758 * Returns a storage controller object with the given name.
5759 *
5760 * @param aName storage controller name to find
5761 * @param aStorageController where to return the found storage controller
5762 * @param aSetError true to set extended error info on failure
5763 */
5764HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
5765 ComObjPtr<StorageController> &aStorageController,
5766 bool aSetError /* = false */)
5767{
5768 AssertReturn (!aName.isEmpty(), E_INVALIDARG);
5769
5770 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5771 it != mStorageControllers->end();
5772 ++it)
5773 {
5774 if ((*it)->name() == aName)
5775 {
5776 aStorageController = (*it);
5777 return S_OK;
5778 }
5779 }
5780
5781 if (aSetError)
5782 return setError(VBOX_E_OBJECT_NOT_FOUND,
5783 tr("Could not find a storage controller named '%s'"),
5784 aName.raw());
5785 return VBOX_E_OBJECT_NOT_FOUND;
5786}
5787
5788HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
5789 MediaData::AttachmentList &atts)
5790{
5791 AutoCaller autoCaller(this);
5792 CheckComRCReturnRC(autoCaller.rc());
5793
5794 AutoReadLock alock(this);
5795
5796 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
5797 it != mMediaData->mAttachments.end();
5798 ++it)
5799 {
5800 if (Bstr((*it)->controllerName()) == aName)
5801 atts.push_back(*it);
5802 }
5803
5804 return S_OK;
5805}
5806
5807/**
5808 * Helper for #saveSettings. Cares about renaming the settings directory and
5809 * file if the machine name was changed and about creating a new settings file
5810 * if this is a new machine.
5811 *
5812 * @note Must be never called directly but only from #saveSettings().
5813 *
5814 * @param aRenamed receives |true| if the name was changed and the settings
5815 * file was renamed as a result, or |false| otherwise. The
5816 * value makes sense only on success.
5817 * @param aNew receives |true| if a virgin settings file was created.
5818 */
5819HRESULT Machine::prepareSaveSettings(bool &aRenamed,
5820 bool &aNew)
5821{
5822 /* Note: tecnhically, mParent needs to be locked only when the machine is
5823 * registered (see prepareSaveSettings() for details) but we don't
5824 * currently differentiate it in callers of saveSettings() so we don't
5825 * make difference here too. */
5826 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5827 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5828
5829 HRESULT rc = S_OK;
5830
5831 aRenamed = false;
5832
5833 /* if we're ready and isConfigLocked() is FALSE then it means
5834 * that no config file exists yet (we will create a virgin one) */
5835 aNew = !mData->m_pMachineConfigFile->fileExists();
5836
5837 /* attempt to rename the settings file if machine name is changed */
5838 if ( mUserData->mNameSync
5839 && mUserData.isBackedUp()
5840 && mUserData.backedUpData()->mName != mUserData->mName
5841 )
5842 {
5843 aRenamed = true;
5844
5845 bool dirRenamed = false;
5846 bool fileRenamed = false;
5847
5848 Utf8Str configFile, newConfigFile;
5849 Utf8Str configDir, newConfigDir;
5850
5851 do
5852 {
5853 int vrc = VINF_SUCCESS;
5854
5855 Utf8Str name = mUserData.backedUpData()->mName;
5856 Utf8Str newName = mUserData->mName;
5857
5858 configFile = mData->m_strConfigFileFull;
5859
5860 /* first, rename the directory if it matches the machine name */
5861 configDir = configFile;
5862 configDir.stripFilename();
5863 newConfigDir = configDir;
5864 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
5865 {
5866 newConfigDir.stripFilename();
5867 newConfigDir = Utf8StrFmt ("%s%c%s",
5868 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
5869 /* new dir and old dir cannot be equal here because of 'if'
5870 * above and because name != newName */
5871 Assert (configDir != newConfigDir);
5872 if (!aNew)
5873 {
5874 /* perform real rename only if the machine is not new */
5875 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
5876 if (RT_FAILURE(vrc))
5877 {
5878 rc = setError(E_FAIL,
5879 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
5880 configDir.raw(),
5881 newConfigDir.raw(),
5882 vrc);
5883 break;
5884 }
5885 dirRenamed = true;
5886 }
5887 }
5888
5889 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
5890 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
5891
5892 /* then try to rename the settings file itself */
5893 if (newConfigFile != configFile)
5894 {
5895 /* get the path to old settings file in renamed directory */
5896 configFile = Utf8StrFmt("%s%c%s",
5897 newConfigDir.raw(),
5898 RTPATH_DELIMITER,
5899 RTPathFilename(configFile.c_str()));
5900 if (!aNew)
5901 {
5902 /* perform real rename only if the machine is not new */
5903 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
5904 if (RT_FAILURE(vrc))
5905 {
5906 rc = setError(E_FAIL,
5907 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
5908 configFile.raw(),
5909 newConfigFile.raw(),
5910 vrc);
5911 break;
5912 }
5913 fileRenamed = true;
5914 }
5915 }
5916
5917 /* update m_strConfigFileFull amd mConfigFile */
5918 Utf8Str oldConfigFileFull = mData->m_strConfigFileFull;
5919 Utf8Str oldConfigFile = mData->m_strConfigFile;
5920 mData->m_strConfigFileFull = newConfigFile;
5921 /* try to get the relative path for mConfigFile */
5922 Utf8Str path = newConfigFile;
5923 mParent->calculateRelativePath (path, path);
5924 mData->m_strConfigFile = path;
5925
5926 /* last, try to update the global settings with the new path */
5927 if (mData->mRegistered)
5928 {
5929 rc = mParent->updateSettings(configDir.c_str(), newConfigDir.c_str());
5930 if (FAILED(rc))
5931 {
5932 /* revert to old values */
5933 mData->m_strConfigFileFull = oldConfigFileFull;
5934 mData->m_strConfigFile = oldConfigFile;
5935 break;
5936 }
5937 }
5938
5939 /* update the snapshot folder */
5940 path = mUserData->mSnapshotFolderFull;
5941 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
5942 {
5943 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
5944 path.raw() + configDir.length());
5945 mUserData->mSnapshotFolderFull = path;
5946 calculateRelativePath (path, path);
5947 mUserData->mSnapshotFolder = path;
5948 }
5949
5950 /* update the saved state file path */
5951 path = mSSData->mStateFilePath;
5952 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
5953 {
5954 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
5955 path.raw() + configDir.length());
5956 mSSData->mStateFilePath = path;
5957 }
5958
5959 /* Update saved state file paths of all online snapshots.
5960 * Note that saveSettings() will recognize name change
5961 * and will save all snapshots in this case. */
5962 if (mData->mFirstSnapshot)
5963 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
5964 newConfigDir.c_str());
5965 }
5966 while (0);
5967
5968 if (FAILED(rc))
5969 {
5970 /* silently try to rename everything back */
5971 if (fileRenamed)
5972 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
5973 if (dirRenamed)
5974 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
5975 }
5976
5977 CheckComRCReturnRC(rc);
5978 }
5979
5980 if (aNew)
5981 {
5982 /* create a virgin config file */
5983 int vrc = VINF_SUCCESS;
5984
5985 /* ensure the settings directory exists */
5986 Utf8Str path(mData->m_strConfigFileFull);
5987 path.stripFilename();
5988 if (!RTDirExists(path.c_str()))
5989 {
5990 vrc = RTDirCreateFullPath(path.c_str(), 0777);
5991 if (RT_FAILURE(vrc))
5992 {
5993 return setError(E_FAIL,
5994 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
5995 path.raw(),
5996 vrc);
5997 }
5998 }
5999
6000 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
6001 path = Utf8Str(mData->m_strConfigFileFull);
6002 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
6003 RTFILE_O_READWRITE | RTFILE_O_CREATE |
6004 RTFILE_O_DENY_WRITE);
6005 if (RT_FAILURE(vrc))
6006 {
6007 mData->mHandleCfgFile = NIL_RTFILE;
6008 return setError(E_FAIL,
6009 tr("Could not create the settings file '%s' (%Rrc)"),
6010 path.raw(),
6011 vrc);
6012 }
6013 RTFileClose(mData->mHandleCfgFile);
6014 }
6015
6016 return rc;
6017}
6018
6019/**
6020 * Saves and commits machine data, user data and hardware data.
6021 *
6022 * Note that on failure, the data remains uncommitted.
6023 *
6024 * @a aFlags may combine the following flags:
6025 *
6026 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
6027 * Used when saving settings after an operation that makes them 100%
6028 * correspond to the settings from the current snapshot.
6029 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
6030 * #isReallyModified() returns false. This is necessary for cases when we
6031 * change machine data diectly, not through the backup()/commit() mechanism.
6032 *
6033 * @note Must be called from under mParent write lock (sometimes needed by
6034 * #prepareSaveSettings()) and this object's write lock. Locks children for
6035 * writing. There is one exception when mParent is unused and therefore may be
6036 * left unlocked: if this machine is an unregistered one.
6037 */
6038HRESULT Machine::saveSettings(int aFlags /*= 0*/)
6039{
6040 LogFlowThisFuncEnter();
6041
6042 /* Note: tecnhically, mParent needs to be locked only when the machine is
6043 * registered (see prepareSaveSettings() for details) but we don't
6044 * currently differentiate it in callers of saveSettings() so we don't
6045 * make difference here too. */
6046 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6047 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6048
6049 /* make sure child objects are unable to modify the settings while we are
6050 * saving them */
6051 ensureNoStateDependencies();
6052
6053 AssertReturn(mType == IsMachine || mType == IsSessionMachine, E_FAIL);
6054
6055 BOOL currentStateModified = mData->mCurrentStateModified;
6056 bool settingsModified;
6057
6058 if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
6059 {
6060 /* We ignore changes to user data when setting mCurrentStateModified
6061 * because the current state will not differ from the current snapshot
6062 * if only user data has been changed (user data is shared by all
6063 * snapshots). */
6064 currentStateModified = isReallyModified (true /* aIgnoreUserData */);
6065 settingsModified = mUserData.hasActualChanges() || currentStateModified;
6066 }
6067 else
6068 {
6069 if (aFlags & SaveS_ResetCurStateModified)
6070 currentStateModified = FALSE;
6071 settingsModified = isReallyModified();
6072 }
6073
6074 HRESULT rc = S_OK;
6075
6076 /* First, prepare to save settings. It will care about renaming the
6077 * settings directory and file if the machine name was changed and about
6078 * creating a new settings file if this is a new machine. */
6079 bool isRenamed = false;
6080 bool isNew = false;
6081 rc = prepareSaveSettings(isRenamed, isNew);
6082 CheckComRCReturnRC(rc);
6083
6084 try
6085 {
6086 mData->m_pMachineConfigFile->uuid = mData->mUuid;
6087 mData->m_pMachineConfigFile->strName = mUserData->mName;
6088 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
6089 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
6090 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
6091
6092 if ( mData->mMachineState == MachineState_Saved
6093 || mData->mMachineState == MachineState_Restoring
6094 )
6095 {
6096 Assert(!mSSData->mStateFilePath.isEmpty());
6097 /* try to make the file name relative to the settings file dir */
6098 Utf8Str stateFilePath = mSSData->mStateFilePath;
6099 calculateRelativePath(stateFilePath, stateFilePath);
6100
6101 mData->m_pMachineConfigFile->strStateFile = stateFilePath;
6102 }
6103 else
6104 {
6105 Assert(mSSData->mStateFilePath.isNull());
6106 mData->m_pMachineConfigFile->strStateFile.setNull();
6107 }
6108
6109 if (mData->mCurrentSnapshot)
6110 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
6111 else
6112 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
6113
6114 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
6115 mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
6116 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6117 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6118
6119 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
6120 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
6121 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
6122 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
6123
6124 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
6125 CheckComRCThrowRC(rc);
6126
6127 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
6128 CheckComRCThrowRC(rc);
6129
6130 // save snapshots
6131 rc = saveAllSnapshots();
6132 CheckComRCThrowRC(rc);
6133
6134 // now spit it all out
6135 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6136 }
6137 catch (HRESULT err)
6138 {
6139 /* we assume that error info is set by the thrower */
6140 rc = err;
6141 }
6142 catch (...)
6143 {
6144 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6145 }
6146
6147 if (SUCCEEDED(rc))
6148 {
6149 commit();
6150
6151 /* memorize the new modified state */
6152 mData->mCurrentStateModified = currentStateModified;
6153 }
6154
6155 if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
6156 {
6157 /* Fire the data change event, even on failure (since we've already
6158 * committed all data). This is done only for SessionMachines because
6159 * mutable Machine instances are always not registered (i.e. private
6160 * to the client process that creates them) and thus don't need to
6161 * inform callbacks. */
6162 if (mType == IsSessionMachine)
6163 mParent->onMachineDataChange(mData->mUuid);
6164 }
6165
6166 LogFlowThisFunc(("rc=%08X\n", rc));
6167 LogFlowThisFuncLeave();
6168 return rc;
6169}
6170
6171HRESULT Machine::saveAllSnapshots()
6172{
6173 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
6174
6175 HRESULT rc = S_OK;
6176
6177 try
6178 {
6179 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
6180
6181 if (mData->mFirstSnapshot)
6182 {
6183 settings::Snapshot snapNew;
6184 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
6185
6186 // get reference to the fresh copy of the snapshot on the list and
6187 // work on that copy directly to avoid excessive copying later
6188 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6189
6190 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
6191 CheckComRCThrowRC(rc);
6192 }
6193
6194// if (mType == IsSessionMachine)
6195// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
6196
6197 }
6198 catch (HRESULT err)
6199 {
6200 /* we assume that error info is set by the thrower */
6201 rc = err;
6202 }
6203 catch (...)
6204 {
6205 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6206 }
6207
6208 return rc;
6209}
6210
6211/**
6212 * Saves the VM hardware configuration. It is assumed that the
6213 * given node is empty.
6214 *
6215 * @param aNode <Hardware> node to save the VM hardware confguration to.
6216 */
6217HRESULT Machine::saveHardware(settings::Hardware &data)
6218{
6219 HRESULT rc = S_OK;
6220
6221 try
6222 {
6223 /* The hardware version attribute (optional).
6224 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
6225 if ( mHWData->mHWVersion == "1"
6226 && mSSData->mStateFilePath.isEmpty()
6227 )
6228 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. */
6229
6230 data.strVersion = mHWData->mHWVersion;
6231
6232 // CPU
6233 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
6234 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
6235 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
6236 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
6237 data.fPAE = !!mHWData->mPAEEnabled;
6238 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
6239
6240 data.cCPUs = mHWData->mCPUCount;
6241
6242 // memory
6243 data.ulMemorySizeMB = mHWData->mMemorySize;
6244
6245 // firmware
6246 data.firmwareType = mHWData->mFirmwareType;
6247
6248 // boot order
6249 data.mapBootOrder.clear();
6250 for (size_t i = 0;
6251 i < RT_ELEMENTS(mHWData->mBootOrder);
6252 ++i)
6253 data.mapBootOrder[i] = mHWData->mBootOrder[i];
6254
6255 // display
6256 data.ulVRAMSizeMB = mHWData->mVRAMSize;
6257 data.cMonitors = mHWData->mMonitorCount;
6258 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
6259 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
6260
6261#ifdef VBOX_WITH_VRDP
6262 /* VRDP settings (optional) */
6263 rc = mVRDPServer->saveSettings(data.vrdpSettings);
6264 CheckComRCThrowRC(rc);
6265#endif
6266
6267 /* BIOS (required) */
6268 rc = mBIOSSettings->saveSettings(data.biosSettings);
6269 CheckComRCThrowRC(rc);
6270
6271 /* USB Controller (required) */
6272 rc = mUSBController->saveSettings(data.usbController);
6273 CheckComRCThrowRC(rc);
6274
6275 /* Network adapters (required) */
6276 data.llNetworkAdapters.clear();
6277 for (ULONG slot = 0;
6278 slot < RT_ELEMENTS(mNetworkAdapters);
6279 ++slot)
6280 {
6281 settings::NetworkAdapter nic;
6282 nic.ulSlot = slot;
6283 rc = mNetworkAdapters[slot]->saveSettings(nic);
6284 CheckComRCThrowRC(rc);
6285
6286 data.llNetworkAdapters.push_back(nic);
6287 }
6288
6289 /* Serial ports */
6290 data.llSerialPorts.clear();
6291 for (ULONG slot = 0;
6292 slot < RT_ELEMENTS(mSerialPorts);
6293 ++slot)
6294 {
6295 settings::SerialPort s;
6296 s.ulSlot = slot;
6297 rc = mSerialPorts[slot]->saveSettings(s);
6298 CheckComRCReturnRC (rc);
6299
6300 data.llSerialPorts.push_back(s);
6301 }
6302
6303 /* Parallel ports */
6304 data.llParallelPorts.clear();
6305 for (ULONG slot = 0;
6306 slot < RT_ELEMENTS(mParallelPorts);
6307 ++slot)
6308 {
6309 settings::ParallelPort p;
6310 p.ulSlot = slot;
6311 rc = mParallelPorts[slot]->saveSettings(p);
6312 CheckComRCReturnRC (rc);
6313
6314 data.llParallelPorts.push_back(p);
6315 }
6316
6317 /* Audio adapter */
6318 rc = mAudioAdapter->saveSettings(data.audioAdapter);
6319 CheckComRCReturnRC (rc);
6320
6321 /* Shared folders */
6322 data.llSharedFolders.clear();
6323 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6324 it != mHWData->mSharedFolders.end();
6325 ++it)
6326 {
6327 ComObjPtr<SharedFolder> pFolder = *it;
6328 settings::SharedFolder sf;
6329 sf.strName = pFolder->name();
6330 sf.strHostPath = pFolder->hostPath();
6331 sf.fWritable = !!pFolder->writable();
6332
6333 data.llSharedFolders.push_back(sf);
6334 }
6335
6336 // clipboard
6337 data.clipboardMode = mHWData->mClipboardMode;
6338
6339 /* Guest */
6340 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
6341 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
6342
6343 // guest properties
6344 data.llGuestProperties.clear();
6345#ifdef VBOX_WITH_GUEST_PROPS
6346 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
6347 it != mHWData->mGuestProperties.end();
6348 ++it)
6349 {
6350 HWData::GuestProperty property = *it;
6351
6352 settings::GuestProperty prop;
6353 prop.strName = property.strName;
6354 prop.strValue = property.strValue;
6355 prop.timestamp = property.mTimestamp;
6356 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
6357 guestProp::writeFlags(property.mFlags, szFlags);
6358 prop.strFlags = szFlags;
6359
6360 data.llGuestProperties.push_back(prop);
6361 }
6362
6363 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
6364#endif /* VBOX_WITH_GUEST_PROPS defined */
6365 }
6366 catch(std::bad_alloc &)
6367 {
6368 return E_OUTOFMEMORY;
6369 }
6370
6371 AssertComRC(rc);
6372 return rc;
6373}
6374
6375/**
6376 * Saves the storage controller configuration.
6377 *
6378 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
6379 */
6380HRESULT Machine::saveStorageControllers(settings::Storage &data)
6381{
6382 data.llStorageControllers.clear();
6383
6384 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6385 it != mStorageControllers->end();
6386 ++it)
6387 {
6388 HRESULT rc;
6389 ComObjPtr<StorageController> pCtl = *it;
6390
6391 settings::StorageController ctl;
6392 ctl.strName = pCtl->name();
6393 ctl.controllerType = pCtl->controllerType();
6394 ctl.storageBus = pCtl->storageBus();
6395
6396 /* Save the port count. */
6397 ULONG portCount;
6398 rc = pCtl->COMGETTER(PortCount)(&portCount);
6399 ComAssertComRCRet(rc, rc);
6400 ctl.ulPortCount = portCount;
6401
6402 /* Save IDE emulation settings. */
6403 if (ctl.controllerType == StorageControllerType_IntelAhci)
6404 {
6405 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
6406 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
6407 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
6408 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
6409 )
6410 ComAssertComRCRet(rc, rc);
6411 }
6412
6413 /* save the devices now. */
6414 rc = saveStorageDevices(pCtl, ctl);
6415 ComAssertComRCRet(rc, rc);
6416
6417 data.llStorageControllers.push_back(ctl);
6418 }
6419
6420 return S_OK;
6421}
6422
6423/**
6424 * Saves the hard disk confguration.
6425 */
6426HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
6427 settings::StorageController &data)
6428{
6429 using namespace settings;
6430
6431 MediaData::AttachmentList atts;
6432
6433 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->name()), atts);
6434 CheckComRCReturnRC (rc);
6435
6436 data.llAttachedDevices.clear();
6437 for (MediaData::AttachmentList::const_iterator it = atts.begin();
6438 it != atts.end();
6439 ++it)
6440 {
6441 settings::AttachedDevice dev;
6442
6443 dev.deviceType = (*it)->type();
6444 dev.lPort = (*it)->port();
6445 dev.lDevice = (*it)->device();
6446 if (!(*it)->medium().isNull())
6447 {
6448 BOOL fHostDrive = false;
6449 rc = (*it)->medium()->COMGETTER(HostDrive)(&fHostDrive);
6450 if (FAILED(rc))
6451 return rc;
6452 if (fHostDrive)
6453 dev.strHostDriveSrc = (*it)->medium()->location();
6454 else
6455 dev.uuid = (*it)->medium()->id();
6456 dev.fPassThrough = (*it)->passthrough();
6457 }
6458
6459 data.llAttachedDevices.push_back(dev);
6460 }
6461
6462 return S_OK;
6463}
6464
6465/**
6466 * Saves machine state settings as defined by aFlags
6467 * (SaveSTS_* values).
6468 *
6469 * @param aFlags Combination of SaveSTS_* flags.
6470 *
6471 * @note Locks objects for writing.
6472 */
6473HRESULT Machine::saveStateSettings(int aFlags)
6474{
6475 if (aFlags == 0)
6476 return S_OK;
6477
6478 AutoCaller autoCaller (this);
6479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6480
6481 /* This object's write lock is also necessary to serialize file access
6482 * (prevent concurrent reads and writes) */
6483 AutoWriteLock alock(this);
6484
6485 HRESULT rc = S_OK;
6486
6487 Assert(mData->m_pMachineConfigFile);
6488
6489 try
6490 {
6491 if (aFlags & SaveSTS_CurStateModified)
6492 mData->m_pMachineConfigFile->fCurrentStateModified = true;
6493
6494 if (aFlags & SaveSTS_StateFilePath)
6495 {
6496 if (mSSData->mStateFilePath)
6497 {
6498 /* try to make the file name relative to the settings file dir */
6499 Utf8Str stateFilePath = mSSData->mStateFilePath;
6500 calculateRelativePath(stateFilePath, stateFilePath);
6501 mData->m_pMachineConfigFile->strStateFile = stateFilePath;
6502 }
6503 else
6504 mData->m_pMachineConfigFile->strStateFile.setNull();
6505 }
6506
6507 if (aFlags & SaveSTS_StateTimeStamp)
6508 {
6509 Assert( mData->mMachineState != MachineState_Aborted
6510 || mSSData->mStateFilePath.isNull());
6511
6512 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6513
6514 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6515 }
6516
6517 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6518 }
6519 catch (...)
6520 {
6521 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6522 }
6523
6524 return rc;
6525}
6526
6527/**
6528 * Creates differencing hard disks for all normal hard disks attached to this
6529 * machine and a new set of attachments to refer to created disks.
6530 *
6531 * Used when taking a snapshot or when discarding the current state.
6532 *
6533 * This method assumes that mMediaData contains the original hard disk attachments
6534 * it needs to create diffs for. On success, these attachments will be replaced
6535 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
6536 * called to delete created diffs which will also rollback mMediaData and restore
6537 * whatever was backed up before calling this method.
6538 *
6539 * Attachments with non-normal hard disks are left as is.
6540 *
6541 * If @a aOnline is @c false then the original hard disks that require implicit
6542 * diffs will be locked for reading. Otherwise it is assumed that they are
6543 * already locked for writing (when the VM was started). Note that in the latter
6544 * case it is responsibility of the caller to lock the newly created diffs for
6545 * writing if this method succeeds.
6546 *
6547 * @param aFolder Folder where to create diff hard disks.
6548 * @param aProgress Progress object to run (must contain at least as
6549 * many operations left as the number of hard disks
6550 * attached).
6551 * @param aOnline Whether the VM was online prior to this operation.
6552 *
6553 * @note The progress object is not marked as completed, neither on success nor
6554 * on failure. This is a responsibility of the caller.
6555 *
6556 * @note Locks this object for writing.
6557 */
6558HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
6559 IProgress *aProgress,
6560 ULONG aWeight,
6561 bool aOnline)
6562{
6563 AssertReturn(!aFolder.isEmpty(), E_FAIL);
6564
6565 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
6566
6567 AutoCaller autoCaller(this);
6568 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6569
6570 AutoWriteLock alock(this);
6571
6572 /* must be in a protective state because we leave the lock below */
6573 AssertReturn( mData->mMachineState == MachineState_Saving
6574 || mData->mMachineState == MachineState_RestoringSnapshot
6575 || mData->mMachineState == MachineState_DeletingSnapshot,
6576 E_FAIL);
6577
6578 HRESULT rc = S_OK;
6579
6580 typedef std::list< ComObjPtr<Medium> > LockedMedia;
6581 LockedMedia lockedMedia;
6582
6583 try
6584 {
6585 if (!aOnline)
6586 {
6587 /* lock all attached hard disks early to detect "in use"
6588 * situations before creating actual diffs */
6589 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6590 it != mMediaData->mAttachments.end();
6591 ++it)
6592 {
6593 MediumAttachment* pAtt = *it;
6594 if (pAtt->type() == DeviceType_HardDisk)
6595 {
6596 Medium* pHD = pAtt->medium();
6597 Assert(pHD);
6598 rc = pHD->LockRead (NULL);
6599 CheckComRCThrowRC(rc);
6600 lockedMedia.push_back(pHD);
6601 }
6602 }
6603 }
6604
6605 /* remember the current list (note that we don't use backup() since
6606 * mMediaData may be already backed up) */
6607 MediaData::AttachmentList atts = mMediaData->mAttachments;
6608
6609 /* start from scratch */
6610 mMediaData->mAttachments.clear();
6611
6612 /* go through remembered attachments and create diffs for normal hard
6613 * disks and attach them */
6614 for (MediaData::AttachmentList::const_iterator it = atts.begin();
6615 it != atts.end();
6616 ++it)
6617 {
6618 MediumAttachment* pAtt = *it;
6619
6620 DeviceType_T devType = pAtt->type();
6621 Medium* medium = pAtt->medium();
6622
6623 if ( devType != DeviceType_HardDisk
6624 || medium == NULL
6625 || medium->type() != MediumType_Normal)
6626 {
6627 /* copy the attachment as is */
6628
6629 /** @todo the progress object created in Console::TakeSnaphot
6630 * only expects operations for hard disks. Later other
6631 * device types need to show up in the progress as well. */
6632 if (devType == DeviceType_HardDisk)
6633 {
6634 if (medium == NULL)
6635 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
6636 aWeight); // weight
6637 else
6638 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
6639 medium->base()->name().raw()),
6640 aWeight); // weight
6641 }
6642
6643 mMediaData->mAttachments.push_back(pAtt);
6644 continue;
6645 }
6646
6647 /* need a diff */
6648 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
6649 medium->base()->name().raw()),
6650 aWeight); // weight
6651
6652 ComObjPtr<Medium> diff;
6653 diff.createObject();
6654 rc = diff->init(mParent,
6655 medium->preferredDiffFormat().raw(),
6656 BstrFmt("%ls"RTPATH_SLASH_STR,
6657 mUserData->mSnapshotFolderFull.raw()).raw());
6658 CheckComRCThrowRC(rc);
6659
6660 /* leave the lock before the potentially lengthy operation */
6661 alock.leave();
6662
6663 rc = medium->createDiffStorageAndWait(diff,
6664 MediumVariant_Standard,
6665 NULL);
6666
6667 // at this point, the old image is still locked for writing, but instead
6668 // we need the new diff image locked for writing and lock the previously
6669 // current one for reading only
6670 if (aOnline)
6671 {
6672 diff->LockWrite(NULL);
6673 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
6674 medium->UnlockWrite(NULL);
6675 medium->LockRead(NULL);
6676 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
6677 }
6678
6679 alock.enter();
6680
6681 CheckComRCThrowRC(rc);
6682
6683 rc = diff->attachTo(mData->mUuid);
6684 AssertComRCThrowRC(rc);
6685
6686 /* add a new attachment */
6687 ComObjPtr<MediumAttachment> attachment;
6688 attachment.createObject();
6689 rc = attachment->init(this,
6690 diff,
6691 pAtt->controllerName(),
6692 pAtt->port(),
6693 pAtt->device(),
6694 DeviceType_HardDisk,
6695 true /* aImplicit */);
6696 CheckComRCThrowRC(rc);
6697
6698 mMediaData->mAttachments.push_back(attachment);
6699 }
6700 }
6701 catch (HRESULT aRC) { rc = aRC; }
6702
6703 /* unlock all hard disks we locked */
6704 if (!aOnline)
6705 {
6706 ErrorInfoKeeper eik;
6707
6708 for (LockedMedia::const_iterator it = lockedMedia.begin();
6709 it != lockedMedia.end();
6710 ++it)
6711 {
6712 HRESULT rc2 = (*it)->UnlockRead(NULL);
6713 AssertComRC(rc2);
6714 }
6715 }
6716
6717 if (FAILED(rc))
6718 {
6719 MultiResultRef mrc (rc);
6720
6721 mrc = deleteImplicitDiffs();
6722 }
6723
6724 return rc;
6725}
6726
6727/**
6728 * Deletes implicit differencing hard disks created either by
6729 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
6730 *
6731 * Note that to delete hard disks created by #AttachMedium() this method is
6732 * called from #fixupMedia() when the changes are rolled back.
6733 *
6734 * @note Locks this object for writing.
6735 */
6736HRESULT Machine::deleteImplicitDiffs()
6737{
6738 AutoCaller autoCaller(this);
6739 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6740
6741 AutoWriteLock alock(this);
6742
6743 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
6744
6745 HRESULT rc = S_OK;
6746
6747 MediaData::AttachmentList implicitAtts;
6748
6749 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
6750
6751 /* enumerate new attachments */
6752 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6753 it != mMediaData->mAttachments.end();
6754 ++it)
6755 {
6756 ComObjPtr<Medium> hd = (*it)->medium();
6757 if (hd.isNull())
6758 continue;
6759
6760 if ((*it)->isImplicit())
6761 {
6762 /* deassociate and mark for deletion */
6763 rc = hd->detachFrom(mData->mUuid);
6764 AssertComRC(rc);
6765 implicitAtts.push_back (*it);
6766 continue;
6767 }
6768
6769 /* was this hard disk attached before? */
6770 if (!findAttachment(oldAtts, hd))
6771 {
6772 /* no: de-associate */
6773 rc = hd->detachFrom(mData->mUuid);
6774 AssertComRC(rc);
6775 continue;
6776 }
6777 }
6778
6779 /* rollback hard disk changes */
6780 mMediaData.rollback();
6781
6782 MultiResult mrc (S_OK);
6783
6784 /* delete unused implicit diffs */
6785 if (implicitAtts.size() != 0)
6786 {
6787 /* will leave the lock before the potentially lengthy
6788 * operation, so protect with the special state (unless already
6789 * protected) */
6790 MachineState_T oldState = mData->mMachineState;
6791 if ( oldState != MachineState_Saving
6792 && oldState != MachineState_RestoringSnapshot
6793 && oldState != MachineState_DeletingSnapshot
6794 )
6795 setMachineState (MachineState_SettingUp);
6796
6797 alock.leave();
6798
6799 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
6800 it != implicitAtts.end();
6801 ++it)
6802 {
6803 ComObjPtr<Medium> hd = (*it)->medium();
6804 mrc = hd->deleteStorageAndWait();
6805 }
6806
6807 alock.enter();
6808
6809 if (mData->mMachineState == MachineState_SettingUp)
6810 {
6811 setMachineState (oldState);
6812 }
6813 }
6814
6815 return mrc;
6816}
6817
6818/**
6819 * Looks through the given list of media attachments for one with the given parameters
6820 * and returns it, or NULL if not found. The list is a parameter so that backup lists
6821 * can be searched as well if needed.
6822 *
6823 * @param list
6824 * @param aControllerName
6825 * @param aControllerPort
6826 * @param aDevice
6827 * @return
6828 */
6829MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
6830 IN_BSTR aControllerName,
6831 LONG aControllerPort,
6832 LONG aDevice)
6833{
6834 for (MediaData::AttachmentList::const_iterator it = ll.begin();
6835 it != ll.end();
6836 ++it)
6837 {
6838 MediumAttachment *pAttach = *it;
6839 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
6840 return pAttach;
6841 }
6842
6843 return NULL;
6844}
6845
6846/**
6847 * Looks through the given list of media attachments for one with the given parameters
6848 * and returns it, or NULL if not found. The list is a parameter so that backup lists
6849 * can be searched as well if needed.
6850 *
6851 * @param list
6852 * @param aControllerName
6853 * @param aControllerPort
6854 * @param aDevice
6855 * @return
6856 */
6857MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
6858 ComObjPtr<Medium> pMedium)
6859{
6860 for (MediaData::AttachmentList::const_iterator it = ll.begin();
6861 it != ll.end();
6862 ++it)
6863 {
6864 MediumAttachment *pAttach = *it;
6865 ComObjPtr<Medium> pMediumThis = pAttach->medium();
6866 if (pMediumThis.equalsTo(pMedium))
6867 return pAttach;
6868 }
6869
6870 return NULL;
6871}
6872
6873/**
6874 * Looks through the given list of media attachments for one with the given parameters
6875 * and returns it, or NULL if not found. The list is a parameter so that backup lists
6876 * can be searched as well if needed.
6877 *
6878 * @param list
6879 * @param aControllerName
6880 * @param aControllerPort
6881 * @param aDevice
6882 * @return
6883 */
6884MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
6885 Guid &id)
6886{
6887 for (MediaData::AttachmentList::const_iterator it = ll.begin();
6888 it != ll.end();
6889 ++it)
6890 {
6891 MediumAttachment *pAttach = *it;
6892 ComObjPtr<Medium> pMediumThis = pAttach->medium();
6893 if (pMediumThis->id() == id)
6894 return pAttach;
6895 }
6896
6897 return NULL;
6898}
6899
6900/**
6901 * Perform deferred hard disk detachments on success and deletion of implicitly
6902 * created diffs on failure.
6903 *
6904 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
6905 * backed up).
6906 *
6907 * When the data is backed up, this method will commit mMediaData if @a aCommit is
6908 * @c true and rollback it otherwise before returning.
6909 *
6910 * If @a aOnline is @c true then this method called with @a aCommit = @c true
6911 * will also unlock the old hard disks for which the new implicit diffs were
6912 * created and will lock these new diffs for writing. When @a aCommit is @c
6913 * false, this argument is ignored.
6914 *
6915 * @param aCommit @c true if called on success.
6916 * @param aOnline Whether the VM was online prior to this operation.
6917 *
6918 * @note Locks this object for writing!
6919 */
6920void Machine::fixupMedia(bool aCommit, bool aOnline /*= false*/)
6921{
6922 AutoCaller autoCaller(this);
6923 AssertComRCReturnVoid (autoCaller.rc());
6924
6925 AutoWriteLock alock(this);
6926
6927 HRESULT rc = S_OK;
6928
6929 /* no attach/detach operations -- nothing to do */
6930 if (!mMediaData.isBackedUp())
6931 return;
6932
6933 if (aCommit)
6934 {
6935 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
6936
6937 /* enumerate new attachments */
6938 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6939 it != mMediaData->mAttachments.end();
6940 ++it)
6941 {
6942 MediumAttachment *pAttach = *it;
6943
6944 if (pAttach->type() == DeviceType_HardDisk)
6945 {
6946 pAttach->commit();
6947
6948 Medium* pMedium = pAttach->medium();
6949
6950 /** @todo convert all this Machine-based voodoo to MediumAttachment
6951 * based commit logic. */
6952 if (pAttach->isImplicit())
6953 {
6954 /* convert implicit attachment to normal */
6955 pAttach->setImplicit(false);
6956
6957 if (aOnline)
6958 {
6959 rc = pMedium->LockWrite(NULL);
6960 AssertComRC(rc);
6961
6962 mData->mSession.mLockedMedia.push_back(
6963 Data::Session::LockedMedia::value_type(
6964 ComPtr<IMedium>(pMedium), true));
6965
6966 /* also, relock the old hard disk which is a base for the
6967 * new diff for reading if the VM is online */
6968
6969 ComObjPtr<Medium> parent = pMedium->parent();
6970 /* make the relock atomic */
6971 AutoWriteLock parentLock (parent);
6972 rc = parent->UnlockWrite(NULL);
6973 AssertComRC(rc);
6974 rc = parent->LockRead(NULL);
6975 AssertComRC(rc);
6976
6977 /* XXX actually we should replace the old entry in that
6978 * vector (write lock => read lock) but this would take
6979 * some effort. So lets just ignore the error code in
6980 * SessionMachine::unlockMedia(). */
6981 mData->mSession.mLockedMedia.push_back(
6982 Data::Session::LockedMedia::value_type (
6983 ComPtr<IMedium>(parent), false));
6984 }
6985
6986 continue;
6987 }
6988
6989 if (pMedium)
6990 {
6991 /* was this hard disk attached before? */
6992 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
6993 oldIt != oldAtts.end();
6994 ++oldIt)
6995 {
6996 MediumAttachment *pOldAttach = *it;
6997 if (pOldAttach->medium().equalsTo(pMedium))
6998 {
6999 /* yes: remove from old to avoid de-association */
7000 oldAtts.erase(oldIt);
7001 break;
7002 }
7003 }
7004 }
7005 }
7006 }
7007
7008 /* enumerate remaining old attachments and de-associate from the
7009 * current machine state */
7010 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
7011 it != oldAtts.end();
7012 ++it)
7013 {
7014 MediumAttachment *pAttach = *it;
7015
7016 if (pAttach->type() == DeviceType_HardDisk)
7017 {
7018 Medium* pMedium = pAttach->medium();
7019
7020 if (pMedium)
7021 {
7022 /* now de-associate from the current machine state */
7023 rc = pMedium->detachFrom(mData->mUuid);
7024 AssertComRC(rc);
7025
7026 if (aOnline)
7027 {
7028 /* unlock since not used anymore */
7029 MediumState_T state;
7030 rc = pMedium->UnlockWrite(&state);
7031 /* the disk may be alredy relocked for reading above */
7032 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead);
7033 }
7034 }
7035 }
7036 }
7037
7038 /* commit the hard disk changes */
7039 mMediaData.commit();
7040
7041 if (mType == IsSessionMachine)
7042 {
7043 /* attach new data to the primary machine and reshare it */
7044 mPeer->mMediaData.attach(mMediaData);
7045 }
7046 }
7047 else
7048 {
7049 /* enumerate new attachments */
7050 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7051 it != mMediaData->mAttachments.end();
7052 ++it)
7053 {
7054 (*it)->rollback();
7055 }
7056
7057 /** @todo convert all this Machine-based voodoo to MediumAttachment
7058 * based rollback logic. */
7059 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
7060 // which gets called if Machine::registeredInit() fails...
7061 deleteImplicitDiffs();
7062 }
7063
7064 return;
7065}
7066
7067/**
7068 * Returns true if the settings file is located in the directory named exactly
7069 * as the machine. This will be true if the machine settings structure was
7070 * created by default in #openConfigLoader().
7071 *
7072 * @param aSettingsDir if not NULL, the full machine settings file directory
7073 * name will be assigned there.
7074 *
7075 * @note Doesn't lock anything.
7076 * @note Not thread safe (must be called from this object's lock).
7077 */
7078bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
7079{
7080 Utf8Str settingsDir = mData->m_strConfigFileFull;
7081 settingsDir.stripFilename();
7082 char *dirName = RTPathFilename(settingsDir.c_str());
7083
7084 AssertReturn(dirName, false);
7085
7086 /* if we don't rename anything on name change, return false shorlty */
7087 if (!mUserData->mNameSync)
7088 return false;
7089
7090 if (aSettingsDir)
7091 *aSettingsDir = settingsDir;
7092
7093 return Bstr (dirName) == mUserData->mName;
7094}
7095
7096/**
7097 * @note Locks objects for reading!
7098 */
7099bool Machine::isModified()
7100{
7101 AutoCaller autoCaller(this);
7102 AssertComRCReturn (autoCaller.rc(), false);
7103
7104 AutoReadLock alock(this);
7105
7106 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7107 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
7108 return true;
7109
7110 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7111 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
7112 return true;
7113
7114 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7115 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
7116 return true;
7117
7118 if (!mStorageControllers.isNull())
7119 {
7120 for (StorageControllerList::const_iterator it =
7121 mStorageControllers->begin();
7122 it != mStorageControllers->end();
7123 ++it)
7124 {
7125 if ((*it)->isModified())
7126 return true;
7127 }
7128 }
7129
7130 return
7131 mUserData.isBackedUp() ||
7132 mHWData.isBackedUp() ||
7133 mMediaData.isBackedUp() ||
7134 mStorageControllers.isBackedUp() ||
7135#ifdef VBOX_WITH_VRDP
7136 (mVRDPServer && mVRDPServer->isModified()) ||
7137#endif
7138 (mAudioAdapter && mAudioAdapter->isModified()) ||
7139 (mUSBController && mUSBController->isModified()) ||
7140 (mBIOSSettings && mBIOSSettings->isModified());
7141}
7142
7143/**
7144 * Returns the logical OR of data.hasActualChanges() of this and all child
7145 * objects.
7146 *
7147 * @param aIgnoreUserData @c true to ignore changes to mUserData
7148 *
7149 * @note Locks objects for reading!
7150 */
7151bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
7152{
7153 AutoCaller autoCaller(this);
7154 AssertComRCReturn (autoCaller.rc(), false);
7155
7156 AutoReadLock alock(this);
7157
7158 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7159 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
7160 return true;
7161
7162 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7163 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
7164 return true;
7165
7166 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7167 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
7168 return true;
7169
7170 if (!mStorageControllers.isBackedUp())
7171 {
7172 /* see whether any of the devices has changed its data */
7173 for (StorageControllerList::const_iterator
7174 it = mStorageControllers->begin();
7175 it != mStorageControllers->end();
7176 ++it)
7177 {
7178 if ((*it)->isReallyModified())
7179 return true;
7180 }
7181 }
7182 else
7183 {
7184 if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
7185 return true;
7186 }
7187
7188 return
7189 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
7190 mHWData.hasActualChanges() ||
7191 mMediaData.hasActualChanges() ||
7192 mStorageControllers.hasActualChanges() ||
7193#ifdef VBOX_WITH_VRDP
7194 (mVRDPServer && mVRDPServer->isReallyModified()) ||
7195#endif
7196 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
7197 (mUSBController && mUSBController->isReallyModified()) ||
7198 (mBIOSSettings && mBIOSSettings->isReallyModified());
7199}
7200
7201/**
7202 * Discards all changes to machine settings.
7203 *
7204 * @param aNotify Whether to notify the direct session about changes or not.
7205 *
7206 * @note Locks objects for writing!
7207 */
7208void Machine::rollback (bool aNotify)
7209{
7210 AutoCaller autoCaller(this);
7211 AssertComRCReturn (autoCaller.rc(), (void) 0);
7212
7213 AutoWriteLock alock(this);
7214
7215 /* check for changes in own data */
7216
7217 bool sharedFoldersChanged = false, storageChanged = false;
7218
7219 if (aNotify && mHWData.isBackedUp())
7220 {
7221 if (mHWData->mSharedFolders.size() !=
7222 mHWData.backedUpData()->mSharedFolders.size())
7223 sharedFoldersChanged = true;
7224 else
7225 {
7226 for (HWData::SharedFolderList::iterator rit =
7227 mHWData->mSharedFolders.begin();
7228 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
7229 ++rit)
7230 {
7231 for (HWData::SharedFolderList::iterator cit =
7232 mHWData.backedUpData()->mSharedFolders.begin();
7233 cit != mHWData.backedUpData()->mSharedFolders.end();
7234 ++cit)
7235 {
7236 if ((*cit)->name() != (*rit)->name() ||
7237 (*cit)->hostPath() != (*rit)->hostPath())
7238 {
7239 sharedFoldersChanged = true;
7240 break;
7241 }
7242 }
7243 }
7244 }
7245 }
7246
7247 if (!mStorageControllers.isNull())
7248 {
7249 if (mStorageControllers.isBackedUp())
7250 {
7251 /* unitialize all new devices (absent in the backed up list). */
7252 StorageControllerList::const_iterator it = mStorageControllers->begin();
7253 StorageControllerList *backedList = mStorageControllers.backedUpData();
7254 while (it != mStorageControllers->end())
7255 {
7256 if (std::find (backedList->begin(), backedList->end(), *it ) ==
7257 backedList->end())
7258 {
7259 (*it)->uninit();
7260 }
7261 ++it;
7262 }
7263
7264 /* restore the list */
7265 mStorageControllers.rollback();
7266 }
7267
7268 /* rollback any changes to devices after restoring the list */
7269 StorageControllerList::const_iterator it = mStorageControllers->begin();
7270 while (it != mStorageControllers->end())
7271 {
7272 if ((*it)->isModified())
7273 (*it)->rollback();
7274
7275 ++it;
7276 }
7277 }
7278
7279 mUserData.rollback();
7280
7281 mHWData.rollback();
7282
7283 if (mMediaData.isBackedUp())
7284 fixupMedia(false /* aCommit */);
7285
7286 /* check for changes in child objects */
7287
7288 bool vrdpChanged = false, usbChanged = false;
7289
7290 ComPtr<INetworkAdapter> networkAdapters [RT_ELEMENTS (mNetworkAdapters)];
7291 ComPtr<ISerialPort> serialPorts [RT_ELEMENTS (mSerialPorts)];
7292 ComPtr<IParallelPort> parallelPorts [RT_ELEMENTS (mParallelPorts)];
7293
7294 if (mBIOSSettings)
7295 mBIOSSettings->rollback();
7296
7297#ifdef VBOX_WITH_VRDP
7298 if (mVRDPServer)
7299 vrdpChanged = mVRDPServer->rollback();
7300#endif
7301
7302 if (mAudioAdapter)
7303 mAudioAdapter->rollback();
7304
7305 if (mUSBController)
7306 usbChanged = mUSBController->rollback();
7307
7308 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7309 if (mNetworkAdapters [slot])
7310 if (mNetworkAdapters [slot]->rollback())
7311 networkAdapters [slot] = mNetworkAdapters [slot];
7312
7313 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7314 if (mSerialPorts [slot])
7315 if (mSerialPorts [slot]->rollback())
7316 serialPorts [slot] = mSerialPorts [slot];
7317
7318 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7319 if (mParallelPorts [slot])
7320 if (mParallelPorts [slot]->rollback())
7321 parallelPorts [slot] = mParallelPorts [slot];
7322
7323 if (aNotify)
7324 {
7325 /* inform the direct session about changes */
7326
7327 ComObjPtr<Machine> that = this;
7328 alock.leave();
7329
7330 if (sharedFoldersChanged)
7331 that->onSharedFolderChange();
7332
7333 if (vrdpChanged)
7334 that->onVRDPServerChange();
7335 if (usbChanged)
7336 that->onUSBControllerChange();
7337
7338 for (ULONG slot = 0; slot < RT_ELEMENTS (networkAdapters); slot ++)
7339 if (networkAdapters [slot])
7340 that->onNetworkAdapterChange (networkAdapters [slot], FALSE);
7341 for (ULONG slot = 0; slot < RT_ELEMENTS (serialPorts); slot ++)
7342 if (serialPorts [slot])
7343 that->onSerialPortChange (serialPorts [slot]);
7344 for (ULONG slot = 0; slot < RT_ELEMENTS (parallelPorts); slot ++)
7345 if (parallelPorts [slot])
7346 that->onParallelPortChange (parallelPorts [slot]);
7347
7348 if (storageChanged)
7349 that->onStorageControllerChange();
7350 }
7351}
7352
7353/**
7354 * Commits all the changes to machine settings.
7355 *
7356 * Note that this operation is supposed to never fail.
7357 *
7358 * @note Locks this object and children for writing.
7359 */
7360void Machine::commit()
7361{
7362 AutoCaller autoCaller(this);
7363 AssertComRCReturnVoid (autoCaller.rc());
7364
7365 AutoCaller peerCaller (mPeer);
7366 AssertComRCReturnVoid (peerCaller.rc());
7367
7368 AutoMultiWriteLock2 alock (mPeer, this);
7369
7370 /*
7371 * use safe commit to ensure Snapshot machines (that share mUserData)
7372 * will still refer to a valid memory location
7373 */
7374 mUserData.commitCopy();
7375
7376 mHWData.commit();
7377
7378 if (mMediaData.isBackedUp())
7379 fixupMedia(true /* aCommit */);
7380
7381 mBIOSSettings->commit();
7382#ifdef VBOX_WITH_VRDP
7383 mVRDPServer->commit();
7384#endif
7385 mAudioAdapter->commit();
7386 mUSBController->commit();
7387
7388 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7389 mNetworkAdapters [slot]->commit();
7390 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7391 mSerialPorts [slot]->commit();
7392 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7393 mParallelPorts [slot]->commit();
7394
7395 bool commitStorageControllers = false;
7396
7397 if (mStorageControllers.isBackedUp())
7398 {
7399 mStorageControllers.commit();
7400
7401 if (mPeer)
7402 {
7403 AutoWriteLock peerlock (mPeer);
7404
7405 /* Commit all changes to new controllers (this will reshare data with
7406 * peers for thos who have peers) */
7407 StorageControllerList *newList = new StorageControllerList();
7408 StorageControllerList::const_iterator it = mStorageControllers->begin();
7409 while (it != mStorageControllers->end())
7410 {
7411 (*it)->commit();
7412
7413 /* look if this controller has a peer device */
7414 ComObjPtr<StorageController> peer = (*it)->peer();
7415 if (!peer)
7416 {
7417 /* no peer means the device is a newly created one;
7418 * create a peer owning data this device share it with */
7419 peer.createObject();
7420 peer->init (mPeer, *it, true /* aReshare */);
7421 }
7422 else
7423 {
7424 /* remove peer from the old list */
7425 mPeer->mStorageControllers->remove (peer);
7426 }
7427 /* and add it to the new list */
7428 newList->push_back(peer);
7429
7430 ++it;
7431 }
7432
7433 /* uninit old peer's controllers that are left */
7434 it = mPeer->mStorageControllers->begin();
7435 while (it != mPeer->mStorageControllers->end())
7436 {
7437 (*it)->uninit();
7438 ++it;
7439 }
7440
7441 /* attach new list of controllers to our peer */
7442 mPeer->mStorageControllers.attach (newList);
7443 }
7444 else
7445 {
7446 /* we have no peer (our parent is the newly created machine);
7447 * just commit changes to devices */
7448 commitStorageControllers = true;
7449 }
7450 }
7451 else
7452 {
7453 /* the list of controllers itself is not changed,
7454 * just commit changes to controllers themselves */
7455 commitStorageControllers = true;
7456 }
7457
7458 if (commitStorageControllers)
7459 {
7460 StorageControllerList::const_iterator it = mStorageControllers->begin();
7461 while (it != mStorageControllers->end())
7462 {
7463 (*it)->commit();
7464 ++it;
7465 }
7466 }
7467
7468 if (mType == IsSessionMachine)
7469 {
7470 /* attach new data to the primary machine and reshare it */
7471 mPeer->mUserData.attach (mUserData);
7472 mPeer->mHWData.attach (mHWData);
7473 /* mMediaData is reshared by fixupMedia */
7474 // mPeer->mMediaData.attach(mMediaData);
7475 Assert(mPeer->mMediaData.data() == mMediaData.data());
7476 }
7477}
7478
7479/**
7480 * Copies all the hardware data from the given machine.
7481 *
7482 * Currently, only called when the VM is being restored from a snapshot. In
7483 * particular, this implies that the VM is not running during this method's
7484 * call.
7485 *
7486 * @note This method must be called from under this object's lock.
7487 *
7488 * @note This method doesn't call #commit(), so all data remains backed up and
7489 * unsaved.
7490 */
7491void Machine::copyFrom (Machine *aThat)
7492{
7493 AssertReturnVoid (mType == IsMachine || mType == IsSessionMachine);
7494 AssertReturnVoid (aThat->mType == IsSnapshotMachine);
7495
7496 AssertReturnVoid (!Global::IsOnline (mData->mMachineState));
7497
7498 mHWData.assignCopy (aThat->mHWData);
7499
7500 // create copies of all shared folders (mHWData after attiching a copy
7501 // contains just references to original objects)
7502 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
7503 it != mHWData->mSharedFolders.end();
7504 ++it)
7505 {
7506 ComObjPtr<SharedFolder> folder;
7507 folder.createObject();
7508 HRESULT rc = folder->initCopy (machine(), *it);
7509 AssertComRC (rc);
7510 *it = folder;
7511 }
7512
7513 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
7514#ifdef VBOX_WITH_VRDP
7515 mVRDPServer->copyFrom (aThat->mVRDPServer);
7516#endif
7517 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
7518 mUSBController->copyFrom (aThat->mUSBController);
7519
7520 /* create private copies of all controllers */
7521 mStorageControllers.backup();
7522 mStorageControllers->clear();
7523 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
7524 it != aThat->mStorageControllers->end();
7525 ++it)
7526 {
7527 ComObjPtr<StorageController> ctrl;
7528 ctrl.createObject();
7529 ctrl->initCopy (this, *it);
7530 mStorageControllers->push_back(ctrl);
7531 }
7532
7533 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7534 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
7535 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7536 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
7537 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7538 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
7539}
7540
7541#ifdef VBOX_WITH_RESOURCE_USAGE_API
7542void Machine::registerMetrics (PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
7543{
7544 pm::CollectorHAL *hal = aCollector->getHAL();
7545 /* Create sub metrics */
7546 pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User",
7547 "Percentage of processor time spent in user mode by VM process.");
7548 pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel",
7549 "Percentage of processor time spent in kernel mode by VM process.");
7550 pm::SubMetric *ramUsageUsed = new pm::SubMetric ("RAM/Usage/Used",
7551 "Size of resident portion of VM process in memory.");
7552 /* Create and register base metrics */
7553 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw (hal, aMachine, pid,
7554 cpuLoadUser, cpuLoadKernel);
7555 aCollector->registerBaseMetric (cpuLoad);
7556 pm::BaseMetric *ramUsage = new pm::MachineRamUsage (hal, aMachine, pid,
7557 ramUsageUsed);
7558 aCollector->registerBaseMetric (ramUsage);
7559
7560 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser, 0));
7561 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7562 new pm::AggregateAvg()));
7563 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7564 new pm::AggregateMin()));
7565 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7566 new pm::AggregateMax()));
7567 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel, 0));
7568 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7569 new pm::AggregateAvg()));
7570 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7571 new pm::AggregateMin()));
7572 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7573 new pm::AggregateMax()));
7574
7575 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed, 0));
7576 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7577 new pm::AggregateAvg()));
7578 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7579 new pm::AggregateMin()));
7580 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7581 new pm::AggregateMax()));
7582};
7583
7584void Machine::unregisterMetrics (PerformanceCollector *aCollector, Machine *aMachine)
7585{
7586 aCollector->unregisterMetricsFor (aMachine);
7587 aCollector->unregisterBaseMetricsFor (aMachine);
7588};
7589#endif /* VBOX_WITH_RESOURCE_USAGE_API */
7590
7591
7592/////////////////////////////////////////////////////////////////////////////
7593// SessionMachine class
7594/////////////////////////////////////////////////////////////////////////////
7595
7596/** Task structure for asynchronous VM operations */
7597struct SessionMachine::Task
7598{
7599 Task (SessionMachine *m, Progress *p)
7600 : machine (m), progress (p)
7601 , state (m->mData->mMachineState) // save the current machine state
7602 , subTask (false)
7603 {}
7604
7605 void modifyLastState (MachineState_T s)
7606 {
7607 *const_cast <MachineState_T *> (&state) = s;
7608 }
7609
7610 virtual void handler() = 0;
7611
7612 ComObjPtr<SessionMachine> machine;
7613 ComObjPtr<Progress> progress;
7614 const MachineState_T state;
7615
7616 bool subTask : 1;
7617};
7618
7619/** Discard snapshot task */
7620struct SessionMachine::DeleteSnapshotTask
7621 : public SessionMachine::Task
7622{
7623 DeleteSnapshotTask(SessionMachine *m, Progress *p, Snapshot *s)
7624 : Task(m, p),
7625 snapshot(s)
7626 {}
7627
7628 DeleteSnapshotTask (const Task &task, Snapshot *s)
7629 : Task(task)
7630 , snapshot(s)
7631 {}
7632
7633 void handler()
7634 {
7635 machine->deleteSnapshotHandler(*this);
7636 }
7637
7638 ComObjPtr<Snapshot> snapshot;
7639};
7640
7641/** Restore snapshot state task */
7642struct SessionMachine::RestoreSnapshotTask
7643 : public SessionMachine::Task
7644{
7645 RestoreSnapshotTask(SessionMachine *m, ComObjPtr<Snapshot> &aSnapshot, Progress *p)
7646 : Task(m, p),
7647 pSnapshot(aSnapshot)
7648 {}
7649
7650 void handler()
7651 {
7652 machine->restoreSnapshotHandler(*this);
7653 }
7654
7655 ComObjPtr<Snapshot> pSnapshot;
7656};
7657
7658////////////////////////////////////////////////////////////////////////////////
7659
7660DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
7661
7662HRESULT SessionMachine::FinalConstruct()
7663{
7664 LogFlowThisFunc(("\n"));
7665
7666 /* set the proper type to indicate we're the SessionMachine instance */
7667 unconst(mType) = IsSessionMachine;
7668
7669#if defined(RT_OS_WINDOWS)
7670 mIPCSem = NULL;
7671#elif defined(RT_OS_OS2)
7672 mIPCSem = NULLHANDLE;
7673#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7674 mIPCSem = -1;
7675#else
7676# error "Port me!"
7677#endif
7678
7679 return S_OK;
7680}
7681
7682void SessionMachine::FinalRelease()
7683{
7684 LogFlowThisFunc(("\n"));
7685
7686 uninit (Uninit::Unexpected);
7687}
7688
7689/**
7690 * @note Must be called only by Machine::openSession() from its own write lock.
7691 */
7692HRESULT SessionMachine::init (Machine *aMachine)
7693{
7694 LogFlowThisFuncEnter();
7695 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
7696
7697 AssertReturn(aMachine, E_INVALIDARG);
7698
7699 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
7700
7701 /* Enclose the state transition NotReady->InInit->Ready */
7702 AutoInitSpan autoInitSpan(this);
7703 AssertReturn(autoInitSpan.isOk(), E_FAIL);
7704
7705 /* create the interprocess semaphore */
7706#if defined(RT_OS_WINDOWS)
7707 mIPCSemName = aMachine->mData->m_strConfigFileFull;
7708 for (size_t i = 0; i < mIPCSemName.length(); i++)
7709 if (mIPCSemName[i] == '\\')
7710 mIPCSemName[i] = '/';
7711 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
7712 ComAssertMsgRet (mIPCSem,
7713 ("Cannot create IPC mutex '%ls', err=%d",
7714 mIPCSemName.raw(), ::GetLastError()),
7715 E_FAIL);
7716#elif defined(RT_OS_OS2)
7717 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%RTuuid}",
7718 aMachine->mData->mUuid.raw());
7719 mIPCSemName = ipcSem;
7720 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
7721 ComAssertMsgRet (arc == NO_ERROR,
7722 ("Cannot create IPC mutex '%s', arc=%ld",
7723 ipcSem.raw(), arc),
7724 E_FAIL);
7725#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7726# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
7727# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
7728 /** @todo Check that this still works correctly. */
7729 AssertCompileSize(key_t, 8);
7730# else
7731 AssertCompileSize(key_t, 4);
7732# endif
7733 key_t key;
7734 mIPCSem = -1;
7735 mIPCKey = "0";
7736 for (uint32_t i = 0; i < 1 << 24; i++)
7737 {
7738 key = ((uint32_t)'V' << 24) | i;
7739 int sem = ::semget (key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
7740 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
7741 {
7742 mIPCSem = sem;
7743 if (sem >= 0)
7744 mIPCKey = BstrFmt ("%u", key);
7745 break;
7746 }
7747 }
7748# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
7749 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
7750 char *pszSemName = NULL;
7751 RTStrUtf8ToCurrentCP (&pszSemName, semName);
7752 key_t key = ::ftok (pszSemName, 'V');
7753 RTStrFree (pszSemName);
7754
7755 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
7756# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
7757
7758 int errnoSave = errno;
7759 if (mIPCSem < 0 && errnoSave == ENOSYS)
7760 {
7761 setError(E_FAIL,
7762 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
7763 "support for SysV IPC. Check the host kernel configuration for "
7764 "CONFIG_SYSVIPC=y"));
7765 return E_FAIL;
7766 }
7767 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
7768 * the IPC semaphores */
7769 if (mIPCSem < 0 && errnoSave == ENOSPC)
7770 {
7771#ifdef RT_OS_LINUX
7772 setError(E_FAIL,
7773 tr("Cannot create IPC semaphore because the system limit for the "
7774 "maximum number of semaphore sets (SEMMNI), or the system wide "
7775 "maximum number of sempahores (SEMMNS) would be exceeded. The "
7776 "current set of SysV IPC semaphores can be determined from "
7777 "the file /proc/sysvipc/sem"));
7778#else
7779 setError(E_FAIL,
7780 tr("Cannot create IPC semaphore because the system-imposed limit "
7781 "on the maximum number of allowed semaphores or semaphore "
7782 "identifiers system-wide would be exceeded"));
7783#endif
7784 return E_FAIL;
7785 }
7786 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
7787 E_FAIL);
7788 /* set the initial value to 1 */
7789 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
7790 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
7791 E_FAIL);
7792#else
7793# error "Port me!"
7794#endif
7795
7796 /* memorize the peer Machine */
7797 unconst(mPeer) = aMachine;
7798 /* share the parent pointer */
7799 unconst(mParent) = aMachine->mParent;
7800
7801 /* take the pointers to data to share */
7802 mData.share (aMachine->mData);
7803 mSSData.share (aMachine->mSSData);
7804
7805 mUserData.share (aMachine->mUserData);
7806 mHWData.share (aMachine->mHWData);
7807 mMediaData.share(aMachine->mMediaData);
7808
7809 mStorageControllers.allocate();
7810 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
7811 it != aMachine->mStorageControllers->end();
7812 ++it)
7813 {
7814 ComObjPtr<StorageController> ctl;
7815 ctl.createObject();
7816 ctl->init(this, *it);
7817 mStorageControllers->push_back (ctl);
7818 }
7819
7820 unconst(mBIOSSettings).createObject();
7821 mBIOSSettings->init (this, aMachine->mBIOSSettings);
7822#ifdef VBOX_WITH_VRDP
7823 /* create another VRDPServer object that will be mutable */
7824 unconst(mVRDPServer).createObject();
7825 mVRDPServer->init (this, aMachine->mVRDPServer);
7826#endif
7827 /* create another audio adapter object that will be mutable */
7828 unconst(mAudioAdapter).createObject();
7829 mAudioAdapter->init (this, aMachine->mAudioAdapter);
7830 /* create a list of serial ports that will be mutable */
7831 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7832 {
7833 unconst(mSerialPorts [slot]).createObject();
7834 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
7835 }
7836 /* create a list of parallel ports that will be mutable */
7837 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7838 {
7839 unconst(mParallelPorts [slot]).createObject();
7840 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
7841 }
7842 /* create another USB controller object that will be mutable */
7843 unconst(mUSBController).createObject();
7844 mUSBController->init (this, aMachine->mUSBController);
7845
7846 /* create a list of network adapters that will be mutable */
7847 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7848 {
7849 unconst(mNetworkAdapters [slot]).createObject();
7850 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
7851 }
7852
7853 /* default is to delete saved state on Saved -> PoweredOff transition */
7854 mRemoveSavedState = true;
7855
7856 /* Confirm a successful initialization when it's the case */
7857 autoInitSpan.setSucceeded();
7858
7859 LogFlowThisFuncLeave();
7860 return S_OK;
7861}
7862
7863/**
7864 * Uninitializes this session object. If the reason is other than
7865 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
7866 *
7867 * @param aReason uninitialization reason
7868 *
7869 * @note Locks mParent + this object for writing.
7870 */
7871void SessionMachine::uninit (Uninit::Reason aReason)
7872{
7873 LogFlowThisFuncEnter();
7874 LogFlowThisFunc(("reason=%d\n", aReason));
7875
7876 /*
7877 * Strongly reference ourselves to prevent this object deletion after
7878 * mData->mSession.mMachine.setNull() below (which can release the last
7879 * reference and call the destructor). Important: this must be done before
7880 * accessing any members (and before AutoUninitSpan that does it as well).
7881 * This self reference will be released as the very last step on return.
7882 */
7883 ComObjPtr<SessionMachine> selfRef = this;
7884
7885 /* Enclose the state transition Ready->InUninit->NotReady */
7886 AutoUninitSpan autoUninitSpan(this);
7887 if (autoUninitSpan.uninitDone())
7888 {
7889 LogFlowThisFunc(("Already uninitialized\n"));
7890 LogFlowThisFuncLeave();
7891 return;
7892 }
7893
7894 if (autoUninitSpan.initFailed())
7895 {
7896 /* We've been called by init() because it's failed. It's not really
7897 * necessary (nor it's safe) to perform the regular uninit sequense
7898 * below, the following is enough.
7899 */
7900 LogFlowThisFunc(("Initialization failed.\n"));
7901#if defined(RT_OS_WINDOWS)
7902 if (mIPCSem)
7903 ::CloseHandle (mIPCSem);
7904 mIPCSem = NULL;
7905#elif defined(RT_OS_OS2)
7906 if (mIPCSem != NULLHANDLE)
7907 ::DosCloseMutexSem (mIPCSem);
7908 mIPCSem = NULLHANDLE;
7909#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7910 if (mIPCSem >= 0)
7911 ::semctl (mIPCSem, 0, IPC_RMID);
7912 mIPCSem = -1;
7913# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
7914 mIPCKey = "0";
7915# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
7916#else
7917# error "Port me!"
7918#endif
7919 uninitDataAndChildObjects();
7920 mData.free();
7921 unconst(mParent).setNull();
7922 unconst(mPeer).setNull();
7923 LogFlowThisFuncLeave();
7924 return;
7925 }
7926
7927 /* We need to lock this object in uninit() because the lock is shared
7928 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
7929 * and others need mParent lock. */
7930 AutoMultiWriteLock2 alock (mParent, this);
7931
7932#ifdef VBOX_WITH_RESOURCE_USAGE_API
7933 unregisterMetrics (mParent->performanceCollector(), mPeer);
7934#endif /* VBOX_WITH_RESOURCE_USAGE_API */
7935
7936 MachineState_T lastState = mData->mMachineState;
7937 NOREF(lastState);
7938
7939 if (aReason == Uninit::Abnormal)
7940 {
7941 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
7942 Global::IsOnlineOrTransient (lastState)));
7943
7944 /* reset the state to Aborted */
7945 if (mData->mMachineState != MachineState_Aborted)
7946 setMachineState (MachineState_Aborted);
7947 }
7948
7949 if (isModified())
7950 {
7951 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
7952 rollback (false /* aNotify */);
7953 }
7954
7955 Assert (!mSnapshotData.mStateFilePath || !mSnapshotData.mSnapshot);
7956 if (mSnapshotData.mStateFilePath)
7957 {
7958 LogWarningThisFunc(("canceling failed save state request!\n"));
7959 endSavingState (FALSE /* aSuccess */);
7960 }
7961 else if (!mSnapshotData.mSnapshot.isNull())
7962 {
7963 LogWarningThisFunc(("canceling untaken snapshot!\n"));
7964 endTakingSnapshot (FALSE /* aSuccess */);
7965 }
7966
7967#ifdef VBOX_WITH_USB
7968 /* release all captured USB devices */
7969 if (aReason == Uninit::Abnormal && Global::IsOnline (lastState))
7970 {
7971 /* Console::captureUSBDevices() is called in the VM process only after
7972 * setting the machine state to Starting or Restoring.
7973 * Console::detachAllUSBDevices() will be called upon successful
7974 * termination. So, we need to release USB devices only if there was
7975 * an abnormal termination of a running VM.
7976 *
7977 * This is identical to SessionMachine::DetachAllUSBDevices except
7978 * for the aAbnormal argument. */
7979 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
7980 AssertComRC(rc);
7981 NOREF (rc);
7982
7983 USBProxyService *service = mParent->host()->usbProxyService();
7984 if (service)
7985 service->detachAllDevicesFromVM (this, true /* aDone */, true /* aAbnormal */);
7986 }
7987#endif /* VBOX_WITH_USB */
7988
7989 if (!mData->mSession.mType.isNull())
7990 {
7991 /* mType is not null when this machine's process has been started by
7992 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
7993 * need to queue the PID to reap the process (and avoid zombies on
7994 * Linux). */
7995 Assert (mData->mSession.mPid != NIL_RTPROCESS);
7996 mParent->addProcessToReap (mData->mSession.mPid);
7997 }
7998
7999 mData->mSession.mPid = NIL_RTPROCESS;
8000
8001 if (aReason == Uninit::Unexpected)
8002 {
8003 /* Uninitialization didn't come from #checkForDeath(), so tell the
8004 * client watcher thread to update the set of machines that have open
8005 * sessions. */
8006 mParent->updateClientWatcher();
8007 }
8008
8009 /* uninitialize all remote controls */
8010 if (mData->mSession.mRemoteControls.size())
8011 {
8012 LogFlowThisFunc(("Closing remote sessions (%d):\n",
8013 mData->mSession.mRemoteControls.size()));
8014
8015 Data::Session::RemoteControlList::iterator it =
8016 mData->mSession.mRemoteControls.begin();
8017 while (it != mData->mSession.mRemoteControls.end())
8018 {
8019 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
8020 HRESULT rc = (*it)->Uninitialize();
8021 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
8022 if (FAILED (rc))
8023 LogWarningThisFunc(("Forgot to close the remote session?\n"));
8024 ++it;
8025 }
8026 mData->mSession.mRemoteControls.clear();
8027 }
8028
8029 /*
8030 * An expected uninitialization can come only from #checkForDeath().
8031 * Otherwise it means that something's got really wrong (for examlple,
8032 * the Session implementation has released the VirtualBox reference
8033 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
8034 * etc). However, it's also possible, that the client releases the IPC
8035 * semaphore correctly (i.e. before it releases the VirtualBox reference),
8036 * but the VirtualBox release event comes first to the server process.
8037 * This case is practically possible, so we should not assert on an
8038 * unexpected uninit, just log a warning.
8039 */
8040
8041 if ((aReason == Uninit::Unexpected))
8042 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
8043
8044 if (aReason != Uninit::Normal)
8045 {
8046 mData->mSession.mDirectControl.setNull();
8047 }
8048 else
8049 {
8050 /* this must be null here (see #OnSessionEnd()) */
8051 Assert (mData->mSession.mDirectControl.isNull());
8052 Assert (mData->mSession.mState == SessionState_Closing);
8053 Assert (!mData->mSession.mProgress.isNull());
8054
8055 mData->mSession.mProgress->notifyComplete (S_OK);
8056 mData->mSession.mProgress.setNull();
8057 }
8058
8059 /* remove the association between the peer machine and this session machine */
8060 Assert (mData->mSession.mMachine == this ||
8061 aReason == Uninit::Unexpected);
8062
8063 /* reset the rest of session data */
8064 mData->mSession.mMachine.setNull();
8065 mData->mSession.mState = SessionState_Closed;
8066 mData->mSession.mType.setNull();
8067
8068 /* close the interprocess semaphore before leaving the shared lock */
8069#if defined(RT_OS_WINDOWS)
8070 if (mIPCSem)
8071 ::CloseHandle (mIPCSem);
8072 mIPCSem = NULL;
8073#elif defined(RT_OS_OS2)
8074 if (mIPCSem != NULLHANDLE)
8075 ::DosCloseMutexSem (mIPCSem);
8076 mIPCSem = NULLHANDLE;
8077#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8078 if (mIPCSem >= 0)
8079 ::semctl (mIPCSem, 0, IPC_RMID);
8080 mIPCSem = -1;
8081# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8082 mIPCKey = "0";
8083# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8084#else
8085# error "Port me!"
8086#endif
8087
8088 /* fire an event */
8089 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
8090
8091 uninitDataAndChildObjects();
8092
8093 /* free the essential data structure last */
8094 mData.free();
8095
8096 /* leave the shared lock before setting the below two to NULL */
8097 alock.leave();
8098
8099 unconst(mParent).setNull();
8100 unconst(mPeer).setNull();
8101
8102 LogFlowThisFuncLeave();
8103}
8104
8105// util::Lockable interface
8106////////////////////////////////////////////////////////////////////////////////
8107
8108/**
8109 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
8110 * with the primary Machine instance (mPeer).
8111 */
8112RWLockHandle *SessionMachine::lockHandle() const
8113{
8114 AssertReturn(!mPeer.isNull(), NULL);
8115 return mPeer->lockHandle();
8116}
8117
8118// IInternalMachineControl methods
8119////////////////////////////////////////////////////////////////////////////////
8120
8121/**
8122 * @note Locks this object for writing.
8123 */
8124STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
8125{
8126 AutoCaller autoCaller(this);
8127 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8128
8129 AutoWriteLock alock(this);
8130
8131 mRemoveSavedState = aRemove;
8132
8133 return S_OK;
8134}
8135
8136/**
8137 * @note Locks the same as #setMachineState() does.
8138 */
8139STDMETHODIMP SessionMachine::UpdateState (MachineState_T aMachineState)
8140{
8141 return setMachineState (aMachineState);
8142}
8143
8144/**
8145 * @note Locks this object for reading.
8146 */
8147STDMETHODIMP SessionMachine::GetIPCId (BSTR *aId)
8148{
8149 AutoCaller autoCaller(this);
8150 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8151
8152 AutoReadLock alock(this);
8153
8154#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8155 mIPCSemName.cloneTo(aId);
8156 return S_OK;
8157#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8158# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8159 mIPCKey.cloneTo(aId);
8160# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8161 mData->m_strConfigFileFull.cloneTo(aId);
8162# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8163 return S_OK;
8164#else
8165# error "Port me!"
8166#endif
8167}
8168
8169/**
8170 * Goes through the USB filters of the given machine to see if the given
8171 * device matches any filter or not.
8172 *
8173 * @note Locks the same as USBController::hasMatchingFilter() does.
8174 */
8175STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
8176 BOOL *aMatched,
8177 ULONG *aMaskedIfs)
8178{
8179 LogFlowThisFunc(("\n"));
8180
8181 CheckComArgNotNull (aUSBDevice);
8182 CheckComArgOutPointerValid(aMatched);
8183
8184 AutoCaller autoCaller(this);
8185 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8186
8187#ifdef VBOX_WITH_USB
8188 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice, aMaskedIfs);
8189#else
8190 NOREF(aUSBDevice);
8191 NOREF(aMaskedIfs);
8192 *aMatched = FALSE;
8193#endif
8194
8195 return S_OK;
8196}
8197
8198/**
8199 * @note Locks the same as Host::captureUSBDevice() does.
8200 */
8201STDMETHODIMP SessionMachine::CaptureUSBDevice (IN_BSTR aId)
8202{
8203 LogFlowThisFunc(("\n"));
8204
8205 AutoCaller autoCaller(this);
8206 AssertComRCReturnRC(autoCaller.rc());
8207
8208#ifdef VBOX_WITH_USB
8209 /* if captureDeviceForVM() fails, it must have set extended error info */
8210 MultiResult rc = mParent->host()->checkUSBProxyService();
8211 CheckComRCReturnRC(rc);
8212
8213 USBProxyService *service = mParent->host()->usbProxyService();
8214 AssertReturn(service, E_FAIL);
8215 return service->captureDeviceForVM (this, Guid(aId));
8216#else
8217 NOREF(aId);
8218 return E_NOTIMPL;
8219#endif
8220}
8221
8222/**
8223 * @note Locks the same as Host::detachUSBDevice() does.
8224 */
8225STDMETHODIMP SessionMachine::DetachUSBDevice (IN_BSTR aId, BOOL aDone)
8226{
8227 LogFlowThisFunc(("\n"));
8228
8229 AutoCaller autoCaller(this);
8230 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8231
8232#ifdef VBOX_WITH_USB
8233 USBProxyService *service = mParent->host()->usbProxyService();
8234 AssertReturn(service, E_FAIL);
8235 return service->detachDeviceFromVM (this, Guid(aId), !!aDone);
8236#else
8237 NOREF(aId);
8238 NOREF(aDone);
8239 return E_NOTIMPL;
8240#endif
8241}
8242
8243/**
8244 * Inserts all machine filters to the USB proxy service and then calls
8245 * Host::autoCaptureUSBDevices().
8246 *
8247 * Called by Console from the VM process upon VM startup.
8248 *
8249 * @note Locks what called methods lock.
8250 */
8251STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
8252{
8253 LogFlowThisFunc(("\n"));
8254
8255 AutoCaller autoCaller(this);
8256 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8257
8258#ifdef VBOX_WITH_USB
8259 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
8260 AssertComRC(rc);
8261 NOREF (rc);
8262
8263 USBProxyService *service = mParent->host()->usbProxyService();
8264 AssertReturn(service, E_FAIL);
8265 return service->autoCaptureDevicesForVM (this);
8266#else
8267 return S_OK;
8268#endif
8269}
8270
8271/**
8272 * Removes all machine filters from the USB proxy service and then calls
8273 * Host::detachAllUSBDevices().
8274 *
8275 * Called by Console from the VM process upon normal VM termination or by
8276 * SessionMachine::uninit() upon abnormal VM termination (from under the
8277 * Machine/SessionMachine lock).
8278 *
8279 * @note Locks what called methods lock.
8280 */
8281STDMETHODIMP SessionMachine::DetachAllUSBDevices (BOOL aDone)
8282{
8283 LogFlowThisFunc(("\n"));
8284
8285 AutoCaller autoCaller(this);
8286 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8287
8288#ifdef VBOX_WITH_USB
8289 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8290 AssertComRC(rc);
8291 NOREF (rc);
8292
8293 USBProxyService *service = mParent->host()->usbProxyService();
8294 AssertReturn(service, E_FAIL);
8295 return service->detachAllDevicesFromVM (this, !!aDone, false /* aAbnormal */);
8296#else
8297 NOREF(aDone);
8298 return S_OK;
8299#endif
8300}
8301
8302/**
8303 * @note Locks this object for writing.
8304 */
8305STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
8306 IProgress **aProgress)
8307{
8308 LogFlowThisFuncEnter();
8309
8310 AssertReturn(aSession, E_INVALIDARG);
8311 AssertReturn(aProgress, E_INVALIDARG);
8312
8313 AutoCaller autoCaller(this);
8314
8315 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
8316 /*
8317 * We don't assert below because it might happen that a non-direct session
8318 * informs us it is closed right after we've been uninitialized -- it's ok.
8319 */
8320 CheckComRCReturnRC(autoCaller.rc());
8321
8322 /* get IInternalSessionControl interface */
8323 ComPtr<IInternalSessionControl> control (aSession);
8324
8325 ComAssertRet (!control.isNull(), E_INVALIDARG);
8326
8327 AutoWriteLock alock(this);
8328
8329 if (control.equalsTo (mData->mSession.mDirectControl))
8330 {
8331 ComAssertRet (aProgress, E_POINTER);
8332
8333 /* The direct session is being normally closed by the client process
8334 * ----------------------------------------------------------------- */
8335
8336 /* go to the closing state (essential for all open*Session() calls and
8337 * for #checkForDeath()) */
8338 Assert (mData->mSession.mState == SessionState_Open);
8339 mData->mSession.mState = SessionState_Closing;
8340
8341 /* set direct control to NULL to release the remote instance */
8342 mData->mSession.mDirectControl.setNull();
8343 LogFlowThisFunc(("Direct control is set to NULL\n"));
8344
8345 /* Create the progress object the client will use to wait until
8346 * #checkForDeath() is called to uninitialize this session object after
8347 * it releases the IPC semaphore. */
8348 ComObjPtr<Progress> progress;
8349 progress.createObject();
8350 progress->init (mParent, static_cast <IMachine *> (mPeer),
8351 Bstr (tr ("Closing session")), FALSE /* aCancelable */);
8352 progress.queryInterfaceTo(aProgress);
8353 mData->mSession.mProgress = progress;
8354 }
8355 else
8356 {
8357 /* the remote session is being normally closed */
8358 Data::Session::RemoteControlList::iterator it =
8359 mData->mSession.mRemoteControls.begin();
8360 while (it != mData->mSession.mRemoteControls.end())
8361 {
8362 if (control.equalsTo (*it))
8363 break;
8364 ++it;
8365 }
8366 BOOL found = it != mData->mSession.mRemoteControls.end();
8367 ComAssertMsgRet (found, ("The session is not found in the session list!"),
8368 E_INVALIDARG);
8369 mData->mSession.mRemoteControls.remove (*it);
8370 }
8371
8372 LogFlowThisFuncLeave();
8373 return S_OK;
8374}
8375
8376/**
8377 * @note Locks this object for writing.
8378 */
8379STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
8380{
8381 LogFlowThisFuncEnter();
8382
8383 AssertReturn(aProgress, E_INVALIDARG);
8384 AssertReturn(aStateFilePath, E_POINTER);
8385
8386 AutoCaller autoCaller(this);
8387 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8388
8389 AutoWriteLock alock(this);
8390
8391 AssertReturn(mData->mMachineState == MachineState_Paused &&
8392 mSnapshotData.mLastState == MachineState_Null &&
8393 mSnapshotData.mProgressId.isEmpty() &&
8394 mSnapshotData.mStateFilePath.isNull(),
8395 E_FAIL);
8396
8397 /* memorize the progress ID and add it to the global collection */
8398 Bstr progressId;
8399 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
8400 AssertComRCReturn (rc, rc);
8401 rc = mParent->addProgress (aProgress);
8402 AssertComRCReturn (rc, rc);
8403
8404 Bstr stateFilePath;
8405 /* stateFilePath is null when the machine is not running */
8406 if (mData->mMachineState == MachineState_Paused)
8407 {
8408 stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
8409 mUserData->mSnapshotFolderFull.raw(),
8410 RTPATH_DELIMITER, mData->mUuid.raw());
8411 }
8412
8413 /* fill in the snapshot data */
8414 mSnapshotData.mLastState = mData->mMachineState;
8415 mSnapshotData.mProgressId = Guid(progressId);
8416 mSnapshotData.mStateFilePath = stateFilePath;
8417
8418 /* set the state to Saving (this is expected by Console::SaveState()) */
8419 setMachineState (MachineState_Saving);
8420
8421 stateFilePath.cloneTo(aStateFilePath);
8422
8423 return S_OK;
8424}
8425
8426/**
8427 * @note Locks mParent + this object for writing.
8428 */
8429STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
8430{
8431 LogFlowThisFunc(("\n"));
8432
8433 AutoCaller autoCaller(this);
8434 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8435
8436 /* endSavingState() need mParent lock */
8437 AutoMultiWriteLock2 alock (mParent, this);
8438
8439 AssertReturn(mData->mMachineState == MachineState_Saving &&
8440 mSnapshotData.mLastState != MachineState_Null &&
8441 !mSnapshotData.mProgressId.isEmpty() &&
8442 !mSnapshotData.mStateFilePath.isNull(),
8443 E_FAIL);
8444
8445 /*
8446 * on success, set the state to Saved;
8447 * on failure, set the state to the state we had when BeginSavingState() was
8448 * called (this is expected by Console::SaveState() and
8449 * Console::saveStateThread())
8450 */
8451 if (aSuccess)
8452 setMachineState (MachineState_Saved);
8453 else
8454 setMachineState (mSnapshotData.mLastState);
8455
8456 return endSavingState (aSuccess);
8457}
8458
8459/**
8460 * @note Locks this object for writing.
8461 */
8462STDMETHODIMP SessionMachine::AdoptSavedState (IN_BSTR aSavedStateFile)
8463{
8464 LogFlowThisFunc(("\n"));
8465
8466 AssertReturn(aSavedStateFile, E_INVALIDARG);
8467
8468 AutoCaller autoCaller(this);
8469 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8470
8471 AutoWriteLock alock(this);
8472
8473 AssertReturn(mData->mMachineState == MachineState_PoweredOff ||
8474 mData->mMachineState == MachineState_Aborted,
8475 E_FAIL);
8476
8477 Utf8Str stateFilePathFull = aSavedStateFile;
8478 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8479 if (RT_FAILURE(vrc))
8480 return setError(VBOX_E_FILE_ERROR,
8481 tr("Invalid saved state file path '%ls' (%Rrc)"),
8482 aSavedStateFile,
8483 vrc);
8484
8485 mSSData->mStateFilePath = stateFilePathFull;
8486
8487 /* The below setMachineState() will detect the state transition and will
8488 * update the settings file */
8489
8490 return setMachineState (MachineState_Saved);
8491}
8492
8493/**
8494 * @note Locks mParent + this object for writing.
8495 */
8496STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
8497 IN_BSTR aName,
8498 IN_BSTR aDescription,
8499 IProgress *aConsoleProgress,
8500 BOOL fTakingSnapshotOnline,
8501 BSTR *aStateFilePath)
8502{
8503 LogFlowThisFuncEnter();
8504
8505 AssertReturn(aInitiator && aName, E_INVALIDARG);
8506 AssertReturn(aStateFilePath, E_POINTER);
8507
8508 LogFlowThisFunc(("aName='%ls'\n", aName));
8509
8510 AutoCaller autoCaller(this);
8511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8512
8513 /* saveSettings() needs mParent lock */
8514 AutoMultiWriteLock2 alock(mParent, this);
8515
8516 AssertReturn( !Global::IsOnlineOrTransient(mData->mMachineState)
8517 || mData->mMachineState == MachineState_Running
8518 || mData->mMachineState == MachineState_Paused, E_FAIL);
8519 AssertReturn(mSnapshotData.mLastState == MachineState_Null, E_FAIL);
8520 AssertReturn(mSnapshotData.mSnapshot.isNull(), E_FAIL);
8521
8522 if ( !fTakingSnapshotOnline
8523 && mData->mMachineState != MachineState_Saved
8524 )
8525 {
8526 /* save all current settings to ensure current changes are committed and
8527 * hard disks are fixed up */
8528 HRESULT rc = saveSettings();
8529 CheckComRCReturnRC(rc);
8530 }
8531
8532 /* create an ID for the snapshot */
8533 Guid snapshotId;
8534 snapshotId.create();
8535
8536 Utf8Str strStateFilePath;
8537 /* stateFilePath is null when the machine is not online nor saved */
8538 if ( fTakingSnapshotOnline
8539 || mData->mMachineState == MachineState_Saved)
8540 {
8541 strStateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
8542 mUserData->mSnapshotFolderFull.raw(),
8543 RTPATH_DELIMITER,
8544 snapshotId.ptr());
8545 /* ensure the directory for the saved state file exists */
8546 HRESULT rc = VirtualBox::ensureFilePathExists(strStateFilePath);
8547 CheckComRCReturnRC(rc);
8548 }
8549
8550 /* create a snapshot machine object */
8551 ComObjPtr<SnapshotMachine> snapshotMachine;
8552 snapshotMachine.createObject();
8553 HRESULT rc = snapshotMachine->init(this, snapshotId, strStateFilePath);
8554 AssertComRCReturn(rc, rc);
8555
8556 /* create a snapshot object */
8557 RTTIMESPEC time;
8558 ComObjPtr<Snapshot> pSnapshot;
8559 pSnapshot.createObject();
8560 rc = pSnapshot->init(mParent,
8561 snapshotId,
8562 aName,
8563 aDescription,
8564 *RTTimeNow(&time),
8565 snapshotMachine,
8566 mData->mCurrentSnapshot);
8567 AssertComRCReturnRC(rc);
8568
8569 /* fill in the snapshot data */
8570 mSnapshotData.mLastState = mData->mMachineState;
8571 mSnapshotData.mSnapshot = pSnapshot;
8572
8573 try
8574 {
8575 LogFlowThisFunc(("Creating differencing hard disks (online=%d)...\n",
8576 fTakingSnapshotOnline));
8577
8578 // backup the media data so we can recover if things goes wrong along the day;
8579 // the matching commit() is in fixupMedia() during endSnapshot()
8580 mMediaData.backup();
8581
8582 /* set the state to Saving (this is expected by Console::TakeSnapshot()) */
8583 setMachineState(MachineState_Saving);
8584
8585 /* create new differencing hard disks and attach them to this machine */
8586 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
8587 aConsoleProgress,
8588 1, // operation weight; must be the same as in Console::TakeSnapshot()
8589 !!fTakingSnapshotOnline);
8590
8591 if (SUCCEEDED(rc) && mSnapshotData.mLastState == MachineState_Saved)
8592 {
8593 Utf8Str stateFrom = mSSData->mStateFilePath;
8594 Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
8595
8596 LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
8597 stateFrom.raw(), stateTo.raw()));
8598
8599 aConsoleProgress->SetNextOperation(Bstr(tr("Copying the execution state")),
8600 1); // weight
8601
8602 /* Leave the lock before a lengthy operation (mMachineState is
8603 * MachineState_Saving here) */
8604 alock.leave();
8605
8606 /* copy the state file */
8607 int vrc = RTFileCopyEx(stateFrom.c_str(),
8608 stateTo.c_str(),
8609 0,
8610 progressCallback,
8611 aConsoleProgress);
8612 alock.enter();
8613
8614 if (RT_FAILURE(vrc))
8615 throw setError(E_FAIL,
8616 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
8617 stateFrom.raw(),
8618 stateTo.raw(),
8619 vrc);
8620 }
8621 }
8622 catch (HRESULT hrc)
8623 {
8624 pSnapshot->uninit();
8625 pSnapshot.setNull();
8626 rc = hrc;
8627 }
8628
8629 if (fTakingSnapshotOnline)
8630 strStateFilePath.cloneTo(aStateFilePath);
8631 else
8632 *aStateFilePath = NULL;
8633
8634 LogFlowThisFuncLeave();
8635 return rc;
8636}
8637
8638/**
8639 * @note Locks this object for writing.
8640 */
8641STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
8642{
8643 LogFlowThisFunc(("\n"));
8644
8645 AutoCaller autoCaller(this);
8646 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8647
8648 AutoWriteLock alock(this);
8649
8650 AssertReturn(!aSuccess ||
8651 (mData->mMachineState == MachineState_Saving &&
8652 mSnapshotData.mLastState != MachineState_Null &&
8653 !mSnapshotData.mSnapshot.isNull()),
8654 E_FAIL);
8655
8656 /*
8657 * Restore the state we had when BeginTakingSnapshot() was called,
8658 * Console::fntTakeSnapshotWorker restores its local copy when we return.
8659 * If the state was Running, then let Console::fntTakeSnapshotWorker it
8660 * all via Console::Resume().
8661 */
8662 if ( mData->mMachineState != mSnapshotData.mLastState
8663 && mSnapshotData.mLastState != MachineState_Running)
8664 setMachineState(mSnapshotData.mLastState);
8665
8666 return endTakingSnapshot(aSuccess);
8667}
8668
8669/**
8670 * @note Locks mParent + this + children objects for writing!
8671 */
8672STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
8673 IN_BSTR aId,
8674 MachineState_T *aMachineState,
8675 IProgress **aProgress)
8676{
8677 LogFlowThisFunc(("\n"));
8678
8679 Guid id(aId);
8680 AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
8681 AssertReturn(aMachineState && aProgress, E_POINTER);
8682
8683 AutoCaller autoCaller(this);
8684 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8685
8686 /* saveSettings() needs mParent lock */
8687 AutoMultiWriteLock2 alock(mParent, this);
8688
8689 ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
8690
8691 AutoWriteLock treeLock(snapshotsTreeLockHandle());
8692
8693 ComObjPtr<Snapshot> snapshot;
8694 HRESULT rc = findSnapshot(id, snapshot, true /* aSetError */);
8695 CheckComRCReturnRC(rc);
8696
8697 AutoWriteLock snapshotLock(snapshot);
8698
8699 size_t childrenCount = snapshot->getChildrenCount();
8700 if (childrenCount > 1)
8701 return setError(VBOX_E_INVALID_OBJECT_STATE,
8702 tr("Snapshot '%s' of the machine '%ls' has more than one child snapshot (%d)"),
8703 snapshot->getName().c_str(),
8704 mUserData->mName.raw(),
8705 childrenCount);
8706
8707 /* If the snapshot being discarded is the current one, ensure current
8708 * settings are committed and saved.
8709 */
8710 if (snapshot == mData->mCurrentSnapshot)
8711 {
8712 if (isModified())
8713 {
8714 rc = saveSettings();
8715 CheckComRCReturnRC(rc);
8716 }
8717 }
8718
8719 /* create a progress object. The number of operations is:
8720 * 1 (preparing) + # of hard disks + 1 if the snapshot is online
8721 */
8722 ComObjPtr<Progress> progress;
8723 progress.createObject();
8724 rc = progress->init(mParent, aInitiator,
8725 BstrFmt(tr("Discarding snapshot '%s'"),
8726 snapshot->getName().c_str()),
8727 FALSE /* aCancelable */,
8728 1 + (ULONG)snapshot->getSnapshotMachine()->mMediaData->mAttachments.size() +
8729 (snapshot->stateFilePath().isNull() ? 0 : 1),
8730 Bstr (tr ("Preparing to discard snapshot")));
8731 AssertComRCReturn(rc, rc);
8732
8733 /* create and start the task on a separate thread */
8734 DeleteSnapshotTask *task = new DeleteSnapshotTask(this, progress, snapshot);
8735 int vrc = RTThreadCreate(NULL,
8736 taskHandler,
8737 (void*)task,
8738 0,
8739 RTTHREADTYPE_MAIN_WORKER,
8740 0,
8741 "DeleteSnapshot");
8742 if (RT_FAILURE(vrc))
8743 delete task;
8744 ComAssertRCRet (vrc, E_FAIL);
8745
8746 /* set the proper machine state (note: after creating a Task instance) */
8747 setMachineState(MachineState_DeletingSnapshot);
8748
8749 /* return the progress to the caller */
8750 progress.queryInterfaceTo(aProgress);
8751
8752 /* return the new state to the caller */
8753 *aMachineState = mData->mMachineState;
8754
8755 return S_OK;
8756}
8757
8758/**
8759 * @note Locks this + children objects for writing!
8760 */
8761STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator,
8762 ISnapshot *aSnapshot,
8763 MachineState_T *aMachineState,
8764 IProgress **aProgress)
8765{
8766 LogFlowThisFunc(("\n"));
8767
8768 AssertReturn(aInitiator, E_INVALIDARG);
8769 AssertReturn(aSnapshot && aMachineState && aProgress, E_POINTER);
8770
8771 AutoCaller autoCaller(this);
8772 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8773
8774 AutoWriteLock alock(this);
8775
8776 ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState),
8777 E_FAIL);
8778
8779 ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot));
8780
8781 /* create a progress object. The number of operations is: 1 (preparing) + #
8782 * of hard disks + 1 (if we need to copy the saved state file) */
8783 ComObjPtr<Progress> progress;
8784 progress.createObject();
8785 {
8786 ULONG opCount = 1 // preparing
8787 + (ULONG)pSnapshot->getSnapshotMachine()->mMediaData->mAttachments.size(); // one for each attachment @todo only for HDs!
8788 if (pSnapshot->stateFilePath())
8789 ++opCount; // one for the saved state
8790 progress->init(mParent, aInitiator,
8791 Bstr(tr("Restoring snapshot")),
8792 FALSE /* aCancelable */, opCount,
8793 Bstr(tr("Preparing to restore machine state from snapshot")));
8794 }
8795
8796 /* create and start the task on a separate thread (note that it will not
8797 * start working until we release alock) */
8798 RestoreSnapshotTask *task = new RestoreSnapshotTask(this, pSnapshot, progress);
8799 int vrc = RTThreadCreate(NULL,
8800 taskHandler,
8801 (void*)task,
8802 0,
8803 RTTHREADTYPE_MAIN_WORKER,
8804 0,
8805 "DiscardCurState");
8806 if (RT_FAILURE(vrc))
8807 {
8808 delete task;
8809 ComAssertRCRet(vrc, E_FAIL);
8810 }
8811
8812 /* set the proper machine state (note: after creating a Task instance) */
8813 setMachineState(MachineState_RestoringSnapshot);
8814
8815 /* return the progress to the caller */
8816 progress.queryInterfaceTo(aProgress);
8817
8818 /* return the new state to the caller */
8819 *aMachineState = mData->mMachineState;
8820
8821 return S_OK;
8822}
8823
8824STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
8825 ComSafeArrayOut(BSTR, aValues),
8826 ComSafeArrayOut(ULONG64, aTimestamps),
8827 ComSafeArrayOut(BSTR, aFlags))
8828{
8829 LogFlowThisFunc(("\n"));
8830
8831#ifdef VBOX_WITH_GUEST_PROPS
8832 using namespace guestProp;
8833
8834 AutoCaller autoCaller(this);
8835 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8836
8837 AutoReadLock alock(this);
8838
8839 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
8840 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
8841 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
8842 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
8843
8844 size_t cEntries = mHWData->mGuestProperties.size();
8845 com::SafeArray<BSTR> names (cEntries);
8846 com::SafeArray<BSTR> values (cEntries);
8847 com::SafeArray<ULONG64> timestamps (cEntries);
8848 com::SafeArray<BSTR> flags (cEntries);
8849 unsigned i = 0;
8850 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
8851 it != mHWData->mGuestProperties.end();
8852 ++it)
8853 {
8854 char szFlags[MAX_FLAGS_LEN + 1];
8855 it->strName.cloneTo(&names[i]);
8856 it->strValue.cloneTo(&values[i]);
8857 timestamps[i] = it->mTimestamp;
8858 /* If it is NULL, keep it NULL. */
8859 if (it->mFlags)
8860 {
8861 writeFlags(it->mFlags, szFlags);
8862 Bstr(szFlags).cloneTo(&flags[i]);
8863 }
8864 else
8865 flags[i] = NULL;
8866 ++i;
8867 }
8868 names.detachTo(ComSafeArrayOutArg(aNames));
8869 values.detachTo(ComSafeArrayOutArg(aValues));
8870 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
8871 flags.detachTo(ComSafeArrayOutArg(aFlags));
8872 mHWData->mPropertyServiceActive = true;
8873 return S_OK;
8874#else
8875 ReturnComNotImplemented();
8876#endif
8877}
8878
8879STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn (IN_BSTR, aNames),
8880 ComSafeArrayIn (IN_BSTR, aValues),
8881 ComSafeArrayIn (ULONG64, aTimestamps),
8882 ComSafeArrayIn (IN_BSTR, aFlags))
8883{
8884 LogFlowThisFunc(("\n"));
8885
8886#ifdef VBOX_WITH_GUEST_PROPS
8887 using namespace guestProp;
8888
8889 AutoCaller autoCaller(this);
8890 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8891
8892 AutoWriteLock alock(this);
8893
8894 /* Temporarily reset the registered flag, so that our machine state
8895 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in
8896 * all setters will return FALSE for a Machine instance if mRegistered
8897 * is TRUE). This is copied from registeredInit(), and may or may not be
8898 * the right way to handle this. */
8899 mData->mRegistered = FALSE;
8900 HRESULT rc = checkStateDependency(MutableStateDep);
8901 LogRel (("checkStateDependency(MutableStateDep) returned 0x%x\n", rc));
8902 CheckComRCReturnRC(rc);
8903
8904 // ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
8905
8906 AssertReturn(!ComSafeArrayInIsNull (aNames), E_POINTER);
8907 AssertReturn(!ComSafeArrayInIsNull (aValues), E_POINTER);
8908 AssertReturn(!ComSafeArrayInIsNull (aTimestamps), E_POINTER);
8909 AssertReturn(!ComSafeArrayInIsNull (aFlags), E_POINTER);
8910
8911 com::SafeArray<IN_BSTR> names (ComSafeArrayInArg (aNames));
8912 com::SafeArray<IN_BSTR> values (ComSafeArrayInArg (aValues));
8913 com::SafeArray<ULONG64> timestamps (ComSafeArrayInArg (aTimestamps));
8914 com::SafeArray<IN_BSTR> flags (ComSafeArrayInArg (aFlags));
8915 DiscardSettings();
8916 mHWData.backup();
8917 mHWData->mGuestProperties.erase (mHWData->mGuestProperties.begin(),
8918 mHWData->mGuestProperties.end());
8919 for (unsigned i = 0; i < names.size(); ++i)
8920 {
8921 uint32_t fFlags = NILFLAG;
8922 validateFlags (Utf8Str (flags[i]).raw(), &fFlags);
8923 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
8924 mHWData->mGuestProperties.push_back (property);
8925 }
8926 mHWData->mPropertyServiceActive = false;
8927 alock.unlock();
8928 SaveSettings();
8929 /* Restore the mRegistered flag. */
8930 mData->mRegistered = TRUE;
8931 return S_OK;
8932#else
8933 ReturnComNotImplemented();
8934#endif
8935}
8936
8937STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
8938 IN_BSTR aValue,
8939 ULONG64 aTimestamp,
8940 IN_BSTR aFlags)
8941{
8942 LogFlowThisFunc(("\n"));
8943
8944#ifdef VBOX_WITH_GUEST_PROPS
8945 using namespace guestProp;
8946
8947 CheckComArgNotNull (aName);
8948 if ((aValue != NULL) && (!VALID_PTR (aValue) || !VALID_PTR (aFlags)))
8949 return E_POINTER; /* aValue can be NULL to indicate deletion */
8950
8951 try
8952 {
8953 Utf8Str utf8Name(aName);
8954 Utf8Str utf8Flags(aFlags);
8955 Utf8Str utf8Patterns(mHWData->mGuestPropertyNotificationPatterns);
8956
8957 uint32_t fFlags = NILFLAG;
8958 if ((aFlags != NULL) && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags)))
8959 return E_INVALIDARG;
8960
8961 bool matchAll = false;
8962 if (utf8Patterns.isEmpty())
8963 matchAll = true;
8964
8965 AutoCaller autoCaller(this);
8966 CheckComRCReturnRC(autoCaller.rc());
8967
8968 AutoWriteLock alock(this);
8969
8970 HRESULT rc = checkStateDependency(MutableStateDep);
8971 CheckComRCReturnRC(rc);
8972
8973 mHWData.backup();
8974 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
8975 iter != mHWData->mGuestProperties.end();
8976 ++iter)
8977 if (utf8Name == iter->strName)
8978 {
8979 mHWData->mGuestProperties.erase(iter);
8980 break;
8981 }
8982 if (aValue != NULL)
8983 {
8984 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
8985 mHWData->mGuestProperties.push_back (property);
8986 }
8987
8988 /* send a callback notification if appropriate */
8989 alock.leave();
8990 if ( matchAll
8991 || RTStrSimplePatternMultiMatch(utf8Patterns.raw(),
8992 RTSTR_MAX,
8993 utf8Name.raw(),
8994 RTSTR_MAX, NULL)
8995 )
8996 mParent->onGuestPropertyChange(mData->mUuid,
8997 aName,
8998 aValue,
8999 aFlags);
9000 }
9001 catch(std::bad_alloc &)
9002 {
9003 return E_OUTOFMEMORY;
9004 }
9005 return S_OK;
9006#else
9007 ReturnComNotImplemented();
9008#endif
9009}
9010
9011// public methods only for internal purposes
9012/////////////////////////////////////////////////////////////////////////////
9013
9014/**
9015 * Called from the client watcher thread to check for expected or unexpected
9016 * death of the client process that has a direct session to this machine.
9017 *
9018 * On Win32 and on OS/2, this method is called only when we've got the
9019 * mutex (i.e. the client has either died or terminated normally) so it always
9020 * returns @c true (the client is terminated, the session machine is
9021 * uninitialized).
9022 *
9023 * On other platforms, the method returns @c true if the client process has
9024 * terminated normally or abnormally and the session machine was uninitialized,
9025 * and @c false if the client process is still alive.
9026 *
9027 * @note Locks this object for writing.
9028 */
9029bool SessionMachine::checkForDeath()
9030{
9031 Uninit::Reason reason;
9032 bool terminated = false;
9033
9034 /* Enclose autoCaller with a block because calling uninit() from under it
9035 * will deadlock. */
9036 {
9037 AutoCaller autoCaller(this);
9038 if (!autoCaller.isOk())
9039 {
9040 /* return true if not ready, to cause the client watcher to exclude
9041 * the corresponding session from watching */
9042 LogFlowThisFunc(("Already uninitialized!"));
9043 return true;
9044 }
9045
9046 AutoWriteLock alock(this);
9047
9048 /* Determine the reason of death: if the session state is Closing here,
9049 * everything is fine. Otherwise it means that the client did not call
9050 * OnSessionEnd() before it released the IPC semaphore. This may happen
9051 * either because the client process has abnormally terminated, or
9052 * because it simply forgot to call ISession::Close() before exiting. We
9053 * threat the latter also as an abnormal termination (see
9054 * Session::uninit() for details). */
9055 reason = mData->mSession.mState == SessionState_Closing ?
9056 Uninit::Normal :
9057 Uninit::Abnormal;
9058
9059#if defined(RT_OS_WINDOWS)
9060
9061 AssertMsg (mIPCSem, ("semaphore must be created"));
9062
9063 /* release the IPC mutex */
9064 ::ReleaseMutex (mIPCSem);
9065
9066 terminated = true;
9067
9068#elif defined(RT_OS_OS2)
9069
9070 AssertMsg (mIPCSem, ("semaphore must be created"));
9071
9072 /* release the IPC mutex */
9073 ::DosReleaseMutexSem (mIPCSem);
9074
9075 terminated = true;
9076
9077#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9078
9079 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9080
9081 int val = ::semctl (mIPCSem, 0, GETVAL);
9082 if (val > 0)
9083 {
9084 /* the semaphore is signaled, meaning the session is terminated */
9085 terminated = true;
9086 }
9087
9088#else
9089# error "Port me!"
9090#endif
9091
9092 } /* AutoCaller block */
9093
9094 if (terminated)
9095 uninit (reason);
9096
9097 return terminated;
9098}
9099
9100/**
9101 * @note Locks this object for reading.
9102 */
9103HRESULT SessionMachine::onNetworkAdapterChange (INetworkAdapter *networkAdapter, BOOL changeAdapter)
9104{
9105 LogFlowThisFunc(("\n"));
9106
9107 AutoCaller autoCaller(this);
9108 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9109
9110 ComPtr<IInternalSessionControl> directControl;
9111 {
9112 AutoReadLock alock(this);
9113 directControl = mData->mSession.mDirectControl;
9114 }
9115
9116 /* ignore notifications sent after #OnSessionEnd() is called */
9117 if (!directControl)
9118 return S_OK;
9119
9120 return directControl->OnNetworkAdapterChange (networkAdapter, changeAdapter);
9121}
9122
9123/**
9124 * @note Locks this object for reading.
9125 */
9126HRESULT SessionMachine::onSerialPortChange (ISerialPort *serialPort)
9127{
9128 LogFlowThisFunc(("\n"));
9129
9130 AutoCaller autoCaller(this);
9131 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9132
9133 ComPtr<IInternalSessionControl> directControl;
9134 {
9135 AutoReadLock alock(this);
9136 directControl = mData->mSession.mDirectControl;
9137 }
9138
9139 /* ignore notifications sent after #OnSessionEnd() is called */
9140 if (!directControl)
9141 return S_OK;
9142
9143 return directControl->OnSerialPortChange (serialPort);
9144}
9145
9146/**
9147 * @note Locks this object for reading.
9148 */
9149HRESULT SessionMachine::onParallelPortChange (IParallelPort *parallelPort)
9150{
9151 LogFlowThisFunc(("\n"));
9152
9153 AutoCaller autoCaller(this);
9154 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9155
9156 ComPtr<IInternalSessionControl> directControl;
9157 {
9158 AutoReadLock alock(this);
9159 directControl = mData->mSession.mDirectControl;
9160 }
9161
9162 /* ignore notifications sent after #OnSessionEnd() is called */
9163 if (!directControl)
9164 return S_OK;
9165
9166 return directControl->OnParallelPortChange (parallelPort);
9167}
9168
9169/**
9170 * @note Locks this object for reading.
9171 */
9172HRESULT SessionMachine::onStorageControllerChange ()
9173{
9174 LogFlowThisFunc(("\n"));
9175
9176 AutoCaller autoCaller(this);
9177 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9178
9179 ComPtr<IInternalSessionControl> directControl;
9180 {
9181 AutoReadLock alock(this);
9182 directControl = mData->mSession.mDirectControl;
9183 }
9184
9185 /* ignore notifications sent after #OnSessionEnd() is called */
9186 if (!directControl)
9187 return S_OK;
9188
9189 return directControl->OnStorageControllerChange ();
9190}
9191
9192/**
9193 * @note Locks this object for reading.
9194 */
9195HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment)
9196{
9197 LogFlowThisFunc(("\n"));
9198
9199 AutoCaller autoCaller(this);
9200 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9201
9202 ComPtr<IInternalSessionControl> directControl;
9203 {
9204 AutoReadLock alock(this);
9205 directControl = mData->mSession.mDirectControl;
9206 }
9207
9208 /* ignore notifications sent after #OnSessionEnd() is called */
9209 if (!directControl)
9210 return S_OK;
9211
9212 return directControl->OnMediumChange(aAttachment);
9213}
9214
9215/**
9216 * @note Locks this object for reading.
9217 */
9218HRESULT SessionMachine::onVRDPServerChange()
9219{
9220 LogFlowThisFunc(("\n"));
9221
9222 AutoCaller autoCaller(this);
9223 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9224
9225 ComPtr<IInternalSessionControl> directControl;
9226 {
9227 AutoReadLock alock(this);
9228 directControl = mData->mSession.mDirectControl;
9229 }
9230
9231 /* ignore notifications sent after #OnSessionEnd() is called */
9232 if (!directControl)
9233 return S_OK;
9234
9235 return directControl->OnVRDPServerChange();
9236}
9237
9238/**
9239 * @note Locks this object for reading.
9240 */
9241HRESULT SessionMachine::onUSBControllerChange()
9242{
9243 LogFlowThisFunc(("\n"));
9244
9245 AutoCaller autoCaller(this);
9246 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9247
9248 ComPtr<IInternalSessionControl> directControl;
9249 {
9250 AutoReadLock alock(this);
9251 directControl = mData->mSession.mDirectControl;
9252 }
9253
9254 /* ignore notifications sent after #OnSessionEnd() is called */
9255 if (!directControl)
9256 return S_OK;
9257
9258 return directControl->OnUSBControllerChange();
9259}
9260
9261/**
9262 * @note Locks this object for reading.
9263 */
9264HRESULT SessionMachine::onSharedFolderChange()
9265{
9266 LogFlowThisFunc(("\n"));
9267
9268 AutoCaller autoCaller(this);
9269 AssertComRCReturnRC(autoCaller.rc());
9270
9271 ComPtr<IInternalSessionControl> directControl;
9272 {
9273 AutoReadLock alock(this);
9274 directControl = mData->mSession.mDirectControl;
9275 }
9276
9277 /* ignore notifications sent after #OnSessionEnd() is called */
9278 if (!directControl)
9279 return S_OK;
9280
9281 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
9282}
9283
9284/**
9285 * Returns @c true if this machine's USB controller reports it has a matching
9286 * filter for the given USB device and @c false otherwise.
9287 *
9288 * @note Locks this object for reading.
9289 */
9290bool SessionMachine::hasMatchingUSBFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
9291{
9292 AutoCaller autoCaller(this);
9293 /* silently return if not ready -- this method may be called after the
9294 * direct machine session has been called */
9295 if (!autoCaller.isOk())
9296 return false;
9297
9298 AutoReadLock alock(this);
9299
9300#ifdef VBOX_WITH_USB
9301 switch (mData->mMachineState)
9302 {
9303 case MachineState_Starting:
9304 case MachineState_Restoring:
9305 case MachineState_TeleportingFrom:
9306 case MachineState_Paused:
9307 case MachineState_Running:
9308 return mUSBController->hasMatchingFilter (aDevice, aMaskedIfs);
9309 default: break;
9310 }
9311#else
9312 NOREF(aDevice);
9313 NOREF(aMaskedIfs);
9314#endif
9315 return false;
9316}
9317
9318/**
9319 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9320 */
9321HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
9322 IVirtualBoxErrorInfo *aError,
9323 ULONG aMaskedIfs)
9324{
9325 LogFlowThisFunc(("\n"));
9326
9327 AutoCaller autoCaller(this);
9328
9329 /* This notification may happen after the machine object has been
9330 * uninitialized (the session was closed), so don't assert. */
9331 CheckComRCReturnRC(autoCaller.rc());
9332
9333 ComPtr<IInternalSessionControl> directControl;
9334 {
9335 AutoReadLock alock(this);
9336 directControl = mData->mSession.mDirectControl;
9337 }
9338
9339 /* fail on notifications sent after #OnSessionEnd() is called, it is
9340 * expected by the caller */
9341 if (!directControl)
9342 return E_FAIL;
9343
9344 /* No locks should be held at this point. */
9345 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9346 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9347
9348 return directControl->OnUSBDeviceAttach (aDevice, aError, aMaskedIfs);
9349}
9350
9351/**
9352 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9353 */
9354HRESULT SessionMachine::onUSBDeviceDetach (IN_BSTR aId,
9355 IVirtualBoxErrorInfo *aError)
9356{
9357 LogFlowThisFunc(("\n"));
9358
9359 AutoCaller autoCaller(this);
9360
9361 /* This notification may happen after the machine object has been
9362 * uninitialized (the session was closed), so don't assert. */
9363 CheckComRCReturnRC(autoCaller.rc());
9364
9365 ComPtr<IInternalSessionControl> directControl;
9366 {
9367 AutoReadLock alock(this);
9368 directControl = mData->mSession.mDirectControl;
9369 }
9370
9371 /* fail on notifications sent after #OnSessionEnd() is called, it is
9372 * expected by the caller */
9373 if (!directControl)
9374 return E_FAIL;
9375
9376 /* No locks should be held at this point. */
9377 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9378 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9379
9380 return directControl->OnUSBDeviceDetach (aId, aError);
9381}
9382
9383// protected methods
9384/////////////////////////////////////////////////////////////////////////////
9385
9386/**
9387 * Helper method to finalize saving the state.
9388 *
9389 * @note Must be called from under this object's lock.
9390 *
9391 * @param aSuccess TRUE if the snapshot has been taken successfully
9392 *
9393 * @note Locks mParent + this objects for writing.
9394 */
9395HRESULT SessionMachine::endSavingState (BOOL aSuccess)
9396{
9397 LogFlowThisFuncEnter();
9398
9399 AutoCaller autoCaller(this);
9400 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9401
9402 /* saveSettings() needs mParent lock */
9403 AutoMultiWriteLock2 alock (mParent, this);
9404
9405 HRESULT rc = S_OK;
9406
9407 if (aSuccess)
9408 {
9409 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
9410
9411 /* save all VM settings */
9412 rc = saveSettings();
9413 }
9414 else
9415 {
9416 /* delete the saved state file (it might have been already created) */
9417 RTFileDelete(Utf8Str(mSnapshotData.mStateFilePath).c_str());
9418 }
9419
9420 /* remove the completed progress object */
9421 mParent->removeProgress (mSnapshotData.mProgressId);
9422
9423 /* clear out the temporary saved state data */
9424 mSnapshotData.mLastState = MachineState_Null;
9425 mSnapshotData.mProgressId.clear();
9426 mSnapshotData.mStateFilePath.setNull();
9427
9428 LogFlowThisFuncLeave();
9429 return rc;
9430}
9431
9432/**
9433 * Helper method to finalize taking a snapshot. Gets called to finalize the
9434 * "take snapshot" procedure, either from the public SessionMachine::EndTakingSnapshot()
9435 * if taking the snapshot failed/was aborted or from the takeSnapshotHandler thread
9436 * when taking the snapshot succeeded.
9437 *
9438 * Expected to be called after completing *all* the tasks related to taking the
9439 * snapshot, either successfully or unsuccessfilly.
9440 *
9441 * @param aSuccess TRUE if the snapshot has been taken successfully.
9442 *
9443 * @note Locks this objects for writing.
9444 */
9445HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
9446{
9447 LogFlowThisFuncEnter();
9448
9449 AutoCaller autoCaller(this);
9450 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9451
9452 AutoMultiWriteLock2 alock(mParent, this);
9453 // saveSettings needs VirtualBox lock
9454
9455 AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL);
9456
9457 MultiResult rc(S_OK);
9458
9459 Snapshot *pOldFirstSnap = mData->mFirstSnapshot;
9460 Snapshot *pOldCurrentSnap = mData->mCurrentSnapshot;
9461
9462 bool fOnline = Global::IsOnline(mSnapshotData.mLastState);
9463
9464 if (aSuccess)
9465 {
9466 mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
9467
9468 /* memorize the first snapshot if necessary */
9469 if (!mData->mFirstSnapshot)
9470 mData->mFirstSnapshot = mData->mCurrentSnapshot;
9471
9472 if (!fOnline)
9473 /* the machine was powered off or saved when taking a snapshot, so
9474 * reset the mCurrentStateModified flag */
9475 mData->mCurrentStateModified = FALSE;
9476
9477 rc = saveSettings();
9478 }
9479
9480 if (aSuccess && SUCCEEDED(rc))
9481 {
9482 /* associate old hard disks with the snapshot and do locking/unlocking*/
9483 fixupMedia(true /* aCommit */, fOnline);
9484
9485 /* inform callbacks */
9486 mParent->onSnapshotTaken(mData->mUuid,
9487 mSnapshotData.mSnapshot->getId());
9488 }
9489 else
9490 {
9491 /* delete all differencing hard disks created (this will also attach
9492 * their parents back by rolling back mMediaData) */
9493 fixupMedia(false /* aCommit */);
9494
9495 mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above
9496 mData->mCurrentSnapshot = pOldCurrentSnap; // might have been changed above
9497
9498 /* delete the saved state file (it might have been already created) */
9499 if (mSnapshotData.mSnapshot->stateFilePath())
9500 RTFileDelete(Utf8Str(mSnapshotData.mSnapshot->stateFilePath()).c_str());
9501
9502 mSnapshotData.mSnapshot->uninit();
9503 }
9504
9505 /* clear out the snapshot data */
9506 mSnapshotData.mLastState = MachineState_Null;
9507 mSnapshotData.mSnapshot.setNull();
9508
9509 LogFlowThisFuncLeave();
9510 return rc;
9511}
9512
9513/**
9514 * Helper struct for SessionMachine::deleteSnapshotHandler().
9515 */
9516struct MediumDiscardRec
9517{
9518 MediumDiscardRec() : chain (NULL) {}
9519
9520 MediumDiscardRec (const ComObjPtr<Medium> &aHd,
9521 Medium::MergeChain *aChain = NULL)
9522 : hd (aHd), chain (aChain) {}
9523
9524 MediumDiscardRec (const ComObjPtr<Medium> &aHd,
9525 Medium::MergeChain *aChain,
9526 const ComObjPtr<Medium> &aReplaceHd,
9527 const ComObjPtr<MediumAttachment> &aReplaceHda,
9528 const Guid &aSnapshotId)
9529 : hd (aHd), chain (aChain)
9530 , replaceHd (aReplaceHd), replaceHda (aReplaceHda)
9531 , snapshotId (aSnapshotId) {}
9532
9533 ComObjPtr<Medium> hd;
9534 Medium::MergeChain *chain;
9535 /* these are for the replace hard disk case: */
9536 ComObjPtr<Medium> replaceHd;
9537 ComObjPtr<MediumAttachment> replaceHda;
9538 Guid snapshotId;
9539};
9540
9541typedef std::list <MediumDiscardRec> MediumDiscardRecList;
9542
9543/**
9544 * Discard snapshot task handler. Must be called only by
9545 * DeleteSnapshotTask::handler()!
9546 *
9547 * When aTask.subTask is true, the associated progress object is left
9548 * uncompleted on success. On failure, the progress is marked as completed
9549 * regardless of this parameter.
9550 *
9551 * @note Locks mParent + this + child objects for writing!
9552 */
9553void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
9554{
9555 LogFlowThisFuncEnter();
9556
9557 AutoCaller autoCaller(this);
9558
9559 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
9560 if (!autoCaller.isOk())
9561 {
9562 /* we might have been uninitialized because the session was accidentally
9563 * closed by the client, so don't assert */
9564 aTask.progress->notifyComplete(E_FAIL,
9565 COM_IIDOF(IMachine),
9566 getComponentName(),
9567 tr("The session has been accidentally closed"));
9568 LogFlowThisFuncLeave();
9569 return;
9570 }
9571
9572 /* Locking order: */
9573 AutoMultiWriteLock3 alock(this->lockHandle(),
9574 this->snapshotsTreeLockHandle(),
9575 aTask.snapshot->lockHandle());
9576
9577 ComPtr<SnapshotMachine> sm = aTask.snapshot->getSnapshotMachine();
9578 /* no need to lock the snapshot machine since it is const by definiton */
9579
9580 HRESULT rc = S_OK;
9581
9582 /* save the snapshot ID (for callbacks) */
9583 Guid snapshotId = aTask.snapshot->getId();
9584
9585 MediumDiscardRecList toDiscard;
9586
9587 bool settingsChanged = false;
9588
9589 try
9590 {
9591 /* first pass: */
9592 LogFlowThisFunc(("1: Checking hard disk merge prerequisites...\n"));
9593
9594 for (MediaData::AttachmentList::const_iterator it = sm->mMediaData->mAttachments.begin();
9595 it != sm->mMediaData->mAttachments.end();
9596 ++it)
9597 {
9598 ComObjPtr<MediumAttachment> hda = *it;
9599 ComObjPtr<Medium> hd = hda->medium();
9600
9601 // medium can be NULL only for non-hard-disk types
9602 Assert( !hd.isNull()
9603 || hda->type() != DeviceType_HardDisk);
9604 if (hd.isNull())
9605 continue;
9606
9607 /* Medium::prepareDiscard() reqiuires a write lock */
9608 AutoWriteLock hdLock(hd);
9609
9610 if (hd->type() != MediumType_Normal)
9611 {
9612 /* skip writethrough hard disks */
9613 Assert(hd->type() == MediumType_Writethrough);
9614 rc = aTask.progress->SetNextOperation(BstrFmt(tr("Skipping writethrough hard disk '%s'"),
9615 hd->base()->name().raw()),
9616 1); // weight
9617 CheckComRCThrowRC(rc);
9618 continue;
9619 }
9620
9621 Medium::MergeChain *chain = NULL;
9622
9623 /* needs to be discarded (merged with the child if any), check
9624 * prerequisites */
9625 rc = hd->prepareDiscard(chain);
9626 CheckComRCThrowRC(rc);
9627
9628 if (hd->parent().isNull() && chain != NULL)
9629 {
9630 /* it's a base hard disk so it will be a backward merge of its
9631 * only child to it (prepareDiscard() does necessary checks). We
9632 * need then to update the attachment that refers to the child
9633 * to refer to the parent instead. Don't forget to detach the
9634 * child (otherwise mergeTo() called by discard() will assert
9635 * because it will be going to delete the child) */
9636
9637 /* The below assert would be nice but I don't want to move
9638 * Medium::MergeChain to the header just for that
9639 * Assert (!chain->isForward()); */
9640
9641 Assert(hd->children().size() == 1);
9642
9643 ComObjPtr<Medium> replaceHd = hd->children().front();
9644
9645 const Guid *pReplaceMachineId = replaceHd->getFirstMachineBackrefId();
9646 Assert(pReplaceMachineId && *pReplaceMachineId == mData->mUuid);
9647
9648 Guid snapshotId;
9649 const Guid *pSnapshotId = replaceHd->getFirstMachineBackrefSnapshotId();
9650 if (pSnapshotId)
9651 snapshotId = *pSnapshotId;
9652
9653 HRESULT rc2 = S_OK;
9654
9655 /* adjust back references */
9656 rc2 = replaceHd->detachFrom (mData->mUuid, snapshotId);
9657 AssertComRC(rc2);
9658
9659 rc2 = hd->attachTo (mData->mUuid, snapshotId);
9660 AssertComRC(rc2);
9661
9662 /* replace the hard disk in the attachment object */
9663 if (snapshotId.isEmpty())
9664 {
9665 /* in current state */
9666 AssertBreak(hda = findAttachment(mMediaData->mAttachments, replaceHd));
9667 }
9668 else
9669 {
9670 /* in snapshot */
9671 ComObjPtr<Snapshot> snapshot;
9672 rc2 = findSnapshot(snapshotId, snapshot);
9673 AssertComRC(rc2);
9674
9675 /* don't lock the snapshot; cannot be modified outside */
9676 MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
9677 AssertBreak(hda = findAttachment(snapAtts, replaceHd));
9678 }
9679
9680 AutoWriteLock attLock(hda);
9681 hda->updateMedium(hd, false /* aImplicit */);
9682
9683 toDiscard.push_back(MediumDiscardRec(hd,
9684 chain,
9685 replaceHd,
9686 hda,
9687 snapshotId));
9688 continue;
9689 }
9690
9691 toDiscard.push_back(MediumDiscardRec(hd, chain));
9692 }
9693
9694 /* Now we checked that we can successfully merge all normal hard disks
9695 * (unless a runtime error like end-of-disc happens). Prior to
9696 * performing the actual merge, we want to discard the snapshot itself
9697 * and remove it from the XML file to make sure that a possible merge
9698 * ruintime error will not make this snapshot inconsistent because of
9699 * the partially merged or corrupted hard disks */
9700
9701 /* second pass: */
9702 LogFlowThisFunc(("2: Discarding snapshot...\n"));
9703
9704 {
9705 ComObjPtr<Snapshot> parentSnapshot = aTask.snapshot->parent();
9706 Bstr stateFilePath = aTask.snapshot->stateFilePath();
9707
9708 /* Note that discarding the snapshot will deassociate it from the
9709 * hard disks which will allow the merge+delete operation for them*/
9710 aTask.snapshot->beginDiscard();
9711 aTask.snapshot->uninit();
9712
9713 rc = saveAllSnapshots();
9714 CheckComRCThrowRC(rc);
9715
9716 /// @todo (dmik)
9717 // if we implement some warning mechanism later, we'll have
9718 // to return a warning if the state file path cannot be deleted
9719 if (stateFilePath)
9720 {
9721 aTask.progress->SetNextOperation(Bstr(tr("Discarding the execution state")),
9722 1); // weight
9723
9724 RTFileDelete(Utf8Str(stateFilePath).c_str());
9725 }
9726
9727 /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
9728 /// should restore the shapshot in the snapshot tree if
9729 /// saveSnapshotSettings fails. Actually, we may call
9730 /// #saveSnapshotSettings() with a special flag that will tell it to
9731 /// skip the given snapshot as if it would have been discarded and
9732 /// only actually discard it if the save operation succeeds.
9733 }
9734
9735 /* here we come when we've irrevesibly discarded the snapshot which
9736 * means that the VM settigns (our relevant changes to mData) need to be
9737 * saved too */
9738 /// @todo NEWMEDIA maybe save everything in one operation in place of
9739 /// saveSnapshotSettings() above
9740 settingsChanged = true;
9741
9742 /* third pass: */
9743 LogFlowThisFunc(("3: Performing actual hard disk merging...\n"));
9744
9745 /* leave the locks before the potentially lengthy operation */
9746 alock.leave();
9747
9748 /// @todo NEWMEDIA turn the following errors into warnings because the
9749 /// snapshot itself has been already deleted (and interpret these
9750 /// warnings properly on the GUI side)
9751
9752 for (MediumDiscardRecList::iterator it = toDiscard.begin();
9753 it != toDiscard.end();)
9754 {
9755 rc = it->hd->discard (aTask.progress, it->chain);
9756 CheckComRCBreakRC(rc);
9757
9758 /* prevent from calling cancelDiscard() */
9759 it = toDiscard.erase (it);
9760 }
9761
9762 alock.enter();
9763
9764 CheckComRCThrowRC(rc);
9765 }
9766 catch (HRESULT aRC) { rc = aRC; }
9767
9768 if (FAILED(rc))
9769 {
9770 HRESULT rc2 = S_OK;
9771
9772 /* un-prepare the remaining hard disks */
9773 for (MediumDiscardRecList::const_iterator it = toDiscard.begin();
9774 it != toDiscard.end(); ++it)
9775 {
9776 it->hd->cancelDiscard (it->chain);
9777
9778 if (!it->replaceHd.isNull())
9779 {
9780 /* undo hard disk replacement */
9781
9782 rc2 = it->replaceHd->attachTo (mData->mUuid, it->snapshotId);
9783 AssertComRC(rc2);
9784
9785 rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
9786 AssertComRC(rc2);
9787
9788 AutoWriteLock attLock (it->replaceHda);
9789 it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */);
9790 }
9791 }
9792 }
9793
9794 if (!aTask.subTask || FAILED(rc))
9795 {
9796 if (!aTask.subTask)
9797 {
9798 /* saveSettings() below needs a VirtualBox write lock and we need to
9799 * leave this object's lock to do this to follow the {parent-child}
9800 * locking rule. This is the last chance to do that while we are
9801 * still in a protective state which allows us to temporarily leave
9802 * the lock */
9803 alock.unlock();
9804 AutoWriteLock vboxLock(mParent);
9805 alock.lock();
9806
9807 /* preserve existing error info */
9808 ErrorInfoKeeper eik;
9809
9810 /* restore the machine state */
9811 setMachineState(aTask.state);
9812 updateMachineStateOnClient();
9813
9814 if (settingsChanged)
9815 saveSettings(SaveS_InformCallbacksAnyway);
9816 }
9817
9818 /* set the result (this will try to fetch current error info on failure) */
9819 aTask.progress->notifyComplete (rc);
9820 }
9821
9822 if (SUCCEEDED(rc))
9823 mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
9824
9825 LogFlowThisFunc(("Done discarding snapshot (rc=%08X)\n", rc));
9826 LogFlowThisFuncLeave();
9827}
9828
9829/**
9830 * Restore snapshot state task handler. Must be called only by
9831 * RestoreSnapshotTask::handler()!
9832 *
9833 * @note Locks mParent + this object for writing.
9834 */
9835void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)
9836{
9837 LogFlowThisFuncEnter();
9838
9839 AutoCaller autoCaller(this);
9840
9841 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
9842 if (!autoCaller.isOk())
9843 {
9844 /* we might have been uninitialized because the session was accidentally
9845 * closed by the client, so don't assert */
9846 aTask.progress->notifyComplete(E_FAIL,
9847 COM_IIDOF(IMachine),
9848 getComponentName(),
9849 tr("The session has been accidentally closed"));
9850
9851 LogFlowThisFuncLeave();
9852 return;
9853 }
9854
9855 /* saveSettings() needs mParent lock */
9856 AutoWriteLock vboxLock(mParent);
9857
9858 /* @todo We don't need mParent lock so far so unlock() it. Better is to
9859 * provide an AutoWriteLock argument that lets create a non-locking
9860 * instance */
9861 vboxLock.unlock();
9862
9863 AutoWriteLock alock(this);
9864
9865 /* discard all current changes to mUserData (name, OSType etc.) (note that
9866 * the machine is powered off, so there is no need to inform the direct
9867 * session) */
9868 if (isModified())
9869 rollback(false /* aNotify */);
9870
9871 HRESULT rc = S_OK;
9872
9873 bool errorInSubtask = false;
9874 bool stateRestored = false;
9875
9876 try
9877 {
9878 /* discard the saved state file if the machine was Saved prior to this
9879 * operation */
9880 if (aTask.state == MachineState_Saved)
9881 {
9882 Assert(!mSSData->mStateFilePath.isEmpty());
9883 RTFileDelete(Utf8Str(mSSData->mStateFilePath).c_str());
9884 mSSData->mStateFilePath.setNull();
9885 aTask.modifyLastState(MachineState_PoweredOff);
9886 rc = saveStateSettings(SaveSTS_StateFilePath);
9887 CheckComRCThrowRC(rc);
9888 }
9889
9890 RTTIMESPEC snapshotTimeStamp;
9891 RTTimeSpecSetMilli(&snapshotTimeStamp, 0);
9892
9893 {
9894 AutoReadLock snapshotLock(aTask.pSnapshot);
9895
9896 /* remember the timestamp of the snapshot we're restoring from */
9897 snapshotTimeStamp = aTask.pSnapshot->getTimeStamp();
9898
9899 ComPtr<SnapshotMachine> pSnapshotMachine(aTask.pSnapshot->getSnapshotMachine());
9900
9901 /* copy all hardware data from the snapshot */
9902 copyFrom(pSnapshotMachine);
9903
9904 LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));
9905
9906 /* restore the attachments from the snapshot */
9907 mMediaData.backup();
9908 mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments;
9909
9910 /* leave the locks before the potentially lengthy operation */
9911 snapshotLock.unlock();
9912 alock.leave();
9913
9914 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
9915 aTask.progress,
9916 1,
9917 false /* aOnline */);
9918
9919 alock.enter();
9920 snapshotLock.lock();
9921
9922 CheckComRCThrowRC(rc);
9923
9924 /* Note: on success, current (old) hard disks will be
9925 * deassociated/deleted on #commit() called from #saveSettings() at
9926 * the end. On failure, newly created implicit diffs will be
9927 * deleted by #rollback() at the end. */
9928
9929 /* should not have a saved state file associated at this point */
9930 Assert(mSSData->mStateFilePath.isNull());
9931
9932 if (aTask.pSnapshot->stateFilePath())
9933 {
9934 Utf8Str snapStateFilePath = aTask.pSnapshot->stateFilePath();
9935
9936 Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
9937 mUserData->mSnapshotFolderFull.raw(),
9938 RTPATH_DELIMITER,
9939 mData->mUuid.raw());
9940
9941 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
9942 snapStateFilePath.raw(), stateFilePath.raw()));
9943
9944 aTask.progress->SetNextOperation(Bstr(tr("Restoring the execution state")),
9945 1); // weight
9946
9947 /* leave the lock before the potentially lengthy operation */
9948 snapshotLock.unlock();
9949 alock.leave();
9950
9951 /* copy the state file */
9952 int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
9953 stateFilePath.c_str(),
9954 0,
9955 progressCallback,
9956 aTask.progress);
9957
9958 alock.enter();
9959 snapshotLock.lock();
9960
9961 if (RT_SUCCESS(vrc))
9962 {
9963 mSSData->mStateFilePath = stateFilePath;
9964
9965 /* make the snapshot we restored from the current snapshot */
9966 mData->mCurrentSnapshot = aTask.pSnapshot;
9967 }
9968 else
9969 {
9970 throw setError(E_FAIL,
9971 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
9972 snapStateFilePath.raw(),
9973 stateFilePath.raw(),
9974 vrc);
9975 }
9976 }
9977 }
9978
9979 /* grab differencing hard disks from the old attachments that will
9980 * become unused and need to be auto-deleted */
9981
9982 std::list< ComObjPtr<Medium> > diffs;
9983
9984 for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin();
9985 it != mMediaData.backedUpData()->mAttachments.end();
9986 ++it)
9987 {
9988 ComObjPtr<Medium> hd = (*it)->medium();
9989
9990 /* while the hard disk is attached, the number of children or the
9991 * parent cannot change, so no lock */
9992 if (!hd.isNull() && !hd->parent().isNull() && hd->children().size() == 0)
9993 diffs.push_back (hd);
9994 }
9995
9996 int saveFlags = 0;
9997
9998 /* @todo saveSettings() below needs a VirtualBox write lock and we need
9999 * to leave this object's lock to do this to follow the {parent-child}
10000 * locking rule. This is the last chance to do that while we are still
10001 * in a protective state which allows us to temporarily leave the lock*/
10002 alock.unlock();
10003 vboxLock.lock();
10004 alock.lock();
10005
10006 /* we have already discarded the current state, so set the execution
10007 * state accordingly no matter of the discard snapshot result */
10008 if (mSSData->mStateFilePath)
10009 setMachineState(MachineState_Saved);
10010 else
10011 setMachineState(MachineState_PoweredOff);
10012
10013 updateMachineStateOnClient();
10014 stateRestored = true;
10015
10016 /* assign the timestamp from the snapshot */
10017 Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
10018 mData->mLastStateChange = snapshotTimeStamp;
10019
10020 /* save all settings, reset the modified flag and commit. Note that we
10021 * do so even if the subtask failed (errorInSubtask=true) because we've
10022 * already committed machine data and deleted old diffs before
10023 * discarding the snapshot so there is no way to rollback */
10024 HRESULT rc2 = saveSettings(SaveS_ResetCurStateModified | saveFlags);
10025
10026 /// @todo NEWMEDIA return multiple errors
10027 if (errorInSubtask)
10028 throw rc;
10029
10030 rc = rc2;
10031
10032 if (SUCCEEDED(rc))
10033 {
10034 /* now, delete the unused diffs (only on success!) and uninit them*/
10035 for (std::list< ComObjPtr<Medium> >::const_iterator it = diffs.begin();
10036 it != diffs.end();
10037 ++it)
10038 {
10039 /// @todo for now, we ignore errors since we've already
10040 /// discarded and therefore cannot fail. Later, we may want to
10041 /// report a warning through the Progress object
10042 HRESULT rc2 = (*it)->deleteStorageAndWait();
10043 if (SUCCEEDED(rc2))
10044 (*it)->uninit();
10045 }
10046 }
10047 }
10048 catch (HRESULT aRC) { rc = aRC; }
10049
10050 if (FAILED (rc))
10051 {
10052 /* preserve existing error info */
10053 ErrorInfoKeeper eik;
10054
10055 if (!errorInSubtask)
10056 {
10057 /* undo all changes on failure unless the subtask has done so */
10058 rollback (false /* aNotify */);
10059 }
10060
10061 if (!stateRestored)
10062 {
10063 /* restore the machine state */
10064 setMachineState(aTask.state);
10065 updateMachineStateOnClient();
10066 }
10067 }
10068
10069 if (!errorInSubtask)
10070 {
10071 /* set the result (this will try to fetch current error info on failure) */
10072 aTask.progress->notifyComplete (rc);
10073 }
10074
10075 if (SUCCEEDED(rc))
10076 mParent->onSnapshotDiscarded(mData->mUuid, Guid());
10077
10078 LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc));
10079
10080 LogFlowThisFuncLeave();
10081}
10082
10083/**
10084 * Locks the attached media.
10085 *
10086 * All attached hard disks are locked for writing and DVD/floppy are locked for
10087 * reading. Parents of attached hard disks (if any) are locked for reading.
10088 *
10089 * This method also performs accessibility check of all media it locks: if some
10090 * media is inaccessible, the method will return a failure and a bunch of
10091 * extended error info objects per each inaccessible medium.
10092 *
10093 * Note that this method is atomic: if it returns a success, all media are
10094 * locked as described above; on failure no media is locked at all (all
10095 * succeeded individual locks will be undone).
10096 *
10097 * This method is intended to be called when the machine is in Starting or
10098 * Restoring state and asserts otherwise.
10099 *
10100 * The locks made by this method must be undone by calling #unlockMedia() when
10101 * no more needed.
10102 */
10103HRESULT SessionMachine::lockMedia()
10104{
10105 AutoCaller autoCaller(this);
10106 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10107
10108 AutoWriteLock alock(this);
10109
10110 AssertReturn( mData->mMachineState == MachineState_Starting
10111 || mData->mMachineState == MachineState_Restoring
10112 || mData->mMachineState == MachineState_TeleportingFrom, E_FAIL);
10113
10114 typedef std::list <ComPtr<IMedium> > MediaList;
10115 MediaList mediaToCheck;
10116 MediumState_T mediaState;
10117
10118 try
10119 {
10120 HRESULT rc = S_OK;
10121
10122 /* lock all medium objects attached to the VM */
10123 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10124 it != mMediaData->mAttachments.end();
10125 ++it)
10126 {
10127 DeviceType_T devType = (*it)->type();
10128 ComObjPtr<Medium> hd = (*it)->medium();
10129
10130 bool first = true;
10131
10132 /** @todo split out the media locking, and put it into
10133 * MediumImpl.cpp, as it needs this functionality too. */
10134 while (!hd.isNull())
10135 {
10136 if (first)
10137 {
10138 if (devType != DeviceType_DVD)
10139 {
10140 /* HardDisk and Floppy medium must be locked for writing */
10141 rc = hd->LockWrite(&mediaState);
10142 CheckComRCThrowRC(rc);
10143 }
10144 else
10145 {
10146 /* DVD medium must be locked for reading */
10147 rc = hd->LockRead(&mediaState);
10148 CheckComRCThrowRC(rc);
10149 }
10150
10151 mData->mSession.mLockedMedia.push_back (
10152 Data::Session::LockedMedia::value_type (
10153 ComPtr<IMedium> (hd), true));
10154
10155 first = false;
10156 }
10157 else
10158 {
10159 rc = hd->LockRead (&mediaState);
10160 CheckComRCThrowRC(rc);
10161
10162 mData->mSession.mLockedMedia.push_back (
10163 Data::Session::LockedMedia::value_type (
10164 ComPtr<IMedium> (hd), false));
10165 }
10166
10167 if (mediaState == MediumState_Inaccessible)
10168 mediaToCheck.push_back (ComPtr<IMedium> (hd));
10169
10170 /* no locks or callers here since there should be no way to
10171 * change the hard disk parent at this point (as it is still
10172 * attached to the machine) */
10173 hd = hd->parent();
10174 }
10175 }
10176
10177 /* SUCCEEDED locking all media, now check accessibility */
10178
10179 ErrorInfoKeeper eik (true /* aIsNull */);
10180 MultiResult mrc (S_OK);
10181
10182 /* perform a check of inaccessible media deferred above */
10183 for (MediaList::const_iterator
10184 it = mediaToCheck.begin();
10185 it != mediaToCheck.end(); ++it)
10186 {
10187 MediumState_T mediaState;
10188 rc = (*it)->COMGETTER(State) (&mediaState);
10189 CheckComRCThrowRC(rc);
10190
10191 Assert (mediaState == MediumState_LockedRead ||
10192 mediaState == MediumState_LockedWrite);
10193
10194 /* Note that we locked the medium already, so use the error
10195 * value to see if there was an accessibility failure */
10196
10197 Bstr error;
10198 rc = (*it)->COMGETTER(LastAccessError) (error.asOutParam());
10199 CheckComRCThrowRC(rc);
10200
10201 if (!error.isEmpty())
10202 {
10203 Bstr loc;
10204 rc = (*it)->COMGETTER(Location) (loc.asOutParam());
10205 CheckComRCThrowRC(rc);
10206
10207 /* collect multiple errors */
10208 eik.restore();
10209
10210 /* be in sync with MediumBase::setStateError() */
10211 Assert (!error.isEmpty());
10212 mrc = setError(E_FAIL,
10213 tr("Medium '%ls' is not accessible. %ls"),
10214 loc.raw(),
10215 error.raw());
10216
10217 eik.fetch();
10218 }
10219 }
10220
10221 eik.restore();
10222 CheckComRCThrowRC((HRESULT) mrc);
10223 }
10224 catch (HRESULT aRC)
10225 {
10226 /* Unlock all locked media on failure */
10227 unlockMedia();
10228 return aRC;
10229 }
10230
10231 return S_OK;
10232}
10233
10234/**
10235 * Undoes the locks made by by #lockMedia().
10236 */
10237void SessionMachine::unlockMedia()
10238{
10239 AutoCaller autoCaller(this);
10240 AssertComRCReturnVoid (autoCaller.rc());
10241
10242 AutoWriteLock alock(this);
10243
10244 /* we may be holding important error info on the current thread;
10245 * preserve it */
10246 ErrorInfoKeeper eik;
10247
10248 HRESULT rc = S_OK;
10249
10250 for (Data::Session::LockedMedia::const_iterator
10251 it = mData->mSession.mLockedMedia.begin();
10252 it != mData->mSession.mLockedMedia.end(); ++it)
10253 {
10254 MediumState_T state;
10255 if (it->second)
10256 rc = it->first->UnlockWrite (&state);
10257 else
10258 rc = it->first->UnlockRead (&state);
10259
10260 /* The second can happen if an object was re-locked in
10261 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
10262 * image was unmounted at runtime. */
10263 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
10264 }
10265
10266 mData->mSession.mLockedMedia.clear();
10267}
10268
10269/**
10270 * Helper to change the machine state (reimplementation).
10271 *
10272 * @note Locks this object for writing.
10273 */
10274HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
10275{
10276 LogFlowThisFuncEnter();
10277 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
10278
10279 AutoCaller autoCaller(this);
10280 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10281
10282 AutoWriteLock alock(this);
10283
10284 MachineState_T oldMachineState = mData->mMachineState;
10285
10286 AssertMsgReturn (oldMachineState != aMachineState,
10287 ("oldMachineState=%d, aMachineState=%d\n",
10288 oldMachineState, aMachineState), E_FAIL);
10289
10290 HRESULT rc = S_OK;
10291
10292 int stsFlags = 0;
10293 bool deleteSavedState = false;
10294
10295 /* detect some state transitions */
10296
10297 if ( ( oldMachineState == MachineState_Saved
10298 && aMachineState == MachineState_Restoring)
10299 || ( oldMachineState == MachineState_PoweredOff
10300 && aMachineState == MachineState_TeleportingFrom)
10301 || ( oldMachineState < MachineState_Running /* any other OFF state */
10302 && aMachineState == MachineState_Starting)
10303 )
10304 {
10305 /* The EMT thread is about to start */
10306
10307 /* Nothing to do here for now... */
10308
10309 /// @todo NEWMEDIA don't let mDVDDrive and other children
10310 /// change anything when in the Starting/Restoring state
10311 }
10312 else if ( oldMachineState >= MachineState_Running
10313 && oldMachineState != MachineState_RestoringSnapshot
10314 && oldMachineState != MachineState_DeletingSnapshot
10315 && oldMachineState != MachineState_SettingUp
10316 && aMachineState < MachineState_Running
10317 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10318 * snapshot */
10319 && ( mSnapshotData.mSnapshot.isNull()
10320 || mSnapshotData.mLastState >= MachineState_Running)
10321 )
10322 {
10323 /* The EMT thread has just stopped, unlock attached media. Note that as
10324 * opposed to locking that is done from Console, we do unlocking here
10325 * because the VM process may have aborted before having a chance to
10326 * properly unlock all media it locked. */
10327
10328 unlockMedia();
10329 }
10330
10331 if (oldMachineState == MachineState_Restoring)
10332 {
10333 if (aMachineState != MachineState_Saved)
10334 {
10335 /*
10336 * delete the saved state file once the machine has finished
10337 * restoring from it (note that Console sets the state from
10338 * Restoring to Saved if the VM couldn't restore successfully,
10339 * to give the user an ability to fix an error and retry --
10340 * we keep the saved state file in this case)
10341 */
10342 deleteSavedState = true;
10343 }
10344 }
10345 else if ( oldMachineState == MachineState_Saved
10346 && ( aMachineState == MachineState_PoweredOff
10347 || aMachineState == MachineState_Aborted)
10348 )
10349 {
10350 /*
10351 * delete the saved state after Console::DiscardSavedState() is called
10352 * or if the VM process (owning a direct VM session) crashed while the
10353 * VM was Saved
10354 */
10355
10356 /// @todo (dmik)
10357 // Not sure that deleting the saved state file just because of the
10358 // client death before it attempted to restore the VM is a good
10359 // thing. But when it crashes we need to go to the Aborted state
10360 // which cannot have the saved state file associated... The only
10361 // way to fix this is to make the Aborted condition not a VM state
10362 // but a bool flag: i.e., when a crash occurs, set it to true and
10363 // change the state to PoweredOff or Saved depending on the
10364 // saved state presence.
10365
10366 deleteSavedState = true;
10367 mData->mCurrentStateModified = TRUE;
10368 stsFlags |= SaveSTS_CurStateModified;
10369 }
10370
10371 if ( aMachineState == MachineState_Starting
10372 || aMachineState == MachineState_Restoring
10373 || aMachineState == MachineState_TeleportingFrom
10374 )
10375 {
10376 /* set the current state modified flag to indicate that the current
10377 * state is no more identical to the state in the
10378 * current snapshot */
10379 if (!mData->mCurrentSnapshot.isNull())
10380 {
10381 mData->mCurrentStateModified = TRUE;
10382 stsFlags |= SaveSTS_CurStateModified;
10383 }
10384 }
10385
10386 if (deleteSavedState)
10387 {
10388 if (mRemoveSavedState)
10389 {
10390 Assert (!mSSData->mStateFilePath.isEmpty());
10391 RTFileDelete(Utf8Str(mSSData->mStateFilePath).c_str());
10392 }
10393 mSSData->mStateFilePath.setNull();
10394 stsFlags |= SaveSTS_StateFilePath;
10395 }
10396
10397 /* redirect to the underlying peer machine */
10398 mPeer->setMachineState (aMachineState);
10399
10400 if (aMachineState == MachineState_PoweredOff ||
10401 aMachineState == MachineState_Aborted ||
10402 aMachineState == MachineState_Saved)
10403 {
10404 /* the machine has stopped execution
10405 * (or the saved state file was adopted) */
10406 stsFlags |= SaveSTS_StateTimeStamp;
10407 }
10408
10409 if ((oldMachineState == MachineState_PoweredOff ||
10410 oldMachineState == MachineState_Aborted) &&
10411 aMachineState == MachineState_Saved)
10412 {
10413 /* the saved state file was adopted */
10414 Assert (!mSSData->mStateFilePath.isNull());
10415 stsFlags |= SaveSTS_StateFilePath;
10416 }
10417
10418 rc = saveStateSettings (stsFlags);
10419
10420 if ((oldMachineState != MachineState_PoweredOff &&
10421 oldMachineState != MachineState_Aborted) &&
10422 (aMachineState == MachineState_PoweredOff ||
10423 aMachineState == MachineState_Aborted))
10424 {
10425 /* we've been shut down for any reason */
10426 /* no special action so far */
10427 }
10428
10429 LogFlowThisFunc(("rc=%08X\n", rc));
10430 LogFlowThisFuncLeave();
10431 return rc;
10432}
10433
10434/**
10435 * Sends the current machine state value to the VM process.
10436 *
10437 * @note Locks this object for reading, then calls a client process.
10438 */
10439HRESULT SessionMachine::updateMachineStateOnClient()
10440{
10441 AutoCaller autoCaller(this);
10442 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10443
10444 ComPtr<IInternalSessionControl> directControl;
10445 {
10446 AutoReadLock alock(this);
10447 AssertReturn(!!mData, E_FAIL);
10448 directControl = mData->mSession.mDirectControl;
10449
10450 /* directControl may be already set to NULL here in #OnSessionEnd()
10451 * called too early by the direct session process while there is still
10452 * some operation (like discarding the snapshot) in progress. The client
10453 * process in this case is waiting inside Session::close() for the
10454 * "end session" process object to complete, while #uninit() called by
10455 * #checkForDeath() on the Watcher thread is waiting for the pending
10456 * operation to complete. For now, we accept this inconsitent behavior
10457 * and simply do nothing here. */
10458
10459 if (mData->mSession.mState == SessionState_Closing)
10460 return S_OK;
10461
10462 AssertReturn(!directControl.isNull(), E_FAIL);
10463 }
10464
10465 return directControl->UpdateMachineState (mData->mMachineState);
10466}
10467
10468/* static */
10469DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD /* thread */, void *pvUser)
10470{
10471 AssertReturn(pvUser, VERR_INVALID_POINTER);
10472
10473 Task *task = static_cast <Task *> (pvUser);
10474 task->handler();
10475
10476 // it's our responsibility to delete the task
10477 delete task;
10478
10479 return 0;
10480}
10481
10482/////////////////////////////////////////////////////////////////////////////
10483// SnapshotMachine class
10484/////////////////////////////////////////////////////////////////////////////
10485
10486DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
10487
10488HRESULT SnapshotMachine::FinalConstruct()
10489{
10490 LogFlowThisFunc(("\n"));
10491
10492 /* set the proper type to indicate we're the SnapshotMachine instance */
10493 unconst(mType) = IsSnapshotMachine;
10494
10495 return S_OK;
10496}
10497
10498void SnapshotMachine::FinalRelease()
10499{
10500 LogFlowThisFunc(("\n"));
10501
10502 uninit();
10503}
10504
10505/**
10506 * Initializes the SnapshotMachine object when taking a snapshot.
10507 *
10508 * @param aSessionMachine machine to take a snapshot from
10509 * @param aSnapshotId snapshot ID of this snapshot machine
10510 * @param aStateFilePath file where the execution state will be later saved
10511 * (or NULL for the offline snapshot)
10512 *
10513 * @note The aSessionMachine must be locked for writing.
10514 */
10515HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
10516 IN_GUID aSnapshotId,
10517 const Utf8Str &aStateFilePath)
10518{
10519 LogFlowThisFuncEnter();
10520 LogFlowThisFunc(("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
10521
10522 AssertReturn(aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
10523
10524 /* Enclose the state transition NotReady->InInit->Ready */
10525 AutoInitSpan autoInitSpan(this);
10526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
10527
10528 AssertReturn(aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
10529
10530 mSnapshotId = aSnapshotId;
10531
10532 /* memorize the primary Machine instance (i.e. not SessionMachine!) */
10533 unconst(mPeer) = aSessionMachine->mPeer;
10534 /* share the parent pointer */
10535 unconst(mParent) = mPeer->mParent;
10536
10537 /* take the pointer to Data to share */
10538 mData.share (mPeer->mData);
10539
10540 /* take the pointer to UserData to share (our UserData must always be the
10541 * same as Machine's data) */
10542 mUserData.share (mPeer->mUserData);
10543 /* make a private copy of all other data (recent changes from SessionMachine) */
10544 mHWData.attachCopy (aSessionMachine->mHWData);
10545 mMediaData.attachCopy(aSessionMachine->mMediaData);
10546
10547 /* SSData is always unique for SnapshotMachine */
10548 mSSData.allocate();
10549 mSSData->mStateFilePath = aStateFilePath;
10550
10551 HRESULT rc = S_OK;
10552
10553 /* create copies of all shared folders (mHWData after attiching a copy
10554 * contains just references to original objects) */
10555 for (HWData::SharedFolderList::iterator
10556 it = mHWData->mSharedFolders.begin();
10557 it != mHWData->mSharedFolders.end();
10558 ++it)
10559 {
10560 ComObjPtr<SharedFolder> folder;
10561 folder.createObject();
10562 rc = folder->initCopy (this, *it);
10563 CheckComRCReturnRC(rc);
10564 *it = folder;
10565 }
10566
10567 /* associate hard disks with the snapshot
10568 * (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
10569 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10570 it != mMediaData->mAttachments.end();
10571 ++it)
10572 {
10573 MediumAttachment *pAtt = *it;
10574 Medium *pMedium = pAtt->medium();
10575 if (pMedium) // can be NULL for non-harddisk
10576 {
10577 rc = pMedium->attachTo(mData->mUuid, mSnapshotId);
10578 AssertComRC(rc);
10579 }
10580 }
10581
10582 /* create copies of all storage controllers (mStorageControllerData
10583 * after attaching a copy contains just references to original objects) */
10584 mStorageControllers.allocate();
10585 for (StorageControllerList::const_iterator
10586 it = aSessionMachine->mStorageControllers->begin();
10587 it != aSessionMachine->mStorageControllers->end();
10588 ++it)
10589 {
10590 ComObjPtr<StorageController> ctrl;
10591 ctrl.createObject();
10592 ctrl->initCopy (this, *it);
10593 mStorageControllers->push_back(ctrl);
10594 }
10595
10596 /* create all other child objects that will be immutable private copies */
10597
10598 unconst(mBIOSSettings).createObject();
10599 mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
10600
10601#ifdef VBOX_WITH_VRDP
10602 unconst(mVRDPServer).createObject();
10603 mVRDPServer->initCopy (this, mPeer->mVRDPServer);
10604#endif
10605
10606 unconst(mAudioAdapter).createObject();
10607 mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
10608
10609 unconst(mUSBController).createObject();
10610 mUSBController->initCopy (this, mPeer->mUSBController);
10611
10612 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
10613 {
10614 unconst(mNetworkAdapters [slot]).createObject();
10615 mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
10616 }
10617
10618 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
10619 {
10620 unconst(mSerialPorts [slot]).createObject();
10621 mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
10622 }
10623
10624 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
10625 {
10626 unconst(mParallelPorts [slot]).createObject();
10627 mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
10628 }
10629
10630 /* Confirm a successful initialization when it's the case */
10631 autoInitSpan.setSucceeded();
10632
10633 LogFlowThisFuncLeave();
10634 return S_OK;
10635}
10636
10637/**
10638 * Initializes the SnapshotMachine object when loading from the settings file.
10639 *
10640 * @param aMachine machine the snapshot belngs to
10641 * @param aHWNode <Hardware> node
10642 * @param aHDAsNode <HardDiskAttachments> node
10643 * @param aSnapshotId snapshot ID of this snapshot machine
10644 * @param aStateFilePath file where the execution state is saved
10645 * (or NULL for the offline snapshot)
10646 *
10647 * @note Doesn't lock anything.
10648 */
10649HRESULT SnapshotMachine::init(Machine *aMachine,
10650 const settings::Hardware &hardware,
10651 const settings::Storage &storage,
10652 IN_GUID aSnapshotId,
10653 const Utf8Str &aStateFilePath)
10654{
10655 LogFlowThisFuncEnter();
10656 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
10657
10658 AssertReturn(aMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
10659
10660 /* Enclose the state transition NotReady->InInit->Ready */
10661 AutoInitSpan autoInitSpan(this);
10662 AssertReturn(autoInitSpan.isOk(), E_FAIL);
10663
10664 /* Don't need to lock aMachine when VirtualBox is starting up */
10665
10666 mSnapshotId = aSnapshotId;
10667
10668 /* memorize the primary Machine instance */
10669 unconst(mPeer) = aMachine;
10670 /* share the parent pointer */
10671 unconst(mParent) = mPeer->mParent;
10672
10673 /* take the pointer to Data to share */
10674 mData.share (mPeer->mData);
10675 /*
10676 * take the pointer to UserData to share
10677 * (our UserData must always be the same as Machine's data)
10678 */
10679 mUserData.share (mPeer->mUserData);
10680 /* allocate private copies of all other data (will be loaded from settings) */
10681 mHWData.allocate();
10682 mMediaData.allocate();
10683 mStorageControllers.allocate();
10684
10685 /* SSData is always unique for SnapshotMachine */
10686 mSSData.allocate();
10687 mSSData->mStateFilePath = aStateFilePath;
10688
10689 /* create all other child objects that will be immutable private copies */
10690
10691 unconst(mBIOSSettings).createObject();
10692 mBIOSSettings->init (this);
10693
10694#ifdef VBOX_WITH_VRDP
10695 unconst(mVRDPServer).createObject();
10696 mVRDPServer->init (this);
10697#endif
10698
10699 unconst(mAudioAdapter).createObject();
10700 mAudioAdapter->init (this);
10701
10702 unconst(mUSBController).createObject();
10703 mUSBController->init (this);
10704
10705 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
10706 {
10707 unconst(mNetworkAdapters [slot]).createObject();
10708 mNetworkAdapters [slot]->init (this, slot);
10709 }
10710
10711 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
10712 {
10713 unconst(mSerialPorts [slot]).createObject();
10714 mSerialPorts [slot]->init (this, slot);
10715 }
10716
10717 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
10718 {
10719 unconst(mParallelPorts [slot]).createObject();
10720 mParallelPorts [slot]->init (this, slot);
10721 }
10722
10723 /* load hardware and harddisk settings */
10724
10725 HRESULT rc = loadHardware(hardware);
10726 if (SUCCEEDED(rc))
10727 rc = loadStorageControllers(storage, true /* aRegistered */, &mSnapshotId);
10728
10729 if (SUCCEEDED(rc))
10730 /* commit all changes made during the initialization */
10731 commit();
10732
10733 /* Confirm a successful initialization when it's the case */
10734 if (SUCCEEDED(rc))
10735 autoInitSpan.setSucceeded();
10736
10737 LogFlowThisFuncLeave();
10738 return rc;
10739}
10740
10741/**
10742 * Uninitializes this SnapshotMachine object.
10743 */
10744void SnapshotMachine::uninit()
10745{
10746 LogFlowThisFuncEnter();
10747
10748 /* Enclose the state transition Ready->InUninit->NotReady */
10749 AutoUninitSpan autoUninitSpan(this);
10750 if (autoUninitSpan.uninitDone())
10751 return;
10752
10753 uninitDataAndChildObjects();
10754
10755 /* free the essential data structure last */
10756 mData.free();
10757
10758 unconst(mParent).setNull();
10759 unconst(mPeer).setNull();
10760
10761 LogFlowThisFuncLeave();
10762}
10763
10764// util::Lockable interface
10765////////////////////////////////////////////////////////////////////////////////
10766
10767/**
10768 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
10769 * with the primary Machine instance (mPeer).
10770 */
10771RWLockHandle *SnapshotMachine::lockHandle() const
10772{
10773 AssertReturn(!mPeer.isNull(), NULL);
10774 return mPeer->lockHandle();
10775}
10776
10777// public methods only for internal purposes
10778////////////////////////////////////////////////////////////////////////////////
10779
10780/**
10781 * Called by the snapshot object associated with this SnapshotMachine when
10782 * snapshot data such as name or description is changed.
10783 *
10784 * @note Locks this object for writing.
10785 */
10786HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
10787{
10788 AutoWriteLock alock(this);
10789
10790 // mPeer->saveAllSnapshots(); @todo
10791
10792 /* inform callbacks */
10793 mParent->onSnapshotChange(mData->mUuid, aSnapshot->getId());
10794
10795 return S_OK;
10796}
10797/* 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