VirtualBox

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

Last change on this file since 23633 was 23600, checked in by vboxsync, 15 years ago

Main: three new Machine settings (not enabled).

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