VirtualBox

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

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

Added option to emulate PIIX4 IDE controller. Defaults to PIIX4 for new VMs
and to PIIX3 when no setting is present in XML.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 347.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 /* IDE controller type (optional, defaults to PIIX3) */
4416 {
4417 CFGNODE ideControllerNode = 0;
4418 IDEControllerType_T controllerType = IDEControllerType_IDEControllerPIIX3;
4419 CFGLDRGetChildNode (biosNode, "IDEController", 0, &ideControllerNode);
4420 if (ideControllerNode)
4421 {
4422 Bstr IDEControllerType;
4423
4424 CFGLDRQueryBSTR (ideControllerNode, "type", IDEControllerType.asOutParam());
4425 ComAssertBreak (IDEControllerType, rc = E_FAIL);
4426
4427 if (IDEControllerType.compare(Bstr("PIIX3")) == 0)
4428 controllerType = IDEControllerType_IDEControllerPIIX3;
4429 else if (IDEControllerType.compare(Bstr("PIIX4")) == 0)
4430 controllerType = IDEControllerType_IDEControllerPIIX4;
4431 else
4432 ComAssertBreak (0, rc = E_FAIL);
4433
4434 CFGLDRReleaseNode (ideControllerNode);
4435 }
4436 mBIOSSettings->COMSETTER(IDEControllerType)(controllerType);
4437 }
4438
4439 }
4440 while (0);
4441
4442 CFGLDRReleaseNode (biosNode);
4443 if (FAILED (rc))
4444 return rc;
4445 }
4446
4447 /* DVD drive (contains either Image or HostDrive or nothing) */
4448 /// @todo (dmik) move the code to DVDDrive
4449 {
4450 HRESULT rc = S_OK;
4451
4452 CFGNODE dvdDriveNode = 0;
4453 CFGLDRGetChildNode (aNode, "DVDDrive", 0, &dvdDriveNode);
4454 ComAssertRet (dvdDriveNode, E_FAIL);
4455
4456 bool fPassthrough;
4457 CFGLDRQueryBool(dvdDriveNode, "passthrough", &fPassthrough);
4458 mDVDDrive->COMSETTER(Passthrough)(fPassthrough);
4459
4460 CFGNODE typeNode = 0;
4461
4462 do
4463 {
4464 CFGLDRGetChildNode (dvdDriveNode, "Image", 0, &typeNode);
4465 if (typeNode)
4466 {
4467 Guid uuid;
4468 CFGLDRQueryUUID (typeNode, "uuid", uuid.ptr());
4469 rc = mDVDDrive->MountImage (uuid);
4470 }
4471 else
4472 {
4473 CFGLDRGetChildNode (dvdDriveNode, "HostDrive", 0, &typeNode);
4474 if (typeNode)
4475 {
4476 Bstr src;
4477 CFGLDRQueryBSTR (typeNode, "src", src.asOutParam());
4478
4479 /* find the correspoding object */
4480 ComPtr <IHost> host;
4481 rc = mParent->COMGETTER(Host) (host.asOutParam());
4482 ComAssertComRCBreak (rc, rc = rc);
4483
4484 ComPtr <IHostDVDDriveCollection> coll;
4485 rc = host->COMGETTER(DVDDrives) (coll.asOutParam());
4486 ComAssertComRCBreak (rc, rc = rc);
4487
4488 ComPtr <IHostDVDDrive> drive;
4489 rc = coll->FindByName (src, drive.asOutParam());
4490 if (SUCCEEDED (rc))
4491 rc = mDVDDrive->CaptureHostDrive (drive);
4492 else if (rc == E_INVALIDARG)
4493 {
4494 /* the host DVD drive is not currently available. we
4495 * assume it will be available later and create an
4496 * extra object now */
4497 ComObjPtr <HostDVDDrive> hostDrive;
4498 hostDrive.createObject();
4499 rc = hostDrive->init (src);
4500 ComAssertComRCBreak (rc, rc = rc);
4501 rc = mDVDDrive->CaptureHostDrive (hostDrive);
4502 }
4503 else
4504 ComAssertComRCBreakRC (rc);
4505 }
4506 }
4507 }
4508 while (0);
4509
4510 if (typeNode)
4511 CFGLDRReleaseNode (typeNode);
4512 CFGLDRReleaseNode (dvdDriveNode);
4513
4514 if (FAILED (rc))
4515 return rc;
4516 }
4517
4518 /* Floppy drive (contains either Image or HostDrive or nothing) */
4519 /// @todo (dmik) move the code to FloppyDrive
4520 {
4521 HRESULT rc = S_OK;
4522
4523 CFGNODE driveNode = 0;
4524 CFGLDRGetChildNode (aNode, "FloppyDrive", 0, &driveNode);
4525 ComAssertRet (driveNode, E_FAIL);
4526
4527 BOOL fFloppyEnabled = TRUE;
4528 CFGLDRQueryBool (driveNode, "enabled", (bool*)&fFloppyEnabled);
4529 rc = mFloppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
4530
4531 CFGNODE typeNode = 0;
4532 do
4533 {
4534 CFGLDRGetChildNode (driveNode, "Image", 0, &typeNode);
4535 if (typeNode)
4536 {
4537 Guid uuid;
4538 CFGLDRQueryUUID (typeNode, "uuid", uuid.ptr());
4539 rc = mFloppyDrive->MountImage (uuid);
4540 }
4541 else
4542 {
4543 CFGLDRGetChildNode (driveNode, "HostDrive", 0, &typeNode);
4544 if (typeNode)
4545 {
4546 Bstr src;
4547 CFGLDRQueryBSTR (typeNode, "src", src.asOutParam());
4548
4549 /* find the correspoding object */
4550 ComPtr <IHost> host;
4551 rc = mParent->COMGETTER(Host) (host.asOutParam());
4552 ComAssertComRCBreak (rc, rc = rc);
4553
4554 ComPtr <IHostFloppyDriveCollection> coll;
4555 rc = host->COMGETTER(FloppyDrives) (coll.asOutParam());
4556 ComAssertComRCBreak (rc, rc = rc);
4557
4558 ComPtr <IHostFloppyDrive> drive;
4559 rc = coll->FindByName (src, drive.asOutParam());
4560 if (SUCCEEDED (rc))
4561 rc = mFloppyDrive->CaptureHostDrive (drive);
4562 else if (rc == E_INVALIDARG)
4563 {
4564 /* the host Floppy drive is not currently available. we
4565 * assume it will be available later and create an
4566 * extra object now */
4567 ComObjPtr <HostFloppyDrive> hostDrive;
4568 hostDrive.createObject();
4569 rc = hostDrive->init (src);
4570 ComAssertComRCBreak (rc, rc = rc);
4571 rc = mFloppyDrive->CaptureHostDrive (hostDrive);
4572 }
4573 else
4574 ComAssertComRCBreakRC (rc);
4575 }
4576 }
4577 }
4578 while (0);
4579
4580 if (typeNode)
4581 CFGLDRReleaseNode (typeNode);
4582 CFGLDRReleaseNode (driveNode);
4583
4584 CheckComRCReturnRC (rc);
4585 }
4586
4587 /* USB Controller */
4588 {
4589 HRESULT rc = mUSBController->loadSettings (aNode);
4590 CheckComRCReturnRC (rc);
4591 }
4592
4593 /* Network node (required) */
4594 /// @todo (dmik) move the code to NetworkAdapter
4595 {
4596 /* we assume that all network adapters are initially disabled
4597 * and detached */
4598
4599 CFGNODE networkNode = 0;
4600 CFGLDRGetChildNode (aNode, "Network", 0, &networkNode);
4601 ComAssertRet (networkNode, E_FAIL);
4602
4603 HRESULT rc = S_OK;
4604
4605 unsigned cAdapters = 0;
4606 CFGLDRCountChildren (networkNode, "Adapter", &cAdapters);
4607 for (unsigned i = 0; i < cAdapters; i++)
4608 {
4609 CFGNODE adapterNode = 0;
4610 CFGLDRGetChildNode (networkNode, "Adapter", i, &adapterNode);
4611 ComAssertBreak (adapterNode, rc = E_FAIL);
4612
4613 /* slot number (required) */
4614 /* slot unicity is guaranteed by XML Schema */
4615 uint32_t slot = 0;
4616 CFGLDRQueryUInt32 (adapterNode, "slot", &slot);
4617 Assert (slot < ELEMENTS (mNetworkAdapters));
4618
4619 /* type */
4620 Bstr adapterType;
4621 CFGLDRQueryBSTR (adapterNode, "type", adapterType.asOutParam());
4622 ComAssertBreak (adapterType, rc = E_FAIL);
4623
4624 /* enabled (required) */
4625 bool enabled = false;
4626 CFGLDRQueryBool (adapterNode, "enabled", &enabled);
4627 /* MAC address (can be null) */
4628 Bstr macAddr;
4629 CFGLDRQueryBSTR (adapterNode, "MACAddress", macAddr.asOutParam());
4630 /* cable (required) */
4631 bool cableConnected;
4632 CFGLDRQueryBool (adapterNode, "cable", &cableConnected);
4633 /* line speed (defaults to 100 Mbps) */
4634 uint32_t lineSpeed = 100000;
4635 CFGLDRQueryUInt32 (adapterNode, "speed", &lineSpeed);
4636 /* tracing (defaults to false) */
4637 bool traceEnabled;
4638 CFGLDRQueryBool (adapterNode, "trace", &traceEnabled);
4639 Bstr traceFile;
4640 CFGLDRQueryBSTR (adapterNode, "tracefile", traceFile.asOutParam());
4641
4642 rc = mNetworkAdapters [slot]->COMSETTER(Enabled) (enabled);
4643 CheckComRCBreakRC (rc);
4644 rc = mNetworkAdapters [slot]->COMSETTER(MACAddress) (macAddr);
4645 CheckComRCBreakRC (rc);
4646 rc = mNetworkAdapters [slot]->COMSETTER(CableConnected) (cableConnected);
4647 CheckComRCBreakRC (rc);
4648 rc = mNetworkAdapters [slot]->COMSETTER(LineSpeed) (lineSpeed);
4649 CheckComRCBreakRC (rc);
4650 rc = mNetworkAdapters [slot]->COMSETTER(TraceEnabled) (traceEnabled);
4651 CheckComRCBreakRC (rc);
4652 rc = mNetworkAdapters [slot]->COMSETTER(TraceFile) (traceFile);
4653 CheckComRCBreakRC (rc);
4654
4655 if (adapterType.compare(Bstr("Am79C970A")) == 0)
4656 mNetworkAdapters [slot]->COMSETTER(AdapterType)(NetworkAdapterType_NetworkAdapterAm79C970A);
4657 else if (adapterType.compare(Bstr("Am79C973")) == 0)
4658 mNetworkAdapters [slot]->COMSETTER(AdapterType)(NetworkAdapterType_NetworkAdapterAm79C973);
4659 else
4660 ComAssertBreak (0, rc = E_FAIL);
4661
4662 CFGNODE attachmentNode = 0;
4663 if (CFGLDRGetChildNode (adapterNode, "NAT", 0, &attachmentNode), attachmentNode)
4664 {
4665 rc = mNetworkAdapters [slot]->AttachToNAT();
4666 CheckComRCBreakRC (rc);
4667 }
4668 else
4669 if (CFGLDRGetChildNode (adapterNode, "HostInterface", 0, &attachmentNode), attachmentNode)
4670 {
4671 /* Host Interface Networking */
4672 Bstr name;
4673 CFGLDRQueryBSTR (attachmentNode, "name", name.asOutParam());
4674#ifdef RT_OS_WINDOWS
4675 /* @name can be empty on Win32, but not null */
4676 ComAssertBreak (!name.isNull(), rc = E_FAIL);
4677#endif
4678 rc = mNetworkAdapters [slot]->COMSETTER(HostInterface) (name);
4679 CheckComRCBreakRC (rc);
4680#ifdef VBOX_WITH_UNIXY_TAP_NETWORKING
4681 Bstr tapSetupApp;
4682 CFGLDRQueryBSTR (attachmentNode, "TAPSetup", tapSetupApp.asOutParam());
4683 Bstr tapTerminateApp;
4684 CFGLDRQueryBSTR (attachmentNode, "TAPTerminate", tapTerminateApp.asOutParam());
4685
4686 rc = mNetworkAdapters [slot]->COMSETTER(TAPSetupApplication) (tapSetupApp);
4687 CheckComRCBreakRC (rc);
4688 rc = mNetworkAdapters [slot]->COMSETTER(TAPTerminateApplication) (tapTerminateApp);
4689 CheckComRCBreakRC (rc);
4690#endif // VBOX_WITH_UNIXY_TAP_NETWORKING
4691 rc = mNetworkAdapters [slot]->AttachToHostInterface();
4692 CheckComRCBreakRC (rc);
4693 }
4694 else
4695 if (CFGLDRGetChildNode(adapterNode, "InternalNetwork", 0, &attachmentNode), attachmentNode)
4696 {
4697 /* Internal Networking */
4698 Bstr name;
4699 CFGLDRQueryBSTR (attachmentNode, "name", name.asOutParam());
4700 ComAssertBreak (!name.isNull(), rc = E_FAIL);
4701 rc = mNetworkAdapters[slot]->AttachToInternalNetwork();
4702 CheckComRCBreakRC (rc);
4703 rc = mNetworkAdapters[slot]->COMSETTER(InternalNetwork) (name);
4704 CheckComRCBreakRC (rc);
4705 }
4706 else
4707 {
4708 /* Adapter has no children */
4709 rc = mNetworkAdapters [slot]->Detach();
4710 CheckComRCBreakRC (rc);
4711 }
4712 if (attachmentNode)
4713 CFGLDRReleaseNode (attachmentNode);
4714
4715 CFGLDRReleaseNode (adapterNode);
4716 }
4717
4718 CFGLDRReleaseNode (networkNode);
4719 CheckComRCReturnRC (rc);
4720 }
4721
4722 /* Serial node (optional) */
4723 CFGNODE serialNode = 0;
4724 CFGLDRGetChildNode (aNode, "Uart", 0, &serialNode);
4725 if (serialNode)
4726 {
4727 HRESULT rc = S_OK;
4728 unsigned cPorts = 0;
4729 CFGLDRCountChildren (serialNode, "Port", &cPorts);
4730 for (unsigned slot = 0; slot < cPorts; slot++)
4731 {
4732 rc = mSerialPorts [slot]->loadSettings (serialNode, slot);
4733 CheckComRCBreakRC (rc);
4734 }
4735
4736 CFGLDRReleaseNode (serialNode);
4737 CheckComRCReturnRC (rc);
4738 }
4739
4740 /* Parallel node (optional) */
4741 CFGNODE parallelNode = 0;
4742 CFGLDRGetChildNode (aNode, "Lpt", 0, &parallelNode);
4743 if (parallelNode)
4744 {
4745 HRESULT rc = S_OK;
4746 unsigned cPorts = 0;
4747 CFGLDRCountChildren (parallelNode, "Port", &cPorts);
4748 for (unsigned slot = 0; slot < cPorts; slot++)
4749 {
4750 rc = mParallelPorts [slot]->loadSettings (parallelNode, slot);
4751 CheckComRCBreakRC (rc);
4752 }
4753
4754 CFGLDRReleaseNode (parallelNode);
4755 CheckComRCReturnRC (rc);
4756 }
4757
4758 /* AudioAdapter node (required) */
4759 /// @todo (dmik) move the code to AudioAdapter
4760 {
4761 CFGNODE audioAdapterNode = 0;
4762 CFGLDRGetChildNode (aNode, "AudioAdapter", 0, &audioAdapterNode);
4763 ComAssertRet (audioAdapterNode, E_FAIL);
4764
4765 /* is the adapter enabled? */
4766 bool enabled = false;
4767 CFGLDRQueryBool (audioAdapterNode, "enabled", &enabled);
4768 mAudioAdapter->COMSETTER(Enabled) (enabled);
4769 /* now check the audio driver */
4770 Bstr driver;
4771 CFGLDRQueryBSTR (audioAdapterNode, "driver", driver.asOutParam());
4772 AudioDriverType_T audioDriver;
4773 audioDriver = AudioDriverType_NullAudioDriver;
4774 if (driver == L"null")
4775 ; /* Null has been set above */
4776#ifdef RT_OS_WINDOWS
4777 else if (driver == L"winmm")
4778#ifdef VBOX_WITH_WINMM
4779 audioDriver = AudioDriverType_WINMMAudioDriver;
4780#else
4781 /* fall back to dsound */
4782 audioDriver = AudioDriverType_DSOUNDAudioDriver;
4783#endif
4784 else if (driver == L"dsound")
4785 audioDriver = AudioDriverType_DSOUNDAudioDriver;
4786#endif // RT_OS_WINDOWS
4787#ifdef RT_OS_LINUX
4788 else if (driver == L"oss")
4789 audioDriver = AudioDriverType_OSSAudioDriver;
4790 else if (driver == L"alsa")
4791#ifdef VBOX_WITH_ALSA
4792 audioDriver = AudioDriverType_ALSAAudioDriver;
4793#else
4794 /* fall back to OSS */
4795 audioDriver = AudioDriverType_OSSAudioDriver;
4796#endif
4797#endif // RT_OS_LINUX
4798#ifdef RT_OS_DARWIN
4799 else if (driver == L"coreaudio")
4800 audioDriver = AudioDriverType_CoreAudioDriver;
4801#endif
4802#ifdef RT_OS_OS2
4803 else if (driver == L"mmpm")
4804 audioDriver = AudioDriverType_MMPMAudioDriver;
4805#endif
4806 else
4807 AssertMsgFailed (("Invalid driver: %ls\n", driver.raw()));
4808
4809 HRESULT rc = mAudioAdapter->COMSETTER(AudioDriver) (audioDriver);
4810
4811 CFGLDRReleaseNode (audioAdapterNode);
4812 CheckComRCReturnRC (rc);
4813 }
4814
4815 /* Shared folders (optional) */
4816 /// @todo (dmik) make required on next format change!
4817 do
4818 {
4819 CFGNODE sharedFoldersNode = 0;
4820 CFGLDRGetChildNode (aNode, "SharedFolders", 0, &sharedFoldersNode);
4821
4822 if (!sharedFoldersNode)
4823 break;
4824
4825 HRESULT rc = S_OK;
4826
4827 unsigned cFolders = 0;
4828 CFGLDRCountChildren (sharedFoldersNode, "SharedFolder", &cFolders);
4829
4830 for (unsigned i = 0; i < cFolders; i++)
4831 {
4832 CFGNODE folderNode = 0;
4833 CFGLDRGetChildNode (sharedFoldersNode, "SharedFolder", i, &folderNode);
4834 ComAssertBreak (folderNode, rc = E_FAIL);
4835
4836 // folder logical name (required)
4837 Bstr name;
4838 CFGLDRQueryBSTR (folderNode, "name", name.asOutParam());
4839
4840 // folder host path (required)
4841 Bstr hostPath;
4842 CFGLDRQueryBSTR (folderNode, "hostPath", hostPath.asOutParam());
4843
4844 rc = CreateSharedFolder (name, hostPath);
4845 CheckComRCBreakRC (rc);
4846
4847 CFGLDRReleaseNode (folderNode);
4848 }
4849
4850 CFGLDRReleaseNode (sharedFoldersNode);
4851 CheckComRCReturnRC (rc);
4852 }
4853 while (0);
4854
4855 /* Clipboard node (currently not required) */
4856 /// @todo (dmik) make required on next format change!
4857 {
4858 /* default value in case the node is not there */
4859 mHWData->mClipboardMode = ClipboardMode_ClipDisabled;
4860
4861 CFGNODE clipNode = 0;
4862 CFGLDRGetChildNode (aNode, "Clipboard", 0, &clipNode);
4863 if (clipNode)
4864 {
4865 Bstr mode;
4866 CFGLDRQueryBSTR (clipNode, "mode", mode.asOutParam());
4867 if (mode == L"Disabled")
4868 mHWData->mClipboardMode = ClipboardMode_ClipDisabled;
4869 else if (mode == L"HostToGuest")
4870 mHWData->mClipboardMode = ClipboardMode_ClipHostToGuest;
4871 else if (mode == L"GuestToHost")
4872 mHWData->mClipboardMode = ClipboardMode_ClipGuestToHost;
4873 else if (mode == L"Bidirectional")
4874 mHWData->mClipboardMode = ClipboardMode_ClipBidirectional;
4875 else
4876 AssertMsgFailed (("%ls clipboard mode is invalid\n", mode.raw()));
4877 CFGLDRReleaseNode (clipNode);
4878 }
4879 }
4880
4881 /* Guest node (optional) */
4882 /// @todo (dmik) make required on next format change!
4883 {
4884 CFGNODE guestNode = 0;
4885 CFGLDRGetChildNode (aNode, "Guest", 0, &guestNode);
4886 if (guestNode)
4887 {
4888 uint32_t memoryBalloonSize = 0;
4889 CFGLDRQueryUInt32 (guestNode, "MemoryBalloonSize",
4890 &memoryBalloonSize);
4891 mHWData->mMemoryBalloonSize = memoryBalloonSize;
4892
4893 uint32_t statisticsUpdateInterval = 0;
4894 CFGLDRQueryUInt32 (guestNode, "StatisticsUpdateInterval",
4895 &statisticsUpdateInterval);
4896 mHWData->mStatisticsUpdateInterval = statisticsUpdateInterval;
4897
4898 CFGLDRReleaseNode (guestNode);
4899 }
4900 }
4901
4902 return S_OK;
4903}
4904
4905/**
4906 * @param aNode <HardDiskAttachments> node
4907 * @param aRegistered true when the machine is being loaded on VirtualBox
4908 * startup, or when a snapshot is being loaded (wchich
4909 * currently can happen on startup only)
4910 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
4911 */
4912HRESULT Machine::loadHardDisks (CFGNODE aNode, bool aRegistered,
4913 const Guid *aSnapshotId /* = NULL */)
4914{
4915 AssertReturn (aNode, E_INVALIDARG);
4916 AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
4917 (mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
4918
4919 HRESULT rc = S_OK;
4920
4921 unsigned cbDisks = 0;
4922 CFGLDRCountChildren (aNode, "HardDiskAttachment", &cbDisks);
4923
4924 if (!aRegistered && cbDisks > 0)
4925 {
4926 /* when the machine is being loaded (opened) from a file, it cannot
4927 * have hard disks attached (this should not happen normally,
4928 * because we don't allow to attach hard disks to an unregistered
4929 * VM at all */
4930 return setError (E_FAIL,
4931 tr ("Unregistered machine '%ls' cannot have hard disks attached "
4932 "(found %d hard disk attachments)"),
4933 mUserData->mName.raw(), cbDisks);
4934 }
4935
4936 for (unsigned i = 0; i < cbDisks && SUCCEEDED (rc); ++ i)
4937 {
4938 CFGNODE hdNode;
4939 CFGLDRGetChildNode (aNode, "HardDiskAttachment", i, &hdNode);
4940 ComAssertRet (hdNode, E_FAIL);
4941
4942 do
4943 {
4944 /* hardDisk uuid (required) */
4945 Guid uuid;
4946 CFGLDRQueryUUID (hdNode, "hardDisk", uuid.ptr());
4947 /* bus (controller) type (required) */
4948 Bstr bus;
4949 CFGLDRQueryBSTR (hdNode, "bus", bus.asOutParam());
4950 /* device (required) */
4951 Bstr device;
4952 CFGLDRQueryBSTR (hdNode, "device", device.asOutParam());
4953
4954 /* find a hard disk by UUID */
4955 ComObjPtr <HardDisk> hd;
4956 rc = mParent->getHardDisk (uuid, hd);
4957 if (FAILED (rc))
4958 break;
4959
4960 AutoLock hdLock (hd);
4961
4962 if (!hd->machineId().isEmpty())
4963 {
4964 rc = setError (E_FAIL,
4965 tr ("Hard disk '%ls' with UUID {%s} is already "
4966 "attached to a machine with UUID {%s} (see '%ls')"),
4967 hd->toString().raw(), uuid.toString().raw(),
4968 hd->machineId().toString().raw(),
4969 mData->mConfigFileFull.raw());
4970 break;
4971 }
4972
4973 if (hd->type() == HardDiskType_ImmutableHardDisk)
4974 {
4975 rc = setError (E_FAIL,
4976 tr ("Immutable hard disk '%ls' with UUID {%s} cannot be "
4977 "directly attached to a machine (see '%ls')"),
4978 hd->toString().raw(), uuid.toString().raw(),
4979 mData->mConfigFileFull.raw());
4980 break;
4981 }
4982
4983 /* attach the device */
4984 DiskControllerType_T ctl = DiskControllerType_InvalidController;
4985 LONG dev = -1;
4986
4987 if (bus == L"ide0")
4988 {
4989 ctl = DiskControllerType_IDE0Controller;
4990 if (device == L"master")
4991 dev = 0;
4992 else if (device == L"slave")
4993 dev = 1;
4994 else
4995 ComAssertMsgFailedBreak (("Invalid device: %ls\n", device.raw()),
4996 rc = E_FAIL);
4997 }
4998 else if (bus == L"ide1")
4999 {
5000 ctl = DiskControllerType_IDE1Controller;
5001 if (device == L"master")
5002 rc = setError (E_FAIL, tr("Could not attach a disk as a master "
5003 "device on the secondary controller"));
5004 else if (device == L"slave")
5005 dev = 1;
5006 else
5007 ComAssertMsgFailedBreak (("Invalid device: %ls\n", device.raw()),
5008 rc = E_FAIL);
5009 }
5010 else
5011 ComAssertMsgFailedBreak (("Invalid bus: %ls\n", bus.raw()),
5012 rc = E_FAIL);
5013
5014 ComObjPtr <HardDiskAttachment> attachment;
5015 attachment.createObject();
5016 rc = attachment->init (hd, ctl, dev, false /* aDirty */);
5017 if (FAILED (rc))
5018 break;
5019
5020 /* associate the hard disk with this machine */
5021 hd->setMachineId (mData->mUuid);
5022
5023 /* associate the hard disk with the given snapshot ID */
5024 if (mType == IsSnapshotMachine)
5025 hd->setSnapshotId (*aSnapshotId);
5026
5027 mHDData->mHDAttachments.push_back (attachment);
5028 }
5029 while (0);
5030
5031 CFGLDRReleaseNode (hdNode);
5032 }
5033
5034 return rc;
5035}
5036
5037/**
5038 * Creates a config loader and loads the settings file.
5039 *
5040 * @param aIsNew |true| if a newly created settings file is to be opened
5041 * (must be the case only when called from #saveSettings())
5042 *
5043 * @note
5044 * XML Schema errors are not detected by this method because
5045 * it assumes that it will load settings from an exclusively locked
5046 * file (using a file handle) that was previously validated when opened
5047 * for the first time. Thus, this method should be used only when
5048 * it's necessary to modify (save) the settings file.
5049 *
5050 * @note The object must be locked at least for reading before calling
5051 * this method.
5052 */
5053HRESULT Machine::openConfigLoader (CFGHANDLE *aLoader, bool aIsNew /* = false */)
5054{
5055 AssertReturn (aLoader, E_FAIL);
5056
5057 /* The settings file must be created and locked at this point */
5058 ComAssertRet (isConfigLocked(), E_FAIL);
5059
5060 /* load the config file */
5061 int vrc = CFGLDRLoad (aLoader,
5062 Utf8Str (mData->mConfigFileFull), mData->mHandleCfgFile,
5063 aIsNew ? NULL : XmlSchemaNS, true, cfgLdrEntityResolver,
5064 NULL);
5065 ComAssertRCRet (vrc, E_FAIL);
5066
5067 return S_OK;
5068}
5069
5070/**
5071 * Closes the config loader previously created by #openConfigLoader().
5072 * If \a aSaveBeforeClose is true, then the config is saved to the settings file
5073 * before closing. If saving fails, a proper error message is set.
5074 *
5075 * @param aSaveBeforeClose whether to save the config before closing or not
5076 */
5077HRESULT Machine::closeConfigLoader (CFGHANDLE aLoader, bool aSaveBeforeClose)
5078{
5079 HRESULT rc = S_OK;
5080
5081 if (aSaveBeforeClose)
5082 {
5083 char *loaderError = NULL;
5084 int vrc = CFGLDRSave (aLoader, &loaderError);
5085 if (VBOX_FAILURE (vrc))
5086 {
5087 rc = setError (E_FAIL,
5088 tr ("Could not save the settings file '%ls' (%Vrc)%s%s"),
5089 mData->mConfigFileFull.raw(), vrc,
5090 loaderError ? ".\n" : "", loaderError ? loaderError : "");
5091 if (loaderError)
5092 RTMemTmpFree (loaderError);
5093 }
5094 }
5095
5096 CFGLDRFree (aLoader);
5097
5098 return rc;
5099}
5100
5101/**
5102 * Searches for a <Snapshot> node for the given snapshot.
5103 * If the search is successful, \a aSnapshotNode will contain the found node.
5104 * In this case, \a aSnapshotsNode can be NULL meaning the found node is a
5105 * direct child of \a aMachineNode.
5106 *
5107 * If the search fails, a failure is returned and both \a aSnapshotsNode and
5108 * \a aSnapshotNode are set to 0.
5109 *
5110 * @param aSnapshot snapshot to search for
5111 * @param aMachineNode <Machine> node to start from
5112 * @param aSnapshotsNode <Snapshots> node containing the found <Snapshot> node
5113 * (may be NULL if the caller is not interested)
5114 * @param aSnapshotNode found <Snapshot> node
5115 */
5116HRESULT Machine::findSnapshotNode (Snapshot *aSnapshot, CFGNODE aMachineNode,
5117 CFGNODE *aSnapshotsNode, CFGNODE *aSnapshotNode)
5118{
5119 AssertReturn (aSnapshot && aMachineNode && aSnapshotNode, E_FAIL);
5120
5121 if (aSnapshotsNode)
5122 *aSnapshotsNode = 0;
5123 *aSnapshotNode = 0;
5124
5125 // build the full uuid path (from the fist parent to the given snapshot)
5126 std::list <Guid> path;
5127 {
5128 ComObjPtr <Snapshot> parent = aSnapshot;
5129 while (parent)
5130 {
5131 path.push_front (parent->data().mId);
5132 parent = parent->parent();
5133 }
5134 }
5135
5136 CFGNODE snapshotsNode = aMachineNode;
5137 CFGNODE snapshotNode = 0;
5138
5139 for (std::list <Guid>::const_iterator it = path.begin();
5140 it != path.end();
5141 ++ it)
5142 {
5143 if (snapshotNode)
5144 {
5145 // proceed to the nested <Snapshots> node
5146 Assert (snapshotsNode);
5147 if (snapshotsNode != aMachineNode)
5148 {
5149 CFGLDRReleaseNode (snapshotsNode);
5150 snapshotsNode = 0;
5151 }
5152 CFGLDRGetChildNode (snapshotNode, "Snapshots", 0, &snapshotsNode);
5153 CFGLDRReleaseNode (snapshotNode);
5154 snapshotNode = 0;
5155 }
5156
5157 AssertReturn (snapshotsNode, E_FAIL);
5158
5159 unsigned count = 0, i = 0;
5160 CFGLDRCountChildren (snapshotsNode, "Snapshot", &count);
5161 for (; i < count; ++ i)
5162 {
5163 snapshotNode = 0;
5164 CFGLDRGetChildNode (snapshotsNode, "Snapshot", i, &snapshotNode);
5165 Guid id;
5166 CFGLDRQueryUUID (snapshotNode, "uuid", id.ptr());
5167 if (id == (*it))
5168 {
5169 // we keep (don't release) snapshotNode and snapshotsNode
5170 break;
5171 }
5172 CFGLDRReleaseNode (snapshotNode);
5173 snapshotNode = 0;
5174 }
5175
5176 if (i == count)
5177 {
5178 // the next uuid is not found, no need to continue...
5179 AssertFailed();
5180 if (snapshotsNode != aMachineNode)
5181 {
5182 CFGLDRReleaseNode (snapshotsNode);
5183 snapshotsNode = 0;
5184 }
5185 break;
5186 }
5187 }
5188
5189 // we must always succesfully find the node
5190 AssertReturn (snapshotNode, E_FAIL);
5191 AssertReturn (snapshotsNode, E_FAIL);
5192
5193 if (aSnapshotsNode)
5194 *aSnapshotsNode = snapshotsNode != aMachineNode ? snapshotsNode : 0;
5195 *aSnapshotNode = snapshotNode;
5196
5197 return S_OK;
5198}
5199
5200/**
5201 * Returns the snapshot with the given UUID or fails of no such snapshot.
5202 *
5203 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
5204 * @param aSnapshot where to return the found snapshot
5205 * @param aSetError true to set extended error info on failure
5206 */
5207HRESULT Machine::findSnapshot (const Guid &aId, ComObjPtr <Snapshot> &aSnapshot,
5208 bool aSetError /* = false */)
5209{
5210 if (!mData->mFirstSnapshot)
5211 {
5212 if (aSetError)
5213 return setError (E_FAIL,
5214 tr ("This machine does not have any snapshots"));
5215 return E_FAIL;
5216 }
5217
5218 if (aId.isEmpty())
5219 aSnapshot = mData->mFirstSnapshot;
5220 else
5221 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aId);
5222
5223 if (!aSnapshot)
5224 {
5225 if (aSetError)
5226 return setError (E_FAIL,
5227 tr ("Could not find a snapshot with UUID {%s}"),
5228 aId.toString().raw());
5229 return E_FAIL;
5230 }
5231
5232 return S_OK;
5233}
5234
5235/**
5236 * Returns the snapshot with the given name or fails of no such snapshot.
5237 *
5238 * @param aName snapshot name to find
5239 * @param aSnapshot where to return the found snapshot
5240 * @param aSetError true to set extended error info on failure
5241 */
5242HRESULT Machine::findSnapshot (const BSTR aName, ComObjPtr <Snapshot> &aSnapshot,
5243 bool aSetError /* = false */)
5244{
5245 AssertReturn (aName, E_INVALIDARG);
5246
5247 if (!mData->mFirstSnapshot)
5248 {
5249 if (aSetError)
5250 return setError (E_FAIL,
5251 tr ("This machine does not have any snapshots"));
5252 return E_FAIL;
5253 }
5254
5255 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
5256
5257 if (!aSnapshot)
5258 {
5259 if (aSetError)
5260 return setError (E_FAIL,
5261 tr ("Could not find a snapshot named '%ls'"), aName);
5262 return E_FAIL;
5263 }
5264
5265 return S_OK;
5266}
5267
5268/**
5269 * Searches for an attachment that contains the given hard disk.
5270 * The hard disk must be associated with some VM and can be optionally
5271 * associated with some snapshot. If the attachment is stored in the snapshot
5272 * (i.e. the hard disk is associated with some snapshot), @a aSnapshot
5273 * will point to a non-null object on output.
5274 *
5275 * @param aHd hard disk to search an attachment for
5276 * @param aMachine where to store the hard disk's machine (can be NULL)
5277 * @param aSnapshot where to store the hard disk's snapshot (can be NULL)
5278 * @param aHda where to store the hard disk's attachment (can be NULL)
5279 *
5280 *
5281 * @note
5282 * It is assumed that the machine where the attachment is found,
5283 * is already placed to the Discarding state, when this method is called.
5284 * @note
5285 * The object returned in @a aHda is the attachment from the snapshot
5286 * machine if the hard disk is associated with the snapshot, not from the
5287 * primary machine object returned returned in @a aMachine.
5288 */
5289HRESULT Machine::findHardDiskAttachment (const ComObjPtr <HardDisk> &aHd,
5290 ComObjPtr <Machine> *aMachine,
5291 ComObjPtr <Snapshot> *aSnapshot,
5292 ComObjPtr <HardDiskAttachment> *aHda)
5293{
5294 AssertReturn (!aHd.isNull(), E_INVALIDARG);
5295
5296 Guid mid = aHd->machineId();
5297 Guid sid = aHd->snapshotId();
5298
5299 AssertReturn (!mid.isEmpty(), E_INVALIDARG);
5300
5301 ComObjPtr <Machine> m;
5302 mParent->getMachine (mid, m);
5303 ComAssertRet (!m.isNull(), E_FAIL);
5304
5305 HDData::HDAttachmentList *attachments = &m->mHDData->mHDAttachments;
5306
5307 ComObjPtr <Snapshot> s;
5308 if (!sid.isEmpty())
5309 {
5310 m->findSnapshot (sid, s);
5311 ComAssertRet (!s.isNull(), E_FAIL);
5312 attachments = &s->data().mMachine->mHDData->mHDAttachments;
5313 }
5314
5315 AssertReturn (attachments, E_FAIL);
5316
5317 for (HDData::HDAttachmentList::const_iterator it = attachments->begin();
5318 it != attachments->end();
5319 ++ it)
5320 {
5321 if ((*it)->hardDisk() == aHd)
5322 {
5323 if (aMachine) *aMachine = m;
5324 if (aSnapshot) *aSnapshot = s;
5325 if (aHda) *aHda = (*it);
5326 return S_OK;
5327 }
5328 }
5329
5330 ComAssertFailed();
5331 return E_FAIL;
5332}
5333
5334/**
5335 * Helper for #saveSettings. Cares about renaming the settings directory and
5336 * file if the machine name was changed and about creating a new settings file
5337 * if this is a new machine.
5338 *
5339 * @note Must be never called directly.
5340 *
5341 * @param aRenamed receives |true| if the name was changed and the settings
5342 * file was renamed as a result, or |false| otherwise. The
5343 * value makes sense only on success.
5344 * @param aNew receives |true| if a virgin settings file was created.
5345 */
5346HRESULT Machine::prepareSaveSettings (bool &aRenamed, bool &aNew)
5347{
5348 HRESULT rc = S_OK;
5349
5350 aRenamed = false;
5351
5352 /* if we're ready and isConfigLocked() is FALSE then it means
5353 * that no config file exists yet (we will create a virgin one) */
5354 aNew = !isConfigLocked();
5355
5356 /* attempt to rename the settings file if machine name is changed */
5357 if (mUserData->mNameSync &&
5358 mUserData.isBackedUp() &&
5359 mUserData.backedUpData()->mName != mUserData->mName)
5360 {
5361 aRenamed = true;
5362
5363 if (!aNew)
5364 {
5365 /* unlock the old config file */
5366 rc = unlockConfig();
5367 CheckComRCReturnRC (rc);
5368 }
5369
5370 bool dirRenamed = false;
5371 bool fileRenamed = false;
5372
5373 Utf8Str configFile, newConfigFile;
5374 Utf8Str configDir, newConfigDir;
5375
5376 do
5377 {
5378 int vrc = VINF_SUCCESS;
5379
5380 Utf8Str name = mUserData.backedUpData()->mName;
5381 Utf8Str newName = mUserData->mName;
5382
5383 configFile = mData->mConfigFileFull;
5384
5385 /* first, rename the directory if it matches the machine name */
5386 configDir = configFile;
5387 RTPathStripFilename (configDir.mutableRaw());
5388 newConfigDir = configDir;
5389 if (RTPathFilename (configDir) == name)
5390 {
5391 RTPathStripFilename (newConfigDir.mutableRaw());
5392 newConfigDir = Utf8StrFmt ("%s%c%s",
5393 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
5394 /* new dir and old dir cannot be equal here because of 'if'
5395 * above and because name != newName */
5396 Assert (configDir != newConfigDir);
5397 if (!aNew)
5398 {
5399 /* perform real rename only if the machine is not new */
5400 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
5401 if (VBOX_FAILURE (vrc))
5402 {
5403 rc = setError (E_FAIL,
5404 tr ("Could not rename the directory '%s' to '%s' "
5405 "to save the settings file (%Vrc)"),
5406 configDir.raw(), newConfigDir.raw(), vrc);
5407 break;
5408 }
5409 dirRenamed = true;
5410 }
5411 }
5412
5413 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
5414 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
5415
5416 /* then try to rename the settings file itself */
5417 if (newConfigFile != configFile)
5418 {
5419 /* get the path to old settings file in renamed directory */
5420 configFile = Utf8StrFmt ("%s%c%s",
5421 newConfigDir.raw(), RTPATH_DELIMITER,
5422 RTPathFilename (configFile));
5423 if (!aNew)
5424 {
5425 /* perform real rename only if the machine is not new */
5426 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
5427 if (VBOX_FAILURE (vrc))
5428 {
5429 rc = setError (E_FAIL,
5430 tr ("Could not rename the settings file '%s' to '%s' "
5431 "(%Vrc)"),
5432 configFile.raw(), newConfigFile.raw(), vrc);
5433 break;
5434 }
5435 fileRenamed = true;
5436 }
5437 }
5438
5439 /* update mConfigFileFull amd mConfigFile */
5440 Bstr oldConfigFileFull = mData->mConfigFileFull;
5441 Bstr oldConfigFile = mData->mConfigFile;
5442 mData->mConfigFileFull = newConfigFile;
5443 /* try to get the relative path for mConfigFile */
5444 Utf8Str path = newConfigFile;
5445 mParent->calculateRelativePath (path, path);
5446 mData->mConfigFile = path;
5447
5448 /* last, try to update the global settings with the new path */
5449 if (mData->mRegistered)
5450 {
5451 rc = mParent->updateSettings (configDir, newConfigDir);
5452 if (FAILED (rc))
5453 {
5454 /* revert to old values */
5455 mData->mConfigFileFull = oldConfigFileFull;
5456 mData->mConfigFile = oldConfigFile;
5457 break;
5458 }
5459 }
5460
5461 /* update the snapshot folder */
5462 path = mUserData->mSnapshotFolderFull;
5463 if (RTPathStartsWith (path, configDir))
5464 {
5465 path = Utf8StrFmt ("%s%s", newConfigDir.raw(),
5466 path.raw() + configDir.length());
5467 mUserData->mSnapshotFolderFull = path;
5468 calculateRelativePath (path, path);
5469 mUserData->mSnapshotFolder = path;
5470 }
5471
5472 /* update the saved state file path */
5473 path = mSSData->mStateFilePath;
5474 if (RTPathStartsWith (path, configDir))
5475 {
5476 path = Utf8StrFmt ("%s%s", newConfigDir.raw(),
5477 path.raw() + configDir.length());
5478 mSSData->mStateFilePath = path;
5479 }
5480
5481 /* Update saved state file paths of all online snapshots.
5482 * Note that saveSettings() will recognize name change
5483 * and will save all snapshots in this case. */
5484 if (mData->mFirstSnapshot)
5485 mData->mFirstSnapshot->updateSavedStatePaths (configDir,
5486 newConfigDir);
5487 }
5488 while (0);
5489
5490 if (FAILED (rc))
5491 {
5492 /* silently try to rename everything back */
5493 if (fileRenamed)
5494 RTFileRename (newConfigFile.raw(), configFile.raw(), 0);
5495 if (dirRenamed)
5496 RTPathRename (newConfigDir.raw(), configDir.raw(), 0);
5497 }
5498
5499 if (!aNew)
5500 {
5501 /* lock the config again */
5502 HRESULT rc2 = lockConfig();
5503 if (SUCCEEDED (rc))
5504 rc = rc2;
5505 }
5506
5507 CheckComRCReturnRC (rc);
5508 }
5509
5510 if (aNew)
5511 {
5512 /* create a virgin config file */
5513 int vrc = VINF_SUCCESS;
5514
5515 /* ensure the settings directory exists */
5516 Utf8Str path = mData->mConfigFileFull;
5517 RTPathStripFilename (path.mutableRaw());
5518 if (!RTDirExists (path))
5519 {
5520 vrc = RTDirCreateFullPath (path, 0777);
5521 if (VBOX_FAILURE (vrc))
5522 {
5523 return setError (E_FAIL,
5524 tr ("Could not create a directory '%s' "
5525 "to save the settings file (%Vrc)"),
5526 path.raw(), vrc);
5527 }
5528 }
5529
5530 /* Note: open flags must correlated with RTFileOpen() in lockConfig() */
5531 path = Utf8Str (mData->mConfigFileFull);
5532 vrc = RTFileOpen (&mData->mHandleCfgFile, path,
5533 RTFILE_O_READWRITE | RTFILE_O_CREATE |
5534 RTFILE_O_DENY_WRITE);
5535 if (VBOX_SUCCESS (vrc))
5536 {
5537 vrc = RTFileWrite (mData->mHandleCfgFile,
5538 (void *) DefaultMachineConfig,
5539 sizeof (DefaultMachineConfig), NULL);
5540 }
5541 if (VBOX_FAILURE (vrc))
5542 {
5543 mData->mHandleCfgFile = NIL_RTFILE;
5544 return setError (E_FAIL,
5545 tr ("Could not create the settings file '%s' (%Vrc)"),
5546 path.raw(), vrc);
5547 }
5548 /* we do not close the file to simulate lockConfig() */
5549 }
5550
5551 return rc;
5552}
5553
5554/**
5555 * Saves machine data, user data and hardware data.
5556 *
5557 * @param aMarkCurStateAsModified
5558 * if true (default), mData->mCurrentStateModified will be set to
5559 * what #isReallyModified() returns prior to saving settings to a file,
5560 * otherwise the current value of mData->mCurrentStateModified will be
5561 * saved.
5562 * @param aInformCallbacksAnyway
5563 * if true, callbacks will be informed even if #isReallyModified()
5564 * returns false. This is necessary for cases when we change machine data
5565 * diectly, not through the backup()/commit() mechanism.
5566 *
5567 * @note Locks mParent (only in some cases, and only when #isConfigLocked() is
5568 * |TRUE|, see the #prepareSaveSettings() code for details) +
5569 * this object + children for writing.
5570 */
5571HRESULT Machine::saveSettings (bool aMarkCurStateAsModified /* = true */,
5572 bool aInformCallbacksAnyway /* = false */)
5573{
5574 LogFlowThisFuncEnter();
5575
5576 /// @todo (dmik) I guess we should lock all our child objects here
5577 // (such as mVRDPServer etc.) to ensure they are not changed
5578 // until completely saved to disk and committed
5579
5580 /// @todo (dmik) also, we need to delegate saving child objects' settings
5581 // to objects themselves to ensure operations 'commit + save changes'
5582 // are atomic (amd done from the object's lock so that nobody can change
5583 // settings again until completely saved).
5584
5585 AssertReturn (mType == IsMachine || mType == IsSessionMachine, E_FAIL);
5586
5587 bool wasModified;
5588
5589 if (aMarkCurStateAsModified)
5590 {
5591 /*
5592 * We ignore changes to user data when setting mCurrentStateModified
5593 * because the current state will not differ from the current snapshot
5594 * if only user data has been changed (user data is shared by all
5595 * snapshots).
5596 */
5597 mData->mCurrentStateModified = isReallyModified (true /* aIgnoreUserData */);
5598 wasModified = mUserData.hasActualChanges() || mData->mCurrentStateModified;
5599 }
5600 else
5601 {
5602 wasModified = isReallyModified();
5603 }
5604
5605 HRESULT rc = S_OK;
5606
5607 /* First, prepare to save settings. It will will care about renaming the
5608 * settings directory and file if the machine name was changed and about
5609 * creating a new settings file if this is a new machine. */
5610 bool isRenamed = false;
5611 bool isNew = false;
5612 rc = prepareSaveSettings (isRenamed, isNew);
5613 CheckComRCReturnRC (rc);
5614
5615 /* then, open the settings file */
5616 CFGHANDLE configLoader = 0;
5617 rc = openConfigLoader (&configLoader, isNew);
5618 CheckComRCReturnRC (rc);
5619
5620 /* save all snapshots when the machine name was changed since
5621 * it may affect saved state file paths for online snapshots (see
5622 * #openConfigLoader() for details) */
5623 bool updateAllSnapshots = isRenamed;
5624
5625 /* commit before saving, since it may change settings
5626 * (for example, perform fixup of lazy hard disk changes) */
5627 rc = commit();
5628 if (FAILED (rc))
5629 {
5630 closeConfigLoader (configLoader, false /* aSaveBeforeClose */);
5631 return rc;
5632 }
5633
5634 /* include hard disk changes to the modified flag */
5635 wasModified |= mHDData->mHDAttachmentsChanged;
5636 if (aMarkCurStateAsModified)
5637 mData->mCurrentStateModified |= BOOL (mHDData->mHDAttachmentsChanged);
5638
5639
5640 CFGNODE machineNode = 0;
5641 /* create if not exists */
5642 CFGLDRCreateNode (configLoader, "VirtualBox/Machine", &machineNode);
5643
5644 do
5645 {
5646 ComAssertBreak (machineNode, rc = E_FAIL);
5647
5648 /* uuid (required) */
5649 Assert (mData->mUuid);
5650 CFGLDRSetUUID (machineNode, "uuid", mData->mUuid.raw());
5651
5652 /* name (required) */
5653 Assert (!mUserData->mName.isEmpty());
5654 CFGLDRSetBSTR (machineNode, "name", mUserData->mName);
5655
5656 /* nameSync (optional, default is true) */
5657 if (!mUserData->mNameSync)
5658 CFGLDRSetBool (machineNode, "nameSync", false);
5659 else
5660 CFGLDRDeleteAttribute (machineNode, "nameSync");
5661
5662 /* Description node (optional) */
5663 if (!mUserData->mDescription.isNull())
5664 {
5665 CFGNODE descNode = 0;
5666 CFGLDRCreateChildNode (machineNode, "Description", &descNode);
5667 Assert (descNode);
5668 CFGLDRSetBSTR (descNode, NULL, mUserData->mDescription);
5669 CFGLDRReleaseNode (descNode);
5670 }
5671 else
5672 {
5673 CFGNODE descNode = 0;
5674 CFGLDRGetChildNode (machineNode, "Description", 0, &descNode);
5675 if (descNode)
5676 CFGLDRDeleteNode (descNode);
5677 }
5678
5679 /* OSType (required) */
5680 {
5681 CFGLDRSetBSTR (machineNode, "OSType", mUserData->mOSTypeId);
5682 }
5683
5684 /* stateFile (optional) */
5685 if (mData->mMachineState == MachineState_Saved)
5686 {
5687 Assert (!mSSData->mStateFilePath.isEmpty());
5688 /* try to make the file name relative to the settings file dir */
5689 Utf8Str stateFilePath = mSSData->mStateFilePath;
5690 calculateRelativePath (stateFilePath, stateFilePath);
5691 CFGLDRSetString (machineNode, "stateFile", stateFilePath);
5692 }
5693 else
5694 {
5695 Assert (mSSData->mStateFilePath.isNull());
5696 CFGLDRDeleteAttribute (machineNode, "stateFile");
5697 }
5698
5699 /* currentSnapshot ID (optional) */
5700 if (!mData->mCurrentSnapshot.isNull())
5701 {
5702 Assert (!mData->mFirstSnapshot.isNull());
5703 CFGLDRSetUUID (machineNode, "currentSnapshot",
5704 mData->mCurrentSnapshot->data().mId);
5705 }
5706 else
5707 {
5708 Assert (mData->mFirstSnapshot.isNull());
5709 CFGLDRDeleteAttribute (machineNode, "currentSnapshot");
5710 }
5711
5712 /* snapshotFolder (optional) */
5713 if (!mUserData->mSnapshotFolder.isEmpty())
5714 CFGLDRSetBSTR (machineNode, "snapshotFolder", mUserData->mSnapshotFolder);
5715 else
5716 CFGLDRDeleteAttribute (machineNode, "snapshotFolder");
5717
5718 /* currentStateModified (optional, default is yes) */
5719 if (!mData->mCurrentStateModified)
5720 CFGLDRSetBool (machineNode, "currentStateModified", false);
5721 else
5722 CFGLDRDeleteAttribute (machineNode, "currentStateModified");
5723
5724 /* lastStateChange */
5725 CFGLDRSetDateTime (machineNode, "lastStateChange",
5726 mData->mLastStateChange);
5727
5728 /* Hardware node (required) */
5729 {
5730 CFGNODE hwNode = 0;
5731 CFGLDRGetChildNode (machineNode, "Hardware", 0, &hwNode);
5732 /* first, delete the entire node if exists */
5733 if (hwNode)
5734 CFGLDRDeleteNode (hwNode);
5735 /* then recreate it */
5736 hwNode = 0;
5737 CFGLDRCreateChildNode (machineNode, "Hardware", &hwNode);
5738 ComAssertBreak (hwNode, rc = E_FAIL);
5739
5740 rc = saveHardware (hwNode);
5741
5742 CFGLDRReleaseNode (hwNode);
5743 if (FAILED (rc))
5744 break;
5745 }
5746
5747 /* HardDiskAttachments node (required) */
5748 {
5749 CFGNODE hdasNode = 0;
5750 CFGLDRGetChildNode (machineNode, "HardDiskAttachments", 0, &hdasNode);
5751 /* first, delete the entire node if exists */
5752 if (hdasNode)
5753 CFGLDRDeleteNode (hdasNode);
5754 /* then recreate it */
5755 hdasNode = 0;
5756 CFGLDRCreateChildNode (machineNode, "HardDiskAttachments", &hdasNode);
5757 ComAssertBreak (hdasNode, rc = E_FAIL);
5758
5759 rc = saveHardDisks (hdasNode);
5760
5761 CFGLDRReleaseNode (hdasNode);
5762 if (FAILED (rc))
5763 break;
5764 }
5765
5766 /* update all snapshots if requested */
5767 if (updateAllSnapshots)
5768 rc = saveSnapshotSettingsWorker (machineNode, NULL,
5769 SaveSS_UpdateAllOp);
5770 }
5771 while (0);
5772
5773 if (machineNode)
5774 CFGLDRReleaseNode (machineNode);
5775
5776 if (SUCCEEDED (rc))
5777 rc = closeConfigLoader (configLoader, true /* aSaveBeforeClose */);
5778 else
5779 closeConfigLoader (configLoader, false /* aSaveBeforeClose */);
5780
5781 if (FAILED (rc))
5782 {
5783 /*
5784 * backup arbitrary data item to cause #isModified() to still return
5785 * true in case of any error
5786 */
5787 mHWData.backup();
5788 }
5789
5790 if (wasModified || aInformCallbacksAnyway)
5791 {
5792 /*
5793 * Fire the data change event, even on failure (since we've already
5794 * committed all data). This is done only for SessionMachines because
5795 * mutable Machine instances are always not registered (i.e. private
5796 * to the client process that creates them) and thus don't need to
5797 * inform callbacks.
5798 */
5799 if (mType == IsSessionMachine)
5800 mParent->onMachineDataChange (mData->mUuid);
5801 }
5802
5803 LogFlowThisFunc (("rc=%08X\n", rc));
5804 LogFlowThisFuncLeave();
5805 return rc;
5806}
5807
5808/**
5809 * Wrapper for #saveSnapshotSettingsWorker() that opens the settings file
5810 * and locates the <Machine> node in there. See #saveSnapshotSettingsWorker()
5811 * for more details.
5812 *
5813 * @param aSnapshot Snapshot to operate on
5814 * @param aOpFlags Operation to perform, one of SaveSS_NoOp, SaveSS_AddOp
5815 * or SaveSS_UpdateAttrsOp possibly combined with
5816 * SaveSS_UpdateCurrentId.
5817 *
5818 * @note Locks this object for writing + other child objects.
5819 */
5820HRESULT Machine::saveSnapshotSettings (Snapshot *aSnapshot, int aOpFlags)
5821{
5822 AutoCaller autoCaller (this);
5823 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
5824
5825 AssertReturn (mType == IsMachine || mType == IsSessionMachine, E_FAIL);
5826
5827 AutoLock alock (this);
5828
5829 AssertReturn (isConfigLocked(), E_FAIL);
5830
5831 HRESULT rc = S_OK;
5832
5833 /* load the config file */
5834 CFGHANDLE configLoader = 0;
5835 rc = openConfigLoader (&configLoader);
5836 if (FAILED (rc))
5837 return rc;
5838
5839 CFGNODE machineNode = 0;
5840 CFGLDRGetNode (configLoader, "VirtualBox/Machine", 0, &machineNode);
5841
5842 do
5843 {
5844 ComAssertBreak (machineNode, rc = E_FAIL);
5845
5846 rc = saveSnapshotSettingsWorker (machineNode, aSnapshot, aOpFlags);
5847
5848 CFGLDRReleaseNode (machineNode);
5849 }
5850 while (0);
5851
5852 if (SUCCEEDED (rc))
5853 rc = closeConfigLoader (configLoader, true /* aSaveBeforeClose */);
5854 else
5855 closeConfigLoader (configLoader, false /* aSaveBeforeClose */);
5856
5857 return rc;
5858}
5859
5860/**
5861 * Performs the specified operation on the given snapshot
5862 * in the settings file represented by \a aMachineNode.
5863 *
5864 * If \a aOpFlags = SaveSS_UpdateAllOp, \a aSnapshot can be NULL to indicate
5865 * that the whole tree of the snapshots should be updated in <Machine>.
5866 * One particular case is when the last (and the only) snapshot should be
5867 * removed (it is so when both mCurrentSnapshot and mFirstSnapshot are NULL).
5868 *
5869 * \a aOp may be just SaveSS_UpdateCurrentId if only the currentSnapshot
5870 * attribute of <Machine> needs to be updated.
5871 *
5872 * @param aMachineNode <Machine> node in the opened settings file
5873 * @param aSnapshot Snapshot to operate on
5874 * @param aOpFlags Operation to perform, one of SaveSS_NoOp, SaveSS_AddOp
5875 * or SaveSS_UpdateAttrsOp possibly combined with
5876 * SaveSS_UpdateCurrentId.
5877 *
5878 * @note Must be called with this object locked for writing.
5879 * Locks child objects.
5880 */
5881HRESULT Machine::saveSnapshotSettingsWorker (CFGNODE aMachineNode,
5882 Snapshot *aSnapshot, int aOpFlags)
5883{
5884 AssertReturn (aMachineNode, E_FAIL);
5885
5886 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
5887
5888 int op = aOpFlags & SaveSS_OpMask;
5889 AssertReturn (
5890 (aSnapshot && (op == SaveSS_AddOp || op == SaveSS_UpdateAttrsOp ||
5891 op == SaveSS_UpdateAllOp)) ||
5892 (!aSnapshot && ((op == SaveSS_NoOp && (aOpFlags & SaveSS_UpdateCurrentId)) ||
5893 op == SaveSS_UpdateAllOp)),
5894 E_FAIL);
5895
5896 HRESULT rc = S_OK;
5897
5898 bool recreateWholeTree = false;
5899
5900 do
5901 {
5902 if (op == SaveSS_NoOp)
5903 break;
5904
5905 /* quick path: recreate the whole tree of the snapshots */
5906 if (op == SaveSS_UpdateAllOp && !aSnapshot)
5907 {
5908 /* first, delete the entire root snapshot node if it exists */
5909 CFGNODE snapshotNode = 0;
5910 CFGLDRGetChildNode (aMachineNode, "Snapshot", 0, &snapshotNode);
5911 if (snapshotNode)
5912 CFGLDRDeleteNode (snapshotNode);
5913
5914 /*
5915 * second, if we have any snapshots left, substitute aSnapshot with
5916 * the first snapshot to recreate the whole tree, otherwise break
5917 */
5918 if (mData->mFirstSnapshot)
5919 {
5920 aSnapshot = mData->mFirstSnapshot;
5921 recreateWholeTree = true;
5922 }
5923 else
5924 break;
5925 }
5926
5927 Assert (!!aSnapshot);
5928 ComObjPtr <Snapshot> parent = aSnapshot->parent();
5929
5930 if (op == SaveSS_AddOp)
5931 {
5932 CFGNODE parentNode = 0;
5933
5934 if (parent)
5935 {
5936 rc = findSnapshotNode (parent, aMachineNode, NULL, &parentNode);
5937 if (FAILED (rc))
5938 break;
5939 ComAssertBreak (parentNode, rc = E_FAIL);
5940 }
5941
5942 do
5943 {
5944 CFGNODE snapshotsNode = 0;
5945
5946 if (parentNode)
5947 {
5948 CFGLDRCreateChildNode (parentNode, "Snapshots", &snapshotsNode);
5949 ComAssertBreak (snapshotsNode, rc = E_FAIL);
5950 }
5951 else
5952 snapshotsNode = aMachineNode;
5953 do
5954 {
5955 CFGNODE snapshotNode = 0;
5956 CFGLDRAppendChildNode (snapshotsNode, "Snapshot", &snapshotNode);
5957 ComAssertBreak (snapshotNode, rc = E_FAIL);
5958 rc = saveSnapshot (snapshotNode, aSnapshot, false /* aAttrsOnly */);
5959 CFGLDRReleaseNode (snapshotNode);
5960
5961 if (FAILED (rc))
5962 break;
5963
5964 /*
5965 * when a new snapshot is added, this means diffs were created
5966 * for every normal/immutable hard disk of the VM, so we need to
5967 * save the current hard disk attachments
5968 */
5969
5970 CFGNODE hdasNode = 0;
5971 CFGLDRGetChildNode (aMachineNode, "HardDiskAttachments", 0, &hdasNode);
5972 if (hdasNode)
5973 CFGLDRDeleteNode (hdasNode);
5974 CFGLDRCreateChildNode (aMachineNode, "HardDiskAttachments", &hdasNode);
5975 ComAssertBreak (hdasNode, rc = E_FAIL);
5976
5977 rc = saveHardDisks (hdasNode);
5978
5979 if (mHDData->mHDAttachments.size() != 0)
5980 {
5981 /*
5982 * If we have one or more attachments then we definitely
5983 * created diffs for them and associated new diffs with
5984 * current settngs. So, since we don't use saveSettings(),
5985 * we need to inform callbacks manually.
5986 */
5987 if (mType == IsSessionMachine)
5988 mParent->onMachineDataChange (mData->mUuid);
5989 }
5990
5991 CFGLDRReleaseNode (hdasNode);
5992 }
5993 while (0);
5994
5995 if (snapshotsNode != aMachineNode)
5996 CFGLDRReleaseNode (snapshotsNode);
5997 }
5998 while (0);
5999
6000 if (parentNode)
6001 CFGLDRReleaseNode (parentNode);
6002
6003 break;
6004 }
6005
6006 Assert (op == SaveSS_UpdateAttrsOp && !recreateWholeTree ||
6007 op == SaveSS_UpdateAllOp);
6008
6009 CFGNODE snapshotsNode = 0;
6010 CFGNODE snapshotNode = 0;
6011
6012 if (!recreateWholeTree)
6013 {
6014 rc = findSnapshotNode (aSnapshot, aMachineNode,
6015 &snapshotsNode, &snapshotNode);
6016 if (FAILED (rc))
6017 break;
6018 ComAssertBreak (snapshotNode, rc = E_FAIL);
6019 }
6020
6021 if (!snapshotsNode)
6022 snapshotsNode = aMachineNode;
6023
6024 if (op == SaveSS_UpdateAttrsOp)
6025 rc = saveSnapshot (snapshotNode, aSnapshot, true /* aAttrsOnly */);
6026 else do
6027 {
6028 if (snapshotNode)
6029 {
6030 CFGLDRDeleteNode (snapshotNode);
6031 snapshotNode = 0;
6032 }
6033 CFGLDRAppendChildNode (snapshotsNode, "Snapshot", &snapshotNode);
6034 ComAssertBreak (snapshotNode, rc = E_FAIL);
6035 rc = saveSnapshot (snapshotNode, aSnapshot, false /* aAttrsOnly */);
6036 }
6037 while (0);
6038
6039 CFGLDRReleaseNode (snapshotNode);
6040 if (snapshotsNode != aMachineNode)
6041 CFGLDRReleaseNode (snapshotsNode);
6042 }
6043 while (0);
6044
6045 if (SUCCEEDED (rc))
6046 {
6047 /* update currentSnapshot when appropriate */
6048 if (aOpFlags & SaveSS_UpdateCurrentId)
6049 {
6050 if (!mData->mCurrentSnapshot.isNull())
6051 CFGLDRSetUUID (aMachineNode, "currentSnapshot",
6052 mData->mCurrentSnapshot->data().mId);
6053 else
6054 CFGLDRDeleteAttribute (aMachineNode, "currentSnapshot");
6055 }
6056 if (aOpFlags & SaveSS_UpdateCurStateModified)
6057 {
6058 if (!mData->mCurrentStateModified)
6059 CFGLDRSetBool (aMachineNode, "currentStateModified", false);
6060 else
6061 CFGLDRDeleteAttribute (aMachineNode, "currentStateModified");
6062 }
6063 }
6064
6065 return rc;
6066}
6067
6068/**
6069 * Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
6070 * It is assumed that the given node is empty (unless \a aAttrsOnly is true).
6071 *
6072 * @param aNode <Snapshot> node to save the snapshot to
6073 * @param aSnapshot snapshot to save
6074 * @param aAttrsOnly if true, only updatge user-changeable attrs
6075 */
6076HRESULT Machine::saveSnapshot (CFGNODE aNode, Snapshot *aSnapshot, bool aAttrsOnly)
6077{
6078 AssertReturn (aNode && aSnapshot, E_INVALIDARG);
6079 AssertReturn (mType == IsMachine || mType == IsSessionMachine, E_FAIL);
6080
6081 /* uuid (required) */
6082 if (!aAttrsOnly)
6083 CFGLDRSetUUID (aNode, "uuid", aSnapshot->data().mId);
6084
6085 /* name (required) */
6086 CFGLDRSetBSTR (aNode, "name", aSnapshot->data().mName);
6087
6088 /* timeStamp (required) */
6089 CFGLDRSetDateTime (aNode, "timeStamp", aSnapshot->data().mTimeStamp);
6090
6091 /* Description node (optional) */
6092 if (!aSnapshot->data().mDescription.isNull())
6093 {
6094 CFGNODE descNode = 0;
6095 CFGLDRCreateChildNode (aNode, "Description", &descNode);
6096 Assert (descNode);
6097 CFGLDRSetBSTR (descNode, NULL, aSnapshot->data().mDescription);
6098 CFGLDRReleaseNode (descNode);
6099 }
6100 else
6101 {
6102 CFGNODE descNode = 0;
6103 CFGLDRGetChildNode (aNode, "Description", 0, &descNode);
6104 if (descNode)
6105 CFGLDRDeleteNode (descNode);
6106 }
6107
6108 if (aAttrsOnly)
6109 return S_OK;
6110
6111 /* stateFile (optional) */
6112 if (aSnapshot->stateFilePath())
6113 {
6114 /* try to make the file name relative to the settings file dir */
6115 Utf8Str stateFilePath = aSnapshot->stateFilePath();
6116 calculateRelativePath (stateFilePath, stateFilePath);
6117 CFGLDRSetString (aNode, "stateFile", stateFilePath);
6118 }
6119
6120 {
6121 ComObjPtr <SnapshotMachine> snapshotMachine = aSnapshot->data().mMachine;
6122 ComAssertRet (!snapshotMachine.isNull(), E_FAIL);
6123
6124 /* save hardware */
6125 {
6126 CFGNODE hwNode = 0;
6127 CFGLDRCreateChildNode (aNode, "Hardware", &hwNode);
6128
6129 HRESULT rc = snapshotMachine->saveHardware (hwNode);
6130
6131 CFGLDRReleaseNode (hwNode);
6132 if (FAILED (rc))
6133 return rc;
6134 }
6135
6136 /* save hard disks */
6137 {
6138 CFGNODE hdasNode = 0;
6139 CFGLDRCreateChildNode (aNode, "HardDiskAttachments", &hdasNode);
6140
6141 HRESULT rc = snapshotMachine->saveHardDisks (hdasNode);
6142
6143 CFGLDRReleaseNode (hdasNode);
6144 if (FAILED (rc))
6145 return rc;
6146 }
6147 }
6148
6149 /* save children */
6150 {
6151 AutoLock listLock (aSnapshot->childrenLock());
6152
6153 if (aSnapshot->children().size())
6154 {
6155 CFGNODE snapshotsNode = 0;
6156 CFGLDRCreateChildNode (aNode, "Snapshots", &snapshotsNode);
6157
6158 HRESULT rc = S_OK;
6159
6160 for (Snapshot::SnapshotList::const_iterator it = aSnapshot->children().begin();
6161 it != aSnapshot->children().end() && SUCCEEDED (rc);
6162 ++ it)
6163 {
6164 CFGNODE snapshotNode = 0;
6165 CFGLDRCreateChildNode (snapshotsNode, "Snapshot", &snapshotNode);
6166
6167 rc = saveSnapshot (snapshotNode, (*it), aAttrsOnly);
6168
6169 CFGLDRReleaseNode (snapshotNode);
6170 }
6171
6172 CFGLDRReleaseNode (snapshotsNode);
6173 if (FAILED (rc))
6174 return rc;
6175 }
6176 }
6177
6178 return S_OK;
6179}
6180
6181/**
6182 * Creates Saves the VM hardware configuration.
6183 * It is assumed that the given node is empty.
6184 *
6185 * @param aNode <Hardware> node to save the VM hardware confguration to
6186 */
6187HRESULT Machine::saveHardware (CFGNODE aNode)
6188{
6189 AssertReturn (aNode, E_INVALIDARG);
6190
6191 HRESULT rc = S_OK;
6192
6193 /* CPU */
6194 {
6195 CFGNODE cpuNode = 0;
6196 CFGLDRCreateChildNode (aNode, "CPU", &cpuNode);
6197 CFGNODE hwVirtExNode = 0;
6198 CFGLDRCreateChildNode (cpuNode, "HardwareVirtEx", &hwVirtExNode);
6199 const char *value = NULL;
6200 switch (mHWData->mHWVirtExEnabled)
6201 {
6202 case TriStateBool_False:
6203 value = "false";
6204 break;
6205 case TriStateBool_True:
6206 value = "true";
6207 break;
6208 default:
6209 value = "default";
6210 }
6211 CFGLDRSetString (hwVirtExNode, "enabled", value);
6212 CFGLDRReleaseNode (hwVirtExNode);
6213 CFGLDRReleaseNode (cpuNode);
6214 }
6215
6216 /* memory (required) */
6217 {
6218 CFGNODE memoryNode = 0;
6219 CFGLDRCreateChildNode (aNode, "Memory", &memoryNode);
6220 CFGLDRSetUInt32 (memoryNode, "RAMSize", mHWData->mMemorySize);
6221 CFGLDRReleaseNode (memoryNode);
6222 }
6223
6224 /* boot (required) */
6225 do
6226 {
6227 CFGNODE bootNode = 0;
6228 CFGLDRCreateChildNode (aNode, "Boot", &bootNode);
6229
6230 for (ULONG pos = 0; pos < ELEMENTS (mHWData->mBootOrder); pos ++)
6231 {
6232 const char *device = NULL;
6233 switch (mHWData->mBootOrder [pos])
6234 {
6235 case DeviceType_NoDevice:
6236 /* skip, this is allowed for <Order> nodes
6237 * when loading, the default value NoDevice will remain */
6238 continue;
6239 case DeviceType_FloppyDevice: device = "Floppy"; break;
6240 case DeviceType_DVDDevice: device = "DVD"; break;
6241 case DeviceType_HardDiskDevice: device = "HardDisk"; break;
6242 case DeviceType_NetworkDevice: device = "Network"; break;
6243 default:
6244 ComAssertMsgFailedBreak (("Invalid boot device: %d\n",
6245 mHWData->mBootOrder [pos]),
6246 rc = E_FAIL);
6247 }
6248 if (FAILED (rc))
6249 break;
6250
6251 CFGNODE orderNode = 0;
6252 CFGLDRAppendChildNode (bootNode, "Order", &orderNode);
6253
6254 CFGLDRSetUInt32 (orderNode, "position", pos + 1);
6255 CFGLDRSetString (orderNode, "device", device);
6256
6257 CFGLDRReleaseNode (orderNode);
6258 }
6259
6260 CFGLDRReleaseNode (bootNode);
6261 }
6262 while (0);
6263
6264 CheckComRCReturnRC (rc);
6265
6266 /* display (required) */
6267 {
6268 CFGNODE displayNode = 0;
6269 CFGLDRCreateChildNode (aNode, "Display", &displayNode);
6270 CFGLDRSetUInt32 (displayNode, "VRAMSize", mHWData->mVRAMSize);
6271 CFGLDRSetUInt32 (displayNode, "MonitorCount", mHWData->mMonitorCount);
6272 CFGLDRReleaseNode (displayNode);
6273 }
6274
6275#ifdef VBOX_VRDP
6276 /* VRDP settings (optional) */
6277 {
6278 CFGNODE remoteDisplayNode = 0;
6279 CFGLDRCreateChildNode (aNode, "RemoteDisplay", &remoteDisplayNode);
6280 if (remoteDisplayNode)
6281 {
6282 rc = mVRDPServer->saveSettings (remoteDisplayNode);
6283 CFGLDRReleaseNode (remoteDisplayNode);
6284 CheckComRCReturnRC (rc);
6285 }
6286 }
6287#endif
6288
6289 /* BIOS (required) */
6290 {
6291 CFGNODE biosNode = 0;
6292 CFGLDRCreateChildNode (aNode, "BIOS", &biosNode);
6293 {
6294 BOOL fSet;
6295 /* ACPI */
6296 CFGNODE acpiNode = 0;
6297 CFGLDRCreateChildNode (biosNode, "ACPI", &acpiNode);
6298 mBIOSSettings->COMGETTER(ACPIEnabled)(&fSet);
6299 CFGLDRSetBool (acpiNode, "enabled", !!fSet);
6300 CFGLDRReleaseNode (acpiNode);
6301
6302 /* IOAPIC */
6303 CFGNODE ioapicNode = 0;
6304 CFGLDRCreateChildNode (biosNode, "IOAPIC", &ioapicNode);
6305 mBIOSSettings->COMGETTER(IOAPICEnabled)(&fSet);
6306 CFGLDRSetBool (ioapicNode, "enabled", !!fSet);
6307 CFGLDRReleaseNode (ioapicNode);
6308
6309 /* BIOS logo (optional) **/
6310 CFGNODE logoNode = 0;
6311 CFGLDRCreateChildNode (biosNode, "Logo", &logoNode);
6312 mBIOSSettings->COMGETTER(LogoFadeIn)(&fSet);
6313 CFGLDRSetBool (logoNode, "fadeIn", !!fSet);
6314 mBIOSSettings->COMGETTER(LogoFadeOut)(&fSet);
6315 CFGLDRSetBool (logoNode, "fadeOut", !!fSet);
6316 ULONG ulDisplayTime;
6317 mBIOSSettings->COMGETTER(LogoDisplayTime)(&ulDisplayTime);
6318 CFGLDRSetUInt32 (logoNode, "displayTime", ulDisplayTime);
6319 Bstr logoPath;
6320 mBIOSSettings->COMGETTER(LogoImagePath)(logoPath.asOutParam());
6321 if (logoPath)
6322 CFGLDRSetBSTR (logoNode, "imagePath", logoPath);
6323 else
6324 CFGLDRDeleteAttribute (logoNode, "imagePath");
6325 CFGLDRReleaseNode (logoNode);
6326
6327 /* boot menu (optional) */
6328 CFGNODE bootMenuNode = 0;
6329 CFGLDRCreateChildNode (biosNode, "BootMenu", &bootMenuNode);
6330 BIOSBootMenuMode_T bootMenuMode;
6331 Bstr bootMenuModeStr;
6332 mBIOSSettings->COMGETTER(BootMenuMode)(&bootMenuMode);
6333 switch (bootMenuMode)
6334 {
6335 case BIOSBootMenuMode_Disabled:
6336 bootMenuModeStr = "disabled";
6337 break;
6338 case BIOSBootMenuMode_MenuOnly:
6339 bootMenuModeStr = "menuonly";
6340 break;
6341 default:
6342 bootMenuModeStr = "messageandmenu";
6343 }
6344 CFGLDRSetBSTR (bootMenuNode, "mode", bootMenuModeStr);
6345 CFGLDRReleaseNode (bootMenuNode);
6346
6347 /* time offset (optional) */
6348 CFGNODE timeOffsetNode = 0;
6349 CFGLDRCreateChildNode (biosNode, "TimeOffset", &timeOffsetNode);
6350 LONG64 timeOffset;
6351 mBIOSSettings->COMGETTER(TimeOffset)(&timeOffset);
6352 CFGLDRSetInt64 (timeOffsetNode, "value", timeOffset);
6353 CFGLDRReleaseNode (timeOffsetNode);
6354
6355 /* PXE debug flag (optional) */
6356 CFGNODE pxedebugNode = 0;
6357 CFGLDRCreateChildNode (biosNode, "PXEDebug", &pxedebugNode);
6358 mBIOSSettings->COMGETTER(PXEDebugEnabled)(&fSet);
6359 CFGLDRSetBool (pxedebugNode, "enabled", !!fSet);
6360 CFGLDRReleaseNode (pxedebugNode);
6361
6362 /* IDE controller type */
6363 CFGNODE ideControllerNode = 0;
6364 IDEControllerType_T controllerType;
6365 CFGLDRCreateChildNode (biosNode, "IDEController", &ideControllerNode);
6366 mBIOSSettings->COMGETTER(IDEControllerType)(&controllerType);
6367 switch (controllerType)
6368 {
6369 case IDEControllerType_IDEControllerPIIX3:
6370 CFGLDRSetString (ideControllerNode, "type", "PIIX3");
6371 break;
6372 case IDEControllerType_IDEControllerPIIX4:
6373 CFGLDRSetString (ideControllerNode, "type", "PIIX4");
6374 break;
6375 default:
6376 ComAssertMsgFailedBreak (("Invalid IDE Controller type: %d\n",
6377 controllerType), rc = E_FAIL);
6378 }
6379 CFGLDRReleaseNode (ideControllerNode);
6380
6381 }
6382 CFGLDRReleaseNode(biosNode);
6383 }
6384
6385 /* DVD drive (required) */
6386 /// @todo (dmik) move the code to DVDDrive
6387 do
6388 {
6389 CFGNODE dvdNode = 0;
6390 CFGLDRCreateChildNode (aNode, "DVDDrive", &dvdNode);
6391
6392 BOOL fPassthrough;
6393 mDVDDrive->COMGETTER(Passthrough)(&fPassthrough);
6394 CFGLDRSetBool(dvdNode, "passthrough", !!fPassthrough);
6395
6396 switch (mDVDDrive->data()->mDriveState)
6397 {
6398 case DriveState_ImageMounted:
6399 {
6400 Assert (!mDVDDrive->data()->mDVDImage.isNull());
6401
6402 Guid id;
6403 rc = mDVDDrive->data()->mDVDImage->COMGETTER(Id) (id.asOutParam());
6404 Assert (!id.isEmpty());
6405
6406 CFGNODE imageNode = 0;
6407 CFGLDRCreateChildNode (dvdNode, "Image", &imageNode);
6408 CFGLDRSetUUID (imageNode, "uuid", id.ptr());
6409 CFGLDRReleaseNode (imageNode);
6410 break;
6411 }
6412 case DriveState_HostDriveCaptured:
6413 {
6414 Assert (!mDVDDrive->data()->mHostDrive.isNull());
6415
6416 Bstr name;
6417 rc = mDVDDrive->data()->mHostDrive->COMGETTER(Name) (name.asOutParam());
6418 Assert (!name.isEmpty());
6419
6420 CFGNODE hostDriveNode = 0;
6421 CFGLDRCreateChildNode (dvdNode, "HostDrive", &hostDriveNode);
6422 CFGLDRSetBSTR (hostDriveNode, "src", name);
6423 CFGLDRReleaseNode (hostDriveNode);
6424 break;
6425 }
6426 case DriveState_NotMounted:
6427 /* do nothing, i.e.leave the DVD drive node empty */
6428 break;
6429 default:
6430 ComAssertMsgFailedBreak (("Invalid DVD drive state: %d\n",
6431 mDVDDrive->data()->mDriveState),
6432 rc = E_FAIL);
6433 }
6434
6435 CFGLDRReleaseNode (dvdNode);
6436 }
6437 while (0);
6438
6439 CheckComRCReturnRC (rc);
6440
6441 /* Flooppy drive (required) */
6442 /// @todo (dmik) move the code to DVDDrive
6443 do
6444 {
6445 CFGNODE floppyNode = 0;
6446 CFGLDRCreateChildNode (aNode, "FloppyDrive", &floppyNode);
6447
6448 BOOL fFloppyEnabled;
6449 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
6450 CFGLDRSetBool (floppyNode, "enabled", !!fFloppyEnabled);
6451
6452 switch (mFloppyDrive->data()->mDriveState)
6453 {
6454 case DriveState_ImageMounted:
6455 {
6456 Assert (!mFloppyDrive->data()->mFloppyImage.isNull());
6457
6458 Guid id;
6459 rc = mFloppyDrive->data()->mFloppyImage->COMGETTER(Id) (id.asOutParam());
6460 Assert (!id.isEmpty());
6461
6462 CFGNODE imageNode = 0;
6463 CFGLDRCreateChildNode (floppyNode, "Image", &imageNode);
6464 CFGLDRSetUUID (imageNode, "uuid", id.ptr());
6465 CFGLDRReleaseNode (imageNode);
6466 break;
6467 }
6468 case DriveState_HostDriveCaptured:
6469 {
6470 Assert (!mFloppyDrive->data()->mHostDrive.isNull());
6471
6472 Bstr name;
6473 rc = mFloppyDrive->data()->mHostDrive->COMGETTER(Name) (name.asOutParam());
6474 Assert (!name.isEmpty());
6475
6476 CFGNODE hostDriveNode = 0;
6477 CFGLDRCreateChildNode (floppyNode, "HostDrive", &hostDriveNode);
6478 CFGLDRSetBSTR (hostDriveNode, "src", name);
6479 CFGLDRReleaseNode (hostDriveNode);
6480 break;
6481 }
6482 case DriveState_NotMounted:
6483 /* do nothing, i.e.leave the Floppy drive node empty */
6484 break;
6485 default:
6486 ComAssertMsgFailedBreak (("Invalid Floppy drive state: %d\n",
6487 mFloppyDrive->data()->mDriveState),
6488 rc = E_FAIL);
6489 }
6490
6491 CFGLDRReleaseNode (floppyNode);
6492 }
6493 while (0);
6494
6495 CheckComRCReturnRC (rc);
6496
6497
6498 /* USB Controller (required) */
6499 rc = mUSBController->saveSettings (aNode);
6500 CheckComRCReturnRC (rc);
6501
6502 /* Network adapters (required) */
6503 do
6504 {
6505 CFGNODE nwNode = 0;
6506 CFGLDRCreateChildNode (aNode, "Network", &nwNode);
6507
6508 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
6509 {
6510 CFGNODE networkAdapterNode = 0;
6511 CFGLDRAppendChildNode (nwNode, "Adapter", &networkAdapterNode);
6512
6513 CFGLDRSetUInt32 (networkAdapterNode, "slot", slot);
6514 CFGLDRSetBool (networkAdapterNode, "enabled",
6515 !!mNetworkAdapters [slot]->data()->mEnabled);
6516 CFGLDRSetBSTR (networkAdapterNode, "MACAddress",
6517 mNetworkAdapters [slot]->data()->mMACAddress);
6518 CFGLDRSetBool (networkAdapterNode, "cable",
6519 !!mNetworkAdapters [slot]->data()->mCableConnected);
6520
6521 CFGLDRSetUInt32 (networkAdapterNode, "speed",
6522 mNetworkAdapters [slot]->data()->mLineSpeed);
6523
6524 if (mNetworkAdapters [slot]->data()->mTraceEnabled)
6525 CFGLDRSetBool (networkAdapterNode, "trace", true);
6526
6527 CFGLDRSetBSTR (networkAdapterNode, "tracefile",
6528 mNetworkAdapters [slot]->data()->mTraceFile);
6529
6530 switch (mNetworkAdapters [slot]->data()->mAdapterType)
6531 {
6532 case NetworkAdapterType_NetworkAdapterAm79C970A:
6533 CFGLDRSetString (networkAdapterNode, "type", "Am79C970A");
6534 break;
6535 case NetworkAdapterType_NetworkAdapterAm79C973:
6536 CFGLDRSetString (networkAdapterNode, "type", "Am79C973");
6537 break;
6538 default:
6539 ComAssertMsgFailedBreak (("Invalid network adapter type: %d\n",
6540 mNetworkAdapters [slot]->data()->mAdapterType),
6541 rc = E_FAIL);
6542 }
6543
6544 CFGNODE attachmentNode = 0;
6545 switch (mNetworkAdapters [slot]->data()->mAttachmentType)
6546 {
6547 case NetworkAttachmentType_NoNetworkAttachment:
6548 {
6549 /* do nothing -- empty content */
6550 break;
6551 }
6552 case NetworkAttachmentType_NATNetworkAttachment:
6553 {
6554 CFGLDRAppendChildNode (networkAdapterNode, "NAT", &attachmentNode);
6555 break;
6556 }
6557 case NetworkAttachmentType_HostInterfaceNetworkAttachment:
6558 {
6559 CFGLDRAppendChildNode (networkAdapterNode, "HostInterface", &attachmentNode);
6560 const Bstr &name = mNetworkAdapters [slot]->data()->mHostInterface;
6561#ifdef RT_OS_WINDOWS
6562 Assert (!name.isNull());
6563#endif
6564#ifdef VBOX_WITH_UNIXY_TAP_NETWORKING
6565 if (!name.isEmpty())
6566#endif
6567 CFGLDRSetBSTR (attachmentNode, "name", name);
6568#ifdef VBOX_WITH_UNIXY_TAP_NETWORKING
6569 const Bstr &tapSetupApp =
6570 mNetworkAdapters [slot]->data()->mTAPSetupApplication;
6571 if (!tapSetupApp.isEmpty())
6572 CFGLDRSetBSTR (attachmentNode, "TAPSetup", tapSetupApp);
6573 const Bstr &tapTerminateApp =
6574 mNetworkAdapters [slot]->data()->mTAPTerminateApplication;
6575 if (!tapTerminateApp.isEmpty())
6576 CFGLDRSetBSTR (attachmentNode, "TAPTerminate", tapTerminateApp);
6577#endif /* VBOX_WITH_UNIXY_TAP_NETWORKING */
6578 break;
6579 }
6580 case NetworkAttachmentType_InternalNetworkAttachment:
6581 {
6582 CFGLDRAppendChildNode (networkAdapterNode, "InternalNetwork", &attachmentNode);
6583 const Bstr &name = mNetworkAdapters[slot]->data()->mInternalNetwork;
6584 Assert(!name.isNull());
6585 CFGLDRSetBSTR (attachmentNode, "name", name);
6586 break;
6587 }
6588 default:
6589 {
6590 ComAssertFailedBreak (rc = E_FAIL);
6591 break;
6592 }
6593 }
6594 if (attachmentNode)
6595 CFGLDRReleaseNode (attachmentNode);
6596
6597 CFGLDRReleaseNode (networkAdapterNode);
6598 }
6599
6600 CFGLDRReleaseNode (nwNode);
6601 }
6602 while (0);
6603
6604 if (FAILED (rc))
6605 return rc;
6606
6607 /* Serial ports */
6608 CFGNODE serialNode = 0;
6609 CFGLDRCreateChildNode (aNode, "Uart", &serialNode);
6610
6611 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot++)
6612 {
6613 rc = mSerialPorts [slot]->saveSettings (serialNode);
6614 CheckComRCReturnRC (rc);
6615 }
6616 CFGLDRReleaseNode (serialNode);
6617
6618 /* Parallel ports */
6619 CFGNODE parallelNode = 0;
6620 CFGLDRCreateChildNode (aNode, "Lpt", &parallelNode);
6621
6622 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot++)
6623 {
6624 rc = mParallelPorts [slot]->saveSettings (parallelNode);
6625 CheckComRCReturnRC (rc);
6626 }
6627 CFGLDRReleaseNode (parallelNode);
6628
6629 /* Audio adapter */
6630 do
6631 {
6632 CFGNODE adapterNode = 0;
6633 CFGLDRCreateChildNode (aNode, "AudioAdapter", &adapterNode);
6634
6635 switch (mAudioAdapter->data()->mAudioDriver)
6636 {
6637 case AudioDriverType_NullAudioDriver:
6638 {
6639 CFGLDRSetString (adapterNode, "driver", "null");
6640 break;
6641 }
6642#ifdef RT_OS_WINDOWS
6643 case AudioDriverType_WINMMAudioDriver:
6644# ifdef VBOX_WITH_WINMM
6645 {
6646 CFGLDRSetString (adapterNode, "driver", "winmm");
6647 break;
6648 }
6649# endif
6650 case AudioDriverType_DSOUNDAudioDriver:
6651 {
6652 CFGLDRSetString (adapterNode, "driver", "dsound");
6653 break;
6654 }
6655#endif /* RT_OS_WINDOWS */
6656#ifdef RT_OS_LINUX
6657 case AudioDriverType_ALSAAudioDriver:
6658# ifdef VBOX_WITH_ALSA
6659 {
6660 CFGLDRSetString (adapterNode, "driver", "alsa");
6661 break;
6662 }
6663# endif
6664 case AudioDriverType_OSSAudioDriver:
6665 {
6666 CFGLDRSetString (adapterNode, "driver", "oss");
6667 break;
6668 }
6669#endif /* RT_OS_LINUX */
6670#ifdef RT_OS_DARWIN
6671 case AudioDriverType_CoreAudioDriver:
6672 {
6673 CFGLDRSetString (adapterNode, "driver", "coreaudio");
6674 break;
6675 }
6676#endif
6677#ifdef RT_OS_OS2
6678 case AudioDriverType_MMPMAudioDriver:
6679 {
6680 CFGLDRSetString (adapterNode, "driver", "mmpm");
6681 break;
6682 }
6683#endif
6684 default:
6685 ComAssertMsgFailedBreak (("Wrong audio driver type! driver = %d\n",
6686 mAudioAdapter->data()->mAudioDriver),
6687 rc = E_FAIL);
6688 }
6689
6690 CFGLDRSetBool (adapterNode, "enabled", !!mAudioAdapter->data()->mEnabled);
6691
6692 CFGLDRReleaseNode (adapterNode);
6693 }
6694 while (0);
6695
6696 if (FAILED (rc))
6697 return rc;
6698
6699 /* Shared folders */
6700 do
6701 {
6702 CFGNODE sharedFoldersNode = 0;
6703 CFGLDRCreateChildNode (aNode, "SharedFolders", &sharedFoldersNode);
6704
6705 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6706 it != mHWData->mSharedFolders.end();
6707 ++ it)
6708 {
6709 ComObjPtr <SharedFolder> folder = *it;
6710
6711 CFGNODE folderNode = 0;
6712 CFGLDRAppendChildNode (sharedFoldersNode, "SharedFolder", &folderNode);
6713
6714 /* all are mandatory */
6715 CFGLDRSetBSTR (folderNode, "name", folder->name());
6716 CFGLDRSetBSTR (folderNode, "hostPath", folder->hostPath());
6717
6718 CFGLDRReleaseNode (folderNode);
6719 }
6720
6721 CFGLDRReleaseNode (sharedFoldersNode);
6722 }
6723 while (0);
6724
6725 /* Clipboard */
6726 {
6727 CFGNODE clipNode = 0;
6728 CFGLDRCreateChildNode (aNode, "Clipboard", &clipNode);
6729
6730 const char *mode = "Disabled";
6731 switch (mHWData->mClipboardMode)
6732 {
6733 case ClipboardMode_ClipDisabled:
6734 /* already assigned */
6735 break;
6736 case ClipboardMode_ClipHostToGuest:
6737 mode = "HostToGuest";
6738 break;
6739 case ClipboardMode_ClipGuestToHost:
6740 mode = "GuestToHost";
6741 break;
6742 case ClipboardMode_ClipBidirectional:
6743 mode = "Bidirectional";
6744 break;
6745 default:
6746 AssertMsgFailed (("Clipboard mode %d is invalid",
6747 mHWData->mClipboardMode));
6748 break;
6749 }
6750 CFGLDRSetString (clipNode, "mode", mode);
6751
6752 CFGLDRReleaseNode (clipNode);
6753 }
6754
6755 /* Guest */
6756 {
6757 CFGNODE guestNode = 0;
6758 CFGLDRGetChildNode (aNode, "Guest", 0, &guestNode);
6759 /* first, delete the entire node if exists */
6760 if (guestNode)
6761 CFGLDRDeleteNode (guestNode);
6762 /* then recreate it */
6763 guestNode = 0;
6764 CFGLDRCreateChildNode (aNode, "Guest", &guestNode);
6765
6766 CFGLDRSetUInt32 (guestNode, "MemoryBalloonSize",
6767 mHWData->mMemoryBalloonSize);
6768 CFGLDRSetUInt32 (guestNode, "StatisticsUpdateInterval",
6769 mHWData->mStatisticsUpdateInterval);
6770
6771 CFGLDRReleaseNode (guestNode);
6772 }
6773
6774 return rc;
6775}
6776
6777/**
6778 * Saves the hard disk confguration.
6779 * It is assumed that the given node is empty.
6780 *
6781 * @param aNode <HardDiskAttachments> node to save the hard disk confguration to
6782 */
6783HRESULT Machine::saveHardDisks (CFGNODE aNode)
6784{
6785 AssertReturn (aNode, E_INVALIDARG);
6786
6787 HRESULT rc = S_OK;
6788
6789 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
6790 it != mHDData->mHDAttachments.end() && SUCCEEDED (rc);
6791 ++ it)
6792 {
6793 ComObjPtr <HardDiskAttachment> att = *it;
6794
6795 CFGNODE hdNode = 0;
6796 CFGLDRAppendChildNode (aNode, "HardDiskAttachment", &hdNode);
6797
6798 do
6799 {
6800 const char *bus = NULL;
6801 switch (att->controller())
6802 {
6803 case DiskControllerType_IDE0Controller: bus = "ide0"; break;
6804 case DiskControllerType_IDE1Controller: bus = "ide1"; break;
6805 default:
6806 ComAssertFailedBreak (rc = E_FAIL);
6807 }
6808 if (FAILED (rc))
6809 break;
6810
6811 const char *dev = NULL;
6812 switch (att->deviceNumber())
6813 {
6814 case 0: dev = "master"; break;
6815 case 1: dev = "slave"; break;
6816 default:
6817 ComAssertFailedBreak (rc = E_FAIL);
6818 }
6819 if (FAILED (rc))
6820 break;
6821
6822 CFGLDRSetUUID (hdNode, "hardDisk", att->hardDisk()->id());
6823 CFGLDRSetString (hdNode, "bus", bus);
6824 CFGLDRSetString (hdNode, "device", dev);
6825 }
6826 while (0);
6827
6828 CFGLDRReleaseNode (hdNode);
6829 }
6830
6831 return rc;
6832}
6833
6834/**
6835 * Saves machine state settings as defined by aFlags
6836 * (SaveSTS_* values).
6837 *
6838 * @param aFlags a combination of SaveSTS_* flags
6839 *
6840 * @note Locks objects!
6841 */
6842HRESULT Machine::saveStateSettings (int aFlags)
6843{
6844 if (aFlags == 0)
6845 return S_OK;
6846
6847 AutoCaller autoCaller (this);
6848 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6849
6850 AutoLock alock (this);
6851
6852 /* load the config file */
6853 CFGHANDLE configLoader = 0;
6854 HRESULT rc = openConfigLoader (&configLoader);
6855 if (FAILED (rc))
6856 return rc;
6857
6858 CFGNODE machineNode = 0;
6859 CFGLDRGetNode (configLoader, "VirtualBox/Machine", 0, &machineNode);
6860
6861 do
6862 {
6863 ComAssertBreak (machineNode, rc = E_FAIL);
6864
6865 if (aFlags & SaveSTS_CurStateModified)
6866 {
6867 if (!mData->mCurrentStateModified)
6868 CFGLDRSetBool (machineNode, "currentStateModified", false);
6869 else
6870 CFGLDRDeleteAttribute (machineNode, "currentStateModified");
6871 }
6872
6873 if (aFlags & SaveSTS_StateFilePath)
6874 {
6875 if (mSSData->mStateFilePath)
6876 CFGLDRSetBSTR (machineNode, "stateFile", mSSData->mStateFilePath);
6877 else
6878 CFGLDRDeleteAttribute (machineNode, "stateFile");
6879 }
6880
6881 if (aFlags & SaveSTS_StateTimeStamp)
6882 {
6883 Assert (mData->mMachineState != MachineState_Aborted ||
6884 mSSData->mStateFilePath.isNull());
6885
6886 CFGLDRSetDateTime (machineNode, "lastStateChange",
6887 mData->mLastStateChange);
6888
6889 // set the aborted attribute when appropriate
6890 if (mData->mMachineState == MachineState_Aborted)
6891 CFGLDRSetBool (machineNode, "aborted", true);
6892 else
6893 CFGLDRDeleteAttribute (machineNode, "aborted");
6894 }
6895 }
6896 while (0);
6897
6898 if (machineNode)
6899 CFGLDRReleaseNode (machineNode);
6900
6901 if (SUCCEEDED (rc))
6902 rc = closeConfigLoader (configLoader, true /* aSaveBeforeClose */);
6903 else
6904 closeConfigLoader (configLoader, false /* aSaveBeforeClose */);
6905
6906 return rc;
6907}
6908
6909/**
6910 * Cleans up all differencing hard disks based on immutable hard disks.
6911 *
6912 * @note Locks objects!
6913 */
6914HRESULT Machine::wipeOutImmutableDiffs()
6915{
6916 AutoCaller autoCaller (this);
6917 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6918
6919 AutoReaderLock alock (this);
6920
6921 AssertReturn (mData->mMachineState == MachineState_PoweredOff ||
6922 mData->mMachineState == MachineState_Aborted, E_FAIL);
6923
6924 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
6925 it != mHDData->mHDAttachments.end();
6926 ++ it)
6927 {
6928 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
6929 AutoLock hdLock (hd);
6930
6931 if(hd->isParentImmutable())
6932 {
6933 /// @todo (dmik) no error handling for now
6934 // (need async error reporting for this)
6935 hd->asVDI()->wipeOutImage();
6936 }
6937 }
6938
6939 return S_OK;
6940}
6941
6942/**
6943 * Fixes up lazy hard disk attachments by creating or deleting differencing
6944 * hard disks when machine settings are being committed.
6945 * Must be called only from #commit().
6946 *
6947 * @note Locks objects!
6948 */
6949HRESULT Machine::fixupHardDisks (bool aCommit)
6950{
6951 AutoCaller autoCaller (this);
6952 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6953
6954 AutoLock alock (this);
6955
6956 /* no attac/detach operations -- nothing to do */
6957 if (!mHDData.isBackedUp())
6958 {
6959 mHDData->mHDAttachmentsChanged = false;
6960 return S_OK;
6961 }
6962
6963 AssertReturn (mData->mRegistered, E_FAIL);
6964
6965 if (aCommit)
6966 {
6967 /*
6968 * changes are being committed,
6969 * perform actual diff image creation, deletion etc.
6970 */
6971
6972 /* take a copy of backed up attachments (will modify it) */
6973 HDData::HDAttachmentList backedUp = mHDData.backedUpData()->mHDAttachments;
6974 /* list of new diffs created */
6975 std::list <ComObjPtr <HardDisk> > newDiffs;
6976
6977 HRESULT rc = S_OK;
6978
6979 /* go through current attachments */
6980 for (HDData::HDAttachmentList::const_iterator
6981 it = mHDData->mHDAttachments.begin();
6982 it != mHDData->mHDAttachments.end();
6983 ++ it)
6984 {
6985 ComObjPtr <HardDiskAttachment> hda = *it;
6986 ComObjPtr <HardDisk> hd = hda->hardDisk();
6987 AutoLock hdLock (hd);
6988
6989 if (!hda->isDirty())
6990 {
6991 /*
6992 * not dirty, therefore was either attached before backing up
6993 * or doesn't need any fixup (already fixed up); try to locate
6994 * this hard disk among backed up attachments and remove from
6995 * there to prevent it from being deassociated/deleted
6996 */
6997 HDData::HDAttachmentList::iterator oldIt;
6998 for (oldIt = backedUp.begin(); oldIt != backedUp.end(); ++ oldIt)
6999 if ((*oldIt)->hardDisk().equalsTo (hd))
7000 break;
7001 if (oldIt != backedUp.end())
7002 {
7003 /* remove from there */
7004 backedUp.erase (oldIt);
7005 Log3 (("FC: %ls found in old\n", hd->toString().raw()));
7006 }
7007 }
7008 else
7009 {
7010 /* dirty, determine what to do */
7011
7012 bool needDiff = false;
7013 bool searchAmongSnapshots = false;
7014
7015 switch (hd->type())
7016 {
7017 case HardDiskType_ImmutableHardDisk:
7018 {
7019 /* decrease readers increased in AttachHardDisk() */
7020 hd->releaseReader();
7021 Log3 (("FC: %ls released\n", hd->toString().raw()));
7022 /* indicate we need a diff (indirect attachment) */
7023 needDiff = true;
7024 break;
7025 }
7026 case HardDiskType_WritethroughHardDisk:
7027 {
7028 /* reset the dirty flag */
7029 hda->updateHardDisk (hd, false /* aDirty */);
7030 Log3 (("FC: %ls updated\n", hd->toString().raw()));
7031 break;
7032 }
7033 case HardDiskType_NormalHardDisk:
7034 {
7035 if (hd->snapshotId().isEmpty())
7036 {
7037 /* reset the dirty flag */
7038 hda->updateHardDisk (hd, false /* aDirty */);
7039 Log3 (("FC: %ls updated\n", hd->toString().raw()));
7040 }
7041 else
7042 {
7043 /* decrease readers increased in AttachHardDisk() */
7044 hd->releaseReader();
7045 Log3 (("FC: %ls released\n", hd->toString().raw()));
7046 /* indicate we need a diff (indirect attachment) */
7047 needDiff = true;
7048 /* search for the most recent base among snapshots */
7049 searchAmongSnapshots = true;
7050 }
7051 break;
7052 }
7053 }
7054
7055 if (!needDiff)
7056 continue;
7057
7058 bool createDiff = false;
7059
7060 /*
7061 * see whether any previously attached hard disk has the
7062 * the currently attached one (Normal or Independent) as
7063 * the root
7064 */
7065
7066 HDData::HDAttachmentList::iterator foundIt = backedUp.end();
7067
7068 for (HDData::HDAttachmentList::iterator it = backedUp.begin();
7069 it != backedUp.end();
7070 ++ it)
7071 {
7072 if ((*it)->hardDisk()->root().equalsTo (hd))
7073 {
7074 /*
7075 * matched dev and ctl (i.e. attached to the same place)
7076 * will win and immediately stop the search; otherwise
7077 * the first attachment that matched the hd only will
7078 * be used
7079 */
7080 if ((*it)->deviceNumber() == hda->deviceNumber() &&
7081 (*it)->controller() == hda->controller())
7082 {
7083 foundIt = it;
7084 break;
7085 }
7086 else
7087 if (foundIt == backedUp.end())
7088 {
7089 /*
7090 * not an exact match; ensure there is no exact match
7091 * among other current attachments referring the same
7092 * root (to prevent this attachmend from reusing the
7093 * hard disk of the other attachment that will later
7094 * give the exact match or already gave it before)
7095 */
7096 bool canReuse = true;
7097 for (HDData::HDAttachmentList::const_iterator
7098 it2 = mHDData->mHDAttachments.begin();
7099 it2 != mHDData->mHDAttachments.end();
7100 ++ it2)
7101 {
7102 if ((*it2)->deviceNumber() == (*it)->deviceNumber() &&
7103 (*it2)->controller() == (*it)->controller() &&
7104 (*it2)->hardDisk()->root().equalsTo (hd))
7105 {
7106 /*
7107 * the exact match, either non-dirty or dirty
7108 * one refers the same root: in both cases
7109 * we cannot reuse the hard disk, so break
7110 */
7111 canReuse = false;
7112 break;
7113 }
7114 }
7115
7116 if (canReuse)
7117 foundIt = it;
7118 }
7119 }
7120 }
7121
7122 if (foundIt != backedUp.end())
7123 {
7124 /* found either one or another, reuse the diff */
7125 hda->updateHardDisk ((*foundIt)->hardDisk(),
7126 false /* aDirty */);
7127 Log3 (("FC: %ls reused as %ls\n", hd->toString().raw(),
7128 (*foundIt)->hardDisk()->toString().raw()));
7129 /* remove from there */
7130 backedUp.erase (foundIt);
7131 }
7132 else
7133 {
7134 /* was not attached, need a diff */
7135 createDiff = true;
7136 }
7137
7138 if (!createDiff)
7139 continue;
7140
7141 ComObjPtr <HardDisk> baseHd = hd;
7142
7143 if (searchAmongSnapshots)
7144 {
7145 /*
7146 * find the most recent diff based on the currently
7147 * attached root (Normal hard disk) among snapshots
7148 */
7149
7150 ComObjPtr <Snapshot> snap = mData->mCurrentSnapshot;
7151
7152 while (snap)
7153 {
7154 AutoLock snapLock (snap);
7155
7156 const HDData::HDAttachmentList &snapAtts =
7157 snap->data().mMachine->hdData()->mHDAttachments;
7158
7159 HDData::HDAttachmentList::const_iterator foundIt = snapAtts.end();
7160
7161 for (HDData::HDAttachmentList::const_iterator
7162 it = snapAtts.begin(); it != snapAtts.end(); ++ it)
7163 {
7164 if ((*it)->hardDisk()->root().equalsTo (hd))
7165 {
7166 /*
7167 * matched dev and ctl (i.e. attached to the same place)
7168 * will win and immediately stop the search; otherwise
7169 * the first attachment that matched the hd only will
7170 * be used
7171 */
7172 if ((*it)->deviceNumber() == hda->deviceNumber() &&
7173 (*it)->controller() == hda->controller())
7174 {
7175 foundIt = it;
7176 break;
7177 }
7178 else
7179 if (foundIt == snapAtts.end())
7180 foundIt = it;
7181 }
7182 }
7183
7184 if (foundIt != snapAtts.end())
7185 {
7186 /* the most recent diff has been found, use as a base */
7187 baseHd = (*foundIt)->hardDisk();
7188 Log3 (("FC: %ls: recent found %ls\n",
7189 hd->toString().raw(), baseHd->toString().raw()));
7190 break;
7191 }
7192
7193 snap = snap->parent();
7194 }
7195 }
7196
7197 /* create a new diff for the hard disk being indirectly attached */
7198
7199 AutoLock baseHdLock (baseHd);
7200 baseHd->addReader();
7201
7202 ComObjPtr <HVirtualDiskImage> vdi;
7203 rc = baseHd->createDiffHardDisk (mUserData->mSnapshotFolderFull,
7204 mData->mUuid, vdi, NULL);
7205 baseHd->releaseReader();
7206 CheckComRCBreakRC (rc);
7207
7208 newDiffs.push_back (ComObjPtr <HardDisk> (vdi));
7209
7210 /* update the attachment and reset the dirty flag */
7211 hda->updateHardDisk (ComObjPtr <HardDisk> (vdi),
7212 false /* aDirty */);
7213 Log3 (("FC: %ls: diff created %ls\n",
7214 baseHd->toString().raw(), vdi->toString().raw()));
7215 }
7216 }
7217
7218 if (FAILED (rc))
7219 {
7220 /* delete diffs we created */
7221 for (std::list <ComObjPtr <HardDisk> >::const_iterator
7222 it = newDiffs.begin(); it != newDiffs.end(); ++ it)
7223 {
7224 /*
7225 * unregisterDiffHardDisk() is supposed to delete and uninit
7226 * the differencing hard disk
7227 */
7228 mParent->unregisterDiffHardDisk (*it);
7229 /* too bad if we fail here, but nothing to do, just continue */
7230 }
7231
7232 /* the best is to rollback the changes... */
7233 mHDData.rollback();
7234 mHDData->mHDAttachmentsChanged = false;
7235 Log3 (("FC: ROLLED BACK\n"));
7236 return rc;
7237 }
7238
7239 /*
7240 * go through the rest of old attachments and delete diffs
7241 * or deassociate hard disks from machines (they will become detached)
7242 */
7243 for (HDData::HDAttachmentList::iterator
7244 it = backedUp.begin(); it != backedUp.end(); ++ it)
7245 {
7246 ComObjPtr <HardDiskAttachment> hda = *it;
7247 ComObjPtr <HardDisk> hd = hda->hardDisk();
7248 AutoLock hdLock (hd);
7249
7250 if (hd->isDifferencing())
7251 {
7252 /*
7253 * unregisterDiffHardDisk() is supposed to delete and uninit
7254 * the differencing hard disk
7255 */
7256 Log3 (("FC: %ls diff deleted\n", hd->toString().raw()));
7257 rc = mParent->unregisterDiffHardDisk (hd);
7258 /*
7259 * too bad if we fail here, but nothing to do, just continue
7260 * (the last rc will be returned to the caller though)
7261 */
7262 }
7263 else
7264 {
7265 /* deassociate from this machine */
7266 Log3 (("FC: %ls deassociated\n", hd->toString().raw()));
7267 hd->setMachineId (Guid());
7268 }
7269 }
7270
7271 /* commit all the changes */
7272 mHDData->mHDAttachmentsChanged = mHDData.hasActualChanges();
7273 mHDData.commit();
7274 Log3 (("FC: COMMITTED\n"));
7275
7276 return rc;
7277 }
7278
7279 /*
7280 * changes are being rolled back,
7281 * go trhough all current attachments and fix up dirty ones
7282 * the way it is done in DetachHardDisk()
7283 */
7284
7285 for (HDData::HDAttachmentList::iterator it = mHDData->mHDAttachments.begin();
7286 it != mHDData->mHDAttachments.end();
7287 ++ it)
7288 {
7289 ComObjPtr <HardDiskAttachment> hda = *it;
7290 ComObjPtr <HardDisk> hd = hda->hardDisk();
7291 AutoLock hdLock (hd);
7292
7293 if (hda->isDirty())
7294 {
7295 switch (hd->type())
7296 {
7297 case HardDiskType_ImmutableHardDisk:
7298 {
7299 /* decrease readers increased in AttachHardDisk() */
7300 hd->releaseReader();
7301 Log3 (("FR: %ls released\n", hd->toString().raw()));
7302 break;
7303 }
7304 case HardDiskType_WritethroughHardDisk:
7305 {
7306 /* deassociate from this machine */
7307 hd->setMachineId (Guid());
7308 Log3 (("FR: %ls deassociated\n", hd->toString().raw()));
7309 break;
7310 }
7311 case HardDiskType_NormalHardDisk:
7312 {
7313 if (hd->snapshotId().isEmpty())
7314 {
7315 /* deassociate from this machine */
7316 hd->setMachineId (Guid());
7317 Log3 (("FR: %ls deassociated\n", hd->toString().raw()));
7318 }
7319 else
7320 {
7321 /* decrease readers increased in AttachHardDisk() */
7322 hd->releaseReader();
7323 Log3 (("FR: %ls released\n", hd->toString().raw()));
7324 }
7325
7326 break;
7327 }
7328 }
7329 }
7330 }
7331
7332 /* rollback all the changes */
7333 mHDData.rollback();
7334 Log3 (("FR: ROLLED BACK\n"));
7335
7336 return S_OK;
7337}
7338
7339/**
7340 * Creates differencing hard disks for all normal hard disks
7341 * and replaces attachments to refer to created disks.
7342 * Used when taking a snapshot or when discarding the current state.
7343 *
7344 * @param aSnapshotId ID of the snapshot being taken
7345 * or NULL if the current state is being discarded
7346 * @param aFolder folder where to create diff. hard disks
7347 * @param aProgress progress object to run (must contain at least as
7348 * many operations left as the number of VDIs attached)
7349 * @param aOnline whether the machine is online (i.e., when the EMT
7350 * thread is paused, OR when current hard disks are
7351 * marked as busy for some other reason)
7352 *
7353 * @note
7354 * The progress object is not marked as completed, neither on success
7355 * nor on failure. This is a responsibility of the caller.
7356 *
7357 * @note Locks mParent + this object for writing
7358 */
7359HRESULT Machine::createSnapshotDiffs (const Guid *aSnapshotId,
7360 const Bstr &aFolder,
7361 const ComObjPtr <Progress> &aProgress,
7362 bool aOnline)
7363{
7364 AssertReturn (!aFolder.isEmpty(), E_FAIL);
7365
7366 AutoCaller autoCaller (this);
7367 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7368
7369 /* accessing mParent methods below needs mParent lock */
7370 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
7371
7372 HRESULT rc = S_OK;
7373
7374 // first pass: check accessibility before performing changes
7375 if (!aOnline)
7376 {
7377 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
7378 it != mHDData->mHDAttachments.end();
7379 ++ it)
7380 {
7381 ComObjPtr <HardDiskAttachment> hda = *it;
7382 ComObjPtr <HardDisk> hd = hda->hardDisk();
7383 AutoLock hdLock (hd);
7384
7385 ComAssertMsgBreak (hd->type() == HardDiskType_NormalHardDisk,
7386 ("Invalid hard disk type %d\n", hd->type()),
7387 rc = E_FAIL);
7388
7389 ComAssertMsgBreak (!hd->isParentImmutable() ||
7390 hd->storageType() == HardDiskStorageType_VirtualDiskImage,
7391 ("Invalid hard disk storage type %d\n", hd->storageType()),
7392 rc = E_FAIL);
7393
7394 Bstr accessError;
7395 rc = hd->getAccessible (accessError);
7396 CheckComRCBreakRC (rc);
7397
7398 if (!accessError.isNull())
7399 {
7400 rc = setError (E_FAIL,
7401 tr ("Hard disk '%ls' is not accessible (%ls)"),
7402 hd->toString().raw(), accessError.raw());
7403 break;
7404 }
7405 }
7406 CheckComRCReturnRC (rc);
7407 }
7408
7409 HDData::HDAttachmentList attachments;
7410
7411 // second pass: perform changes
7412 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
7413 it != mHDData->mHDAttachments.end();
7414 ++ it)
7415 {
7416 ComObjPtr <HardDiskAttachment> hda = *it;
7417 ComObjPtr <HardDisk> hd = hda->hardDisk();
7418 AutoLock hdLock (hd);
7419
7420 ComObjPtr <HardDisk> parent = hd->parent();
7421 AutoLock parentHdLock (parent);
7422
7423 ComObjPtr <HardDisk> newHd;
7424
7425 // clear busy flag if the VM is online
7426 if (aOnline)
7427 hd->clearBusy();
7428 // increase readers
7429 hd->addReader();
7430
7431 if (hd->isParentImmutable())
7432 {
7433 aProgress->advanceOperation (Bstr (Utf8StrFmt (
7434 tr ("Preserving immutable hard disk '%ls'"),
7435 parent->toString (true /* aShort */).raw())));
7436
7437 parentHdLock.unlock();
7438 alock.leave();
7439
7440 // create a copy of the independent diff
7441 ComObjPtr <HVirtualDiskImage> vdi;
7442 rc = hd->asVDI()->cloneDiffImage (aFolder, mData->mUuid, vdi,
7443 aProgress);
7444 newHd = vdi;
7445
7446 alock.enter();
7447 parentHdLock.lock();
7448
7449 // decrease readers (hd is no more used for reading in any case)
7450 hd->releaseReader();
7451 }
7452 else
7453 {
7454 // checked in the first pass
7455 Assert (hd->type() == HardDiskType_NormalHardDisk);
7456
7457 aProgress->advanceOperation (Bstr (Utf8StrFmt (
7458 tr ("Creating a differencing hard disk for '%ls'"),
7459 hd->root()->toString (true /* aShort */).raw())));
7460
7461 parentHdLock.unlock();
7462 alock.leave();
7463
7464 // create a new diff for the image being attached
7465 ComObjPtr <HVirtualDiskImage> vdi;
7466 rc = hd->createDiffHardDisk (aFolder, mData->mUuid, vdi, aProgress);
7467 newHd = vdi;
7468
7469 alock.enter();
7470 parentHdLock.lock();
7471
7472 if (SUCCEEDED (rc))
7473 {
7474 // if online, hd must keep a reader referece
7475 if (!aOnline)
7476 hd->releaseReader();
7477 }
7478 else
7479 {
7480 // decrease readers
7481 hd->releaseReader();
7482 }
7483 }
7484
7485 if (SUCCEEDED (rc))
7486 {
7487 ComObjPtr <HardDiskAttachment> newHda;
7488 newHda.createObject();
7489 rc = newHda->init (newHd, hda->controller(), hda->deviceNumber(),
7490 false /* aDirty */);
7491
7492 if (SUCCEEDED (rc))
7493 {
7494 // associate the snapshot id with the old hard disk
7495 if (hd->type() != HardDiskType_WritethroughHardDisk && aSnapshotId)
7496 hd->setSnapshotId (*aSnapshotId);
7497
7498 // add the new attachment
7499 attachments.push_back (newHda);
7500
7501 // if online, newHd must be marked as busy
7502 if (aOnline)
7503 newHd->setBusy();
7504 }
7505 }
7506
7507 if (FAILED (rc))
7508 {
7509 // set busy flag back if the VM is online
7510 if (aOnline)
7511 hd->setBusy();
7512 break;
7513 }
7514 }
7515
7516 if (SUCCEEDED (rc))
7517 {
7518 // replace the whole list of attachments with the new one
7519 mHDData->mHDAttachments = attachments;
7520 }
7521 else
7522 {
7523 // delete those diffs we've just created
7524 for (HDData::HDAttachmentList::const_iterator it = attachments.begin();
7525 it != attachments.end();
7526 ++ it)
7527 {
7528 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
7529 AutoLock hdLock (hd);
7530 Assert (hd->children().size() == 0);
7531 Assert (hd->isDifferencing());
7532 // unregisterDiffHardDisk() is supposed to delete and uninit
7533 // the differencing hard disk
7534 mParent->unregisterDiffHardDisk (hd);
7535 }
7536 }
7537
7538 return rc;
7539}
7540
7541/**
7542 * Deletes differencing hard disks created by createSnapshotDiffs() in case
7543 * if snapshot creation was failed.
7544 *
7545 * @param aSnapshot failed snapshot
7546 *
7547 * @note Locks mParent + this object for writing.
7548 */
7549HRESULT Machine::deleteSnapshotDiffs (const ComObjPtr <Snapshot> &aSnapshot)
7550{
7551 AssertReturn (!aSnapshot.isNull(), E_FAIL);
7552
7553 AutoCaller autoCaller (this);
7554 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7555
7556 /* accessing mParent methods below needs mParent lock */
7557 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
7558
7559 /* short cut: check whether attachments are all the same */
7560 if (mHDData->mHDAttachments == aSnapshot->data().mMachine->mHDData->mHDAttachments)
7561 return S_OK;
7562
7563 HRESULT rc = S_OK;
7564
7565 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
7566 it != mHDData->mHDAttachments.end();
7567 ++ it)
7568 {
7569 ComObjPtr <HardDiskAttachment> hda = *it;
7570 ComObjPtr <HardDisk> hd = hda->hardDisk();
7571 AutoLock hdLock (hd);
7572
7573 ComObjPtr <HardDisk> parent = hd->parent();
7574 AutoLock parentHdLock (parent);
7575
7576 if (!parent || parent->snapshotId() != aSnapshot->data().mId)
7577 continue;
7578
7579 /* must not have children */
7580 ComAssertRet (hd->children().size() == 0, E_FAIL);
7581
7582 /* deassociate the old hard disk from the given snapshot's ID */
7583 parent->setSnapshotId (Guid());
7584
7585 /* unregisterDiffHardDisk() is supposed to delete and uninit
7586 * the differencing hard disk */
7587 rc = mParent->unregisterDiffHardDisk (hd);
7588 /* continue on error */
7589 }
7590
7591 /* restore the whole list of attachments from the failed snapshot */
7592 mHDData->mHDAttachments = aSnapshot->data().mMachine->mHDData->mHDAttachments;
7593
7594 return rc;
7595}
7596
7597/**
7598 * Helper to lock the machine configuration for write access.
7599 *
7600 * @return S_OK or E_FAIL and sets error info on failure
7601 *
7602 * @note Doesn't lock anything (must be called from this object's lock)
7603 */
7604HRESULT Machine::lockConfig()
7605{
7606 HRESULT rc = S_OK;
7607
7608 if (!isConfigLocked())
7609 {
7610 /* open the associated config file */
7611 int vrc = RTFileOpen (&mData->mHandleCfgFile,
7612 Utf8Str (mData->mConfigFileFull),
7613 RTFILE_O_READWRITE | RTFILE_O_OPEN |
7614 RTFILE_O_DENY_WRITE);
7615 if (VBOX_FAILURE (vrc))
7616 mData->mHandleCfgFile = NIL_RTFILE;
7617 }
7618
7619 LogFlowThisFunc (("mConfigFile={%ls}, mHandleCfgFile=%d, rc=%08X\n",
7620 mData->mConfigFileFull.raw(), mData->mHandleCfgFile, rc));
7621 return rc;
7622}
7623
7624/**
7625 * Helper to unlock the machine configuration from write access
7626 *
7627 * @return S_OK
7628 *
7629 * @note Doesn't lock anything.
7630 * @note Not thread safe (must be called from this object's lock).
7631 */
7632HRESULT Machine::unlockConfig()
7633{
7634 HRESULT rc = S_OK;
7635
7636 if (isConfigLocked())
7637 {
7638 RTFileFlush(mData->mHandleCfgFile);
7639 RTFileClose(mData->mHandleCfgFile);
7640 /** @todo flush the directory. */
7641 mData->mHandleCfgFile = NIL_RTFILE;
7642 }
7643
7644 LogFlowThisFunc (("\n"));
7645
7646 return rc;
7647}
7648
7649/**
7650 * Returns true if the settings file is located in the directory named exactly
7651 * as the machine. This will be true if the machine settings structure was
7652 * created by default in #openConfigLoader().
7653 *
7654 * @param aSettingsDir if not NULL, the full machine settings file directory
7655 * name will be assigned there.
7656 *
7657 * @note Doesn't lock anything.
7658 * @note Not thread safe (must be called from this object's lock).
7659 */
7660bool Machine::isInOwnDir (Utf8Str *aSettingsDir /* = NULL */)
7661{
7662 Utf8Str settingsDir = mData->mConfigFileFull;
7663 RTPathStripFilename (settingsDir.mutableRaw());
7664 char *dirName = RTPathFilename (settingsDir);
7665
7666 AssertReturn (dirName, false);
7667
7668 /* if we don't rename anything on name change, return false shorlty */
7669 if (!mUserData->mNameSync)
7670 return false;
7671
7672 if (aSettingsDir)
7673 *aSettingsDir = settingsDir;
7674
7675 return Bstr (dirName) == mUserData->mName;
7676}
7677
7678/**
7679 * @note Locks objects for reading!
7680 */
7681bool Machine::isModified()
7682{
7683 AutoCaller autoCaller (this);
7684 AssertComRCReturn (autoCaller.rc(), false);
7685
7686 AutoReaderLock alock (this);
7687
7688 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7689 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
7690 return true;
7691
7692 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7693 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
7694 return true;
7695
7696 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7697 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
7698 return true;
7699
7700 return
7701 mUserData.isBackedUp() ||
7702 mHWData.isBackedUp() ||
7703 mHDData.isBackedUp() ||
7704#ifdef VBOX_VRDP
7705 (mVRDPServer && mVRDPServer->isModified()) ||
7706#endif
7707 (mDVDDrive && mDVDDrive->isModified()) ||
7708 (mFloppyDrive && mFloppyDrive->isModified()) ||
7709 (mAudioAdapter && mAudioAdapter->isModified()) ||
7710 (mUSBController && mUSBController->isModified()) ||
7711 (mBIOSSettings && mBIOSSettings->isModified());
7712}
7713
7714/**
7715 * @note This method doesn't check (ignores) actual changes to mHDData.
7716 * Use mHDData.mHDAttachmentsChanged right after #commit() instead.
7717 *
7718 * @param aIgnoreUserData |true| to ignore changes to mUserData
7719 *
7720 * @note Locks objects for reading!
7721 */
7722bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
7723{
7724 AutoCaller autoCaller (this);
7725 AssertComRCReturn (autoCaller.rc(), false);
7726
7727 AutoReaderLock alock (this);
7728
7729 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7730 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
7731 return true;
7732
7733 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7734 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
7735 return true;
7736
7737 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7738 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
7739 return true;
7740
7741 return
7742 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
7743 mHWData.hasActualChanges() ||
7744 /* ignore mHDData */
7745 //mHDData.hasActualChanges() ||
7746#ifdef VBOX_VRDP
7747 (mVRDPServer && mVRDPServer->isReallyModified()) ||
7748#endif
7749 (mDVDDrive && mDVDDrive->isReallyModified()) ||
7750 (mFloppyDrive && mFloppyDrive->isReallyModified()) ||
7751 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
7752 (mUSBController && mUSBController->isReallyModified()) ||
7753 (mBIOSSettings && mBIOSSettings->isReallyModified());
7754}
7755
7756/**
7757 * Discards all changes to machine settings.
7758 *
7759 * @param aNotify whether to notify the direct session about changes or not
7760 *
7761 * @note Locks objects!
7762 */
7763void Machine::rollback (bool aNotify)
7764{
7765 AutoCaller autoCaller (this);
7766 AssertComRCReturn (autoCaller.rc(), (void) 0);
7767
7768 AutoLock alock (this);
7769
7770 /* check for changes in own data */
7771
7772 bool sharedFoldersChanged = false;
7773
7774 if (aNotify && mHWData.isBackedUp())
7775 {
7776 if (mHWData->mSharedFolders.size() !=
7777 mHWData.backedUpData()->mSharedFolders.size())
7778 sharedFoldersChanged = true;
7779 else
7780 {
7781 for (HWData::SharedFolderList::iterator rit =
7782 mHWData->mSharedFolders.begin();
7783 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
7784 ++ rit)
7785 {
7786 for (HWData::SharedFolderList::iterator cit =
7787 mHWData.backedUpData()->mSharedFolders.begin();
7788 cit != mHWData.backedUpData()->mSharedFolders.end();
7789 ++ cit)
7790 {
7791 if ((*cit)->name() != (*rit)->name() ||
7792 (*cit)->hostPath() != (*rit)->hostPath())
7793 {
7794 sharedFoldersChanged = true;
7795 break;
7796 }
7797 }
7798 }
7799 }
7800 }
7801
7802 mUserData.rollback();
7803
7804 mHWData.rollback();
7805
7806 if (mHDData.isBackedUp())
7807 fixupHardDisks (false /* aCommit */);
7808
7809 /* check for changes in child objects */
7810
7811 bool vrdpChanged = false, dvdChanged = false, floppyChanged = false,
7812 usbChanged = false;
7813
7814 ComPtr <INetworkAdapter> networkAdapters [ELEMENTS (mNetworkAdapters)];
7815 ComPtr <ISerialPort> serialPorts [ELEMENTS (mSerialPorts)];
7816 ComPtr <IParallelPort> parallelPorts [ELEMENTS (mParallelPorts)];
7817
7818 if (mBIOSSettings)
7819 mBIOSSettings->rollback();
7820
7821#ifdef VBOX_VRDP
7822 if (mVRDPServer)
7823 vrdpChanged = mVRDPServer->rollback();
7824#endif
7825
7826 if (mDVDDrive)
7827 dvdChanged = mDVDDrive->rollback();
7828
7829 if (mFloppyDrive)
7830 floppyChanged = mFloppyDrive->rollback();
7831
7832 if (mAudioAdapter)
7833 mAudioAdapter->rollback();
7834
7835 if (mUSBController)
7836 usbChanged = mUSBController->rollback();
7837
7838 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7839 if (mNetworkAdapters [slot])
7840 if (mNetworkAdapters [slot]->rollback())
7841 networkAdapters [slot] = mNetworkAdapters [slot];
7842
7843 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7844 if (mSerialPorts [slot])
7845 if (mSerialPorts [slot]->rollback())
7846 serialPorts [slot] = mSerialPorts [slot];
7847
7848 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7849 if (mParallelPorts [slot])
7850 if (mParallelPorts [slot]->rollback())
7851 parallelPorts [slot] = mParallelPorts [slot];
7852
7853 if (aNotify)
7854 {
7855 /* inform the direct session about changes */
7856
7857 ComObjPtr <Machine> that = this;
7858 alock.leave();
7859
7860 if (sharedFoldersChanged)
7861 that->onSharedFolderChange();
7862
7863 if (vrdpChanged)
7864 that->onVRDPServerChange();
7865 if (dvdChanged)
7866 that->onDVDDriveChange();
7867 if (floppyChanged)
7868 that->onFloppyDriveChange();
7869 if (usbChanged)
7870 that->onUSBControllerChange();
7871
7872 for (ULONG slot = 0; slot < ELEMENTS (networkAdapters); slot ++)
7873 if (networkAdapters [slot])
7874 that->onNetworkAdapterChange (networkAdapters [slot]);
7875 for (ULONG slot = 0; slot < ELEMENTS (serialPorts); slot ++)
7876 if (serialPorts [slot])
7877 that->onSerialPortChange (serialPorts [slot]);
7878 for (ULONG slot = 0; slot < ELEMENTS (parallelPorts); slot ++)
7879 if (parallelPorts [slot])
7880 that->onParallelPortChange (parallelPorts [slot]);
7881 }
7882}
7883
7884/**
7885 * Commits all the changes to machine settings.
7886 *
7887 * Note that when committing fails at some stage, it still continues
7888 * until the end. So, all data will either be actually committed or rolled
7889 * back (for failed cases) and the returned result code will describe the
7890 * first failure encountered. However, #isModified() will still return true
7891 * in case of failure, to indicade that settings in memory and on disk are
7892 * out of sync.
7893 *
7894 * @note Locks objects!
7895 */
7896HRESULT Machine::commit()
7897{
7898 AutoCaller autoCaller (this);
7899 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7900
7901 AutoLock alock (this);
7902
7903 HRESULT rc = S_OK;
7904
7905 /*
7906 * use safe commit to ensure Snapshot machines (that share mUserData)
7907 * will still refer to a valid memory location
7908 */
7909 mUserData.commitCopy();
7910
7911 mHWData.commit();
7912
7913 if (mHDData.isBackedUp())
7914 rc = fixupHardDisks (true /* aCommit */);
7915
7916 mBIOSSettings->commit();
7917#ifdef VBOX_VRDP
7918 mVRDPServer->commit();
7919#endif
7920 mDVDDrive->commit();
7921 mFloppyDrive->commit();
7922 mAudioAdapter->commit();
7923 mUSBController->commit();
7924
7925 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7926 mNetworkAdapters [slot]->commit();
7927 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7928 mSerialPorts [slot]->commit();
7929 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7930 mParallelPorts [slot]->commit();
7931
7932 if (mType == IsSessionMachine)
7933 {
7934 /* attach new data to the primary machine and reshare it */
7935 mPeer->mUserData.attach (mUserData);
7936 mPeer->mHWData.attach (mHWData);
7937 mPeer->mHDData.attach (mHDData);
7938 }
7939
7940 if (FAILED (rc))
7941 {
7942 /*
7943 * backup arbitrary data item to cause #isModified() to still return
7944 * true in case of any error
7945 */
7946 mHWData.backup();
7947 }
7948
7949 return rc;
7950}
7951
7952/**
7953 * Copies all the hardware data from the given machine.
7954 *
7955 * @note
7956 * This method must be called from under this object's lock.
7957 * @note
7958 * This method doesn't call #commit(), so all data remains backed up
7959 * and unsaved.
7960 */
7961void Machine::copyFrom (Machine *aThat)
7962{
7963 AssertReturn (mType == IsMachine || mType == IsSessionMachine, (void) 0);
7964 AssertReturn (aThat->mType == IsSnapshotMachine, (void) 0);
7965
7966 mHWData.assignCopy (aThat->mHWData);
7967
7968 // create copies of all shared folders (mHWData after attiching a copy
7969 // contains just references to original objects)
7970 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
7971 it != mHWData->mSharedFolders.end();
7972 ++ it)
7973 {
7974 ComObjPtr <SharedFolder> folder;
7975 folder.createObject();
7976 HRESULT rc = folder->initCopy (machine(), *it);
7977 AssertComRC (rc);
7978 *it = folder;
7979 }
7980
7981 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
7982#ifdef VBOX_VRDP
7983 mVRDPServer->copyFrom (aThat->mVRDPServer);
7984#endif
7985 mDVDDrive->copyFrom (aThat->mDVDDrive);
7986 mFloppyDrive->copyFrom (aThat->mFloppyDrive);
7987 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
7988 mUSBController->copyFrom (aThat->mUSBController);
7989
7990 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7991 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
7992 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7993 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
7994 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7995 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
7996}
7997
7998/////////////////////////////////////////////////////////////////////////////
7999// SessionMachine class
8000/////////////////////////////////////////////////////////////////////////////
8001
8002/** Task structure for asynchronous VM operations */
8003struct SessionMachine::Task
8004{
8005 Task (SessionMachine *m, Progress *p)
8006 : machine (m), progress (p)
8007 , state (m->data()->mMachineState) // save the current machine state
8008 , subTask (false), settingsChanged (false)
8009 {}
8010
8011 void modifyLastState (MachineState_T s)
8012 {
8013 *const_cast <MachineState_T *> (&state) = s;
8014 }
8015
8016 virtual void handler() = 0;
8017
8018 const ComObjPtr <SessionMachine> machine;
8019 const ComObjPtr <Progress> progress;
8020 const MachineState_T state;
8021
8022 bool subTask : 1;
8023 bool settingsChanged : 1;
8024};
8025
8026/** Take snapshot task */
8027struct SessionMachine::TakeSnapshotTask : public SessionMachine::Task
8028{
8029 TakeSnapshotTask (SessionMachine *m)
8030 : Task (m, NULL) {}
8031
8032 void handler() { machine->takeSnapshotHandler (*this); }
8033};
8034
8035/** Discard snapshot task */
8036struct SessionMachine::DiscardSnapshotTask : public SessionMachine::Task
8037{
8038 DiscardSnapshotTask (SessionMachine *m, Progress *p, Snapshot *s)
8039 : Task (m, p)
8040 , snapshot (s) {}
8041
8042 DiscardSnapshotTask (const Task &task, Snapshot *s)
8043 : Task (task)
8044 , snapshot (s) {}
8045
8046 void handler() { machine->discardSnapshotHandler (*this); }
8047
8048 const ComObjPtr <Snapshot> snapshot;
8049};
8050
8051/** Discard current state task */
8052struct SessionMachine::DiscardCurrentStateTask : public SessionMachine::Task
8053{
8054 DiscardCurrentStateTask (SessionMachine *m, Progress *p,
8055 bool discardCurSnapshot)
8056 : Task (m, p), discardCurrentSnapshot (discardCurSnapshot) {}
8057
8058 void handler() { machine->discardCurrentStateHandler (*this); }
8059
8060 const bool discardCurrentSnapshot;
8061};
8062
8063////////////////////////////////////////////////////////////////////////////////
8064
8065DEFINE_EMPTY_CTOR_DTOR (SessionMachine)
8066
8067HRESULT SessionMachine::FinalConstruct()
8068{
8069 LogFlowThisFunc (("\n"));
8070
8071 /* set the proper type to indicate we're the SessionMachine instance */
8072 unconst (mType) = IsSessionMachine;
8073
8074#if defined(RT_OS_WINDOWS)
8075 mIPCSem = NULL;
8076#elif defined(RT_OS_OS2)
8077 mIPCSem = NULLHANDLE;
8078#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8079 mIPCSem = -1;
8080#else
8081# error "Port me!"
8082#endif
8083
8084 return S_OK;
8085}
8086
8087void SessionMachine::FinalRelease()
8088{
8089 LogFlowThisFunc (("\n"));
8090
8091 uninit (Uninit::Unexpected);
8092}
8093
8094/**
8095 * @note Must be called only by Machine::openSession() from its own write lock.
8096 */
8097HRESULT SessionMachine::init (Machine *aMachine)
8098{
8099 LogFlowThisFuncEnter();
8100 LogFlowThisFunc (("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8101
8102 AssertReturn (aMachine, E_INVALIDARG);
8103
8104 AssertReturn (aMachine->lockHandle()->isLockedOnCurrentThread(), E_FAIL);
8105
8106 /* Enclose the state transition NotReady->InInit->Ready */
8107 AutoInitSpan autoInitSpan (this);
8108 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
8109
8110 /* create the interprocess semaphore */
8111#if defined(RT_OS_WINDOWS)
8112 mIPCSemName = aMachine->mData->mConfigFileFull;
8113 for (size_t i = 0; i < mIPCSemName.length(); i++)
8114 if (mIPCSemName[i] == '\\')
8115 mIPCSemName[i] = '/';
8116 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
8117 ComAssertMsgRet (mIPCSem,
8118 ("Cannot create IPC mutex '%ls', err=%d\n",
8119 mIPCSemName.raw(), ::GetLastError()),
8120 E_FAIL);
8121#elif defined(RT_OS_OS2)
8122 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%Vuuid}",
8123 aMachine->mData->mUuid.raw());
8124 mIPCSemName = ipcSem;
8125 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
8126 ComAssertMsgRet (arc == NO_ERROR,
8127 ("Cannot create IPC mutex '%s', arc=%ld\n",
8128 ipcSem.raw(), arc),
8129 E_FAIL);
8130#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8131 Utf8Str configFile = aMachine->mData->mConfigFileFull;
8132 char *configFileCP = NULL;
8133 RTStrUtf8ToCurrentCP (&configFileCP, configFile);
8134 key_t key = ::ftok (configFileCP, 0);
8135 RTStrFree (configFileCP);
8136 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8137 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errno),
8138 E_FAIL);
8139 /* set the initial value to 1 */
8140 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
8141 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8142 E_FAIL);
8143#else
8144# error "Port me!"
8145#endif
8146
8147 /* memorize the peer Machine */
8148 unconst (mPeer) = aMachine;
8149 /* share the parent pointer */
8150 unconst (mParent) = aMachine->mParent;
8151
8152 /* take the pointers to data to share */
8153 mData.share (aMachine->mData);
8154 mSSData.share (aMachine->mSSData);
8155
8156 mUserData.share (aMachine->mUserData);
8157 mHWData.share (aMachine->mHWData);
8158 mHDData.share (aMachine->mHDData);
8159
8160 unconst (mBIOSSettings).createObject();
8161 mBIOSSettings->init (this, aMachine->mBIOSSettings);
8162#ifdef VBOX_VRDP
8163 /* create another VRDPServer object that will be mutable */
8164 unconst (mVRDPServer).createObject();
8165 mVRDPServer->init (this, aMachine->mVRDPServer);
8166#endif
8167 /* create another DVD drive object that will be mutable */
8168 unconst (mDVDDrive).createObject();
8169 mDVDDrive->init (this, aMachine->mDVDDrive);
8170 /* create another floppy drive object that will be mutable */
8171 unconst (mFloppyDrive).createObject();
8172 mFloppyDrive->init (this, aMachine->mFloppyDrive);
8173 /* create another audio adapter object that will be mutable */
8174 unconst (mAudioAdapter).createObject();
8175 mAudioAdapter->init (this, aMachine->mAudioAdapter);
8176 /* create a list of serial ports that will be mutable */
8177 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
8178 {
8179 unconst (mSerialPorts [slot]).createObject();
8180 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
8181 }
8182 /* create a list of parallel ports that will be mutable */
8183 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
8184 {
8185 unconst (mParallelPorts [slot]).createObject();
8186 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
8187 }
8188 /* create another USB controller object that will be mutable */
8189 unconst (mUSBController).createObject();
8190 mUSBController->init (this, aMachine->mUSBController);
8191 /* create a list of network adapters that will be mutable */
8192 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
8193 {
8194 unconst (mNetworkAdapters [slot]).createObject();
8195 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
8196 }
8197
8198 /* Confirm a successful initialization when it's the case */
8199 autoInitSpan.setSucceeded();
8200
8201 LogFlowThisFuncLeave();
8202 return S_OK;
8203}
8204
8205/**
8206 * Uninitializes this session object. If the reason is other than
8207 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
8208 *
8209 * @param aReason uninitialization reason
8210 *
8211 * @note Locks mParent + this object for writing.
8212 */
8213void SessionMachine::uninit (Uninit::Reason aReason)
8214{
8215 LogFlowThisFuncEnter();
8216 LogFlowThisFunc (("reason=%d\n", aReason));
8217
8218 /*
8219 * Strongly reference ourselves to prevent this object deletion after
8220 * mData->mSession.mMachine.setNull() below (which can release the last
8221 * reference and call the destructor). Important: this must be done before
8222 * accessing any members (and before AutoUninitSpan that does it as well).
8223 * This self reference will be released as the very last step on return.
8224 */
8225 ComObjPtr <SessionMachine> selfRef = this;
8226
8227 /* Enclose the state transition Ready->InUninit->NotReady */
8228 AutoUninitSpan autoUninitSpan (this);
8229 if (autoUninitSpan.uninitDone())
8230 {
8231 LogFlowThisFunc (("Already uninitialized\n"));
8232 LogFlowThisFuncLeave();
8233 return;
8234 }
8235
8236 if (autoUninitSpan.initFailed())
8237 {
8238 /*
8239 * We've been called by init() because it's failed. It's not really
8240 * necessary (nor it's safe) to perform the regular uninit sequence
8241 * below, the following is enough.
8242 */
8243 LogFlowThisFunc (("Initialization failed.\n"));
8244#if defined(RT_OS_WINDOWS)
8245 if (mIPCSem)
8246 ::CloseHandle (mIPCSem);
8247 mIPCSem = NULL;
8248#elif defined(RT_OS_OS2)
8249 if (mIPCSem != NULLHANDLE)
8250 ::DosCloseMutexSem (mIPCSem);
8251 mIPCSem = NULLHANDLE;
8252#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8253 if (mIPCSem >= 0)
8254 ::semctl (mIPCSem, 0, IPC_RMID);
8255 mIPCSem = -1;
8256#else
8257# error "Port me!"
8258#endif
8259 uninitDataAndChildObjects();
8260 mData.free();
8261 unconst (mParent).setNull();
8262 unconst (mPeer).setNull();
8263 LogFlowThisFuncLeave();
8264 return;
8265 }
8266
8267 /*
8268 * We need to lock this object in uninit() because the lock is shared
8269 * with mPeer (as well as data we modify below).
8270 * mParent->addProcessToReap() and others need mParent lock.
8271 */
8272 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8273
8274 MachineState_T lastState = mData->mMachineState;
8275
8276 if (aReason == Uninit::Abnormal)
8277 {
8278 LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",
8279 lastState >= MachineState_Running));
8280
8281 /* reset the state to Aborted */
8282 if (mData->mMachineState != MachineState_Aborted)
8283 setMachineState (MachineState_Aborted);
8284 }
8285
8286 if (isModified())
8287 {
8288 LogWarningThisFunc (("Discarding unsaved settings changes!\n"));
8289 rollback (false /* aNotify */);
8290 }
8291
8292 Assert (!mSnapshotData.mStateFilePath || !mSnapshotData.mSnapshot);
8293 if (mSnapshotData.mStateFilePath)
8294 {
8295 LogWarningThisFunc (("canceling failed save state request!\n"));
8296 endSavingState (FALSE /* aSuccess */);
8297 }
8298 else if (!!mSnapshotData.mSnapshot)
8299 {
8300 LogWarningThisFunc (("canceling untaken snapshot!\n"));
8301 endTakingSnapshot (FALSE /* aSuccess */);
8302 }
8303
8304 /* release all captured USB devices */
8305 if (aReason == Uninit::Abnormal && lastState >= MachineState_Running)
8306 {
8307 /* Console::captureUSBDevices() is called in the VM process only after
8308 * setting the machine state to Starting or Restoring.
8309 * Console::detachAllUSBDevices() will be called upon successful
8310 * termination. So, we need to release USB devices only if there was
8311 * an abnormal termination of a running VM. */
8312 DetachAllUSBDevices (TRUE /* aDone */);
8313 }
8314
8315 if (!mData->mSession.mType.isNull())
8316 {
8317 /* mType is not null when this machine's process has been started by
8318 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
8319 * need to queue the PID to reap the process (and avoid zombies on
8320 * Linux). */
8321 Assert (mData->mSession.mPid != NIL_RTPROCESS);
8322 mParent->addProcessToReap (mData->mSession.mPid);
8323 }
8324
8325 mData->mSession.mPid = NIL_RTPROCESS;
8326
8327 if (aReason == Uninit::Unexpected)
8328 {
8329 /* Uninitialization didn't come from #checkForDeath(), so tell the
8330 * client watcher thread to update the set of machines that have open
8331 * sessions. */
8332 mParent->updateClientWatcher();
8333 }
8334
8335 /* uninitialize all remote controls */
8336 if (mData->mSession.mRemoteControls.size())
8337 {
8338 LogFlowThisFunc (("Closing remote sessions (%d):\n",
8339 mData->mSession.mRemoteControls.size()));
8340
8341 Data::Session::RemoteControlList::iterator it =
8342 mData->mSession.mRemoteControls.begin();
8343 while (it != mData->mSession.mRemoteControls.end())
8344 {
8345 LogFlowThisFunc ((" Calling remoteControl->Uninitialize()...\n"));
8346 HRESULT rc = (*it)->Uninitialize();
8347 LogFlowThisFunc ((" remoteControl->Uninitialize() returned %08X\n", rc));
8348 if (FAILED (rc))
8349 LogWarningThisFunc (("Forgot to close the remote session?\n"));
8350 ++ it;
8351 }
8352 mData->mSession.mRemoteControls.clear();
8353 }
8354
8355 /*
8356 * An expected uninitialization can come only from #checkForDeath().
8357 * Otherwise it means that something's got really wrong (for examlple,
8358 * the Session implementation has released the VirtualBox reference
8359 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
8360 * etc). However, it's also possible, that the client releases the IPC
8361 * semaphore correctly (i.e. before it releases the VirtualBox reference),
8362 * but but the VirtualBox release event comes first to the server process.
8363 * This case is practically possible, so we should not assert on an
8364 * unexpected uninit, just log a warning.
8365 */
8366
8367 if ((aReason == Uninit::Unexpected))
8368 LogWarningThisFunc (("Unexpected SessionMachine uninitialization!\n"));
8369
8370 if (aReason != Uninit::Normal)
8371 mData->mSession.mDirectControl.setNull();
8372 else
8373 {
8374 /* this must be null here (see #OnSessionEnd()) */
8375 Assert (mData->mSession.mDirectControl.isNull());
8376 Assert (mData->mSession.mState == SessionState_SessionClosing);
8377 Assert (!mData->mSession.mProgress.isNull());
8378
8379 mData->mSession.mProgress->notifyComplete (S_OK);
8380 mData->mSession.mProgress.setNull();
8381 }
8382
8383 /* remove the association between the peer machine and this session machine */
8384 Assert (mData->mSession.mMachine == this ||
8385 aReason == Uninit::Unexpected);
8386
8387 /* reset the rest of session data */
8388 mData->mSession.mMachine.setNull();
8389 mData->mSession.mState = SessionState_SessionClosed;
8390 mData->mSession.mType.setNull();
8391
8392 /* close the interprocess semaphore before leaving the shared lock */
8393#if defined(RT_OS_WINDOWS)
8394 if (mIPCSem)
8395 ::CloseHandle (mIPCSem);
8396 mIPCSem = NULL;
8397#elif defined(RT_OS_OS2)
8398 if (mIPCSem != NULLHANDLE)
8399 ::DosCloseMutexSem (mIPCSem);
8400 mIPCSem = NULLHANDLE;
8401#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8402 if (mIPCSem >= 0)
8403 ::semctl (mIPCSem, 0, IPC_RMID);
8404 mIPCSem = -1;
8405#else
8406# error "Port me!"
8407#endif
8408
8409 /* fire an event */
8410 mParent->onSessionStateChange (mData->mUuid, SessionState_SessionClosed);
8411
8412 uninitDataAndChildObjects();
8413
8414 /* free the essential data structure last */
8415 mData.free();
8416
8417 /* leave the shared lock before setting the below two to NULL */
8418 alock.leave();
8419
8420 unconst (mParent).setNull();
8421 unconst (mPeer).setNull();
8422
8423 LogFlowThisFuncLeave();
8424}
8425
8426// AutoLock::Lockable interface
8427////////////////////////////////////////////////////////////////////////////////
8428
8429/**
8430 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
8431 * with the primary Machine instance (mPeer).
8432 */
8433AutoLock::Handle *SessionMachine::lockHandle() const
8434{
8435 AssertReturn (!mPeer.isNull(), NULL);
8436 return mPeer->lockHandle();
8437}
8438
8439// IInternalMachineControl methods
8440////////////////////////////////////////////////////////////////////////////////
8441
8442/**
8443 * @note Locks the same as #setMachineState() does.
8444 */
8445STDMETHODIMP SessionMachine::UpdateState (MachineState_T machineState)
8446{
8447 return setMachineState (machineState);
8448}
8449
8450/**
8451 * @note Locks this object for reading.
8452 */
8453STDMETHODIMP SessionMachine::GetIPCId (BSTR *id)
8454{
8455 AutoCaller autoCaller (this);
8456 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8457
8458 AutoReaderLock alock (this);
8459
8460#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8461 mIPCSemName.cloneTo (id);
8462 return S_OK;
8463#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8464 mData->mConfigFileFull.cloneTo (id);
8465 return S_OK;
8466#else
8467# error "Port me!"
8468#endif
8469}
8470
8471/**
8472 * Goes through the USB filters of the given machine to see if the given
8473 * device matches any filter or not.
8474 *
8475 * @note Locks the same as USBController::hasMatchingFilter() does.
8476 */
8477STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
8478 BOOL *aMatched)
8479{
8480 LogFlowThisFunc (("\n"));
8481
8482 if (!aUSBDevice)
8483 return E_INVALIDARG;
8484 if (!aMatched)
8485 return E_POINTER;
8486
8487 AutoCaller autoCaller (this);
8488 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8489
8490 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice);
8491
8492 return S_OK;
8493}
8494
8495/**
8496 * @note Locks the same as Host::captureUSBDevice() does.
8497 */
8498STDMETHODIMP SessionMachine::CaptureUSBDevice (INPTR GUIDPARAM aId)
8499{
8500 LogFlowThisFunc (("\n"));
8501
8502 AutoCaller autoCaller (this);
8503 AssertComRCReturnRC (autoCaller.rc());
8504
8505 /* if cautureUSBDevice() fails, it must have set extended error info */
8506 return mParent->host()->captureUSBDevice (this, aId);
8507}
8508
8509/**
8510 * @note Locks the same as Host::detachUSBDevice() does.
8511 */
8512STDMETHODIMP SessionMachine::DetachUSBDevice (INPTR GUIDPARAM aId, BOOL aDone)
8513{
8514 LogFlowThisFunc (("\n"));
8515
8516 AutoCaller autoCaller (this);
8517 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8518
8519 return mParent->host()->detachUSBDevice (this, aId, aDone);
8520}
8521
8522/**
8523 * Inserts all machine filters to the USB proxy service and then calls
8524 * Host::autoCaptureUSBDevices().
8525 *
8526 * Called by Console from the VM process upon VM startup.
8527 *
8528 * @note Locks what called methods lock.
8529 */
8530STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
8531{
8532 LogFlowThisFunc (("\n"));
8533
8534 AutoCaller autoCaller (this);
8535 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8536
8537 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
8538 AssertComRC (rc);
8539 NOREF (rc);
8540
8541 return mParent->host()->autoCaptureUSBDevices (this);
8542}
8543
8544/**
8545 * Removes all machine filters from the USB proxy service and then calls
8546 * Host::detachAllUSBDevices().
8547 *
8548 * Called by Console from the VM process upon normal VM termination or by
8549 * SessionMachine::uninit() upon abnormal VM termination (from under the
8550 * Machine/SessionMachine lock).
8551 *
8552 * @note Locks what called methods lock.
8553 */
8554STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
8555{
8556 LogFlowThisFunc (("\n"));
8557
8558 AutoCaller autoCaller (this);
8559 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8560
8561 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8562 AssertComRC (rc);
8563 NOREF (rc);
8564
8565 return mParent->host()->detachAllUSBDevices (this, aDone);
8566}
8567
8568/**
8569 * @note Locks mParent + this object for writing.
8570 */
8571STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
8572 IProgress **aProgress)
8573{
8574 LogFlowThisFuncEnter();
8575
8576 AssertReturn (aSession, E_INVALIDARG);
8577 AssertReturn (aProgress, E_INVALIDARG);
8578
8579 AutoCaller autoCaller (this);
8580
8581 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
8582 /*
8583 * We don't assert below because it might happen that a non-direct session
8584 * informs us it is closed right after we've been uninitialized -- it's ok.
8585 */
8586 CheckComRCReturnRC (autoCaller.rc());
8587
8588 /* get IInternalSessionControl interface */
8589 ComPtr <IInternalSessionControl> control (aSession);
8590
8591 ComAssertRet (!control.isNull(), E_INVALIDARG);
8592
8593 /* Progress::init() needs mParent lock */
8594 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8595
8596 if (control.equalsTo (mData->mSession.mDirectControl))
8597 {
8598 ComAssertRet (aProgress, E_POINTER);
8599
8600 /* The direct session is being normally closed by the client process
8601 * ----------------------------------------------------------------- */
8602
8603 /* go to the closing state (essential for all open*Session() calls and
8604 * for #checkForDeath()) */
8605 Assert (mData->mSession.mState == SessionState_SessionOpen);
8606 mData->mSession.mState = SessionState_SessionClosing;
8607
8608 /* set direct control to NULL to release the remote instance */
8609 mData->mSession.mDirectControl.setNull();
8610 LogFlowThisFunc (("Direct control is set to NULL\n"));
8611
8612 /*
8613 * Create the progress object the client will use to wait until
8614 * #checkForDeath() is called to uninitialize this session object
8615 * after it releases the IPC semaphore.
8616 */
8617 ComObjPtr <Progress> progress;
8618 progress.createObject();
8619 progress->init (mParent, (IMachine *) mPeer, Bstr (tr ("Closing session")),
8620 FALSE /* aCancelable */);
8621 progress.queryInterfaceTo (aProgress);
8622 mData->mSession.mProgress = progress;
8623 }
8624 else
8625 {
8626 /* the remote session is being normally closed */
8627 Data::Session::RemoteControlList::iterator it =
8628 mData->mSession.mRemoteControls.begin();
8629 while (it != mData->mSession.mRemoteControls.end())
8630 {
8631 if (control.equalsTo (*it))
8632 break;
8633 ++it;
8634 }
8635 BOOL found = it != mData->mSession.mRemoteControls.end();
8636 ComAssertMsgRet (found, ("The session is not found in the session list!"),
8637 E_INVALIDARG);
8638 mData->mSession.mRemoteControls.remove (*it);
8639 }
8640
8641 LogFlowThisFuncLeave();
8642 return S_OK;
8643}
8644
8645/**
8646 * @note Locks mParent + this object for writing.
8647 */
8648STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
8649{
8650 LogFlowThisFuncEnter();
8651
8652 AssertReturn (aProgress, E_INVALIDARG);
8653 AssertReturn (aStateFilePath, E_POINTER);
8654
8655 AutoCaller autoCaller (this);
8656 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8657
8658 /* mParent->addProgress() needs mParent lock */
8659 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8660
8661 AssertReturn (mData->mMachineState == MachineState_Paused &&
8662 mSnapshotData.mLastState == MachineState_InvalidMachineState &&
8663 mSnapshotData.mProgressId.isEmpty() &&
8664 mSnapshotData.mStateFilePath.isNull(),
8665 E_FAIL);
8666
8667 /* memorize the progress ID and add it to the global collection */
8668 Guid progressId;
8669 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
8670 AssertComRCReturn (rc, rc);
8671 rc = mParent->addProgress (aProgress);
8672 AssertComRCReturn (rc, rc);
8673
8674 Bstr stateFilePath;
8675 /* stateFilePath is null when the machine is not running */
8676 if (mData->mMachineState == MachineState_Paused)
8677 {
8678 stateFilePath = Utf8StrFmt ("%ls%c{%Vuuid}.sav",
8679 mUserData->mSnapshotFolderFull.raw(),
8680 RTPATH_DELIMITER, mData->mUuid.raw());
8681 }
8682
8683 /* fill in the snapshot data */
8684 mSnapshotData.mLastState = mData->mMachineState;
8685 mSnapshotData.mProgressId = progressId;
8686 mSnapshotData.mStateFilePath = stateFilePath;
8687
8688 /* set the state to Saving (this is expected by Console::SaveState()) */
8689 setMachineState (MachineState_Saving);
8690
8691 stateFilePath.cloneTo (aStateFilePath);
8692
8693 return S_OK;
8694}
8695
8696/**
8697 * @note Locks mParent + this objects for writing.
8698 */
8699STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
8700{
8701 LogFlowThisFunc (("\n"));
8702
8703 AutoCaller autoCaller (this);
8704 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8705
8706 /* endSavingState() need mParent lock */
8707 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8708
8709 AssertReturn (mData->mMachineState == MachineState_Saving &&
8710 mSnapshotData.mLastState != MachineState_InvalidMachineState &&
8711 !mSnapshotData.mProgressId.isEmpty() &&
8712 !mSnapshotData.mStateFilePath.isNull(),
8713 E_FAIL);
8714
8715 /*
8716 * on success, set the state to Saved;
8717 * on failure, set the state to the state we had when BeginSavingState() was
8718 * called (this is expected by Console::SaveState() and
8719 * Console::saveStateThread())
8720 */
8721 if (aSuccess)
8722 setMachineState (MachineState_Saved);
8723 else
8724 setMachineState (mSnapshotData.mLastState);
8725
8726 return endSavingState (aSuccess);
8727}
8728
8729/**
8730 * @note Locks mParent + this objects for writing.
8731 */
8732STDMETHODIMP SessionMachine::BeginTakingSnapshot (
8733 IConsole *aInitiator, INPTR BSTR aName, INPTR BSTR aDescription,
8734 IProgress *aProgress, BSTR *aStateFilePath,
8735 IProgress **aServerProgress)
8736{
8737 LogFlowThisFuncEnter();
8738
8739 AssertReturn (aInitiator && aName, E_INVALIDARG);
8740 AssertReturn (aStateFilePath && aServerProgress, E_POINTER);
8741
8742 LogFlowThisFunc (("aName='%ls'\n", aName));
8743
8744 AutoCaller autoCaller (this);
8745 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8746
8747 /* Progress::init() needs mParent lock */
8748 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8749
8750 AssertReturn ((mData->mMachineState < MachineState_Running ||
8751 mData->mMachineState == MachineState_Paused) &&
8752 mSnapshotData.mLastState == MachineState_InvalidMachineState &&
8753 mSnapshotData.mSnapshot.isNull() &&
8754 mSnapshotData.mServerProgress.isNull() &&
8755 mSnapshotData.mCombinedProgress.isNull(),
8756 E_FAIL);
8757
8758 bool takingSnapshotOnline = mData->mMachineState == MachineState_Paused;
8759
8760 if (!takingSnapshotOnline && mData->mMachineState != MachineState_Saved)
8761 {
8762 /*
8763 * save all current settings to ensure current changes are committed
8764 * and hard disks are fixed up
8765 */
8766 HRESULT rc = saveSettings();
8767 CheckComRCReturnRC (rc);
8768 }
8769
8770 /* check that there are no Writethrough hard disks attached */
8771 for (HDData::HDAttachmentList::const_iterator
8772 it = mHDData->mHDAttachments.begin();
8773 it != mHDData->mHDAttachments.end();
8774 ++ it)
8775 {
8776 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
8777 AutoLock hdLock (hd);
8778 if (hd->type() == HardDiskType_WritethroughHardDisk)
8779 return setError (E_FAIL,
8780 tr ("Cannot take a snapshot when there is a Writethrough hard "
8781 " disk attached ('%ls')"), hd->toString().raw());
8782 }
8783
8784 AssertReturn (aProgress || !takingSnapshotOnline, E_FAIL);
8785
8786 /* create an ID for the snapshot */
8787 Guid snapshotId;
8788 snapshotId.create();
8789
8790 Bstr stateFilePath;
8791 /* stateFilePath is null when the machine is not online nor saved */
8792 if (takingSnapshotOnline || mData->mMachineState == MachineState_Saved)
8793 stateFilePath = Utf8StrFmt ("%ls%c{%Vuuid}.sav",
8794 mUserData->mSnapshotFolderFull.raw(),
8795 RTPATH_DELIMITER,
8796 snapshotId.ptr());
8797
8798 /* ensure the directory for the saved state file exists */
8799 if (stateFilePath)
8800 {
8801 Utf8Str dir = stateFilePath;
8802 RTPathStripFilename (dir.mutableRaw());
8803 if (!RTDirExists (dir))
8804 {
8805 int vrc = RTDirCreateFullPath (dir, 0777);
8806 if (VBOX_FAILURE (vrc))
8807 return setError (E_FAIL,
8808 tr ("Could not create a directory '%s' to save the "
8809 "VM state to (%Vrc)"),
8810 dir.raw(), vrc);
8811 }
8812 }
8813
8814 /* create a snapshot machine object */
8815 ComObjPtr <SnapshotMachine> snapshotMachine;
8816 snapshotMachine.createObject();
8817 HRESULT rc = snapshotMachine->init (this, snapshotId, stateFilePath);
8818 AssertComRCReturn (rc, rc);
8819
8820 Bstr progressDesc = Bstr (tr ("Taking snapshot of virtual machine"));
8821 Bstr firstOpDesc = Bstr (tr ("Preparing to take snapshot"));
8822
8823 /*
8824 * create a server-side progress object (it will be descriptionless
8825 * when we need to combine it with the VM-side progress, i.e. when we're
8826 * taking a snapshot online). The number of operations is:
8827 * 1 (preparing) + # of VDIs + 1 (if the state is saved so we need to copy it)
8828 */
8829 ComObjPtr <Progress> serverProgress;
8830 {
8831 ULONG opCount = 1 + mHDData->mHDAttachments.size();
8832 if (mData->mMachineState == MachineState_Saved)
8833 opCount ++;
8834 serverProgress.createObject();
8835 if (takingSnapshotOnline)
8836 rc = serverProgress->init (FALSE, opCount, firstOpDesc);
8837 else
8838 rc = serverProgress->init (mParent, aInitiator, progressDesc, FALSE,
8839 opCount, firstOpDesc);
8840 AssertComRCReturn (rc, rc);
8841 }
8842
8843 /* create a combined server-side progress object when necessary */
8844 ComObjPtr <CombinedProgress> combinedProgress;
8845 if (takingSnapshotOnline)
8846 {
8847 combinedProgress.createObject();
8848 rc = combinedProgress->init (mParent, aInitiator, progressDesc,
8849 serverProgress, aProgress);
8850 AssertComRCReturn (rc, rc);
8851 }
8852
8853 /* create a snapshot object */
8854 RTTIMESPEC time;
8855 ComObjPtr <Snapshot> snapshot;
8856 snapshot.createObject();
8857 rc = snapshot->init (snapshotId, aName, aDescription,
8858 RTTimeSpecGetMilli (RTTimeNow (&time)),
8859 snapshotMachine, mData->mCurrentSnapshot);
8860 AssertComRCReturn (rc, rc);
8861
8862 /*
8863 * create and start the task on a separate thread
8864 * (note that it will not start working until we release alock)
8865 */
8866 TakeSnapshotTask *task = new TakeSnapshotTask (this);
8867 int vrc = RTThreadCreate (NULL, taskHandler,
8868 (void *) task,
8869 0, RTTHREADTYPE_MAIN_WORKER, 0, "TakeSnapshot");
8870 if (VBOX_FAILURE (vrc))
8871 {
8872 snapshot->uninit();
8873 delete task;
8874 ComAssertFailedRet (E_FAIL);
8875 }
8876
8877 /* fill in the snapshot data */
8878 mSnapshotData.mLastState = mData->mMachineState;
8879 mSnapshotData.mSnapshot = snapshot;
8880 mSnapshotData.mServerProgress = serverProgress;
8881 mSnapshotData.mCombinedProgress = combinedProgress;
8882
8883 /* set the state to Saving (this is expected by Console::TakeSnapshot()) */
8884 setMachineState (MachineState_Saving);
8885
8886 if (takingSnapshotOnline)
8887 stateFilePath.cloneTo (aStateFilePath);
8888 else
8889 *aStateFilePath = NULL;
8890
8891 serverProgress.queryInterfaceTo (aServerProgress);
8892
8893 LogFlowThisFuncLeave();
8894 return S_OK;
8895}
8896
8897/**
8898 * @note Locks mParent + this objects for writing.
8899 */
8900STDMETHODIMP SessionMachine::EndTakingSnapshot (BOOL aSuccess)
8901{
8902 LogFlowThisFunc (("\n"));
8903
8904 AutoCaller autoCaller (this);
8905 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8906
8907 /* Lock mParent because of endTakingSnapshot() */
8908 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8909
8910 AssertReturn (!aSuccess ||
8911 (mData->mMachineState == MachineState_Saving &&
8912 mSnapshotData.mLastState != MachineState_InvalidMachineState &&
8913 !mSnapshotData.mSnapshot.isNull() &&
8914 !mSnapshotData.mServerProgress.isNull() &&
8915 !mSnapshotData.mCombinedProgress.isNull()),
8916 E_FAIL);
8917
8918 /*
8919 * set the state to the state we had when BeginTakingSnapshot() was called
8920 * (this is expected by Console::TakeSnapshot() and
8921 * Console::saveStateThread())
8922 */
8923 setMachineState (mSnapshotData.mLastState);
8924
8925 return endTakingSnapshot (aSuccess);
8926}
8927
8928/**
8929 * @note Locks mParent + this + children objects for writing!
8930 */
8931STDMETHODIMP SessionMachine::DiscardSnapshot (
8932 IConsole *aInitiator, INPTR GUIDPARAM aId,
8933 MachineState_T *aMachineState, IProgress **aProgress)
8934{
8935 LogFlowThisFunc (("\n"));
8936
8937 Guid id = aId;
8938 AssertReturn (aInitiator && !id.isEmpty(), E_INVALIDARG);
8939 AssertReturn (aMachineState && aProgress, E_POINTER);
8940
8941 AutoCaller autoCaller (this);
8942 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8943
8944 /* Progress::init() needs mParent lock */
8945 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8946
8947 ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
8948
8949 ComObjPtr <Snapshot> snapshot;
8950 HRESULT rc = findSnapshot (id, snapshot, true /* aSetError */);
8951 CheckComRCReturnRC (rc);
8952
8953 AutoLock snapshotLock (snapshot);
8954 if (snapshot == mData->mFirstSnapshot)
8955 {
8956 AutoLock chLock (mData->mFirstSnapshot->childrenLock());
8957 size_t childrenCount = mData->mFirstSnapshot->children().size();
8958 if (childrenCount > 1)
8959 return setError (E_FAIL,
8960 tr ("Cannot discard the snapshot '%ls' because it is the first "
8961 "snapshot of the machine '%ls' and it has more than one "
8962 "child snapshot (%d)"),
8963 snapshot->data().mName.raw(), mUserData->mName.raw(),
8964 childrenCount);
8965 }
8966
8967 /*
8968 * If the snapshot being discarded is the current one, ensure current
8969 * settings are committed and saved.
8970 */
8971 if (snapshot == mData->mCurrentSnapshot)
8972 {
8973 if (isModified())
8974 {
8975 rc = saveSettings();
8976 CheckComRCReturnRC (rc);
8977 }
8978 }
8979
8980 /*
8981 * create a progress object. The number of operations is:
8982 * 1 (preparing) + # of VDIs
8983 */
8984 ComObjPtr <Progress> progress;
8985 progress.createObject();
8986 rc = progress->init (mParent, aInitiator,
8987 Bstr (Utf8StrFmt (tr ("Discarding snapshot '%ls'"),
8988 snapshot->data().mName.raw())),
8989 FALSE /* aCancelable */,
8990 1 + snapshot->data().mMachine->mHDData->mHDAttachments.size(),
8991 Bstr (tr ("Preparing to discard snapshot")));
8992 AssertComRCReturn (rc, rc);
8993
8994 /* create and start the task on a separate thread */
8995 DiscardSnapshotTask *task = new DiscardSnapshotTask (this, progress, snapshot);
8996 int vrc = RTThreadCreate (NULL, taskHandler,
8997 (void *) task,
8998 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardSnapshot");
8999 if (VBOX_FAILURE (vrc))
9000 delete task;
9001 ComAssertRCRet (vrc, E_FAIL);
9002
9003 /* set the proper machine state (note: after creating a Task instance) */
9004 setMachineState (MachineState_Discarding);
9005
9006 /* return the progress to the caller */
9007 progress.queryInterfaceTo (aProgress);
9008
9009 /* return the new state to the caller */
9010 *aMachineState = mData->mMachineState;
9011
9012 return S_OK;
9013}
9014
9015/**
9016 * @note Locks mParent + this + children objects for writing!
9017 */
9018STDMETHODIMP SessionMachine::DiscardCurrentState (
9019 IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
9020{
9021 LogFlowThisFunc (("\n"));
9022
9023 AssertReturn (aInitiator, E_INVALIDARG);
9024 AssertReturn (aMachineState && aProgress, E_POINTER);
9025
9026 AutoCaller autoCaller (this);
9027 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9028
9029 /* Progress::init() needs mParent lock */
9030 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9031
9032 ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
9033
9034 if (mData->mCurrentSnapshot.isNull())
9035 return setError (E_FAIL,
9036 tr ("Could not discard the current state of the machine '%ls' "
9037 "because it doesn't have any snapshots"),
9038 mUserData->mName.raw());
9039
9040 /*
9041 * create a progress object. The number of operations is:
9042 * 1 (preparing) + # of VDIs + 1 (if we need to copy the saved state file)
9043 */
9044 ComObjPtr <Progress> progress;
9045 progress.createObject();
9046 {
9047 ULONG opCount = 1 + mData->mCurrentSnapshot->data()
9048 .mMachine->mHDData->mHDAttachments.size();
9049 if (mData->mCurrentSnapshot->stateFilePath())
9050 ++ opCount;
9051 progress->init (mParent, aInitiator,
9052 Bstr (tr ("Discarding current machine state")),
9053 FALSE /* aCancelable */, opCount,
9054 Bstr (tr ("Preparing to discard current state")));
9055 }
9056
9057 /* create and start the task on a separate thread */
9058 DiscardCurrentStateTask *task =
9059 new DiscardCurrentStateTask (this, progress, false /* discardCurSnapshot */);
9060 int vrc = RTThreadCreate (NULL, taskHandler,
9061 (void *) task,
9062 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurState");
9063 if (VBOX_FAILURE (vrc))
9064 delete task;
9065 ComAssertRCRet (vrc, E_FAIL);
9066
9067 /* set the proper machine state (note: after creating a Task instance) */
9068 setMachineState (MachineState_Discarding);
9069
9070 /* return the progress to the caller */
9071 progress.queryInterfaceTo (aProgress);
9072
9073 /* return the new state to the caller */
9074 *aMachineState = mData->mMachineState;
9075
9076 return S_OK;
9077}
9078
9079/**
9080 * @note Locks mParent + other objects for writing!
9081 */
9082STDMETHODIMP SessionMachine::DiscardCurrentSnapshotAndState (
9083 IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
9084{
9085 LogFlowThisFunc (("\n"));
9086
9087 AssertReturn (aInitiator, E_INVALIDARG);
9088 AssertReturn (aMachineState && aProgress, E_POINTER);
9089
9090 AutoCaller autoCaller (this);
9091 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9092
9093 /* Progress::init() needs mParent lock */
9094 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9095
9096 ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
9097
9098 if (mData->mCurrentSnapshot.isNull())
9099 return setError (E_FAIL,
9100 tr ("Could not discard the current state of the machine '%ls' "
9101 "because it doesn't have any snapshots"),
9102 mUserData->mName.raw());
9103
9104 /*
9105 * create a progress object. The number of operations is:
9106 * 1 (preparing) + # of VDIs in the current snapshot +
9107 * # of VDIs in the previous snapshot +
9108 * 1 (if we need to copy the saved state file of the previous snapshot)
9109 * or (if there is no previous snapshot):
9110 * 1 (preparing) + # of VDIs in the current snapshot * 2 +
9111 * 1 (if we need to copy the saved state file of the current snapshot)
9112 */
9113 ComObjPtr <Progress> progress;
9114 progress.createObject();
9115 {
9116 ComObjPtr <Snapshot> curSnapshot = mData->mCurrentSnapshot;
9117 ComObjPtr <Snapshot> prevSnapshot = mData->mCurrentSnapshot->parent();
9118
9119 ULONG opCount = 1;
9120 if (prevSnapshot)
9121 {
9122 opCount += curSnapshot->data().mMachine->mHDData->mHDAttachments.size();
9123 opCount += prevSnapshot->data().mMachine->mHDData->mHDAttachments.size();
9124 if (prevSnapshot->stateFilePath())
9125 ++ opCount;
9126 }
9127 else
9128 {
9129 opCount += curSnapshot->data().mMachine->mHDData->mHDAttachments.size() * 2;
9130 if (curSnapshot->stateFilePath())
9131 ++ opCount;
9132 }
9133
9134 progress->init (mParent, aInitiator,
9135 Bstr (tr ("Discarding current machine snapshot and state")),
9136 FALSE /* aCancelable */, opCount,
9137 Bstr (tr ("Preparing to discard current snapshot and state")));
9138 }
9139
9140 /* create and start the task on a separate thread */
9141 DiscardCurrentStateTask *task =
9142 new DiscardCurrentStateTask (this, progress, true /* discardCurSnapshot */);
9143 int vrc = RTThreadCreate (NULL, taskHandler,
9144 (void *) task,
9145 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurState");
9146 if (VBOX_FAILURE (vrc))
9147 delete task;
9148 ComAssertRCRet (vrc, E_FAIL);
9149
9150 /* set the proper machine state (note: after creating a Task instance) */
9151 setMachineState (MachineState_Discarding);
9152
9153 /* return the progress to the caller */
9154 progress.queryInterfaceTo (aProgress);
9155
9156 /* return the new state to the caller */
9157 *aMachineState = mData->mMachineState;
9158
9159 return S_OK;
9160}
9161
9162// public methods only for internal purposes
9163/////////////////////////////////////////////////////////////////////////////
9164
9165/**
9166 * Called from the client watcher thread to check for unexpected client
9167 * process death.
9168 *
9169 * @note On Win32 and on OS/2, this method is called only when we've got the
9170 * mutex (i.e. the client has either died or terminated normally). This
9171 * method always returns true.
9172 *
9173 * @note On Linux, the method returns true if the client process has
9174 * terminated abnormally (and/or the session has been uninitialized) and
9175 * false if it is still alive.
9176 *
9177 * @note Locks this object for writing.
9178 */
9179bool SessionMachine::checkForDeath()
9180{
9181 Uninit::Reason reason;
9182 bool doUninit = false;
9183 bool ret = false;
9184
9185 /*
9186 * Enclose autoCaller with a block because calling uninit()
9187 * from under it will deadlock.
9188 */
9189 {
9190 AutoCaller autoCaller (this);
9191 if (!autoCaller.isOk())
9192 {
9193 /*
9194 * return true if not ready, to cause the client watcher to exclude
9195 * the corresponding session from watching
9196 */
9197 LogFlowThisFunc (("Already uninitialized!"));
9198 return true;
9199 }
9200
9201 AutoLock alock (this);
9202
9203 /*
9204 * Determine the reason of death: if the session state is Closing here,
9205 * everything is fine. Otherwise it means that the client did not call
9206 * OnSessionEnd() before it released the IPC semaphore.
9207 * This may happen either because the client process has abnormally
9208 * terminated, or because it simply forgot to call ISession::Close()
9209 * before exiting. We threat the latter also as an abnormal termination
9210 * (see Session::uninit() for details).
9211 */
9212 reason = mData->mSession.mState == SessionState_SessionClosing ?
9213 Uninit::Normal :
9214 Uninit::Abnormal;
9215
9216#if defined(RT_OS_WINDOWS)
9217
9218 AssertMsg (mIPCSem, ("semaphore must be created"));
9219
9220 /* release the IPC mutex */
9221 ::ReleaseMutex (mIPCSem);
9222
9223 doUninit = true;
9224
9225 ret = true;
9226
9227#elif defined(RT_OS_OS2)
9228
9229 AssertMsg (mIPCSem, ("semaphore must be created"));
9230
9231 /* release the IPC mutex */
9232 ::DosReleaseMutexSem (mIPCSem);
9233
9234 doUninit = true;
9235
9236 ret = true;
9237
9238#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9239
9240 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9241
9242 int val = ::semctl (mIPCSem, 0, GETVAL);
9243 if (val > 0)
9244 {
9245 /* the semaphore is signaled, meaning the session is terminated */
9246 doUninit = true;
9247 }
9248
9249 ret = val > 0;
9250
9251#else
9252# error "Port me!"
9253#endif
9254
9255 } /* AutoCaller block */
9256
9257 if (doUninit)
9258 uninit (reason);
9259
9260 return ret;
9261}
9262
9263/**
9264 * @note Locks this object for reading.
9265 */
9266HRESULT SessionMachine::onDVDDriveChange()
9267{
9268 LogFlowThisFunc (("\n"));
9269
9270 AutoCaller autoCaller (this);
9271 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9272
9273 ComPtr <IInternalSessionControl> directControl;
9274 {
9275 AutoReaderLock alock (this);
9276 directControl = mData->mSession.mDirectControl;
9277 }
9278
9279 /* ignore notifications sent after #OnSessionEnd() is called */
9280 if (!directControl)
9281 return S_OK;
9282
9283 return directControl->OnDVDDriveChange();
9284}
9285
9286/**
9287 * @note Locks this object for reading.
9288 */
9289HRESULT SessionMachine::onFloppyDriveChange()
9290{
9291 LogFlowThisFunc (("\n"));
9292
9293 AutoCaller autoCaller (this);
9294 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9295
9296 ComPtr <IInternalSessionControl> directControl;
9297 {
9298 AutoReaderLock alock (this);
9299 directControl = mData->mSession.mDirectControl;
9300 }
9301
9302 /* ignore notifications sent after #OnSessionEnd() is called */
9303 if (!directControl)
9304 return S_OK;
9305
9306 return directControl->OnFloppyDriveChange();
9307}
9308
9309/**
9310 * @note Locks this object for reading.
9311 */
9312HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter)
9313{
9314 LogFlowThisFunc (("\n"));
9315
9316 AutoCaller autoCaller (this);
9317 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9318
9319 ComPtr <IInternalSessionControl> directControl;
9320 {
9321 AutoReaderLock alock (this);
9322 directControl = mData->mSession.mDirectControl;
9323 }
9324
9325 /* ignore notifications sent after #OnSessionEnd() is called */
9326 if (!directControl)
9327 return S_OK;
9328
9329 return directControl->OnNetworkAdapterChange(networkAdapter);
9330}
9331
9332/**
9333 * @note Locks this object for reading.
9334 */
9335HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
9336{
9337 LogFlowThisFunc (("\n"));
9338
9339 AutoCaller autoCaller (this);
9340 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9341
9342 ComPtr <IInternalSessionControl> directControl;
9343 {
9344 AutoReaderLock alock (this);
9345 directControl = mData->mSession.mDirectControl;
9346 }
9347
9348 /* ignore notifications sent after #OnSessionEnd() is called */
9349 if (!directControl)
9350 return S_OK;
9351
9352 return directControl->OnSerialPortChange(serialPort);
9353}
9354
9355/**
9356 * @note Locks this object for reading.
9357 */
9358HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
9359{
9360 LogFlowThisFunc (("\n"));
9361
9362 AutoCaller autoCaller (this);
9363 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9364
9365 ComPtr <IInternalSessionControl> directControl;
9366 {
9367 AutoReaderLock alock (this);
9368 directControl = mData->mSession.mDirectControl;
9369 }
9370
9371 /* ignore notifications sent after #OnSessionEnd() is called */
9372 if (!directControl)
9373 return S_OK;
9374
9375 return directControl->OnParallelPortChange(parallelPort);
9376}
9377
9378/**
9379 * @note Locks this object for reading.
9380 */
9381HRESULT SessionMachine::onVRDPServerChange()
9382{
9383 LogFlowThisFunc (("\n"));
9384
9385 AutoCaller autoCaller (this);
9386 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9387
9388 ComPtr <IInternalSessionControl> directControl;
9389 {
9390 AutoReaderLock alock (this);
9391 directControl = mData->mSession.mDirectControl;
9392 }
9393
9394 /* ignore notifications sent after #OnSessionEnd() is called */
9395 if (!directControl)
9396 return S_OK;
9397
9398 return directControl->OnVRDPServerChange();
9399}
9400
9401/**
9402 * @note Locks this object for reading.
9403 */
9404HRESULT SessionMachine::onUSBControllerChange()
9405{
9406 LogFlowThisFunc (("\n"));
9407
9408 AutoCaller autoCaller (this);
9409 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9410
9411 ComPtr <IInternalSessionControl> directControl;
9412 {
9413 AutoReaderLock alock (this);
9414 directControl = mData->mSession.mDirectControl;
9415 }
9416
9417 /* ignore notifications sent after #OnSessionEnd() is called */
9418 if (!directControl)
9419 return S_OK;
9420
9421 return directControl->OnUSBControllerChange();
9422}
9423
9424/**
9425 * @note Locks this object for reading.
9426 */
9427HRESULT SessionMachine::onSharedFolderChange()
9428{
9429 LogFlowThisFunc (("\n"));
9430
9431 AutoCaller autoCaller (this);
9432 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9433
9434 ComPtr <IInternalSessionControl> directControl;
9435 {
9436 AutoReaderLock alock (this);
9437 directControl = mData->mSession.mDirectControl;
9438 }
9439
9440 /* ignore notifications sent after #OnSessionEnd() is called */
9441 if (!directControl)
9442 return S_OK;
9443
9444 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
9445}
9446
9447/**
9448 * @note Locks this object for reading.
9449 */
9450HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
9451 IVirtualBoxErrorInfo *aError)
9452{
9453 LogFlowThisFunc (("\n"));
9454
9455 AutoCaller autoCaller (this);
9456
9457 /* This notification may happen after the machine object has been
9458 * uninitialized (the session was closed), so don't assert. */
9459 CheckComRCReturnRC (autoCaller.rc());
9460
9461 ComPtr <IInternalSessionControl> directControl;
9462 {
9463 AutoReaderLock alock (this);
9464 directControl = mData->mSession.mDirectControl;
9465 }
9466
9467 /* fail on notifications sent after #OnSessionEnd() is called, it is
9468 * expected by the caller */
9469 if (!directControl)
9470 return E_FAIL;
9471
9472 return directControl->OnUSBDeviceAttach (aDevice, aError);
9473}
9474
9475/**
9476 * @note Locks this object for reading.
9477 */
9478HRESULT SessionMachine::onUSBDeviceDetach (INPTR GUIDPARAM aId,
9479 IVirtualBoxErrorInfo *aError)
9480{
9481 LogFlowThisFunc (("\n"));
9482
9483 AutoCaller autoCaller (this);
9484
9485 /* This notification may happen after the machine object has been
9486 * uninitialized (the session was closed), so don't assert. */
9487 CheckComRCReturnRC (autoCaller.rc());
9488
9489 ComPtr <IInternalSessionControl> directControl;
9490 {
9491 AutoReaderLock alock (this);
9492 directControl = mData->mSession.mDirectControl;
9493 }
9494
9495 /* fail on notifications sent after #OnSessionEnd() is called, it is
9496 * expected by the caller */
9497 if (!directControl)
9498 return E_FAIL;
9499
9500 return directControl->OnUSBDeviceDetach (aId, aError);
9501}
9502
9503// protected methods
9504/////////////////////////////////////////////////////////////////////////////
9505
9506/**
9507 * Helper method to finalize saving the state.
9508 *
9509 * @note Must be called from under this object's lock.
9510 *
9511 * @param aSuccess TRUE if the snapshot has been taken successfully
9512 *
9513 * @note Locks mParent + this objects for writing.
9514 */
9515HRESULT SessionMachine::endSavingState (BOOL aSuccess)
9516{
9517 LogFlowThisFuncEnter();
9518
9519 AutoCaller autoCaller (this);
9520 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9521
9522 /* mParent->removeProgress() needs mParent lock */
9523 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9524
9525 HRESULT rc = S_OK;
9526
9527 if (aSuccess)
9528 {
9529 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
9530
9531 /* save all VM settings */
9532 rc = saveSettings();
9533 }
9534 else
9535 {
9536 /* delete the saved state file (it might have been already created) */
9537 RTFileDelete (Utf8Str (mSnapshotData.mStateFilePath));
9538 }
9539
9540 /* remove the completed progress object */
9541 mParent->removeProgress (mSnapshotData.mProgressId);
9542
9543 /* clear out the temporary saved state data */
9544 mSnapshotData.mLastState = MachineState_InvalidMachineState;
9545 mSnapshotData.mProgressId.clear();
9546 mSnapshotData.mStateFilePath.setNull();
9547
9548 LogFlowThisFuncLeave();
9549 return rc;
9550}
9551
9552/**
9553 * Helper method to finalize taking a snapshot.
9554 * Gets called only from #EndTakingSnapshot() that is expected to
9555 * be called by the VM process when it finishes *all* the tasks related to
9556 * taking a snapshot, either scucessfully or unsuccessfilly.
9557 *
9558 * @param aSuccess TRUE if the snapshot has been taken successfully
9559 *
9560 * @note Locks mParent + this objects for writing.
9561 */
9562HRESULT SessionMachine::endTakingSnapshot (BOOL aSuccess)
9563{
9564 LogFlowThisFuncEnter();
9565
9566 AutoCaller autoCaller (this);
9567 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9568
9569 /* Progress object uninitialization needs mParent lock */
9570 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9571
9572 HRESULT rc = S_OK;
9573
9574 if (aSuccess)
9575 {
9576 /* the server progress must be completed on success */
9577 Assert (mSnapshotData.mServerProgress->completed());
9578
9579 mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
9580 /* memorize the first snapshot if necessary */
9581 if (!mData->mFirstSnapshot)
9582 mData->mFirstSnapshot = mData->mCurrentSnapshot;
9583
9584 int opFlags = SaveSS_AddOp | SaveSS_UpdateCurrentId;
9585 if (mSnapshotData.mLastState != MachineState_Paused && !isModified())
9586 {
9587 /*
9588 * the machine was powered off or saved when taking a snapshot,
9589 * so reset the mCurrentStateModified flag
9590 */
9591 mData->mCurrentStateModified = FALSE;
9592 opFlags |= SaveSS_UpdateCurStateModified;
9593 }
9594
9595 rc = saveSnapshotSettings (mSnapshotData.mSnapshot, opFlags);
9596 }
9597
9598 if (!aSuccess || FAILED (rc))
9599 {
9600 if (mSnapshotData.mSnapshot)
9601 {
9602 /* wait for the completion of the server progress (diff VDI creation) */
9603 /// @todo (dmik) later, we will definitely want to cancel it instead
9604 // (when the cancel function is implemented)
9605 mSnapshotData.mServerProgress->WaitForCompletion (-1);
9606
9607 /*
9608 * delete all differencing VDIs created
9609 * (this will attach their parents back)
9610 */
9611 rc = deleteSnapshotDiffs (mSnapshotData.mSnapshot);
9612 /* continue cleanup on error */
9613
9614 /* delete the saved state file (it might have been already created) */
9615 if (mSnapshotData.mSnapshot->stateFilePath())
9616 RTFileDelete (Utf8Str (mSnapshotData.mSnapshot->stateFilePath()));
9617
9618 mSnapshotData.mSnapshot->uninit();
9619 }
9620 }
9621
9622 /* inform callbacks */
9623 if (aSuccess && SUCCEEDED (rc))
9624 mParent->onSnapshotTaken (mData->mUuid, mSnapshotData.mSnapshot->data().mId);
9625
9626 /* clear out the snapshot data */
9627 mSnapshotData.mLastState = MachineState_InvalidMachineState;
9628 mSnapshotData.mSnapshot.setNull();
9629 mSnapshotData.mServerProgress.setNull();
9630 /* uninitialize the combined progress (to remove it from the VBox collection) */
9631 if (!mSnapshotData.mCombinedProgress.isNull())
9632 {
9633 mSnapshotData.mCombinedProgress->uninit();
9634 mSnapshotData.mCombinedProgress.setNull();
9635 }
9636
9637 LogFlowThisFuncLeave();
9638 return rc;
9639}
9640
9641/**
9642 * Take snapshot task handler.
9643 * Must be called only by TakeSnapshotTask::handler()!
9644 *
9645 * The sole purpose of this task is to asynchronously create differencing VDIs
9646 * and copy the saved state file (when necessary). The VM process will wait
9647 * for this task to complete using the mSnapshotData.mServerProgress
9648 * returned to it.
9649 *
9650 * @note Locks mParent + this objects for writing.
9651 */
9652void SessionMachine::takeSnapshotHandler (TakeSnapshotTask &aTask)
9653{
9654 LogFlowThisFuncEnter();
9655
9656 AutoCaller autoCaller (this);
9657
9658 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
9659 if (!autoCaller.isOk())
9660 {
9661 /*
9662 * we might have been uninitialized because the session was
9663 * accidentally closed by the client, so don't assert
9664 */
9665 LogFlowThisFuncLeave();
9666 return;
9667 }
9668
9669 /* endTakingSnapshot() needs mParent lock */
9670 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9671
9672 HRESULT rc = S_OK;
9673
9674 LogFlowThisFunc (("Creating differencing VDIs...\n"));
9675
9676 /* create new differencing hard disks and attach them to this machine */
9677 rc = createSnapshotDiffs (&mSnapshotData.mSnapshot->data().mId,
9678 mUserData->mSnapshotFolderFull,
9679 mSnapshotData.mServerProgress,
9680 true /* aOnline */);
9681
9682 if (SUCCEEDED (rc) && mSnapshotData.mLastState == MachineState_Saved)
9683 {
9684 Utf8Str stateFrom = mSSData->mStateFilePath;
9685 Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
9686
9687 LogFlowThisFunc (("Copying the execution state from '%s' to '%s'...\n",
9688 stateFrom.raw(), stateTo.raw()));
9689
9690 mSnapshotData.mServerProgress->advanceOperation (
9691 Bstr (tr ("Copying the execution state")));
9692
9693 /*
9694 * We can safely leave the lock here:
9695 * mMachineState is MachineState_Saving here
9696 */
9697 alock.leave();
9698
9699 /* copy the state file */
9700 int vrc = RTFileCopyEx (stateFrom, stateTo, progressCallback,
9701 static_cast <Progress *> (mSnapshotData.mServerProgress));
9702
9703 alock.enter();
9704
9705 if (VBOX_FAILURE (vrc))
9706 rc = setError (E_FAIL,
9707 tr ("Could not copy the state file '%ls' to '%ls' (%Vrc)"),
9708 stateFrom.raw(), stateTo.raw());
9709 }
9710
9711 /*
9712 * we have to call endTakingSnapshot() here if the snapshot was taken
9713 * offline, because the VM process will not do it in this case
9714 */
9715 if (mSnapshotData.mLastState != MachineState_Paused)
9716 {
9717 LogFlowThisFunc (("Finalizing the taken snapshot (rc=%08X)...\n", rc));
9718
9719 setMachineState (mSnapshotData.mLastState);
9720 updateMachineStateOnClient();
9721
9722 /* finalize the progress after setting the state, for consistency */
9723 mSnapshotData.mServerProgress->notifyComplete (rc);
9724
9725 endTakingSnapshot (SUCCEEDED (rc));
9726 }
9727 else
9728 {
9729 mSnapshotData.mServerProgress->notifyComplete (rc);
9730 }
9731
9732 LogFlowThisFuncLeave();
9733}
9734
9735/**
9736 * Discard snapshot task handler.
9737 * Must be called only by DiscardSnapshotTask::handler()!
9738 *
9739 * When aTask.subTask is true, the associated progress object is left
9740 * uncompleted on success. On failure, the progress is marked as completed
9741 * regardless of this parameter.
9742 *
9743 * @note Locks mParent + this + child objects for writing!
9744 */
9745void SessionMachine::discardSnapshotHandler (DiscardSnapshotTask &aTask)
9746{
9747 LogFlowThisFuncEnter();
9748
9749 AutoCaller autoCaller (this);
9750
9751 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
9752 if (!autoCaller.isOk())
9753 {
9754 /*
9755 * we might have been uninitialized because the session was
9756 * accidentally closed by the client, so don't assert
9757 */
9758 aTask.progress->notifyComplete (
9759 E_FAIL, COM_IIDOF (IMachine), getComponentName(),
9760 tr ("The session has been accidentally closed"));
9761
9762 LogFlowThisFuncLeave();
9763 return;
9764 }
9765
9766 ComObjPtr <SnapshotMachine> sm = aTask.snapshot->data().mMachine;
9767
9768 /* mParent is locked because of Progress::notifyComplete(), etc. */
9769 AutoMultiLock <3> alock (mParent->wlock(), this->wlock(), sm->rlock());
9770
9771 /* Safe locking in the direction parent->child */
9772 AutoLock snapshotLock (aTask.snapshot);
9773 AutoLock snapshotChildrenLock (aTask.snapshot->childrenLock());
9774
9775 HRESULT rc = S_OK;
9776
9777 /* save the snapshot ID (for callbacks) */
9778 Guid snapshotId = aTask.snapshot->data().mId;
9779
9780 do
9781 {
9782 /* first pass: */
9783 LogFlowThisFunc (("Check hard disk accessibility and affected machines...\n"));
9784
9785 HDData::HDAttachmentList::const_iterator it;
9786 for (it = sm->mHDData->mHDAttachments.begin();
9787 it != sm->mHDData->mHDAttachments.end();
9788 ++ it)
9789 {
9790 ComObjPtr <HardDiskAttachment> hda = *it;
9791 ComObjPtr <HardDisk> hd = hda->hardDisk();
9792 ComObjPtr <HardDisk> parent = hd->parent();
9793
9794 AutoLock hdLock (hd);
9795
9796 if (hd->hasForeignChildren())
9797 {
9798 rc = setError (E_FAIL,
9799 tr ("One or more hard disks belonging to other machines are "
9800 "based on the hard disk '%ls' stored in the snapshot '%ls'"),
9801 hd->toString().raw(), aTask.snapshot->data().mName.raw());
9802 break;
9803 }
9804
9805 if (hd->type() == HardDiskType_NormalHardDisk)
9806 {
9807 AutoLock hdChildrenLock (hd->childrenLock());
9808 size_t childrenCount = hd->children().size();
9809 if (childrenCount > 1)
9810 {
9811 rc = setError (E_FAIL,
9812 tr ("Normal hard disk '%ls' stored in the snapshot '%ls' "
9813 "has more than one child hard disk (%d)"),
9814 hd->toString().raw(), aTask.snapshot->data().mName.raw(),
9815 childrenCount);
9816 break;
9817 }
9818 }
9819 else
9820 {
9821 ComAssertMsgFailedBreak (("Invalid hard disk type %d\n", hd->type()),
9822 rc = E_FAIL);
9823 }
9824
9825 Bstr accessError;
9826 rc = hd->getAccessibleWithChildren (accessError);
9827 CheckComRCBreakRC (rc);
9828
9829 if (!accessError.isNull())
9830 {
9831 rc = setError (E_FAIL,
9832 tr ("Hard disk '%ls' stored in the snapshot '%ls' is not "
9833 "accessible (%ls)"),
9834 hd->toString().raw(), aTask.snapshot->data().mName.raw(),
9835 accessError.raw());
9836 break;
9837 }
9838
9839 rc = hd->setBusyWithChildren();
9840 if (FAILED (rc))
9841 {
9842 /* reset the busy flag of all previous hard disks */
9843 while (it != sm->mHDData->mHDAttachments.begin())
9844 (*(-- it))->hardDisk()->clearBusyWithChildren();
9845 break;
9846 }
9847 }
9848
9849 CheckComRCBreakRC (rc);
9850
9851 /* second pass: */
9852 LogFlowThisFunc (("Performing actual vdi merging...\n"));
9853
9854 for (it = sm->mHDData->mHDAttachments.begin();
9855 it != sm->mHDData->mHDAttachments.end();
9856 ++ it)
9857 {
9858 ComObjPtr <HardDiskAttachment> hda = *it;
9859 ComObjPtr <HardDisk> hd = hda->hardDisk();
9860 ComObjPtr <HardDisk> parent = hd->parent();
9861
9862 AutoLock hdLock (hd);
9863
9864 Bstr hdRootString = hd->root()->toString (true /* aShort */);
9865
9866 if (parent)
9867 {
9868 if (hd->isParentImmutable())
9869 {
9870 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9871 tr ("Discarding changes to immutable hard disk '%ls'"),
9872 hdRootString.raw())));
9873
9874 /* clear the busy flag before unregistering */
9875 hd->clearBusy();
9876
9877 /*
9878 * unregisterDiffHardDisk() is supposed to delete and uninit
9879 * the differencing hard disk
9880 */
9881 rc = mParent->unregisterDiffHardDisk (hd);
9882 CheckComRCBreakRC (rc);
9883 continue;
9884 }
9885 else
9886 {
9887 /*
9888 * differencing VDI:
9889 * merge this image to all its children
9890 */
9891
9892 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9893 tr ("Merging changes to normal hard disk '%ls' to children"),
9894 hdRootString.raw())));
9895
9896 snapshotChildrenLock.unlock();
9897 snapshotLock.unlock();
9898 alock.leave();
9899
9900 rc = hd->asVDI()->mergeImageToChildren (aTask.progress);
9901
9902 alock.enter();
9903 snapshotLock.lock();
9904 snapshotChildrenLock.lock();
9905
9906 // debug code
9907 // if (it != sm->mHDData->mHDAttachments.begin())
9908 // {
9909 // rc = setError (E_FAIL, "Simulated failure");
9910 // break;
9911 //}
9912
9913 if (SUCCEEDED (rc))
9914 rc = mParent->unregisterDiffHardDisk (hd);
9915 else
9916 hd->clearBusyWithChildren();
9917
9918 CheckComRCBreakRC (rc);
9919 }
9920 }
9921 else if (hd->type() == HardDiskType_NormalHardDisk)
9922 {
9923 /*
9924 * normal vdi has the only child or none
9925 * (checked in the first pass)
9926 */
9927
9928 ComObjPtr <HardDisk> child;
9929 {
9930 AutoLock hdChildrenLock (hd->childrenLock());
9931 if (hd->children().size())
9932 child = hd->children().front();
9933 }
9934
9935 if (child.isNull())
9936 {
9937 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9938 tr ("Detaching normal hard disk '%ls'"),
9939 hdRootString.raw())));
9940
9941 /* just deassociate the normal image from this machine */
9942 hd->setMachineId (Guid());
9943 hd->setSnapshotId (Guid());
9944
9945 /* clear the busy flag */
9946 hd->clearBusy();
9947 }
9948 else
9949 {
9950 AutoLock childLock (child);
9951
9952 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9953 tr ("Preserving changes to normal hard disk '%ls'"),
9954 hdRootString.raw())));
9955
9956 ComObjPtr <Machine> cm;
9957 ComObjPtr <Snapshot> cs;
9958 ComObjPtr <HardDiskAttachment> childHda;
9959 rc = findHardDiskAttachment (child, &cm, &cs, &childHda);
9960 CheckComRCBreakRC (rc);
9961 /* must be the same machine (checked in the first pass) */
9962 ComAssertBreak (cm->mData->mUuid == mData->mUuid, rc = E_FAIL);
9963
9964 /* merge the child to this basic image */
9965
9966 snapshotChildrenLock.unlock();
9967 snapshotLock.unlock();
9968 alock.leave();
9969
9970 rc = child->asVDI()->mergeImageToParent (aTask.progress);
9971
9972 alock.enter();
9973 snapshotLock.lock();
9974 snapshotChildrenLock.lock();
9975
9976 if (SUCCEEDED (rc))
9977 rc = mParent->unregisterDiffHardDisk (child);
9978 else
9979 hd->clearBusyWithChildren();
9980
9981 CheckComRCBreakRC (rc);
9982
9983 /* reset the snapshot Id */
9984 hd->setSnapshotId (Guid());
9985
9986 /* replace the child image in the appropriate place */
9987 childHda->updateHardDisk (hd, FALSE /* aDirty */);
9988
9989 if (!cs)
9990 {
9991 aTask.settingsChanged = true;
9992 }
9993 else
9994 {
9995 rc = cm->saveSnapshotSettings (cs, SaveSS_UpdateAllOp);
9996 CheckComRCBreakRC (rc);
9997 }
9998 }
9999 }
10000 else
10001 {
10002 ComAssertMsgFailedBreak (("Invalid hard disk type %d\n", hd->type()),
10003 rc = E_FAIL);
10004 }
10005 }
10006
10007 /* preserve existing error info */
10008 ErrorInfoKeeper mergeEik;
10009 HRESULT mergeRc = rc;
10010
10011 if (FAILED (rc))
10012 {
10013 /* clear the busy flag on the rest of hard disks */
10014 for (++ it; it != sm->mHDData->mHDAttachments.end(); ++ it)
10015 (*it)->hardDisk()->clearBusyWithChildren();
10016 }
10017
10018 /*
10019 * we have to try to discard the snapshot even if merging failed
10020 * because some images might have been already merged (and deleted)
10021 */
10022
10023 do
10024 {
10025 LogFlowThisFunc (("Discarding the snapshot (reparenting children)...\n"));
10026
10027 /* It is important to uninitialize and delete all snapshot's hard
10028 * disk attachments as they are no longer valid -- otherwise the
10029 * code in Machine::uninitDataAndChildObjects() will mistakenly
10030 * perform hard disk deassociation. */
10031 for (HDData::HDAttachmentList::iterator it = sm->mHDData->mHDAttachments.begin();
10032 it != sm->mHDData->mHDAttachments.end();)
10033 {
10034 (*it)->uninit();
10035 it = sm->mHDData->mHDAttachments.erase (it);
10036 }
10037
10038 ComObjPtr <Snapshot> parentSnapshot = aTask.snapshot->parent();
10039
10040 /// @todo (dmik):
10041 // when we introduce clones later, discarding the snapshot
10042 // will affect the current and first snapshots of clones, if they are
10043 // direct children of this snapshot. So we will need to lock machines
10044 // associated with child snapshots as well and update mCurrentSnapshot
10045 // and/or mFirstSnapshot fields.
10046
10047 if (aTask.snapshot == mData->mCurrentSnapshot)
10048 {
10049 /* currently, the parent snapshot must refer to the same machine */
10050 ComAssertBreak (
10051 !parentSnapshot ||
10052 parentSnapshot->data().mMachine->mData->mUuid == mData->mUuid,
10053 rc = E_FAIL);
10054 mData->mCurrentSnapshot = parentSnapshot;
10055 /* mark the current state as modified */
10056 mData->mCurrentStateModified = TRUE;
10057 }
10058
10059 if (aTask.snapshot == mData->mFirstSnapshot)
10060 {
10061 /*
10062 * the first snapshot must have only one child when discarded,
10063 * or no children at all
10064 */
10065 ComAssertBreak (aTask.snapshot->children().size() <= 1, rc = E_FAIL);
10066
10067 if (aTask.snapshot->children().size() == 1)
10068 {
10069 ComObjPtr <Snapshot> childSnapshot = aTask.snapshot->children().front();
10070 ComAssertBreak (
10071 childSnapshot->data().mMachine->mData->mUuid == mData->mUuid,
10072 rc = E_FAIL);
10073 mData->mFirstSnapshot = childSnapshot;
10074 }
10075 else
10076 mData->mFirstSnapshot.setNull();
10077 }
10078
10079 /// @todo (dmik)
10080 // if we implement some warning mechanism later, we'll have
10081 // to return a warning if the state file path cannot be deleted
10082 Bstr stateFilePath = aTask.snapshot->stateFilePath();
10083 if (stateFilePath)
10084 RTFileDelete (Utf8Str (stateFilePath));
10085
10086 aTask.snapshot->discard();
10087
10088 rc = saveSnapshotSettings (parentSnapshot,
10089 SaveSS_UpdateAllOp | SaveSS_UpdateCurrentId);
10090 }
10091 while (0);
10092
10093 /* restore the merge error if any (ErrorInfo will be restored
10094 * automatically) */
10095 if (FAILED (mergeRc))
10096 rc = mergeRc;
10097 }
10098 while (0);
10099
10100 if (!aTask.subTask || FAILED (rc))
10101 {
10102 if (!aTask.subTask)
10103 {
10104 /* preserve existing error info */
10105 ErrorInfoKeeper eik;
10106
10107 /* restore the machine state */
10108 setMachineState (aTask.state);
10109 updateMachineStateOnClient();
10110
10111 /*
10112 * save settings anyway, since we've already changed the current
10113 * machine configuration
10114 */
10115 if (aTask.settingsChanged)
10116 {
10117 saveSettings (true /* aMarkCurStateAsModified */,
10118 true /* aInformCallbacksAnyway */);
10119 }
10120 }
10121
10122 /* set the result (this will try to fetch current error info on failure) */
10123 aTask.progress->notifyComplete (rc);
10124 }
10125
10126 if (SUCCEEDED (rc))
10127 mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
10128
10129 LogFlowThisFunc (("Done discarding snapshot (rc=%08X)\n", rc));
10130 LogFlowThisFuncLeave();
10131}
10132
10133/**
10134 * Discard current state task handler.
10135 * Must be called only by DiscardCurrentStateTask::handler()!
10136 *
10137 * @note Locks mParent + this object for writing.
10138 */
10139void SessionMachine::discardCurrentStateHandler (DiscardCurrentStateTask &aTask)
10140{
10141 LogFlowThisFuncEnter();
10142
10143 AutoCaller autoCaller (this);
10144
10145 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
10146 if (!autoCaller.isOk())
10147 {
10148 /*
10149 * we might have been uninitialized because the session was
10150 * accidentally closed by the client, so don't assert
10151 */
10152 aTask.progress->notifyComplete (
10153 E_FAIL, COM_IIDOF (IMachine), getComponentName(),
10154 tr ("The session has been accidentally closed"));
10155
10156 LogFlowThisFuncLeave();
10157 return;
10158 }
10159
10160 /* mParent is locked because of Progress::notifyComplete(), etc. */
10161 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
10162
10163 /*
10164 * discard all current changes to mUserData (name, OSType etc.)
10165 * (note that the machine is powered off, so there is no need
10166 * to inform the direct session)
10167 */
10168 if (isModified())
10169 rollback (false /* aNotify */);
10170
10171 HRESULT rc = S_OK;
10172
10173 bool errorInSubtask = false;
10174 bool stateRestored = false;
10175
10176 const bool isLastSnapshot = mData->mCurrentSnapshot->parent().isNull();
10177
10178 do
10179 {
10180 /*
10181 * discard the saved state file if the machine was Saved prior
10182 * to this operation
10183 */
10184 if (aTask.state == MachineState_Saved)
10185 {
10186 Assert (!mSSData->mStateFilePath.isEmpty());
10187 RTFileDelete (Utf8Str (mSSData->mStateFilePath));
10188 mSSData->mStateFilePath.setNull();
10189 aTask.modifyLastState (MachineState_PoweredOff);
10190 rc = saveStateSettings (SaveSTS_StateFilePath);
10191 CheckComRCBreakRC (rc);
10192 }
10193
10194 if (aTask.discardCurrentSnapshot && !isLastSnapshot)
10195 {
10196 /*
10197 * the "discard current snapshot and state" task is in action,
10198 * the current snapshot is not the last one.
10199 * Discard the current snapshot first.
10200 */
10201
10202 DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
10203 subTask.subTask = true;
10204 discardSnapshotHandler (subTask);
10205 aTask.settingsChanged = subTask.settingsChanged;
10206 if (aTask.progress->completed())
10207 {
10208 /*
10209 * the progress can be completed by a subtask only if there was
10210 * a failure
10211 */
10212 Assert (FAILED (aTask.progress->resultCode()));
10213 errorInSubtask = true;
10214 rc = aTask.progress->resultCode();
10215 break;
10216 }
10217 }
10218
10219 LONG64 snapshotTimeStamp = 0;
10220
10221 {
10222 ComObjPtr <Snapshot> curSnapshot = mData->mCurrentSnapshot;
10223 AutoLock snapshotLock (curSnapshot);
10224
10225 /* remember the timestamp of the snapshot we're restoring from */
10226 snapshotTimeStamp = curSnapshot->data().mTimeStamp;
10227
10228 /* copy all hardware data from the current snapshot */
10229 copyFrom (curSnapshot->data().mMachine);
10230
10231 LogFlowThisFunc (("Restoring VDIs from the snapshot...\n"));
10232
10233 /* restore the attachmends from the snapshot */
10234 mHDData.backup();
10235 mHDData->mHDAttachments =
10236 curSnapshot->data().mMachine->mHDData->mHDAttachments;
10237
10238 snapshotLock.unlock();
10239 alock.leave();
10240 rc = createSnapshotDiffs (NULL, mUserData->mSnapshotFolderFull,
10241 aTask.progress,
10242 false /* aOnline */);
10243 alock.enter();
10244 snapshotLock.lock();
10245
10246 if (FAILED (rc))
10247 {
10248 /* here we can still safely rollback, so do it */
10249 /* preserve existing error info */
10250 ErrorInfoKeeper eik;
10251 /* undo all changes */
10252 rollback (false /* aNotify */);
10253 break;
10254 }
10255
10256 /*
10257 * note: old VDIs will be deassociated/deleted on #commit() called
10258 * either from #saveSettings() or directly at the end
10259 */
10260
10261 /* should not have a saved state file associated at this point */
10262 Assert (mSSData->mStateFilePath.isNull());
10263
10264 if (curSnapshot->stateFilePath())
10265 {
10266 Utf8Str snapStateFilePath = curSnapshot->stateFilePath();
10267
10268 Utf8Str stateFilePath = Utf8StrFmt ("%ls%c{%Vuuid}.sav",
10269 mUserData->mSnapshotFolderFull.raw(),
10270 RTPATH_DELIMITER, mData->mUuid.raw());
10271
10272 LogFlowThisFunc (("Copying saved state file from '%s' to '%s'...\n",
10273 snapStateFilePath.raw(), stateFilePath.raw()));
10274
10275 aTask.progress->advanceOperation (
10276 Bstr (tr ("Restoring the execution state")));
10277
10278 /* copy the state file */
10279 snapshotLock.unlock();
10280 alock.leave();
10281 int vrc = RTFileCopyEx (snapStateFilePath, stateFilePath,
10282 progressCallback, aTask.progress);
10283 alock.enter();
10284 snapshotLock.lock();
10285
10286 if (VBOX_SUCCESS (vrc))
10287 {
10288 mSSData->mStateFilePath = stateFilePath;
10289 }
10290 else
10291 {
10292 rc = setError (E_FAIL,
10293 tr ("Could not copy the state file '%s' to '%s' (%Vrc)"),
10294 snapStateFilePath.raw(), stateFilePath.raw(), vrc);
10295 break;
10296 }
10297 }
10298 }
10299
10300 bool informCallbacks = false;
10301
10302 if (aTask.discardCurrentSnapshot && isLastSnapshot)
10303 {
10304 /*
10305 * discard the current snapshot and state task is in action,
10306 * the current snapshot is the last one.
10307 * Discard the current snapshot after discarding the current state.
10308 */
10309
10310 /* commit changes to fixup hard disks before discarding */
10311 rc = commit();
10312 if (SUCCEEDED (rc))
10313 {
10314 DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
10315 subTask.subTask = true;
10316 discardSnapshotHandler (subTask);
10317 aTask.settingsChanged = subTask.settingsChanged;
10318 if (aTask.progress->completed())
10319 {
10320 /*
10321 * the progress can be completed by a subtask only if there
10322 * was a failure
10323 */
10324 Assert (FAILED (aTask.progress->resultCode()));
10325 errorInSubtask = true;
10326 rc = aTask.progress->resultCode();
10327 }
10328 }
10329
10330 /*
10331 * we've committed already, so inform callbacks anyway to ensure
10332 * they don't miss some change
10333 */
10334 informCallbacks = true;
10335 }
10336
10337 /*
10338 * we have already discarded the current state, so set the
10339 * execution state accordingly no matter of the discard snapshot result
10340 */
10341 if (mSSData->mStateFilePath)
10342 setMachineState (MachineState_Saved);
10343 else
10344 setMachineState (MachineState_PoweredOff);
10345
10346 updateMachineStateOnClient();
10347 stateRestored = true;
10348
10349 if (errorInSubtask)
10350 break;
10351
10352 /* assign the timestamp from the snapshot */
10353 Assert (snapshotTimeStamp != 0);
10354 mData->mLastStateChange = snapshotTimeStamp;
10355
10356 /* mark the current state as not modified */
10357 mData->mCurrentStateModified = FALSE;
10358
10359 /* save all settings and commit */
10360 rc = saveSettings (false /* aMarkCurStateAsModified */,
10361 informCallbacks);
10362 aTask.settingsChanged = false;
10363 }
10364 while (0);
10365
10366 if (FAILED (rc))
10367 {
10368 /* preserve existing error info */
10369 ErrorInfoKeeper eik;
10370
10371 if (!stateRestored)
10372 {
10373 /* restore the machine state */
10374 setMachineState (aTask.state);
10375 updateMachineStateOnClient();
10376 }
10377
10378 /*
10379 * save all settings and commit if still modified (there is no way to
10380 * rollback properly). Note that isModified() will return true after
10381 * copyFrom(). Also save the settings if requested by the subtask.
10382 */
10383 if (isModified() || aTask.settingsChanged)
10384 {
10385 if (aTask.settingsChanged)
10386 saveSettings (true /* aMarkCurStateAsModified */,
10387 true /* aInformCallbacksAnyway */);
10388 else
10389 saveSettings();
10390 }
10391 }
10392
10393 if (!errorInSubtask)
10394 {
10395 /* set the result (this will try to fetch current error info on failure) */
10396 aTask.progress->notifyComplete (rc);
10397 }
10398
10399 if (SUCCEEDED (rc))
10400 mParent->onSnapshotDiscarded (mData->mUuid, Guid());
10401
10402 LogFlowThisFunc (("Done discarding current state (rc=%08X)\n", rc));
10403
10404 LogFlowThisFuncLeave();
10405}
10406
10407/**
10408 * Helper to change the machine state (reimplementation).
10409 *
10410 * @note Locks this object for writing.
10411 */
10412HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
10413{
10414 LogFlowThisFuncEnter();
10415 LogFlowThisFunc (("aMachineState=%d\n", aMachineState));
10416
10417 AutoCaller autoCaller (this);
10418 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10419
10420 AutoLock alock (this);
10421
10422 MachineState_T oldMachineState = mData->mMachineState;
10423
10424 AssertMsgReturn (oldMachineState != aMachineState,
10425 ("oldMachineState=%d, aMachineState=%d\n",
10426 oldMachineState, aMachineState), E_FAIL);
10427
10428 HRESULT rc = S_OK;
10429
10430 int stsFlags = 0;
10431 bool deleteSavedState = false;
10432
10433 /* detect some state transitions */
10434
10435 if (oldMachineState < MachineState_Running &&
10436 aMachineState >= MachineState_Running &&
10437 aMachineState != MachineState_Discarding)
10438 {
10439 /*
10440 * the EMT thread is about to start, so mark attached HDDs as busy
10441 * and all its ancestors as being in use
10442 */
10443 for (HDData::HDAttachmentList::const_iterator it =
10444 mHDData->mHDAttachments.begin();
10445 it != mHDData->mHDAttachments.end();
10446 ++ it)
10447 {
10448 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
10449 AutoLock hdLock (hd);
10450 hd->setBusy();
10451 hd->addReaderOnAncestors();
10452 }
10453 }
10454 else
10455 if (oldMachineState >= MachineState_Running &&
10456 oldMachineState != MachineState_Discarding &&
10457 aMachineState < MachineState_Running)
10458 {
10459 /*
10460 * the EMT thread stopped, so mark attached HDDs as no more busy
10461 * and remove the in-use flag from all its ancestors
10462 */
10463 for (HDData::HDAttachmentList::const_iterator it =
10464 mHDData->mHDAttachments.begin();
10465 it != mHDData->mHDAttachments.end();
10466 ++ it)
10467 {
10468 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
10469 AutoLock hdLock (hd);
10470 hd->releaseReaderOnAncestors();
10471 hd->clearBusy();
10472 }
10473 }
10474
10475 if (oldMachineState == MachineState_Restoring)
10476 {
10477 if (aMachineState != MachineState_Saved)
10478 {
10479 /*
10480 * delete the saved state file once the machine has finished
10481 * restoring from it (note that Console sets the state from
10482 * Restoring to Saved if the VM couldn't restore successfully,
10483 * to give the user an ability to fix an error and retry --
10484 * we keep the saved state file in this case)
10485 */
10486 deleteSavedState = true;
10487 }
10488 }
10489 else
10490 if (oldMachineState == MachineState_Saved &&
10491 (aMachineState == MachineState_PoweredOff ||
10492 aMachineState == MachineState_Aborted))
10493 {
10494 /*
10495 * delete the saved state after Console::DiscardSavedState() is called
10496 * or if the VM process (owning a direct VM session) crashed while the
10497 * VM was Saved
10498 */
10499
10500 /// @todo (dmik)
10501 // Not sure that deleting the saved state file just because of the
10502 // client death before it attempted to restore the VM is a good
10503 // thing. But when it crashes we need to go to the Aborted state
10504 // which cannot have the saved state file associated... The only
10505 // way to fix this is to make the Aborted condition not a VM state
10506 // but a bool flag: i.e., when a crash occurs, set it to true and
10507 // change the state to PoweredOff or Saved depending on the
10508 // saved state presence.
10509
10510 deleteSavedState = true;
10511 mData->mCurrentStateModified = TRUE;
10512 stsFlags |= SaveSTS_CurStateModified;
10513 }
10514
10515 if (aMachineState == MachineState_Starting ||
10516 aMachineState == MachineState_Restoring)
10517 {
10518 /*
10519 * set the current state modified flag to indicate that the
10520 * current state is no more identical to the state in the
10521 * current snapshot
10522 */
10523 if (!mData->mCurrentSnapshot.isNull())
10524 {
10525 mData->mCurrentStateModified = TRUE;
10526 stsFlags |= SaveSTS_CurStateModified;
10527 }
10528 }
10529
10530 if (deleteSavedState == true)
10531 {
10532 Assert (!mSSData->mStateFilePath.isEmpty());
10533 RTFileDelete (Utf8Str (mSSData->mStateFilePath));
10534 mSSData->mStateFilePath.setNull();
10535 stsFlags |= SaveSTS_StateFilePath;
10536 }
10537
10538 /* redirect to the underlying peer machine */
10539 mPeer->setMachineState (aMachineState);
10540
10541 if (aMachineState == MachineState_PoweredOff ||
10542 aMachineState == MachineState_Aborted ||
10543 aMachineState == MachineState_Saved)
10544 {
10545 stsFlags |= SaveSTS_StateTimeStamp;
10546 }
10547
10548 rc = saveStateSettings (stsFlags);
10549
10550 if ((oldMachineState != MachineState_PoweredOff &&
10551 oldMachineState != MachineState_Aborted) &&
10552 (aMachineState == MachineState_PoweredOff ||
10553 aMachineState == MachineState_Aborted))
10554 {
10555 /*
10556 * clear differencing hard disks based on immutable hard disks
10557 * once we've been shut down for any reason
10558 */
10559 rc = wipeOutImmutableDiffs();
10560 }
10561
10562 LogFlowThisFunc (("rc=%08X\n", rc));
10563 LogFlowThisFuncLeave();
10564 return rc;
10565}
10566
10567/**
10568 * Sends the current machine state value to the VM process.
10569 *
10570 * @note Locks this object for reading, then calls a client process.
10571 */
10572HRESULT SessionMachine::updateMachineStateOnClient()
10573{
10574 AutoCaller autoCaller (this);
10575 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10576
10577 ComPtr <IInternalSessionControl> directControl;
10578 {
10579 AutoReaderLock alock (this);
10580 AssertReturn (!!mData, E_FAIL);
10581 directControl = mData->mSession.mDirectControl;
10582
10583 /* directControl may be already set to NULL here in #OnSessionEnd()
10584 * called too early by the direct session process while there is still
10585 * some operation (like discarding the snapshot) in progress. The client
10586 * process in this case is waiting inside Session::close() for the
10587 * "end session" process object to complete, while #uninit() called by
10588 * #checkForDeath() on the Watcher thread is waiting for the pending
10589 * operation to complete. For now, we accept this inconsitent behavior
10590 * and simply do nothing here. */
10591
10592 if (mData->mSession.mState == SessionState_SessionClosing)
10593 return S_OK;
10594
10595 AssertReturn (!directControl.isNull(), E_FAIL);
10596 }
10597
10598 return directControl->UpdateMachineState (mData->mMachineState);
10599}
10600
10601/* static */
10602DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD thread, void *pvUser)
10603{
10604 AssertReturn (pvUser, VERR_INVALID_POINTER);
10605
10606 Task *task = static_cast <Task *> (pvUser);
10607 task->handler();
10608
10609 // it's our responsibility to delete the task
10610 delete task;
10611
10612 return 0;
10613}
10614
10615/////////////////////////////////////////////////////////////////////////////
10616// SnapshotMachine class
10617/////////////////////////////////////////////////////////////////////////////
10618
10619DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
10620
10621HRESULT SnapshotMachine::FinalConstruct()
10622{
10623 LogFlowThisFunc (("\n"));
10624
10625 /* set the proper type to indicate we're the SnapshotMachine instance */
10626 unconst (mType) = IsSnapshotMachine;
10627
10628 return S_OK;
10629}
10630
10631void SnapshotMachine::FinalRelease()
10632{
10633 LogFlowThisFunc (("\n"));
10634
10635 uninit();
10636}
10637
10638/**
10639 * Initializes the SnapshotMachine object when taking a snapshot.
10640 *
10641 * @param aSessionMachine machine to take a snapshot from
10642 * @param aSnapshotId snapshot ID of this snapshot machine
10643 * @param aStateFilePath file where the execution state will be later saved
10644 * (or NULL for the offline snapshot)
10645 *
10646 * @note Locks aSessionMachine object for reading.
10647 */
10648HRESULT SnapshotMachine::init (SessionMachine *aSessionMachine,
10649 INPTR GUIDPARAM aSnapshotId,
10650 INPTR BSTR aStateFilePath)
10651{
10652 LogFlowThisFuncEnter();
10653 LogFlowThisFunc (("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
10654
10655 AssertReturn (aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
10656
10657 /* Enclose the state transition NotReady->InInit->Ready */
10658 AutoInitSpan autoInitSpan (this);
10659 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
10660
10661 mSnapshotId = aSnapshotId;
10662
10663 AutoReaderLock alock (aSessionMachine);
10664
10665 /* memorize the primary Machine instance (i.e. not SessionMachine!) */
10666 unconst (mPeer) = aSessionMachine->mPeer;
10667 /* share the parent pointer */
10668 unconst (mParent) = mPeer->mParent;
10669
10670 /* take the pointer to Data to share */
10671 mData.share (mPeer->mData);
10672 /*
10673 * take the pointer to UserData to share
10674 * (our UserData must always be the same as Machine's data)
10675 */
10676 mUserData.share (mPeer->mUserData);
10677 /* make a private copy of all other data (recent changes from SessionMachine) */
10678 mHWData.attachCopy (aSessionMachine->mHWData);
10679 mHDData.attachCopy (aSessionMachine->mHDData);
10680
10681 /* SSData is always unique for SnapshotMachine */
10682 mSSData.allocate();
10683 mSSData->mStateFilePath = aStateFilePath;
10684
10685 /*
10686 * create copies of all shared folders (mHWData after attiching a copy
10687 * contains just references to original objects)
10688 */
10689 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
10690 it != mHWData->mSharedFolders.end();
10691 ++ it)
10692 {
10693 ComObjPtr <SharedFolder> folder;
10694 folder.createObject();
10695 HRESULT rc = folder->initCopy (this, *it);
10696 CheckComRCReturnRC (rc);
10697 *it = folder;
10698 }
10699
10700 /* create all other child objects that will be immutable private copies */
10701
10702 unconst (mBIOSSettings).createObject();
10703 mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
10704
10705#ifdef VBOX_VRDP
10706 unconst (mVRDPServer).createObject();
10707 mVRDPServer->initCopy (this, mPeer->mVRDPServer);
10708#endif
10709
10710 unconst (mDVDDrive).createObject();
10711 mDVDDrive->initCopy (this, mPeer->mDVDDrive);
10712
10713 unconst (mFloppyDrive).createObject();
10714 mFloppyDrive->initCopy (this, mPeer->mFloppyDrive);
10715
10716 unconst (mAudioAdapter).createObject();
10717 mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
10718
10719 unconst (mUSBController).createObject();
10720 mUSBController->initCopy (this, mPeer->mUSBController);
10721
10722 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
10723 {
10724 unconst (mNetworkAdapters [slot]).createObject();
10725 mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
10726 }
10727
10728 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
10729 {
10730 unconst (mSerialPorts [slot]).createObject();
10731 mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
10732 }
10733
10734 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
10735 {
10736 unconst (mParallelPorts [slot]).createObject();
10737 mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
10738 }
10739
10740 /* Confirm a successful initialization when it's the case */
10741 autoInitSpan.setSucceeded();
10742
10743 LogFlowThisFuncLeave();
10744 return S_OK;
10745}
10746
10747/**
10748 * Initializes the SnapshotMachine object when loading from the settings file.
10749 *
10750 * @param aMachine machine the snapshot belngs to
10751 * @param aHWNode <Hardware> node
10752 * @param aHDAsNode <HardDiskAttachments> node
10753 * @param aSnapshotId snapshot ID of this snapshot machine
10754 * @param aStateFilePath file where the execution state is saved
10755 * (or NULL for the offline snapshot)
10756 *
10757 * @note Locks aMachine object for reading.
10758 */
10759HRESULT SnapshotMachine::init (Machine *aMachine, CFGNODE aHWNode, CFGNODE aHDAsNode,
10760 INPTR GUIDPARAM aSnapshotId, INPTR BSTR aStateFilePath)
10761{
10762 LogFlowThisFuncEnter();
10763 LogFlowThisFunc (("mName={%ls}\n", aMachine->mUserData->mName.raw()));
10764
10765 AssertReturn (aMachine && aHWNode && aHDAsNode && !Guid (aSnapshotId).isEmpty(),
10766 E_INVALIDARG);
10767
10768 /* Enclose the state transition NotReady->InInit->Ready */
10769 AutoInitSpan autoInitSpan (this);
10770 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
10771
10772 mSnapshotId = aSnapshotId;
10773
10774 AutoReaderLock alock (aMachine);
10775
10776 /* memorize the primary Machine instance */
10777 unconst (mPeer) = aMachine;
10778 /* share the parent pointer */
10779 unconst (mParent) = mPeer->mParent;
10780
10781 /* take the pointer to Data to share */
10782 mData.share (mPeer->mData);
10783 /*
10784 * take the pointer to UserData to share
10785 * (our UserData must always be the same as Machine's data)
10786 */
10787 mUserData.share (mPeer->mUserData);
10788 /* allocate private copies of all other data (will be loaded from settings) */
10789 mHWData.allocate();
10790 mHDData.allocate();
10791
10792 /* SSData is always unique for SnapshotMachine */
10793 mSSData.allocate();
10794 mSSData->mStateFilePath = aStateFilePath;
10795
10796 /* create all other child objects that will be immutable private copies */
10797
10798 unconst (mBIOSSettings).createObject();
10799 mBIOSSettings->init (this);
10800
10801#ifdef VBOX_VRDP
10802 unconst (mVRDPServer).createObject();
10803 mVRDPServer->init (this);
10804#endif
10805
10806 unconst (mDVDDrive).createObject();
10807 mDVDDrive->init (this);
10808
10809 unconst (mFloppyDrive).createObject();
10810 mFloppyDrive->init (this);
10811
10812 unconst (mAudioAdapter).createObject();
10813 mAudioAdapter->init (this);
10814
10815 unconst (mUSBController).createObject();
10816 mUSBController->init (this);
10817
10818 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
10819 {
10820 unconst (mNetworkAdapters [slot]).createObject();
10821 mNetworkAdapters [slot]->init (this, slot);
10822 }
10823
10824 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
10825 {
10826 unconst (mSerialPorts [slot]).createObject();
10827 mSerialPorts [slot]->init (this, slot);
10828 }
10829
10830 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
10831 {
10832 unconst (mParallelPorts [slot]).createObject();
10833 mParallelPorts [slot]->init (this, slot);
10834 }
10835
10836 /* load hardware and harddisk settings */
10837
10838 HRESULT rc = loadHardware (aHWNode);
10839 if (SUCCEEDED (rc))
10840 rc = loadHardDisks (aHDAsNode, true /* aRegistered */, &mSnapshotId);
10841
10842 if (SUCCEEDED (rc))
10843 {
10844 /* commit all changes made during the initialization */
10845 commit();
10846 }
10847
10848 /* Confirm a successful initialization when it's the case */
10849 if (SUCCEEDED (rc))
10850 autoInitSpan.setSucceeded();
10851
10852 LogFlowThisFuncLeave();
10853 return rc;
10854}
10855
10856/**
10857 * Uninitializes this SnapshotMachine object.
10858 */
10859void SnapshotMachine::uninit()
10860{
10861 LogFlowThisFuncEnter();
10862
10863 /* Enclose the state transition Ready->InUninit->NotReady */
10864 AutoUninitSpan autoUninitSpan (this);
10865 if (autoUninitSpan.uninitDone())
10866 return;
10867
10868 uninitDataAndChildObjects();
10869
10870 /* free the essential data structure last */
10871 mData.free();
10872
10873 unconst (mParent).setNull();
10874 unconst (mPeer).setNull();
10875
10876 LogFlowThisFuncLeave();
10877}
10878
10879// AutoLock::Lockable interface
10880////////////////////////////////////////////////////////////////////////////////
10881
10882/**
10883 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
10884 * with the primary Machine instance (mPeer).
10885 */
10886AutoLock::Handle *SnapshotMachine::lockHandle() const
10887{
10888 AssertReturn (!mPeer.isNull(), NULL);
10889 return mPeer->lockHandle();
10890}
10891
10892// public methods only for internal purposes
10893////////////////////////////////////////////////////////////////////////////////
10894
10895/**
10896 * Called by the snapshot object associated with this SnapshotMachine when
10897 * snapshot data such as name or description is changed.
10898 *
10899 * @note Locks this object for writing.
10900 */
10901HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
10902{
10903 AutoLock alock (this);
10904
10905 mPeer->saveSnapshotSettings (aSnapshot, SaveSS_UpdateAttrsOp);
10906
10907 /* inform callbacks */
10908 mParent->onSnapshotChange (mData->mUuid, aSnapshot->data().mId);
10909
10910 return S_OK;
10911}
Note: See TracBrowser for help on using the repository browser.

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