VirtualBox

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

Last change on this file since 5320 was 5292, checked in by vboxsync, 17 years ago

API change to allow VM to be created with predefined UUID.

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