VirtualBox

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

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

Main: Implemented IConsole::AdoptSavedState(); Prototyped IVirtualBox::WaitForPropertyChange().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 348.8 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 {
6877 /* try to make the file name relative to the settings file dir */
6878 Utf8Str stateFilePath = mSSData->mStateFilePath;
6879 calculateRelativePath (stateFilePath, stateFilePath);
6880 CFGLDRSetString (machineNode, "stateFile", stateFilePath);
6881 }
6882 else
6883 CFGLDRDeleteAttribute (machineNode, "stateFile");
6884 }
6885
6886 if (aFlags & SaveSTS_StateTimeStamp)
6887 {
6888 Assert (mData->mMachineState != MachineState_Aborted ||
6889 mSSData->mStateFilePath.isNull());
6890
6891 CFGLDRSetDateTime (machineNode, "lastStateChange",
6892 mData->mLastStateChange);
6893
6894 // set the aborted attribute when appropriate
6895 if (mData->mMachineState == MachineState_Aborted)
6896 CFGLDRSetBool (machineNode, "aborted", true);
6897 else
6898 CFGLDRDeleteAttribute (machineNode, "aborted");
6899 }
6900 }
6901 while (0);
6902
6903 if (machineNode)
6904 CFGLDRReleaseNode (machineNode);
6905
6906 if (SUCCEEDED (rc))
6907 rc = closeConfigLoader (configLoader, true /* aSaveBeforeClose */);
6908 else
6909 closeConfigLoader (configLoader, false /* aSaveBeforeClose */);
6910
6911 return rc;
6912}
6913
6914/**
6915 * Cleans up all differencing hard disks based on immutable hard disks.
6916 *
6917 * @note Locks objects!
6918 */
6919HRESULT Machine::wipeOutImmutableDiffs()
6920{
6921 AutoCaller autoCaller (this);
6922 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6923
6924 AutoReaderLock alock (this);
6925
6926 AssertReturn (mData->mMachineState == MachineState_PoweredOff ||
6927 mData->mMachineState == MachineState_Aborted, E_FAIL);
6928
6929 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
6930 it != mHDData->mHDAttachments.end();
6931 ++ it)
6932 {
6933 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
6934 AutoLock hdLock (hd);
6935
6936 if(hd->isParentImmutable())
6937 {
6938 /// @todo (dmik) no error handling for now
6939 // (need async error reporting for this)
6940 hd->asVDI()->wipeOutImage();
6941 }
6942 }
6943
6944 return S_OK;
6945}
6946
6947/**
6948 * Fixes up lazy hard disk attachments by creating or deleting differencing
6949 * hard disks when machine settings are being committed.
6950 * Must be called only from #commit().
6951 *
6952 * @note Locks objects!
6953 */
6954HRESULT Machine::fixupHardDisks (bool aCommit)
6955{
6956 AutoCaller autoCaller (this);
6957 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6958
6959 AutoLock alock (this);
6960
6961 /* no attac/detach operations -- nothing to do */
6962 if (!mHDData.isBackedUp())
6963 {
6964 mHDData->mHDAttachmentsChanged = false;
6965 return S_OK;
6966 }
6967
6968 AssertReturn (mData->mRegistered, E_FAIL);
6969
6970 if (aCommit)
6971 {
6972 /*
6973 * changes are being committed,
6974 * perform actual diff image creation, deletion etc.
6975 */
6976
6977 /* take a copy of backed up attachments (will modify it) */
6978 HDData::HDAttachmentList backedUp = mHDData.backedUpData()->mHDAttachments;
6979 /* list of new diffs created */
6980 std::list <ComObjPtr <HardDisk> > newDiffs;
6981
6982 HRESULT rc = S_OK;
6983
6984 /* go through current attachments */
6985 for (HDData::HDAttachmentList::const_iterator
6986 it = mHDData->mHDAttachments.begin();
6987 it != mHDData->mHDAttachments.end();
6988 ++ it)
6989 {
6990 ComObjPtr <HardDiskAttachment> hda = *it;
6991 ComObjPtr <HardDisk> hd = hda->hardDisk();
6992 AutoLock hdLock (hd);
6993
6994 if (!hda->isDirty())
6995 {
6996 /*
6997 * not dirty, therefore was either attached before backing up
6998 * or doesn't need any fixup (already fixed up); try to locate
6999 * this hard disk among backed up attachments and remove from
7000 * there to prevent it from being deassociated/deleted
7001 */
7002 HDData::HDAttachmentList::iterator oldIt;
7003 for (oldIt = backedUp.begin(); oldIt != backedUp.end(); ++ oldIt)
7004 if ((*oldIt)->hardDisk().equalsTo (hd))
7005 break;
7006 if (oldIt != backedUp.end())
7007 {
7008 /* remove from there */
7009 backedUp.erase (oldIt);
7010 Log3 (("FC: %ls found in old\n", hd->toString().raw()));
7011 }
7012 }
7013 else
7014 {
7015 /* dirty, determine what to do */
7016
7017 bool needDiff = false;
7018 bool searchAmongSnapshots = false;
7019
7020 switch (hd->type())
7021 {
7022 case HardDiskType_ImmutableHardDisk:
7023 {
7024 /* decrease readers increased in AttachHardDisk() */
7025 hd->releaseReader();
7026 Log3 (("FC: %ls released\n", hd->toString().raw()));
7027 /* indicate we need a diff (indirect attachment) */
7028 needDiff = true;
7029 break;
7030 }
7031 case HardDiskType_WritethroughHardDisk:
7032 {
7033 /* reset the dirty flag */
7034 hda->updateHardDisk (hd, false /* aDirty */);
7035 Log3 (("FC: %ls updated\n", hd->toString().raw()));
7036 break;
7037 }
7038 case HardDiskType_NormalHardDisk:
7039 {
7040 if (hd->snapshotId().isEmpty())
7041 {
7042 /* reset the dirty flag */
7043 hda->updateHardDisk (hd, false /* aDirty */);
7044 Log3 (("FC: %ls updated\n", hd->toString().raw()));
7045 }
7046 else
7047 {
7048 /* decrease readers increased in AttachHardDisk() */
7049 hd->releaseReader();
7050 Log3 (("FC: %ls released\n", hd->toString().raw()));
7051 /* indicate we need a diff (indirect attachment) */
7052 needDiff = true;
7053 /* search for the most recent base among snapshots */
7054 searchAmongSnapshots = true;
7055 }
7056 break;
7057 }
7058 }
7059
7060 if (!needDiff)
7061 continue;
7062
7063 bool createDiff = false;
7064
7065 /*
7066 * see whether any previously attached hard disk has the
7067 * the currently attached one (Normal or Independent) as
7068 * the root
7069 */
7070
7071 HDData::HDAttachmentList::iterator foundIt = backedUp.end();
7072
7073 for (HDData::HDAttachmentList::iterator it = backedUp.begin();
7074 it != backedUp.end();
7075 ++ it)
7076 {
7077 if ((*it)->hardDisk()->root().equalsTo (hd))
7078 {
7079 /*
7080 * matched dev and ctl (i.e. attached to the same place)
7081 * will win and immediately stop the search; otherwise
7082 * the first attachment that matched the hd only will
7083 * be used
7084 */
7085 if ((*it)->deviceNumber() == hda->deviceNumber() &&
7086 (*it)->controller() == hda->controller())
7087 {
7088 foundIt = it;
7089 break;
7090 }
7091 else
7092 if (foundIt == backedUp.end())
7093 {
7094 /*
7095 * not an exact match; ensure there is no exact match
7096 * among other current attachments referring the same
7097 * root (to prevent this attachmend from reusing the
7098 * hard disk of the other attachment that will later
7099 * give the exact match or already gave it before)
7100 */
7101 bool canReuse = true;
7102 for (HDData::HDAttachmentList::const_iterator
7103 it2 = mHDData->mHDAttachments.begin();
7104 it2 != mHDData->mHDAttachments.end();
7105 ++ it2)
7106 {
7107 if ((*it2)->deviceNumber() == (*it)->deviceNumber() &&
7108 (*it2)->controller() == (*it)->controller() &&
7109 (*it2)->hardDisk()->root().equalsTo (hd))
7110 {
7111 /*
7112 * the exact match, either non-dirty or dirty
7113 * one refers the same root: in both cases
7114 * we cannot reuse the hard disk, so break
7115 */
7116 canReuse = false;
7117 break;
7118 }
7119 }
7120
7121 if (canReuse)
7122 foundIt = it;
7123 }
7124 }
7125 }
7126
7127 if (foundIt != backedUp.end())
7128 {
7129 /* found either one or another, reuse the diff */
7130 hda->updateHardDisk ((*foundIt)->hardDisk(),
7131 false /* aDirty */);
7132 Log3 (("FC: %ls reused as %ls\n", hd->toString().raw(),
7133 (*foundIt)->hardDisk()->toString().raw()));
7134 /* remove from there */
7135 backedUp.erase (foundIt);
7136 }
7137 else
7138 {
7139 /* was not attached, need a diff */
7140 createDiff = true;
7141 }
7142
7143 if (!createDiff)
7144 continue;
7145
7146 ComObjPtr <HardDisk> baseHd = hd;
7147
7148 if (searchAmongSnapshots)
7149 {
7150 /*
7151 * find the most recent diff based on the currently
7152 * attached root (Normal hard disk) among snapshots
7153 */
7154
7155 ComObjPtr <Snapshot> snap = mData->mCurrentSnapshot;
7156
7157 while (snap)
7158 {
7159 AutoLock snapLock (snap);
7160
7161 const HDData::HDAttachmentList &snapAtts =
7162 snap->data().mMachine->hdData()->mHDAttachments;
7163
7164 HDData::HDAttachmentList::const_iterator foundIt = snapAtts.end();
7165
7166 for (HDData::HDAttachmentList::const_iterator
7167 it = snapAtts.begin(); it != snapAtts.end(); ++ it)
7168 {
7169 if ((*it)->hardDisk()->root().equalsTo (hd))
7170 {
7171 /*
7172 * matched dev and ctl (i.e. attached to the same place)
7173 * will win and immediately stop the search; otherwise
7174 * the first attachment that matched the hd only will
7175 * be used
7176 */
7177 if ((*it)->deviceNumber() == hda->deviceNumber() &&
7178 (*it)->controller() == hda->controller())
7179 {
7180 foundIt = it;
7181 break;
7182 }
7183 else
7184 if (foundIt == snapAtts.end())
7185 foundIt = it;
7186 }
7187 }
7188
7189 if (foundIt != snapAtts.end())
7190 {
7191 /* the most recent diff has been found, use as a base */
7192 baseHd = (*foundIt)->hardDisk();
7193 Log3 (("FC: %ls: recent found %ls\n",
7194 hd->toString().raw(), baseHd->toString().raw()));
7195 break;
7196 }
7197
7198 snap = snap->parent();
7199 }
7200 }
7201
7202 /* create a new diff for the hard disk being indirectly attached */
7203
7204 AutoLock baseHdLock (baseHd);
7205 baseHd->addReader();
7206
7207 ComObjPtr <HVirtualDiskImage> vdi;
7208 rc = baseHd->createDiffHardDisk (mUserData->mSnapshotFolderFull,
7209 mData->mUuid, vdi, NULL);
7210 baseHd->releaseReader();
7211 CheckComRCBreakRC (rc);
7212
7213 newDiffs.push_back (ComObjPtr <HardDisk> (vdi));
7214
7215 /* update the attachment and reset the dirty flag */
7216 hda->updateHardDisk (ComObjPtr <HardDisk> (vdi),
7217 false /* aDirty */);
7218 Log3 (("FC: %ls: diff created %ls\n",
7219 baseHd->toString().raw(), vdi->toString().raw()));
7220 }
7221 }
7222
7223 if (FAILED (rc))
7224 {
7225 /* delete diffs we created */
7226 for (std::list <ComObjPtr <HardDisk> >::const_iterator
7227 it = newDiffs.begin(); it != newDiffs.end(); ++ it)
7228 {
7229 /*
7230 * unregisterDiffHardDisk() is supposed to delete and uninit
7231 * the differencing hard disk
7232 */
7233 mParent->unregisterDiffHardDisk (*it);
7234 /* too bad if we fail here, but nothing to do, just continue */
7235 }
7236
7237 /* the best is to rollback the changes... */
7238 mHDData.rollback();
7239 mHDData->mHDAttachmentsChanged = false;
7240 Log3 (("FC: ROLLED BACK\n"));
7241 return rc;
7242 }
7243
7244 /*
7245 * go through the rest of old attachments and delete diffs
7246 * or deassociate hard disks from machines (they will become detached)
7247 */
7248 for (HDData::HDAttachmentList::iterator
7249 it = backedUp.begin(); it != backedUp.end(); ++ it)
7250 {
7251 ComObjPtr <HardDiskAttachment> hda = *it;
7252 ComObjPtr <HardDisk> hd = hda->hardDisk();
7253 AutoLock hdLock (hd);
7254
7255 if (hd->isDifferencing())
7256 {
7257 /*
7258 * unregisterDiffHardDisk() is supposed to delete and uninit
7259 * the differencing hard disk
7260 */
7261 Log3 (("FC: %ls diff deleted\n", hd->toString().raw()));
7262 rc = mParent->unregisterDiffHardDisk (hd);
7263 /*
7264 * too bad if we fail here, but nothing to do, just continue
7265 * (the last rc will be returned to the caller though)
7266 */
7267 }
7268 else
7269 {
7270 /* deassociate from this machine */
7271 Log3 (("FC: %ls deassociated\n", hd->toString().raw()));
7272 hd->setMachineId (Guid());
7273 }
7274 }
7275
7276 /* commit all the changes */
7277 mHDData->mHDAttachmentsChanged = mHDData.hasActualChanges();
7278 mHDData.commit();
7279 Log3 (("FC: COMMITTED\n"));
7280
7281 return rc;
7282 }
7283
7284 /*
7285 * changes are being rolled back,
7286 * go trhough all current attachments and fix up dirty ones
7287 * the way it is done in DetachHardDisk()
7288 */
7289
7290 for (HDData::HDAttachmentList::iterator it = mHDData->mHDAttachments.begin();
7291 it != mHDData->mHDAttachments.end();
7292 ++ it)
7293 {
7294 ComObjPtr <HardDiskAttachment> hda = *it;
7295 ComObjPtr <HardDisk> hd = hda->hardDisk();
7296 AutoLock hdLock (hd);
7297
7298 if (hda->isDirty())
7299 {
7300 switch (hd->type())
7301 {
7302 case HardDiskType_ImmutableHardDisk:
7303 {
7304 /* decrease readers increased in AttachHardDisk() */
7305 hd->releaseReader();
7306 Log3 (("FR: %ls released\n", hd->toString().raw()));
7307 break;
7308 }
7309 case HardDiskType_WritethroughHardDisk:
7310 {
7311 /* deassociate from this machine */
7312 hd->setMachineId (Guid());
7313 Log3 (("FR: %ls deassociated\n", hd->toString().raw()));
7314 break;
7315 }
7316 case HardDiskType_NormalHardDisk:
7317 {
7318 if (hd->snapshotId().isEmpty())
7319 {
7320 /* deassociate from this machine */
7321 hd->setMachineId (Guid());
7322 Log3 (("FR: %ls deassociated\n", hd->toString().raw()));
7323 }
7324 else
7325 {
7326 /* decrease readers increased in AttachHardDisk() */
7327 hd->releaseReader();
7328 Log3 (("FR: %ls released\n", hd->toString().raw()));
7329 }
7330
7331 break;
7332 }
7333 }
7334 }
7335 }
7336
7337 /* rollback all the changes */
7338 mHDData.rollback();
7339 Log3 (("FR: ROLLED BACK\n"));
7340
7341 return S_OK;
7342}
7343
7344/**
7345 * Creates differencing hard disks for all normal hard disks
7346 * and replaces attachments to refer to created disks.
7347 * Used when taking a snapshot or when discarding the current state.
7348 *
7349 * @param aSnapshotId ID of the snapshot being taken
7350 * or NULL if the current state is being discarded
7351 * @param aFolder folder where to create diff. hard disks
7352 * @param aProgress progress object to run (must contain at least as
7353 * many operations left as the number of VDIs attached)
7354 * @param aOnline whether the machine is online (i.e., when the EMT
7355 * thread is paused, OR when current hard disks are
7356 * marked as busy for some other reason)
7357 *
7358 * @note
7359 * The progress object is not marked as completed, neither on success
7360 * nor on failure. This is a responsibility of the caller.
7361 *
7362 * @note Locks mParent + this object for writing
7363 */
7364HRESULT Machine::createSnapshotDiffs (const Guid *aSnapshotId,
7365 const Bstr &aFolder,
7366 const ComObjPtr <Progress> &aProgress,
7367 bool aOnline)
7368{
7369 AssertReturn (!aFolder.isEmpty(), E_FAIL);
7370
7371 AutoCaller autoCaller (this);
7372 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7373
7374 /* accessing mParent methods below needs mParent lock */
7375 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
7376
7377 HRESULT rc = S_OK;
7378
7379 // first pass: check accessibility before performing changes
7380 if (!aOnline)
7381 {
7382 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
7383 it != mHDData->mHDAttachments.end();
7384 ++ it)
7385 {
7386 ComObjPtr <HardDiskAttachment> hda = *it;
7387 ComObjPtr <HardDisk> hd = hda->hardDisk();
7388 AutoLock hdLock (hd);
7389
7390 ComAssertMsgBreak (hd->type() == HardDiskType_NormalHardDisk,
7391 ("Invalid hard disk type %d\n", hd->type()),
7392 rc = E_FAIL);
7393
7394 ComAssertMsgBreak (!hd->isParentImmutable() ||
7395 hd->storageType() == HardDiskStorageType_VirtualDiskImage,
7396 ("Invalid hard disk storage type %d\n", hd->storageType()),
7397 rc = E_FAIL);
7398
7399 Bstr accessError;
7400 rc = hd->getAccessible (accessError);
7401 CheckComRCBreakRC (rc);
7402
7403 if (!accessError.isNull())
7404 {
7405 rc = setError (E_FAIL,
7406 tr ("Hard disk '%ls' is not accessible (%ls)"),
7407 hd->toString().raw(), accessError.raw());
7408 break;
7409 }
7410 }
7411 CheckComRCReturnRC (rc);
7412 }
7413
7414 HDData::HDAttachmentList attachments;
7415
7416 // second pass: perform changes
7417 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
7418 it != mHDData->mHDAttachments.end();
7419 ++ it)
7420 {
7421 ComObjPtr <HardDiskAttachment> hda = *it;
7422 ComObjPtr <HardDisk> hd = hda->hardDisk();
7423 AutoLock hdLock (hd);
7424
7425 ComObjPtr <HardDisk> parent = hd->parent();
7426 AutoLock parentHdLock (parent);
7427
7428 ComObjPtr <HardDisk> newHd;
7429
7430 // clear busy flag if the VM is online
7431 if (aOnline)
7432 hd->clearBusy();
7433 // increase readers
7434 hd->addReader();
7435
7436 if (hd->isParentImmutable())
7437 {
7438 aProgress->advanceOperation (Bstr (Utf8StrFmt (
7439 tr ("Preserving immutable hard disk '%ls'"),
7440 parent->toString (true /* aShort */).raw())));
7441
7442 parentHdLock.unlock();
7443 alock.leave();
7444
7445 // create a copy of the independent diff
7446 ComObjPtr <HVirtualDiskImage> vdi;
7447 rc = hd->asVDI()->cloneDiffImage (aFolder, mData->mUuid, vdi,
7448 aProgress);
7449 newHd = vdi;
7450
7451 alock.enter();
7452 parentHdLock.lock();
7453
7454 // decrease readers (hd is no more used for reading in any case)
7455 hd->releaseReader();
7456 }
7457 else
7458 {
7459 // checked in the first pass
7460 Assert (hd->type() == HardDiskType_NormalHardDisk);
7461
7462 aProgress->advanceOperation (Bstr (Utf8StrFmt (
7463 tr ("Creating a differencing hard disk for '%ls'"),
7464 hd->root()->toString (true /* aShort */).raw())));
7465
7466 parentHdLock.unlock();
7467 alock.leave();
7468
7469 // create a new diff for the image being attached
7470 ComObjPtr <HVirtualDiskImage> vdi;
7471 rc = hd->createDiffHardDisk (aFolder, mData->mUuid, vdi, aProgress);
7472 newHd = vdi;
7473
7474 alock.enter();
7475 parentHdLock.lock();
7476
7477 if (SUCCEEDED (rc))
7478 {
7479 // if online, hd must keep a reader referece
7480 if (!aOnline)
7481 hd->releaseReader();
7482 }
7483 else
7484 {
7485 // decrease readers
7486 hd->releaseReader();
7487 }
7488 }
7489
7490 if (SUCCEEDED (rc))
7491 {
7492 ComObjPtr <HardDiskAttachment> newHda;
7493 newHda.createObject();
7494 rc = newHda->init (newHd, hda->controller(), hda->deviceNumber(),
7495 false /* aDirty */);
7496
7497 if (SUCCEEDED (rc))
7498 {
7499 // associate the snapshot id with the old hard disk
7500 if (hd->type() != HardDiskType_WritethroughHardDisk && aSnapshotId)
7501 hd->setSnapshotId (*aSnapshotId);
7502
7503 // add the new attachment
7504 attachments.push_back (newHda);
7505
7506 // if online, newHd must be marked as busy
7507 if (aOnline)
7508 newHd->setBusy();
7509 }
7510 }
7511
7512 if (FAILED (rc))
7513 {
7514 // set busy flag back if the VM is online
7515 if (aOnline)
7516 hd->setBusy();
7517 break;
7518 }
7519 }
7520
7521 if (SUCCEEDED (rc))
7522 {
7523 // replace the whole list of attachments with the new one
7524 mHDData->mHDAttachments = attachments;
7525 }
7526 else
7527 {
7528 // delete those diffs we've just created
7529 for (HDData::HDAttachmentList::const_iterator it = attachments.begin();
7530 it != attachments.end();
7531 ++ it)
7532 {
7533 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
7534 AutoLock hdLock (hd);
7535 Assert (hd->children().size() == 0);
7536 Assert (hd->isDifferencing());
7537 // unregisterDiffHardDisk() is supposed to delete and uninit
7538 // the differencing hard disk
7539 mParent->unregisterDiffHardDisk (hd);
7540 }
7541 }
7542
7543 return rc;
7544}
7545
7546/**
7547 * Deletes differencing hard disks created by createSnapshotDiffs() in case
7548 * if snapshot creation was failed.
7549 *
7550 * @param aSnapshot failed snapshot
7551 *
7552 * @note Locks mParent + this object for writing.
7553 */
7554HRESULT Machine::deleteSnapshotDiffs (const ComObjPtr <Snapshot> &aSnapshot)
7555{
7556 AssertReturn (!aSnapshot.isNull(), E_FAIL);
7557
7558 AutoCaller autoCaller (this);
7559 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7560
7561 /* accessing mParent methods below needs mParent lock */
7562 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
7563
7564 /* short cut: check whether attachments are all the same */
7565 if (mHDData->mHDAttachments == aSnapshot->data().mMachine->mHDData->mHDAttachments)
7566 return S_OK;
7567
7568 HRESULT rc = S_OK;
7569
7570 for (HDData::HDAttachmentList::const_iterator it = mHDData->mHDAttachments.begin();
7571 it != mHDData->mHDAttachments.end();
7572 ++ it)
7573 {
7574 ComObjPtr <HardDiskAttachment> hda = *it;
7575 ComObjPtr <HardDisk> hd = hda->hardDisk();
7576 AutoLock hdLock (hd);
7577
7578 ComObjPtr <HardDisk> parent = hd->parent();
7579 AutoLock parentHdLock (parent);
7580
7581 if (!parent || parent->snapshotId() != aSnapshot->data().mId)
7582 continue;
7583
7584 /* must not have children */
7585 ComAssertRet (hd->children().size() == 0, E_FAIL);
7586
7587 /* deassociate the old hard disk from the given snapshot's ID */
7588 parent->setSnapshotId (Guid());
7589
7590 /* unregisterDiffHardDisk() is supposed to delete and uninit
7591 * the differencing hard disk */
7592 rc = mParent->unregisterDiffHardDisk (hd);
7593 /* continue on error */
7594 }
7595
7596 /* restore the whole list of attachments from the failed snapshot */
7597 mHDData->mHDAttachments = aSnapshot->data().mMachine->mHDData->mHDAttachments;
7598
7599 return rc;
7600}
7601
7602/**
7603 * Helper to lock the machine configuration for write access.
7604 *
7605 * @return S_OK or E_FAIL and sets error info on failure
7606 *
7607 * @note Doesn't lock anything (must be called from this object's lock)
7608 */
7609HRESULT Machine::lockConfig()
7610{
7611 HRESULT rc = S_OK;
7612
7613 if (!isConfigLocked())
7614 {
7615 /* open the associated config file */
7616 int vrc = RTFileOpen (&mData->mHandleCfgFile,
7617 Utf8Str (mData->mConfigFileFull),
7618 RTFILE_O_READWRITE | RTFILE_O_OPEN |
7619 RTFILE_O_DENY_WRITE);
7620 if (VBOX_FAILURE (vrc))
7621 mData->mHandleCfgFile = NIL_RTFILE;
7622 }
7623
7624 LogFlowThisFunc (("mConfigFile={%ls}, mHandleCfgFile=%d, rc=%08X\n",
7625 mData->mConfigFileFull.raw(), mData->mHandleCfgFile, rc));
7626 return rc;
7627}
7628
7629/**
7630 * Helper to unlock the machine configuration from write access
7631 *
7632 * @return S_OK
7633 *
7634 * @note Doesn't lock anything.
7635 * @note Not thread safe (must be called from this object's lock).
7636 */
7637HRESULT Machine::unlockConfig()
7638{
7639 HRESULT rc = S_OK;
7640
7641 if (isConfigLocked())
7642 {
7643 RTFileFlush(mData->mHandleCfgFile);
7644 RTFileClose(mData->mHandleCfgFile);
7645 /** @todo flush the directory. */
7646 mData->mHandleCfgFile = NIL_RTFILE;
7647 }
7648
7649 LogFlowThisFunc (("\n"));
7650
7651 return rc;
7652}
7653
7654/**
7655 * Returns true if the settings file is located in the directory named exactly
7656 * as the machine. This will be true if the machine settings structure was
7657 * created by default in #openConfigLoader().
7658 *
7659 * @param aSettingsDir if not NULL, the full machine settings file directory
7660 * name will be assigned there.
7661 *
7662 * @note Doesn't lock anything.
7663 * @note Not thread safe (must be called from this object's lock).
7664 */
7665bool Machine::isInOwnDir (Utf8Str *aSettingsDir /* = NULL */)
7666{
7667 Utf8Str settingsDir = mData->mConfigFileFull;
7668 RTPathStripFilename (settingsDir.mutableRaw());
7669 char *dirName = RTPathFilename (settingsDir);
7670
7671 AssertReturn (dirName, false);
7672
7673 /* if we don't rename anything on name change, return false shorlty */
7674 if (!mUserData->mNameSync)
7675 return false;
7676
7677 if (aSettingsDir)
7678 *aSettingsDir = settingsDir;
7679
7680 return Bstr (dirName) == mUserData->mName;
7681}
7682
7683/**
7684 * @note Locks objects for reading!
7685 */
7686bool Machine::isModified()
7687{
7688 AutoCaller autoCaller (this);
7689 AssertComRCReturn (autoCaller.rc(), false);
7690
7691 AutoReaderLock alock (this);
7692
7693 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7694 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
7695 return true;
7696
7697 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7698 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
7699 return true;
7700
7701 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7702 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
7703 return true;
7704
7705 return
7706 mUserData.isBackedUp() ||
7707 mHWData.isBackedUp() ||
7708 mHDData.isBackedUp() ||
7709#ifdef VBOX_VRDP
7710 (mVRDPServer && mVRDPServer->isModified()) ||
7711#endif
7712 (mDVDDrive && mDVDDrive->isModified()) ||
7713 (mFloppyDrive && mFloppyDrive->isModified()) ||
7714 (mAudioAdapter && mAudioAdapter->isModified()) ||
7715 (mUSBController && mUSBController->isModified()) ||
7716 (mBIOSSettings && mBIOSSettings->isModified());
7717}
7718
7719/**
7720 * @note This method doesn't check (ignores) actual changes to mHDData.
7721 * Use mHDData.mHDAttachmentsChanged right after #commit() instead.
7722 *
7723 * @param aIgnoreUserData |true| to ignore changes to mUserData
7724 *
7725 * @note Locks objects for reading!
7726 */
7727bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
7728{
7729 AutoCaller autoCaller (this);
7730 AssertComRCReturn (autoCaller.rc(), false);
7731
7732 AutoReaderLock alock (this);
7733
7734 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7735 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
7736 return true;
7737
7738 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7739 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
7740 return true;
7741
7742 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7743 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
7744 return true;
7745
7746 return
7747 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
7748 mHWData.hasActualChanges() ||
7749 /* ignore mHDData */
7750 //mHDData.hasActualChanges() ||
7751#ifdef VBOX_VRDP
7752 (mVRDPServer && mVRDPServer->isReallyModified()) ||
7753#endif
7754 (mDVDDrive && mDVDDrive->isReallyModified()) ||
7755 (mFloppyDrive && mFloppyDrive->isReallyModified()) ||
7756 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
7757 (mUSBController && mUSBController->isReallyModified()) ||
7758 (mBIOSSettings && mBIOSSettings->isReallyModified());
7759}
7760
7761/**
7762 * Discards all changes to machine settings.
7763 *
7764 * @param aNotify whether to notify the direct session about changes or not
7765 *
7766 * @note Locks objects!
7767 */
7768void Machine::rollback (bool aNotify)
7769{
7770 AutoCaller autoCaller (this);
7771 AssertComRCReturn (autoCaller.rc(), (void) 0);
7772
7773 AutoLock alock (this);
7774
7775 /* check for changes in own data */
7776
7777 bool sharedFoldersChanged = false;
7778
7779 if (aNotify && mHWData.isBackedUp())
7780 {
7781 if (mHWData->mSharedFolders.size() !=
7782 mHWData.backedUpData()->mSharedFolders.size())
7783 sharedFoldersChanged = true;
7784 else
7785 {
7786 for (HWData::SharedFolderList::iterator rit =
7787 mHWData->mSharedFolders.begin();
7788 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
7789 ++ rit)
7790 {
7791 for (HWData::SharedFolderList::iterator cit =
7792 mHWData.backedUpData()->mSharedFolders.begin();
7793 cit != mHWData.backedUpData()->mSharedFolders.end();
7794 ++ cit)
7795 {
7796 if ((*cit)->name() != (*rit)->name() ||
7797 (*cit)->hostPath() != (*rit)->hostPath())
7798 {
7799 sharedFoldersChanged = true;
7800 break;
7801 }
7802 }
7803 }
7804 }
7805 }
7806
7807 mUserData.rollback();
7808
7809 mHWData.rollback();
7810
7811 if (mHDData.isBackedUp())
7812 fixupHardDisks (false /* aCommit */);
7813
7814 /* check for changes in child objects */
7815
7816 bool vrdpChanged = false, dvdChanged = false, floppyChanged = false,
7817 usbChanged = false;
7818
7819 ComPtr <INetworkAdapter> networkAdapters [ELEMENTS (mNetworkAdapters)];
7820 ComPtr <ISerialPort> serialPorts [ELEMENTS (mSerialPorts)];
7821 ComPtr <IParallelPort> parallelPorts [ELEMENTS (mParallelPorts)];
7822
7823 if (mBIOSSettings)
7824 mBIOSSettings->rollback();
7825
7826#ifdef VBOX_VRDP
7827 if (mVRDPServer)
7828 vrdpChanged = mVRDPServer->rollback();
7829#endif
7830
7831 if (mDVDDrive)
7832 dvdChanged = mDVDDrive->rollback();
7833
7834 if (mFloppyDrive)
7835 floppyChanged = mFloppyDrive->rollback();
7836
7837 if (mAudioAdapter)
7838 mAudioAdapter->rollback();
7839
7840 if (mUSBController)
7841 usbChanged = mUSBController->rollback();
7842
7843 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7844 if (mNetworkAdapters [slot])
7845 if (mNetworkAdapters [slot]->rollback())
7846 networkAdapters [slot] = mNetworkAdapters [slot];
7847
7848 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7849 if (mSerialPorts [slot])
7850 if (mSerialPorts [slot]->rollback())
7851 serialPorts [slot] = mSerialPorts [slot];
7852
7853 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7854 if (mParallelPorts [slot])
7855 if (mParallelPorts [slot]->rollback())
7856 parallelPorts [slot] = mParallelPorts [slot];
7857
7858 if (aNotify)
7859 {
7860 /* inform the direct session about changes */
7861
7862 ComObjPtr <Machine> that = this;
7863 alock.leave();
7864
7865 if (sharedFoldersChanged)
7866 that->onSharedFolderChange();
7867
7868 if (vrdpChanged)
7869 that->onVRDPServerChange();
7870 if (dvdChanged)
7871 that->onDVDDriveChange();
7872 if (floppyChanged)
7873 that->onFloppyDriveChange();
7874 if (usbChanged)
7875 that->onUSBControllerChange();
7876
7877 for (ULONG slot = 0; slot < ELEMENTS (networkAdapters); slot ++)
7878 if (networkAdapters [slot])
7879 that->onNetworkAdapterChange (networkAdapters [slot]);
7880 for (ULONG slot = 0; slot < ELEMENTS (serialPorts); slot ++)
7881 if (serialPorts [slot])
7882 that->onSerialPortChange (serialPorts [slot]);
7883 for (ULONG slot = 0; slot < ELEMENTS (parallelPorts); slot ++)
7884 if (parallelPorts [slot])
7885 that->onParallelPortChange (parallelPorts [slot]);
7886 }
7887}
7888
7889/**
7890 * Commits all the changes to machine settings.
7891 *
7892 * Note that when committing fails at some stage, it still continues
7893 * until the end. So, all data will either be actually committed or rolled
7894 * back (for failed cases) and the returned result code will describe the
7895 * first failure encountered. However, #isModified() will still return true
7896 * in case of failure, to indicade that settings in memory and on disk are
7897 * out of sync.
7898 *
7899 * @note Locks objects!
7900 */
7901HRESULT Machine::commit()
7902{
7903 AutoCaller autoCaller (this);
7904 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7905
7906 AutoLock alock (this);
7907
7908 HRESULT rc = S_OK;
7909
7910 /*
7911 * use safe commit to ensure Snapshot machines (that share mUserData)
7912 * will still refer to a valid memory location
7913 */
7914 mUserData.commitCopy();
7915
7916 mHWData.commit();
7917
7918 if (mHDData.isBackedUp())
7919 rc = fixupHardDisks (true /* aCommit */);
7920
7921 mBIOSSettings->commit();
7922#ifdef VBOX_VRDP
7923 mVRDPServer->commit();
7924#endif
7925 mDVDDrive->commit();
7926 mFloppyDrive->commit();
7927 mAudioAdapter->commit();
7928 mUSBController->commit();
7929
7930 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7931 mNetworkAdapters [slot]->commit();
7932 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7933 mSerialPorts [slot]->commit();
7934 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
7935 mParallelPorts [slot]->commit();
7936
7937 if (mType == IsSessionMachine)
7938 {
7939 /* attach new data to the primary machine and reshare it */
7940 mPeer->mUserData.attach (mUserData);
7941 mPeer->mHWData.attach (mHWData);
7942 mPeer->mHDData.attach (mHDData);
7943 }
7944
7945 if (FAILED (rc))
7946 {
7947 /*
7948 * backup arbitrary data item to cause #isModified() to still return
7949 * true in case of any error
7950 */
7951 mHWData.backup();
7952 }
7953
7954 return rc;
7955}
7956
7957/**
7958 * Copies all the hardware data from the given machine.
7959 *
7960 * @note
7961 * This method must be called from under this object's lock.
7962 * @note
7963 * This method doesn't call #commit(), so all data remains backed up
7964 * and unsaved.
7965 */
7966void Machine::copyFrom (Machine *aThat)
7967{
7968 AssertReturn (mType == IsMachine || mType == IsSessionMachine, (void) 0);
7969 AssertReturn (aThat->mType == IsSnapshotMachine, (void) 0);
7970
7971 mHWData.assignCopy (aThat->mHWData);
7972
7973 // create copies of all shared folders (mHWData after attiching a copy
7974 // contains just references to original objects)
7975 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
7976 it != mHWData->mSharedFolders.end();
7977 ++ it)
7978 {
7979 ComObjPtr <SharedFolder> folder;
7980 folder.createObject();
7981 HRESULT rc = folder->initCopy (machine(), *it);
7982 AssertComRC (rc);
7983 *it = folder;
7984 }
7985
7986 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
7987#ifdef VBOX_VRDP
7988 mVRDPServer->copyFrom (aThat->mVRDPServer);
7989#endif
7990 mDVDDrive->copyFrom (aThat->mDVDDrive);
7991 mFloppyDrive->copyFrom (aThat->mFloppyDrive);
7992 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
7993 mUSBController->copyFrom (aThat->mUSBController);
7994
7995 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
7996 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
7997 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
7998 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
7999 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
8000 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
8001}
8002
8003/////////////////////////////////////////////////////////////////////////////
8004// SessionMachine class
8005/////////////////////////////////////////////////////////////////////////////
8006
8007/** Task structure for asynchronous VM operations */
8008struct SessionMachine::Task
8009{
8010 Task (SessionMachine *m, Progress *p)
8011 : machine (m), progress (p)
8012 , state (m->data()->mMachineState) // save the current machine state
8013 , subTask (false), settingsChanged (false)
8014 {}
8015
8016 void modifyLastState (MachineState_T s)
8017 {
8018 *const_cast <MachineState_T *> (&state) = s;
8019 }
8020
8021 virtual void handler() = 0;
8022
8023 const ComObjPtr <SessionMachine> machine;
8024 const ComObjPtr <Progress> progress;
8025 const MachineState_T state;
8026
8027 bool subTask : 1;
8028 bool settingsChanged : 1;
8029};
8030
8031/** Take snapshot task */
8032struct SessionMachine::TakeSnapshotTask : public SessionMachine::Task
8033{
8034 TakeSnapshotTask (SessionMachine *m)
8035 : Task (m, NULL) {}
8036
8037 void handler() { machine->takeSnapshotHandler (*this); }
8038};
8039
8040/** Discard snapshot task */
8041struct SessionMachine::DiscardSnapshotTask : public SessionMachine::Task
8042{
8043 DiscardSnapshotTask (SessionMachine *m, Progress *p, Snapshot *s)
8044 : Task (m, p)
8045 , snapshot (s) {}
8046
8047 DiscardSnapshotTask (const Task &task, Snapshot *s)
8048 : Task (task)
8049 , snapshot (s) {}
8050
8051 void handler() { machine->discardSnapshotHandler (*this); }
8052
8053 const ComObjPtr <Snapshot> snapshot;
8054};
8055
8056/** Discard current state task */
8057struct SessionMachine::DiscardCurrentStateTask : public SessionMachine::Task
8058{
8059 DiscardCurrentStateTask (SessionMachine *m, Progress *p,
8060 bool discardCurSnapshot)
8061 : Task (m, p), discardCurrentSnapshot (discardCurSnapshot) {}
8062
8063 void handler() { machine->discardCurrentStateHandler (*this); }
8064
8065 const bool discardCurrentSnapshot;
8066};
8067
8068////////////////////////////////////////////////////////////////////////////////
8069
8070DEFINE_EMPTY_CTOR_DTOR (SessionMachine)
8071
8072HRESULT SessionMachine::FinalConstruct()
8073{
8074 LogFlowThisFunc (("\n"));
8075
8076 /* set the proper type to indicate we're the SessionMachine instance */
8077 unconst (mType) = IsSessionMachine;
8078
8079#if defined(RT_OS_WINDOWS)
8080 mIPCSem = NULL;
8081#elif defined(RT_OS_OS2)
8082 mIPCSem = NULLHANDLE;
8083#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8084 mIPCSem = -1;
8085#else
8086# error "Port me!"
8087#endif
8088
8089 return S_OK;
8090}
8091
8092void SessionMachine::FinalRelease()
8093{
8094 LogFlowThisFunc (("\n"));
8095
8096 uninit (Uninit::Unexpected);
8097}
8098
8099/**
8100 * @note Must be called only by Machine::openSession() from its own write lock.
8101 */
8102HRESULT SessionMachine::init (Machine *aMachine)
8103{
8104 LogFlowThisFuncEnter();
8105 LogFlowThisFunc (("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8106
8107 AssertReturn (aMachine, E_INVALIDARG);
8108
8109 AssertReturn (aMachine->lockHandle()->isLockedOnCurrentThread(), E_FAIL);
8110
8111 /* Enclose the state transition NotReady->InInit->Ready */
8112 AutoInitSpan autoInitSpan (this);
8113 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
8114
8115 /* create the interprocess semaphore */
8116#if defined(RT_OS_WINDOWS)
8117 mIPCSemName = aMachine->mData->mConfigFileFull;
8118 for (size_t i = 0; i < mIPCSemName.length(); i++)
8119 if (mIPCSemName[i] == '\\')
8120 mIPCSemName[i] = '/';
8121 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
8122 ComAssertMsgRet (mIPCSem,
8123 ("Cannot create IPC mutex '%ls', err=%d\n",
8124 mIPCSemName.raw(), ::GetLastError()),
8125 E_FAIL);
8126#elif defined(RT_OS_OS2)
8127 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%Vuuid}",
8128 aMachine->mData->mUuid.raw());
8129 mIPCSemName = ipcSem;
8130 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
8131 ComAssertMsgRet (arc == NO_ERROR,
8132 ("Cannot create IPC mutex '%s', arc=%ld\n",
8133 ipcSem.raw(), arc),
8134 E_FAIL);
8135#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8136 Utf8Str configFile = aMachine->mData->mConfigFileFull;
8137 char *configFileCP = NULL;
8138 RTStrUtf8ToCurrentCP (&configFileCP, configFile);
8139 key_t key = ::ftok (configFileCP, 0);
8140 RTStrFree (configFileCP);
8141 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8142 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errno),
8143 E_FAIL);
8144 /* set the initial value to 1 */
8145 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
8146 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8147 E_FAIL);
8148#else
8149# error "Port me!"
8150#endif
8151
8152 /* memorize the peer Machine */
8153 unconst (mPeer) = aMachine;
8154 /* share the parent pointer */
8155 unconst (mParent) = aMachine->mParent;
8156
8157 /* take the pointers to data to share */
8158 mData.share (aMachine->mData);
8159 mSSData.share (aMachine->mSSData);
8160
8161 mUserData.share (aMachine->mUserData);
8162 mHWData.share (aMachine->mHWData);
8163 mHDData.share (aMachine->mHDData);
8164
8165 unconst (mBIOSSettings).createObject();
8166 mBIOSSettings->init (this, aMachine->mBIOSSettings);
8167#ifdef VBOX_VRDP
8168 /* create another VRDPServer object that will be mutable */
8169 unconst (mVRDPServer).createObject();
8170 mVRDPServer->init (this, aMachine->mVRDPServer);
8171#endif
8172 /* create another DVD drive object that will be mutable */
8173 unconst (mDVDDrive).createObject();
8174 mDVDDrive->init (this, aMachine->mDVDDrive);
8175 /* create another floppy drive object that will be mutable */
8176 unconst (mFloppyDrive).createObject();
8177 mFloppyDrive->init (this, aMachine->mFloppyDrive);
8178 /* create another audio adapter object that will be mutable */
8179 unconst (mAudioAdapter).createObject();
8180 mAudioAdapter->init (this, aMachine->mAudioAdapter);
8181 /* create a list of serial ports that will be mutable */
8182 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
8183 {
8184 unconst (mSerialPorts [slot]).createObject();
8185 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
8186 }
8187 /* create a list of parallel ports that will be mutable */
8188 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
8189 {
8190 unconst (mParallelPorts [slot]).createObject();
8191 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
8192 }
8193 /* create another USB controller object that will be mutable */
8194 unconst (mUSBController).createObject();
8195 mUSBController->init (this, aMachine->mUSBController);
8196 /* create a list of network adapters that will be mutable */
8197 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
8198 {
8199 unconst (mNetworkAdapters [slot]).createObject();
8200 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
8201 }
8202
8203 /* Confirm a successful initialization when it's the case */
8204 autoInitSpan.setSucceeded();
8205
8206 LogFlowThisFuncLeave();
8207 return S_OK;
8208}
8209
8210/**
8211 * Uninitializes this session object. If the reason is other than
8212 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
8213 *
8214 * @param aReason uninitialization reason
8215 *
8216 * @note Locks mParent + this object for writing.
8217 */
8218void SessionMachine::uninit (Uninit::Reason aReason)
8219{
8220 LogFlowThisFuncEnter();
8221 LogFlowThisFunc (("reason=%d\n", aReason));
8222
8223 /*
8224 * Strongly reference ourselves to prevent this object deletion after
8225 * mData->mSession.mMachine.setNull() below (which can release the last
8226 * reference and call the destructor). Important: this must be done before
8227 * accessing any members (and before AutoUninitSpan that does it as well).
8228 * This self reference will be released as the very last step on return.
8229 */
8230 ComObjPtr <SessionMachine> selfRef = this;
8231
8232 /* Enclose the state transition Ready->InUninit->NotReady */
8233 AutoUninitSpan autoUninitSpan (this);
8234 if (autoUninitSpan.uninitDone())
8235 {
8236 LogFlowThisFunc (("Already uninitialized\n"));
8237 LogFlowThisFuncLeave();
8238 return;
8239 }
8240
8241 if (autoUninitSpan.initFailed())
8242 {
8243 /*
8244 * We've been called by init() because it's failed. It's not really
8245 * necessary (nor it's safe) to perform the regular uninit sequence
8246 * below, the following is enough.
8247 */
8248 LogFlowThisFunc (("Initialization failed.\n"));
8249#if defined(RT_OS_WINDOWS)
8250 if (mIPCSem)
8251 ::CloseHandle (mIPCSem);
8252 mIPCSem = NULL;
8253#elif defined(RT_OS_OS2)
8254 if (mIPCSem != NULLHANDLE)
8255 ::DosCloseMutexSem (mIPCSem);
8256 mIPCSem = NULLHANDLE;
8257#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8258 if (mIPCSem >= 0)
8259 ::semctl (mIPCSem, 0, IPC_RMID);
8260 mIPCSem = -1;
8261#else
8262# error "Port me!"
8263#endif
8264 uninitDataAndChildObjects();
8265 mData.free();
8266 unconst (mParent).setNull();
8267 unconst (mPeer).setNull();
8268 LogFlowThisFuncLeave();
8269 return;
8270 }
8271
8272 /*
8273 * We need to lock this object in uninit() because the lock is shared
8274 * with mPeer (as well as data we modify below).
8275 * mParent->addProcessToReap() and others need mParent lock.
8276 */
8277 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8278
8279 MachineState_T lastState = mData->mMachineState;
8280
8281 if (aReason == Uninit::Abnormal)
8282 {
8283 LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",
8284 lastState >= MachineState_Running));
8285
8286 /* reset the state to Aborted */
8287 if (mData->mMachineState != MachineState_Aborted)
8288 setMachineState (MachineState_Aborted);
8289 }
8290
8291 if (isModified())
8292 {
8293 LogWarningThisFunc (("Discarding unsaved settings changes!\n"));
8294 rollback (false /* aNotify */);
8295 }
8296
8297 Assert (!mSnapshotData.mStateFilePath || !mSnapshotData.mSnapshot);
8298 if (mSnapshotData.mStateFilePath)
8299 {
8300 LogWarningThisFunc (("canceling failed save state request!\n"));
8301 endSavingState (FALSE /* aSuccess */);
8302 }
8303 else if (!!mSnapshotData.mSnapshot)
8304 {
8305 LogWarningThisFunc (("canceling untaken snapshot!\n"));
8306 endTakingSnapshot (FALSE /* aSuccess */);
8307 }
8308
8309 /* release all captured USB devices */
8310 if (aReason == Uninit::Abnormal && lastState >= MachineState_Running)
8311 {
8312 /* Console::captureUSBDevices() is called in the VM process only after
8313 * setting the machine state to Starting or Restoring.
8314 * Console::detachAllUSBDevices() will be called upon successful
8315 * termination. So, we need to release USB devices only if there was
8316 * an abnormal termination of a running VM. */
8317 DetachAllUSBDevices (TRUE /* aDone */);
8318 }
8319
8320 if (!mData->mSession.mType.isNull())
8321 {
8322 /* mType is not null when this machine's process has been started by
8323 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
8324 * need to queue the PID to reap the process (and avoid zombies on
8325 * Linux). */
8326 Assert (mData->mSession.mPid != NIL_RTPROCESS);
8327 mParent->addProcessToReap (mData->mSession.mPid);
8328 }
8329
8330 mData->mSession.mPid = NIL_RTPROCESS;
8331
8332 if (aReason == Uninit::Unexpected)
8333 {
8334 /* Uninitialization didn't come from #checkForDeath(), so tell the
8335 * client watcher thread to update the set of machines that have open
8336 * sessions. */
8337 mParent->updateClientWatcher();
8338 }
8339
8340 /* uninitialize all remote controls */
8341 if (mData->mSession.mRemoteControls.size())
8342 {
8343 LogFlowThisFunc (("Closing remote sessions (%d):\n",
8344 mData->mSession.mRemoteControls.size()));
8345
8346 Data::Session::RemoteControlList::iterator it =
8347 mData->mSession.mRemoteControls.begin();
8348 while (it != mData->mSession.mRemoteControls.end())
8349 {
8350 LogFlowThisFunc ((" Calling remoteControl->Uninitialize()...\n"));
8351 HRESULT rc = (*it)->Uninitialize();
8352 LogFlowThisFunc ((" remoteControl->Uninitialize() returned %08X\n", rc));
8353 if (FAILED (rc))
8354 LogWarningThisFunc (("Forgot to close the remote session?\n"));
8355 ++ it;
8356 }
8357 mData->mSession.mRemoteControls.clear();
8358 }
8359
8360 /*
8361 * An expected uninitialization can come only from #checkForDeath().
8362 * Otherwise it means that something's got really wrong (for examlple,
8363 * the Session implementation has released the VirtualBox reference
8364 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
8365 * etc). However, it's also possible, that the client releases the IPC
8366 * semaphore correctly (i.e. before it releases the VirtualBox reference),
8367 * but but the VirtualBox release event comes first to the server process.
8368 * This case is practically possible, so we should not assert on an
8369 * unexpected uninit, just log a warning.
8370 */
8371
8372 if ((aReason == Uninit::Unexpected))
8373 LogWarningThisFunc (("Unexpected SessionMachine uninitialization!\n"));
8374
8375 if (aReason != Uninit::Normal)
8376 mData->mSession.mDirectControl.setNull();
8377 else
8378 {
8379 /* this must be null here (see #OnSessionEnd()) */
8380 Assert (mData->mSession.mDirectControl.isNull());
8381 Assert (mData->mSession.mState == SessionState_SessionClosing);
8382 Assert (!mData->mSession.mProgress.isNull());
8383
8384 mData->mSession.mProgress->notifyComplete (S_OK);
8385 mData->mSession.mProgress.setNull();
8386 }
8387
8388 /* remove the association between the peer machine and this session machine */
8389 Assert (mData->mSession.mMachine == this ||
8390 aReason == Uninit::Unexpected);
8391
8392 /* reset the rest of session data */
8393 mData->mSession.mMachine.setNull();
8394 mData->mSession.mState = SessionState_SessionClosed;
8395 mData->mSession.mType.setNull();
8396
8397 /* close the interprocess semaphore before leaving the shared lock */
8398#if defined(RT_OS_WINDOWS)
8399 if (mIPCSem)
8400 ::CloseHandle (mIPCSem);
8401 mIPCSem = NULL;
8402#elif defined(RT_OS_OS2)
8403 if (mIPCSem != NULLHANDLE)
8404 ::DosCloseMutexSem (mIPCSem);
8405 mIPCSem = NULLHANDLE;
8406#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8407 if (mIPCSem >= 0)
8408 ::semctl (mIPCSem, 0, IPC_RMID);
8409 mIPCSem = -1;
8410#else
8411# error "Port me!"
8412#endif
8413
8414 /* fire an event */
8415 mParent->onSessionStateChange (mData->mUuid, SessionState_SessionClosed);
8416
8417 uninitDataAndChildObjects();
8418
8419 /* free the essential data structure last */
8420 mData.free();
8421
8422 /* leave the shared lock before setting the below two to NULL */
8423 alock.leave();
8424
8425 unconst (mParent).setNull();
8426 unconst (mPeer).setNull();
8427
8428 LogFlowThisFuncLeave();
8429}
8430
8431// AutoLock::Lockable interface
8432////////////////////////////////////////////////////////////////////////////////
8433
8434/**
8435 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
8436 * with the primary Machine instance (mPeer).
8437 */
8438AutoLock::Handle *SessionMachine::lockHandle() const
8439{
8440 AssertReturn (!mPeer.isNull(), NULL);
8441 return mPeer->lockHandle();
8442}
8443
8444// IInternalMachineControl methods
8445////////////////////////////////////////////////////////////////////////////////
8446
8447/**
8448 * @note Locks the same as #setMachineState() does.
8449 */
8450STDMETHODIMP SessionMachine::UpdateState (MachineState_T machineState)
8451{
8452 return setMachineState (machineState);
8453}
8454
8455/**
8456 * @note Locks this object for reading.
8457 */
8458STDMETHODIMP SessionMachine::GetIPCId (BSTR *id)
8459{
8460 AutoCaller autoCaller (this);
8461 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8462
8463 AutoReaderLock alock (this);
8464
8465#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8466 mIPCSemName.cloneTo (id);
8467 return S_OK;
8468#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8469 mData->mConfigFileFull.cloneTo (id);
8470 return S_OK;
8471#else
8472# error "Port me!"
8473#endif
8474}
8475
8476/**
8477 * Goes through the USB filters of the given machine to see if the given
8478 * device matches any filter or not.
8479 *
8480 * @note Locks the same as USBController::hasMatchingFilter() does.
8481 */
8482STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
8483 BOOL *aMatched)
8484{
8485 LogFlowThisFunc (("\n"));
8486
8487 if (!aUSBDevice)
8488 return E_INVALIDARG;
8489 if (!aMatched)
8490 return E_POINTER;
8491
8492 AutoCaller autoCaller (this);
8493 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8494
8495 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice);
8496
8497 return S_OK;
8498}
8499
8500/**
8501 * @note Locks the same as Host::captureUSBDevice() does.
8502 */
8503STDMETHODIMP SessionMachine::CaptureUSBDevice (INPTR GUIDPARAM aId)
8504{
8505 LogFlowThisFunc (("\n"));
8506
8507 AutoCaller autoCaller (this);
8508 AssertComRCReturnRC (autoCaller.rc());
8509
8510 /* if cautureUSBDevice() fails, it must have set extended error info */
8511 return mParent->host()->captureUSBDevice (this, aId);
8512}
8513
8514/**
8515 * @note Locks the same as Host::detachUSBDevice() does.
8516 */
8517STDMETHODIMP SessionMachine::DetachUSBDevice (INPTR GUIDPARAM aId, BOOL aDone)
8518{
8519 LogFlowThisFunc (("\n"));
8520
8521 AutoCaller autoCaller (this);
8522 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8523
8524 return mParent->host()->detachUSBDevice (this, aId, aDone);
8525}
8526
8527/**
8528 * Inserts all machine filters to the USB proxy service and then calls
8529 * Host::autoCaptureUSBDevices().
8530 *
8531 * Called by Console from the VM process upon VM startup.
8532 *
8533 * @note Locks what called methods lock.
8534 */
8535STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
8536{
8537 LogFlowThisFunc (("\n"));
8538
8539 AutoCaller autoCaller (this);
8540 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8541
8542 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
8543 AssertComRC (rc);
8544 NOREF (rc);
8545
8546 return mParent->host()->autoCaptureUSBDevices (this);
8547}
8548
8549/**
8550 * Removes all machine filters from the USB proxy service and then calls
8551 * Host::detachAllUSBDevices().
8552 *
8553 * Called by Console from the VM process upon normal VM termination or by
8554 * SessionMachine::uninit() upon abnormal VM termination (from under the
8555 * Machine/SessionMachine lock).
8556 *
8557 * @note Locks what called methods lock.
8558 */
8559STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
8560{
8561 LogFlowThisFunc (("\n"));
8562
8563 AutoCaller autoCaller (this);
8564 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8565
8566 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8567 AssertComRC (rc);
8568 NOREF (rc);
8569
8570 return mParent->host()->detachAllUSBDevices (this, aDone);
8571}
8572
8573/**
8574 * @note Locks mParent + this object for writing.
8575 */
8576STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
8577 IProgress **aProgress)
8578{
8579 LogFlowThisFuncEnter();
8580
8581 AssertReturn (aSession, E_INVALIDARG);
8582 AssertReturn (aProgress, E_INVALIDARG);
8583
8584 AutoCaller autoCaller (this);
8585
8586 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
8587 /*
8588 * We don't assert below because it might happen that a non-direct session
8589 * informs us it is closed right after we've been uninitialized -- it's ok.
8590 */
8591 CheckComRCReturnRC (autoCaller.rc());
8592
8593 /* get IInternalSessionControl interface */
8594 ComPtr <IInternalSessionControl> control (aSession);
8595
8596 ComAssertRet (!control.isNull(), E_INVALIDARG);
8597
8598 /* Progress::init() needs mParent lock */
8599 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8600
8601 if (control.equalsTo (mData->mSession.mDirectControl))
8602 {
8603 ComAssertRet (aProgress, E_POINTER);
8604
8605 /* The direct session is being normally closed by the client process
8606 * ----------------------------------------------------------------- */
8607
8608 /* go to the closing state (essential for all open*Session() calls and
8609 * for #checkForDeath()) */
8610 Assert (mData->mSession.mState == SessionState_SessionOpen);
8611 mData->mSession.mState = SessionState_SessionClosing;
8612
8613 /* set direct control to NULL to release the remote instance */
8614 mData->mSession.mDirectControl.setNull();
8615 LogFlowThisFunc (("Direct control is set to NULL\n"));
8616
8617 /*
8618 * Create the progress object the client will use to wait until
8619 * #checkForDeath() is called to uninitialize this session object
8620 * after it releases the IPC semaphore.
8621 */
8622 ComObjPtr <Progress> progress;
8623 progress.createObject();
8624 progress->init (mParent, (IMachine *) mPeer, Bstr (tr ("Closing session")),
8625 FALSE /* aCancelable */);
8626 progress.queryInterfaceTo (aProgress);
8627 mData->mSession.mProgress = progress;
8628 }
8629 else
8630 {
8631 /* the remote session is being normally closed */
8632 Data::Session::RemoteControlList::iterator it =
8633 mData->mSession.mRemoteControls.begin();
8634 while (it != mData->mSession.mRemoteControls.end())
8635 {
8636 if (control.equalsTo (*it))
8637 break;
8638 ++it;
8639 }
8640 BOOL found = it != mData->mSession.mRemoteControls.end();
8641 ComAssertMsgRet (found, ("The session is not found in the session list!"),
8642 E_INVALIDARG);
8643 mData->mSession.mRemoteControls.remove (*it);
8644 }
8645
8646 LogFlowThisFuncLeave();
8647 return S_OK;
8648}
8649
8650/**
8651 * @note Locks mParent + this object for writing.
8652 */
8653STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
8654{
8655 LogFlowThisFuncEnter();
8656
8657 AssertReturn (aProgress, E_INVALIDARG);
8658 AssertReturn (aStateFilePath, E_POINTER);
8659
8660 AutoCaller autoCaller (this);
8661 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8662
8663 /* mParent->addProgress() needs mParent lock */
8664 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8665
8666 AssertReturn (mData->mMachineState == MachineState_Paused &&
8667 mSnapshotData.mLastState == MachineState_InvalidMachineState &&
8668 mSnapshotData.mProgressId.isEmpty() &&
8669 mSnapshotData.mStateFilePath.isNull(),
8670 E_FAIL);
8671
8672 /* memorize the progress ID and add it to the global collection */
8673 Guid progressId;
8674 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
8675 AssertComRCReturn (rc, rc);
8676 rc = mParent->addProgress (aProgress);
8677 AssertComRCReturn (rc, rc);
8678
8679 Bstr stateFilePath;
8680 /* stateFilePath is null when the machine is not running */
8681 if (mData->mMachineState == MachineState_Paused)
8682 {
8683 stateFilePath = Utf8StrFmt ("%ls%c{%Vuuid}.sav",
8684 mUserData->mSnapshotFolderFull.raw(),
8685 RTPATH_DELIMITER, mData->mUuid.raw());
8686 }
8687
8688 /* fill in the snapshot data */
8689 mSnapshotData.mLastState = mData->mMachineState;
8690 mSnapshotData.mProgressId = progressId;
8691 mSnapshotData.mStateFilePath = stateFilePath;
8692
8693 /* set the state to Saving (this is expected by Console::SaveState()) */
8694 setMachineState (MachineState_Saving);
8695
8696 stateFilePath.cloneTo (aStateFilePath);
8697
8698 return S_OK;
8699}
8700
8701/**
8702 * @note Locks mParent + this objects for writing.
8703 */
8704STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
8705{
8706 LogFlowThisFunc (("\n"));
8707
8708 AutoCaller autoCaller (this);
8709 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8710
8711 /* endSavingState() need mParent lock */
8712 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8713
8714 AssertReturn (mData->mMachineState == MachineState_Saving &&
8715 mSnapshotData.mLastState != MachineState_InvalidMachineState &&
8716 !mSnapshotData.mProgressId.isEmpty() &&
8717 !mSnapshotData.mStateFilePath.isNull(),
8718 E_FAIL);
8719
8720 /*
8721 * on success, set the state to Saved;
8722 * on failure, set the state to the state we had when BeginSavingState() was
8723 * called (this is expected by Console::SaveState() and
8724 * Console::saveStateThread())
8725 */
8726 if (aSuccess)
8727 setMachineState (MachineState_Saved);
8728 else
8729 setMachineState (mSnapshotData.mLastState);
8730
8731 return endSavingState (aSuccess);
8732}
8733
8734/**
8735 * @note Locks this objects for writing.
8736 */
8737STDMETHODIMP SessionMachine::AdoptSavedState (INPTR BSTR aSavedStateFile)
8738{
8739 LogFlowThisFunc (("\n"));
8740
8741 AssertReturn (aSavedStateFile, E_INVALIDARG);
8742
8743 AutoCaller autoCaller (this);
8744 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8745
8746 AutoLock alock (this);
8747
8748 AssertReturn (mData->mMachineState == MachineState_PoweredOff ||
8749 mData->mMachineState == MachineState_Aborted,
8750 E_FAIL);
8751
8752 Utf8Str stateFilePathFull = aSavedStateFile;
8753 int vrc = calculateFullPath (stateFilePathFull, stateFilePathFull);
8754 if (VBOX_FAILURE (vrc))
8755 return setError (E_FAIL,
8756 tr ("Invalid saved state file path: '%ls' (%Vrc)"),
8757 aSavedStateFile, vrc);
8758
8759 mSSData->mStateFilePath = stateFilePathFull;
8760
8761 /* The below setMachineState() will detect the state transition and will
8762 * update the settings file */
8763
8764 return setMachineState (MachineState_Saved);
8765}
8766
8767/**
8768 * @note Locks mParent + this objects for writing.
8769 */
8770STDMETHODIMP SessionMachine::BeginTakingSnapshot (
8771 IConsole *aInitiator, INPTR BSTR aName, INPTR BSTR aDescription,
8772 IProgress *aProgress, BSTR *aStateFilePath,
8773 IProgress **aServerProgress)
8774{
8775 LogFlowThisFuncEnter();
8776
8777 AssertReturn (aInitiator && aName, E_INVALIDARG);
8778 AssertReturn (aStateFilePath && aServerProgress, E_POINTER);
8779
8780 LogFlowThisFunc (("aName='%ls'\n", aName));
8781
8782 AutoCaller autoCaller (this);
8783 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8784
8785 /* Progress::init() needs mParent lock */
8786 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8787
8788 AssertReturn ((mData->mMachineState < MachineState_Running ||
8789 mData->mMachineState == MachineState_Paused) &&
8790 mSnapshotData.mLastState == MachineState_InvalidMachineState &&
8791 mSnapshotData.mSnapshot.isNull() &&
8792 mSnapshotData.mServerProgress.isNull() &&
8793 mSnapshotData.mCombinedProgress.isNull(),
8794 E_FAIL);
8795
8796 bool takingSnapshotOnline = mData->mMachineState == MachineState_Paused;
8797
8798 if (!takingSnapshotOnline && mData->mMachineState != MachineState_Saved)
8799 {
8800 /*
8801 * save all current settings to ensure current changes are committed
8802 * and hard disks are fixed up
8803 */
8804 HRESULT rc = saveSettings();
8805 CheckComRCReturnRC (rc);
8806 }
8807
8808 /* check that there are no Writethrough hard disks attached */
8809 for (HDData::HDAttachmentList::const_iterator
8810 it = mHDData->mHDAttachments.begin();
8811 it != mHDData->mHDAttachments.end();
8812 ++ it)
8813 {
8814 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
8815 AutoLock hdLock (hd);
8816 if (hd->type() == HardDiskType_WritethroughHardDisk)
8817 return setError (E_FAIL,
8818 tr ("Cannot take a snapshot when there is a Writethrough hard "
8819 " disk attached ('%ls')"), hd->toString().raw());
8820 }
8821
8822 AssertReturn (aProgress || !takingSnapshotOnline, E_FAIL);
8823
8824 /* create an ID for the snapshot */
8825 Guid snapshotId;
8826 snapshotId.create();
8827
8828 Bstr stateFilePath;
8829 /* stateFilePath is null when the machine is not online nor saved */
8830 if (takingSnapshotOnline || mData->mMachineState == MachineState_Saved)
8831 stateFilePath = Utf8StrFmt ("%ls%c{%Vuuid}.sav",
8832 mUserData->mSnapshotFolderFull.raw(),
8833 RTPATH_DELIMITER,
8834 snapshotId.ptr());
8835
8836 /* ensure the directory for the saved state file exists */
8837 if (stateFilePath)
8838 {
8839 Utf8Str dir = stateFilePath;
8840 RTPathStripFilename (dir.mutableRaw());
8841 if (!RTDirExists (dir))
8842 {
8843 int vrc = RTDirCreateFullPath (dir, 0777);
8844 if (VBOX_FAILURE (vrc))
8845 return setError (E_FAIL,
8846 tr ("Could not create a directory '%s' to save the "
8847 "VM state to (%Vrc)"),
8848 dir.raw(), vrc);
8849 }
8850 }
8851
8852 /* create a snapshot machine object */
8853 ComObjPtr <SnapshotMachine> snapshotMachine;
8854 snapshotMachine.createObject();
8855 HRESULT rc = snapshotMachine->init (this, snapshotId, stateFilePath);
8856 AssertComRCReturn (rc, rc);
8857
8858 Bstr progressDesc = Bstr (tr ("Taking snapshot of virtual machine"));
8859 Bstr firstOpDesc = Bstr (tr ("Preparing to take snapshot"));
8860
8861 /*
8862 * create a server-side progress object (it will be descriptionless
8863 * when we need to combine it with the VM-side progress, i.e. when we're
8864 * taking a snapshot online). The number of operations is:
8865 * 1 (preparing) + # of VDIs + 1 (if the state is saved so we need to copy it)
8866 */
8867 ComObjPtr <Progress> serverProgress;
8868 {
8869 ULONG opCount = 1 + mHDData->mHDAttachments.size();
8870 if (mData->mMachineState == MachineState_Saved)
8871 opCount ++;
8872 serverProgress.createObject();
8873 if (takingSnapshotOnline)
8874 rc = serverProgress->init (FALSE, opCount, firstOpDesc);
8875 else
8876 rc = serverProgress->init (mParent, aInitiator, progressDesc, FALSE,
8877 opCount, firstOpDesc);
8878 AssertComRCReturn (rc, rc);
8879 }
8880
8881 /* create a combined server-side progress object when necessary */
8882 ComObjPtr <CombinedProgress> combinedProgress;
8883 if (takingSnapshotOnline)
8884 {
8885 combinedProgress.createObject();
8886 rc = combinedProgress->init (mParent, aInitiator, progressDesc,
8887 serverProgress, aProgress);
8888 AssertComRCReturn (rc, rc);
8889 }
8890
8891 /* create a snapshot object */
8892 RTTIMESPEC time;
8893 ComObjPtr <Snapshot> snapshot;
8894 snapshot.createObject();
8895 rc = snapshot->init (snapshotId, aName, aDescription,
8896 RTTimeSpecGetMilli (RTTimeNow (&time)),
8897 snapshotMachine, mData->mCurrentSnapshot);
8898 AssertComRCReturn (rc, rc);
8899
8900 /*
8901 * create and start the task on a separate thread
8902 * (note that it will not start working until we release alock)
8903 */
8904 TakeSnapshotTask *task = new TakeSnapshotTask (this);
8905 int vrc = RTThreadCreate (NULL, taskHandler,
8906 (void *) task,
8907 0, RTTHREADTYPE_MAIN_WORKER, 0, "TakeSnapshot");
8908 if (VBOX_FAILURE (vrc))
8909 {
8910 snapshot->uninit();
8911 delete task;
8912 ComAssertFailedRet (E_FAIL);
8913 }
8914
8915 /* fill in the snapshot data */
8916 mSnapshotData.mLastState = mData->mMachineState;
8917 mSnapshotData.mSnapshot = snapshot;
8918 mSnapshotData.mServerProgress = serverProgress;
8919 mSnapshotData.mCombinedProgress = combinedProgress;
8920
8921 /* set the state to Saving (this is expected by Console::TakeSnapshot()) */
8922 setMachineState (MachineState_Saving);
8923
8924 if (takingSnapshotOnline)
8925 stateFilePath.cloneTo (aStateFilePath);
8926 else
8927 *aStateFilePath = NULL;
8928
8929 serverProgress.queryInterfaceTo (aServerProgress);
8930
8931 LogFlowThisFuncLeave();
8932 return S_OK;
8933}
8934
8935/**
8936 * @note Locks mParent + this objects for writing.
8937 */
8938STDMETHODIMP SessionMachine::EndTakingSnapshot (BOOL aSuccess)
8939{
8940 LogFlowThisFunc (("\n"));
8941
8942 AutoCaller autoCaller (this);
8943 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8944
8945 /* Lock mParent because of endTakingSnapshot() */
8946 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8947
8948 AssertReturn (!aSuccess ||
8949 (mData->mMachineState == MachineState_Saving &&
8950 mSnapshotData.mLastState != MachineState_InvalidMachineState &&
8951 !mSnapshotData.mSnapshot.isNull() &&
8952 !mSnapshotData.mServerProgress.isNull() &&
8953 !mSnapshotData.mCombinedProgress.isNull()),
8954 E_FAIL);
8955
8956 /*
8957 * set the state to the state we had when BeginTakingSnapshot() was called
8958 * (this is expected by Console::TakeSnapshot() and
8959 * Console::saveStateThread())
8960 */
8961 setMachineState (mSnapshotData.mLastState);
8962
8963 return endTakingSnapshot (aSuccess);
8964}
8965
8966/**
8967 * @note Locks mParent + this + children objects for writing!
8968 */
8969STDMETHODIMP SessionMachine::DiscardSnapshot (
8970 IConsole *aInitiator, INPTR GUIDPARAM aId,
8971 MachineState_T *aMachineState, IProgress **aProgress)
8972{
8973 LogFlowThisFunc (("\n"));
8974
8975 Guid id = aId;
8976 AssertReturn (aInitiator && !id.isEmpty(), E_INVALIDARG);
8977 AssertReturn (aMachineState && aProgress, E_POINTER);
8978
8979 AutoCaller autoCaller (this);
8980 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8981
8982 /* Progress::init() needs mParent lock */
8983 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
8984
8985 ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
8986
8987 ComObjPtr <Snapshot> snapshot;
8988 HRESULT rc = findSnapshot (id, snapshot, true /* aSetError */);
8989 CheckComRCReturnRC (rc);
8990
8991 AutoLock snapshotLock (snapshot);
8992 if (snapshot == mData->mFirstSnapshot)
8993 {
8994 AutoLock chLock (mData->mFirstSnapshot->childrenLock());
8995 size_t childrenCount = mData->mFirstSnapshot->children().size();
8996 if (childrenCount > 1)
8997 return setError (E_FAIL,
8998 tr ("Cannot discard the snapshot '%ls' because it is the first "
8999 "snapshot of the machine '%ls' and it has more than one "
9000 "child snapshot (%d)"),
9001 snapshot->data().mName.raw(), mUserData->mName.raw(),
9002 childrenCount);
9003 }
9004
9005 /*
9006 * If the snapshot being discarded is the current one, ensure current
9007 * settings are committed and saved.
9008 */
9009 if (snapshot == mData->mCurrentSnapshot)
9010 {
9011 if (isModified())
9012 {
9013 rc = saveSettings();
9014 CheckComRCReturnRC (rc);
9015 }
9016 }
9017
9018 /*
9019 * create a progress object. The number of operations is:
9020 * 1 (preparing) + # of VDIs
9021 */
9022 ComObjPtr <Progress> progress;
9023 progress.createObject();
9024 rc = progress->init (mParent, aInitiator,
9025 Bstr (Utf8StrFmt (tr ("Discarding snapshot '%ls'"),
9026 snapshot->data().mName.raw())),
9027 FALSE /* aCancelable */,
9028 1 + snapshot->data().mMachine->mHDData->mHDAttachments.size(),
9029 Bstr (tr ("Preparing to discard snapshot")));
9030 AssertComRCReturn (rc, rc);
9031
9032 /* create and start the task on a separate thread */
9033 DiscardSnapshotTask *task = new DiscardSnapshotTask (this, progress, snapshot);
9034 int vrc = RTThreadCreate (NULL, taskHandler,
9035 (void *) task,
9036 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardSnapshot");
9037 if (VBOX_FAILURE (vrc))
9038 delete task;
9039 ComAssertRCRet (vrc, E_FAIL);
9040
9041 /* set the proper machine state (note: after creating a Task instance) */
9042 setMachineState (MachineState_Discarding);
9043
9044 /* return the progress to the caller */
9045 progress.queryInterfaceTo (aProgress);
9046
9047 /* return the new state to the caller */
9048 *aMachineState = mData->mMachineState;
9049
9050 return S_OK;
9051}
9052
9053/**
9054 * @note Locks mParent + this + children objects for writing!
9055 */
9056STDMETHODIMP SessionMachine::DiscardCurrentState (
9057 IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
9058{
9059 LogFlowThisFunc (("\n"));
9060
9061 AssertReturn (aInitiator, E_INVALIDARG);
9062 AssertReturn (aMachineState && aProgress, E_POINTER);
9063
9064 AutoCaller autoCaller (this);
9065 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9066
9067 /* Progress::init() needs mParent lock */
9068 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9069
9070 ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
9071
9072 if (mData->mCurrentSnapshot.isNull())
9073 return setError (E_FAIL,
9074 tr ("Could not discard the current state of the machine '%ls' "
9075 "because it doesn't have any snapshots"),
9076 mUserData->mName.raw());
9077
9078 /*
9079 * create a progress object. The number of operations is:
9080 * 1 (preparing) + # of VDIs + 1 (if we need to copy the saved state file)
9081 */
9082 ComObjPtr <Progress> progress;
9083 progress.createObject();
9084 {
9085 ULONG opCount = 1 + mData->mCurrentSnapshot->data()
9086 .mMachine->mHDData->mHDAttachments.size();
9087 if (mData->mCurrentSnapshot->stateFilePath())
9088 ++ opCount;
9089 progress->init (mParent, aInitiator,
9090 Bstr (tr ("Discarding current machine state")),
9091 FALSE /* aCancelable */, opCount,
9092 Bstr (tr ("Preparing to discard current state")));
9093 }
9094
9095 /* create and start the task on a separate thread */
9096 DiscardCurrentStateTask *task =
9097 new DiscardCurrentStateTask (this, progress, false /* discardCurSnapshot */);
9098 int vrc = RTThreadCreate (NULL, taskHandler,
9099 (void *) task,
9100 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurState");
9101 if (VBOX_FAILURE (vrc))
9102 delete task;
9103 ComAssertRCRet (vrc, E_FAIL);
9104
9105 /* set the proper machine state (note: after creating a Task instance) */
9106 setMachineState (MachineState_Discarding);
9107
9108 /* return the progress to the caller */
9109 progress.queryInterfaceTo (aProgress);
9110
9111 /* return the new state to the caller */
9112 *aMachineState = mData->mMachineState;
9113
9114 return S_OK;
9115}
9116
9117/**
9118 * @note Locks mParent + other objects for writing!
9119 */
9120STDMETHODIMP SessionMachine::DiscardCurrentSnapshotAndState (
9121 IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
9122{
9123 LogFlowThisFunc (("\n"));
9124
9125 AssertReturn (aInitiator, E_INVALIDARG);
9126 AssertReturn (aMachineState && aProgress, E_POINTER);
9127
9128 AutoCaller autoCaller (this);
9129 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9130
9131 /* Progress::init() needs mParent lock */
9132 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9133
9134 ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
9135
9136 if (mData->mCurrentSnapshot.isNull())
9137 return setError (E_FAIL,
9138 tr ("Could not discard the current state of the machine '%ls' "
9139 "because it doesn't have any snapshots"),
9140 mUserData->mName.raw());
9141
9142 /*
9143 * create a progress object. The number of operations is:
9144 * 1 (preparing) + # of VDIs in the current snapshot +
9145 * # of VDIs in the previous snapshot +
9146 * 1 (if we need to copy the saved state file of the previous snapshot)
9147 * or (if there is no previous snapshot):
9148 * 1 (preparing) + # of VDIs in the current snapshot * 2 +
9149 * 1 (if we need to copy the saved state file of the current snapshot)
9150 */
9151 ComObjPtr <Progress> progress;
9152 progress.createObject();
9153 {
9154 ComObjPtr <Snapshot> curSnapshot = mData->mCurrentSnapshot;
9155 ComObjPtr <Snapshot> prevSnapshot = mData->mCurrentSnapshot->parent();
9156
9157 ULONG opCount = 1;
9158 if (prevSnapshot)
9159 {
9160 opCount += curSnapshot->data().mMachine->mHDData->mHDAttachments.size();
9161 opCount += prevSnapshot->data().mMachine->mHDData->mHDAttachments.size();
9162 if (prevSnapshot->stateFilePath())
9163 ++ opCount;
9164 }
9165 else
9166 {
9167 opCount += curSnapshot->data().mMachine->mHDData->mHDAttachments.size() * 2;
9168 if (curSnapshot->stateFilePath())
9169 ++ opCount;
9170 }
9171
9172 progress->init (mParent, aInitiator,
9173 Bstr (tr ("Discarding current machine snapshot and state")),
9174 FALSE /* aCancelable */, opCount,
9175 Bstr (tr ("Preparing to discard current snapshot and state")));
9176 }
9177
9178 /* create and start the task on a separate thread */
9179 DiscardCurrentStateTask *task =
9180 new DiscardCurrentStateTask (this, progress, true /* discardCurSnapshot */);
9181 int vrc = RTThreadCreate (NULL, taskHandler,
9182 (void *) task,
9183 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurState");
9184 if (VBOX_FAILURE (vrc))
9185 delete task;
9186 ComAssertRCRet (vrc, E_FAIL);
9187
9188 /* set the proper machine state (note: after creating a Task instance) */
9189 setMachineState (MachineState_Discarding);
9190
9191 /* return the progress to the caller */
9192 progress.queryInterfaceTo (aProgress);
9193
9194 /* return the new state to the caller */
9195 *aMachineState = mData->mMachineState;
9196
9197 return S_OK;
9198}
9199
9200// public methods only for internal purposes
9201/////////////////////////////////////////////////////////////////////////////
9202
9203/**
9204 * Called from the client watcher thread to check for unexpected client
9205 * process death.
9206 *
9207 * @note On Win32 and on OS/2, this method is called only when we've got the
9208 * mutex (i.e. the client has either died or terminated normally). This
9209 * method always returns true.
9210 *
9211 * @note On Linux, the method returns true if the client process has
9212 * terminated abnormally (and/or the session has been uninitialized) and
9213 * false if it is still alive.
9214 *
9215 * @note Locks this object for writing.
9216 */
9217bool SessionMachine::checkForDeath()
9218{
9219 Uninit::Reason reason;
9220 bool doUninit = false;
9221 bool ret = false;
9222
9223 /*
9224 * Enclose autoCaller with a block because calling uninit()
9225 * from under it will deadlock.
9226 */
9227 {
9228 AutoCaller autoCaller (this);
9229 if (!autoCaller.isOk())
9230 {
9231 /*
9232 * return true if not ready, to cause the client watcher to exclude
9233 * the corresponding session from watching
9234 */
9235 LogFlowThisFunc (("Already uninitialized!"));
9236 return true;
9237 }
9238
9239 AutoLock alock (this);
9240
9241 /*
9242 * Determine the reason of death: if the session state is Closing here,
9243 * everything is fine. Otherwise it means that the client did not call
9244 * OnSessionEnd() before it released the IPC semaphore.
9245 * This may happen either because the client process has abnormally
9246 * terminated, or because it simply forgot to call ISession::Close()
9247 * before exiting. We threat the latter also as an abnormal termination
9248 * (see Session::uninit() for details).
9249 */
9250 reason = mData->mSession.mState == SessionState_SessionClosing ?
9251 Uninit::Normal :
9252 Uninit::Abnormal;
9253
9254#if defined(RT_OS_WINDOWS)
9255
9256 AssertMsg (mIPCSem, ("semaphore must be created"));
9257
9258 /* release the IPC mutex */
9259 ::ReleaseMutex (mIPCSem);
9260
9261 doUninit = true;
9262
9263 ret = true;
9264
9265#elif defined(RT_OS_OS2)
9266
9267 AssertMsg (mIPCSem, ("semaphore must be created"));
9268
9269 /* release the IPC mutex */
9270 ::DosReleaseMutexSem (mIPCSem);
9271
9272 doUninit = true;
9273
9274 ret = true;
9275
9276#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9277
9278 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9279
9280 int val = ::semctl (mIPCSem, 0, GETVAL);
9281 if (val > 0)
9282 {
9283 /* the semaphore is signaled, meaning the session is terminated */
9284 doUninit = true;
9285 }
9286
9287 ret = val > 0;
9288
9289#else
9290# error "Port me!"
9291#endif
9292
9293 } /* AutoCaller block */
9294
9295 if (doUninit)
9296 uninit (reason);
9297
9298 return ret;
9299}
9300
9301/**
9302 * @note Locks this object for reading.
9303 */
9304HRESULT SessionMachine::onDVDDriveChange()
9305{
9306 LogFlowThisFunc (("\n"));
9307
9308 AutoCaller autoCaller (this);
9309 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9310
9311 ComPtr <IInternalSessionControl> directControl;
9312 {
9313 AutoReaderLock alock (this);
9314 directControl = mData->mSession.mDirectControl;
9315 }
9316
9317 /* ignore notifications sent after #OnSessionEnd() is called */
9318 if (!directControl)
9319 return S_OK;
9320
9321 return directControl->OnDVDDriveChange();
9322}
9323
9324/**
9325 * @note Locks this object for reading.
9326 */
9327HRESULT SessionMachine::onFloppyDriveChange()
9328{
9329 LogFlowThisFunc (("\n"));
9330
9331 AutoCaller autoCaller (this);
9332 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9333
9334 ComPtr <IInternalSessionControl> directControl;
9335 {
9336 AutoReaderLock alock (this);
9337 directControl = mData->mSession.mDirectControl;
9338 }
9339
9340 /* ignore notifications sent after #OnSessionEnd() is called */
9341 if (!directControl)
9342 return S_OK;
9343
9344 return directControl->OnFloppyDriveChange();
9345}
9346
9347/**
9348 * @note Locks this object for reading.
9349 */
9350HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter)
9351{
9352 LogFlowThisFunc (("\n"));
9353
9354 AutoCaller autoCaller (this);
9355 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9356
9357 ComPtr <IInternalSessionControl> directControl;
9358 {
9359 AutoReaderLock alock (this);
9360 directControl = mData->mSession.mDirectControl;
9361 }
9362
9363 /* ignore notifications sent after #OnSessionEnd() is called */
9364 if (!directControl)
9365 return S_OK;
9366
9367 return directControl->OnNetworkAdapterChange(networkAdapter);
9368}
9369
9370/**
9371 * @note Locks this object for reading.
9372 */
9373HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
9374{
9375 LogFlowThisFunc (("\n"));
9376
9377 AutoCaller autoCaller (this);
9378 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9379
9380 ComPtr <IInternalSessionControl> directControl;
9381 {
9382 AutoReaderLock alock (this);
9383 directControl = mData->mSession.mDirectControl;
9384 }
9385
9386 /* ignore notifications sent after #OnSessionEnd() is called */
9387 if (!directControl)
9388 return S_OK;
9389
9390 return directControl->OnSerialPortChange(serialPort);
9391}
9392
9393/**
9394 * @note Locks this object for reading.
9395 */
9396HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
9397{
9398 LogFlowThisFunc (("\n"));
9399
9400 AutoCaller autoCaller (this);
9401 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9402
9403 ComPtr <IInternalSessionControl> directControl;
9404 {
9405 AutoReaderLock alock (this);
9406 directControl = mData->mSession.mDirectControl;
9407 }
9408
9409 /* ignore notifications sent after #OnSessionEnd() is called */
9410 if (!directControl)
9411 return S_OK;
9412
9413 return directControl->OnParallelPortChange(parallelPort);
9414}
9415
9416/**
9417 * @note Locks this object for reading.
9418 */
9419HRESULT SessionMachine::onVRDPServerChange()
9420{
9421 LogFlowThisFunc (("\n"));
9422
9423 AutoCaller autoCaller (this);
9424 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9425
9426 ComPtr <IInternalSessionControl> directControl;
9427 {
9428 AutoReaderLock alock (this);
9429 directControl = mData->mSession.mDirectControl;
9430 }
9431
9432 /* ignore notifications sent after #OnSessionEnd() is called */
9433 if (!directControl)
9434 return S_OK;
9435
9436 return directControl->OnVRDPServerChange();
9437}
9438
9439/**
9440 * @note Locks this object for reading.
9441 */
9442HRESULT SessionMachine::onUSBControllerChange()
9443{
9444 LogFlowThisFunc (("\n"));
9445
9446 AutoCaller autoCaller (this);
9447 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9448
9449 ComPtr <IInternalSessionControl> directControl;
9450 {
9451 AutoReaderLock alock (this);
9452 directControl = mData->mSession.mDirectControl;
9453 }
9454
9455 /* ignore notifications sent after #OnSessionEnd() is called */
9456 if (!directControl)
9457 return S_OK;
9458
9459 return directControl->OnUSBControllerChange();
9460}
9461
9462/**
9463 * @note Locks this object for reading.
9464 */
9465HRESULT SessionMachine::onSharedFolderChange()
9466{
9467 LogFlowThisFunc (("\n"));
9468
9469 AutoCaller autoCaller (this);
9470 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9471
9472 ComPtr <IInternalSessionControl> directControl;
9473 {
9474 AutoReaderLock alock (this);
9475 directControl = mData->mSession.mDirectControl;
9476 }
9477
9478 /* ignore notifications sent after #OnSessionEnd() is called */
9479 if (!directControl)
9480 return S_OK;
9481
9482 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
9483}
9484
9485/**
9486 * @note Locks this object for reading.
9487 */
9488HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
9489 IVirtualBoxErrorInfo *aError)
9490{
9491 LogFlowThisFunc (("\n"));
9492
9493 AutoCaller autoCaller (this);
9494
9495 /* This notification may happen after the machine object has been
9496 * uninitialized (the session was closed), so don't assert. */
9497 CheckComRCReturnRC (autoCaller.rc());
9498
9499 ComPtr <IInternalSessionControl> directControl;
9500 {
9501 AutoReaderLock alock (this);
9502 directControl = mData->mSession.mDirectControl;
9503 }
9504
9505 /* fail on notifications sent after #OnSessionEnd() is called, it is
9506 * expected by the caller */
9507 if (!directControl)
9508 return E_FAIL;
9509
9510 return directControl->OnUSBDeviceAttach (aDevice, aError);
9511}
9512
9513/**
9514 * @note Locks this object for reading.
9515 */
9516HRESULT SessionMachine::onUSBDeviceDetach (INPTR GUIDPARAM aId,
9517 IVirtualBoxErrorInfo *aError)
9518{
9519 LogFlowThisFunc (("\n"));
9520
9521 AutoCaller autoCaller (this);
9522
9523 /* This notification may happen after the machine object has been
9524 * uninitialized (the session was closed), so don't assert. */
9525 CheckComRCReturnRC (autoCaller.rc());
9526
9527 ComPtr <IInternalSessionControl> directControl;
9528 {
9529 AutoReaderLock alock (this);
9530 directControl = mData->mSession.mDirectControl;
9531 }
9532
9533 /* fail on notifications sent after #OnSessionEnd() is called, it is
9534 * expected by the caller */
9535 if (!directControl)
9536 return E_FAIL;
9537
9538 return directControl->OnUSBDeviceDetach (aId, aError);
9539}
9540
9541// protected methods
9542/////////////////////////////////////////////////////////////////////////////
9543
9544/**
9545 * Helper method to finalize saving the state.
9546 *
9547 * @note Must be called from under this object's lock.
9548 *
9549 * @param aSuccess TRUE if the snapshot has been taken successfully
9550 *
9551 * @note Locks mParent + this objects for writing.
9552 */
9553HRESULT SessionMachine::endSavingState (BOOL aSuccess)
9554{
9555 LogFlowThisFuncEnter();
9556
9557 AutoCaller autoCaller (this);
9558 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9559
9560 /* mParent->removeProgress() needs mParent lock */
9561 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9562
9563 HRESULT rc = S_OK;
9564
9565 if (aSuccess)
9566 {
9567 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
9568
9569 /* save all VM settings */
9570 rc = saveSettings();
9571 }
9572 else
9573 {
9574 /* delete the saved state file (it might have been already created) */
9575 RTFileDelete (Utf8Str (mSnapshotData.mStateFilePath));
9576 }
9577
9578 /* remove the completed progress object */
9579 mParent->removeProgress (mSnapshotData.mProgressId);
9580
9581 /* clear out the temporary saved state data */
9582 mSnapshotData.mLastState = MachineState_InvalidMachineState;
9583 mSnapshotData.mProgressId.clear();
9584 mSnapshotData.mStateFilePath.setNull();
9585
9586 LogFlowThisFuncLeave();
9587 return rc;
9588}
9589
9590/**
9591 * Helper method to finalize taking a snapshot.
9592 * Gets called only from #EndTakingSnapshot() that is expected to
9593 * be called by the VM process when it finishes *all* the tasks related to
9594 * taking a snapshot, either scucessfully or unsuccessfilly.
9595 *
9596 * @param aSuccess TRUE if the snapshot has been taken successfully
9597 *
9598 * @note Locks mParent + this objects for writing.
9599 */
9600HRESULT SessionMachine::endTakingSnapshot (BOOL aSuccess)
9601{
9602 LogFlowThisFuncEnter();
9603
9604 AutoCaller autoCaller (this);
9605 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9606
9607 /* Progress object uninitialization needs mParent lock */
9608 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9609
9610 HRESULT rc = S_OK;
9611
9612 if (aSuccess)
9613 {
9614 /* the server progress must be completed on success */
9615 Assert (mSnapshotData.mServerProgress->completed());
9616
9617 mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
9618 /* memorize the first snapshot if necessary */
9619 if (!mData->mFirstSnapshot)
9620 mData->mFirstSnapshot = mData->mCurrentSnapshot;
9621
9622 int opFlags = SaveSS_AddOp | SaveSS_UpdateCurrentId;
9623 if (mSnapshotData.mLastState != MachineState_Paused && !isModified())
9624 {
9625 /*
9626 * the machine was powered off or saved when taking a snapshot,
9627 * so reset the mCurrentStateModified flag
9628 */
9629 mData->mCurrentStateModified = FALSE;
9630 opFlags |= SaveSS_UpdateCurStateModified;
9631 }
9632
9633 rc = saveSnapshotSettings (mSnapshotData.mSnapshot, opFlags);
9634 }
9635
9636 if (!aSuccess || FAILED (rc))
9637 {
9638 if (mSnapshotData.mSnapshot)
9639 {
9640 /* wait for the completion of the server progress (diff VDI creation) */
9641 /// @todo (dmik) later, we will definitely want to cancel it instead
9642 // (when the cancel function is implemented)
9643 mSnapshotData.mServerProgress->WaitForCompletion (-1);
9644
9645 /*
9646 * delete all differencing VDIs created
9647 * (this will attach their parents back)
9648 */
9649 rc = deleteSnapshotDiffs (mSnapshotData.mSnapshot);
9650 /* continue cleanup on error */
9651
9652 /* delete the saved state file (it might have been already created) */
9653 if (mSnapshotData.mSnapshot->stateFilePath())
9654 RTFileDelete (Utf8Str (mSnapshotData.mSnapshot->stateFilePath()));
9655
9656 mSnapshotData.mSnapshot->uninit();
9657 }
9658 }
9659
9660 /* inform callbacks */
9661 if (aSuccess && SUCCEEDED (rc))
9662 mParent->onSnapshotTaken (mData->mUuid, mSnapshotData.mSnapshot->data().mId);
9663
9664 /* clear out the snapshot data */
9665 mSnapshotData.mLastState = MachineState_InvalidMachineState;
9666 mSnapshotData.mSnapshot.setNull();
9667 mSnapshotData.mServerProgress.setNull();
9668 /* uninitialize the combined progress (to remove it from the VBox collection) */
9669 if (!mSnapshotData.mCombinedProgress.isNull())
9670 {
9671 mSnapshotData.mCombinedProgress->uninit();
9672 mSnapshotData.mCombinedProgress.setNull();
9673 }
9674
9675 LogFlowThisFuncLeave();
9676 return rc;
9677}
9678
9679/**
9680 * Take snapshot task handler.
9681 * Must be called only by TakeSnapshotTask::handler()!
9682 *
9683 * The sole purpose of this task is to asynchronously create differencing VDIs
9684 * and copy the saved state file (when necessary). The VM process will wait
9685 * for this task to complete using the mSnapshotData.mServerProgress
9686 * returned to it.
9687 *
9688 * @note Locks mParent + this objects for writing.
9689 */
9690void SessionMachine::takeSnapshotHandler (TakeSnapshotTask &aTask)
9691{
9692 LogFlowThisFuncEnter();
9693
9694 AutoCaller autoCaller (this);
9695
9696 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
9697 if (!autoCaller.isOk())
9698 {
9699 /*
9700 * we might have been uninitialized because the session was
9701 * accidentally closed by the client, so don't assert
9702 */
9703 LogFlowThisFuncLeave();
9704 return;
9705 }
9706
9707 /* endTakingSnapshot() needs mParent lock */
9708 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
9709
9710 HRESULT rc = S_OK;
9711
9712 LogFlowThisFunc (("Creating differencing VDIs...\n"));
9713
9714 /* create new differencing hard disks and attach them to this machine */
9715 rc = createSnapshotDiffs (&mSnapshotData.mSnapshot->data().mId,
9716 mUserData->mSnapshotFolderFull,
9717 mSnapshotData.mServerProgress,
9718 true /* aOnline */);
9719
9720 if (SUCCEEDED (rc) && mSnapshotData.mLastState == MachineState_Saved)
9721 {
9722 Utf8Str stateFrom = mSSData->mStateFilePath;
9723 Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
9724
9725 LogFlowThisFunc (("Copying the execution state from '%s' to '%s'...\n",
9726 stateFrom.raw(), stateTo.raw()));
9727
9728 mSnapshotData.mServerProgress->advanceOperation (
9729 Bstr (tr ("Copying the execution state")));
9730
9731 /*
9732 * We can safely leave the lock here:
9733 * mMachineState is MachineState_Saving here
9734 */
9735 alock.leave();
9736
9737 /* copy the state file */
9738 int vrc = RTFileCopyEx (stateFrom, stateTo, progressCallback,
9739 static_cast <Progress *> (mSnapshotData.mServerProgress));
9740
9741 alock.enter();
9742
9743 if (VBOX_FAILURE (vrc))
9744 rc = setError (E_FAIL,
9745 tr ("Could not copy the state file '%ls' to '%ls' (%Vrc)"),
9746 stateFrom.raw(), stateTo.raw());
9747 }
9748
9749 /*
9750 * we have to call endTakingSnapshot() here if the snapshot was taken
9751 * offline, because the VM process will not do it in this case
9752 */
9753 if (mSnapshotData.mLastState != MachineState_Paused)
9754 {
9755 LogFlowThisFunc (("Finalizing the taken snapshot (rc=%08X)...\n", rc));
9756
9757 setMachineState (mSnapshotData.mLastState);
9758 updateMachineStateOnClient();
9759
9760 /* finalize the progress after setting the state, for consistency */
9761 mSnapshotData.mServerProgress->notifyComplete (rc);
9762
9763 endTakingSnapshot (SUCCEEDED (rc));
9764 }
9765 else
9766 {
9767 mSnapshotData.mServerProgress->notifyComplete (rc);
9768 }
9769
9770 LogFlowThisFuncLeave();
9771}
9772
9773/**
9774 * Discard snapshot task handler.
9775 * Must be called only by DiscardSnapshotTask::handler()!
9776 *
9777 * When aTask.subTask is true, the associated progress object is left
9778 * uncompleted on success. On failure, the progress is marked as completed
9779 * regardless of this parameter.
9780 *
9781 * @note Locks mParent + this + child objects for writing!
9782 */
9783void SessionMachine::discardSnapshotHandler (DiscardSnapshotTask &aTask)
9784{
9785 LogFlowThisFuncEnter();
9786
9787 AutoCaller autoCaller (this);
9788
9789 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
9790 if (!autoCaller.isOk())
9791 {
9792 /*
9793 * we might have been uninitialized because the session was
9794 * accidentally closed by the client, so don't assert
9795 */
9796 aTask.progress->notifyComplete (
9797 E_FAIL, COM_IIDOF (IMachine), getComponentName(),
9798 tr ("The session has been accidentally closed"));
9799
9800 LogFlowThisFuncLeave();
9801 return;
9802 }
9803
9804 ComObjPtr <SnapshotMachine> sm = aTask.snapshot->data().mMachine;
9805
9806 /* mParent is locked because of Progress::notifyComplete(), etc. */
9807 AutoMultiLock <3> alock (mParent->wlock(), this->wlock(), sm->rlock());
9808
9809 /* Safe locking in the direction parent->child */
9810 AutoLock snapshotLock (aTask.snapshot);
9811 AutoLock snapshotChildrenLock (aTask.snapshot->childrenLock());
9812
9813 HRESULT rc = S_OK;
9814
9815 /* save the snapshot ID (for callbacks) */
9816 Guid snapshotId = aTask.snapshot->data().mId;
9817
9818 do
9819 {
9820 /* first pass: */
9821 LogFlowThisFunc (("Check hard disk accessibility and affected machines...\n"));
9822
9823 HDData::HDAttachmentList::const_iterator it;
9824 for (it = sm->mHDData->mHDAttachments.begin();
9825 it != sm->mHDData->mHDAttachments.end();
9826 ++ it)
9827 {
9828 ComObjPtr <HardDiskAttachment> hda = *it;
9829 ComObjPtr <HardDisk> hd = hda->hardDisk();
9830 ComObjPtr <HardDisk> parent = hd->parent();
9831
9832 AutoLock hdLock (hd);
9833
9834 if (hd->hasForeignChildren())
9835 {
9836 rc = setError (E_FAIL,
9837 tr ("One or more hard disks belonging to other machines are "
9838 "based on the hard disk '%ls' stored in the snapshot '%ls'"),
9839 hd->toString().raw(), aTask.snapshot->data().mName.raw());
9840 break;
9841 }
9842
9843 if (hd->type() == HardDiskType_NormalHardDisk)
9844 {
9845 AutoLock hdChildrenLock (hd->childrenLock());
9846 size_t childrenCount = hd->children().size();
9847 if (childrenCount > 1)
9848 {
9849 rc = setError (E_FAIL,
9850 tr ("Normal hard disk '%ls' stored in the snapshot '%ls' "
9851 "has more than one child hard disk (%d)"),
9852 hd->toString().raw(), aTask.snapshot->data().mName.raw(),
9853 childrenCount);
9854 break;
9855 }
9856 }
9857 else
9858 {
9859 ComAssertMsgFailedBreak (("Invalid hard disk type %d\n", hd->type()),
9860 rc = E_FAIL);
9861 }
9862
9863 Bstr accessError;
9864 rc = hd->getAccessibleWithChildren (accessError);
9865 CheckComRCBreakRC (rc);
9866
9867 if (!accessError.isNull())
9868 {
9869 rc = setError (E_FAIL,
9870 tr ("Hard disk '%ls' stored in the snapshot '%ls' is not "
9871 "accessible (%ls)"),
9872 hd->toString().raw(), aTask.snapshot->data().mName.raw(),
9873 accessError.raw());
9874 break;
9875 }
9876
9877 rc = hd->setBusyWithChildren();
9878 if (FAILED (rc))
9879 {
9880 /* reset the busy flag of all previous hard disks */
9881 while (it != sm->mHDData->mHDAttachments.begin())
9882 (*(-- it))->hardDisk()->clearBusyWithChildren();
9883 break;
9884 }
9885 }
9886
9887 CheckComRCBreakRC (rc);
9888
9889 /* second pass: */
9890 LogFlowThisFunc (("Performing actual vdi merging...\n"));
9891
9892 for (it = sm->mHDData->mHDAttachments.begin();
9893 it != sm->mHDData->mHDAttachments.end();
9894 ++ it)
9895 {
9896 ComObjPtr <HardDiskAttachment> hda = *it;
9897 ComObjPtr <HardDisk> hd = hda->hardDisk();
9898 ComObjPtr <HardDisk> parent = hd->parent();
9899
9900 AutoLock hdLock (hd);
9901
9902 Bstr hdRootString = hd->root()->toString (true /* aShort */);
9903
9904 if (parent)
9905 {
9906 if (hd->isParentImmutable())
9907 {
9908 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9909 tr ("Discarding changes to immutable hard disk '%ls'"),
9910 hdRootString.raw())));
9911
9912 /* clear the busy flag before unregistering */
9913 hd->clearBusy();
9914
9915 /*
9916 * unregisterDiffHardDisk() is supposed to delete and uninit
9917 * the differencing hard disk
9918 */
9919 rc = mParent->unregisterDiffHardDisk (hd);
9920 CheckComRCBreakRC (rc);
9921 continue;
9922 }
9923 else
9924 {
9925 /*
9926 * differencing VDI:
9927 * merge this image to all its children
9928 */
9929
9930 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9931 tr ("Merging changes to normal hard disk '%ls' to children"),
9932 hdRootString.raw())));
9933
9934 snapshotChildrenLock.unlock();
9935 snapshotLock.unlock();
9936 alock.leave();
9937
9938 rc = hd->asVDI()->mergeImageToChildren (aTask.progress);
9939
9940 alock.enter();
9941 snapshotLock.lock();
9942 snapshotChildrenLock.lock();
9943
9944 // debug code
9945 // if (it != sm->mHDData->mHDAttachments.begin())
9946 // {
9947 // rc = setError (E_FAIL, "Simulated failure");
9948 // break;
9949 //}
9950
9951 if (SUCCEEDED (rc))
9952 rc = mParent->unregisterDiffHardDisk (hd);
9953 else
9954 hd->clearBusyWithChildren();
9955
9956 CheckComRCBreakRC (rc);
9957 }
9958 }
9959 else if (hd->type() == HardDiskType_NormalHardDisk)
9960 {
9961 /*
9962 * normal vdi has the only child or none
9963 * (checked in the first pass)
9964 */
9965
9966 ComObjPtr <HardDisk> child;
9967 {
9968 AutoLock hdChildrenLock (hd->childrenLock());
9969 if (hd->children().size())
9970 child = hd->children().front();
9971 }
9972
9973 if (child.isNull())
9974 {
9975 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9976 tr ("Detaching normal hard disk '%ls'"),
9977 hdRootString.raw())));
9978
9979 /* just deassociate the normal image from this machine */
9980 hd->setMachineId (Guid());
9981 hd->setSnapshotId (Guid());
9982
9983 /* clear the busy flag */
9984 hd->clearBusy();
9985 }
9986 else
9987 {
9988 AutoLock childLock (child);
9989
9990 aTask.progress->advanceOperation (Bstr (Utf8StrFmt (
9991 tr ("Preserving changes to normal hard disk '%ls'"),
9992 hdRootString.raw())));
9993
9994 ComObjPtr <Machine> cm;
9995 ComObjPtr <Snapshot> cs;
9996 ComObjPtr <HardDiskAttachment> childHda;
9997 rc = findHardDiskAttachment (child, &cm, &cs, &childHda);
9998 CheckComRCBreakRC (rc);
9999 /* must be the same machine (checked in the first pass) */
10000 ComAssertBreak (cm->mData->mUuid == mData->mUuid, rc = E_FAIL);
10001
10002 /* merge the child to this basic image */
10003
10004 snapshotChildrenLock.unlock();
10005 snapshotLock.unlock();
10006 alock.leave();
10007
10008 rc = child->asVDI()->mergeImageToParent (aTask.progress);
10009
10010 alock.enter();
10011 snapshotLock.lock();
10012 snapshotChildrenLock.lock();
10013
10014 if (SUCCEEDED (rc))
10015 rc = mParent->unregisterDiffHardDisk (child);
10016 else
10017 hd->clearBusyWithChildren();
10018
10019 CheckComRCBreakRC (rc);
10020
10021 /* reset the snapshot Id */
10022 hd->setSnapshotId (Guid());
10023
10024 /* replace the child image in the appropriate place */
10025 childHda->updateHardDisk (hd, FALSE /* aDirty */);
10026
10027 if (!cs)
10028 {
10029 aTask.settingsChanged = true;
10030 }
10031 else
10032 {
10033 rc = cm->saveSnapshotSettings (cs, SaveSS_UpdateAllOp);
10034 CheckComRCBreakRC (rc);
10035 }
10036 }
10037 }
10038 else
10039 {
10040 ComAssertMsgFailedBreak (("Invalid hard disk type %d\n", hd->type()),
10041 rc = E_FAIL);
10042 }
10043 }
10044
10045 /* preserve existing error info */
10046 ErrorInfoKeeper mergeEik;
10047 HRESULT mergeRc = rc;
10048
10049 if (FAILED (rc))
10050 {
10051 /* clear the busy flag on the rest of hard disks */
10052 for (++ it; it != sm->mHDData->mHDAttachments.end(); ++ it)
10053 (*it)->hardDisk()->clearBusyWithChildren();
10054 }
10055
10056 /*
10057 * we have to try to discard the snapshot even if merging failed
10058 * because some images might have been already merged (and deleted)
10059 */
10060
10061 do
10062 {
10063 LogFlowThisFunc (("Discarding the snapshot (reparenting children)...\n"));
10064
10065 /* It is important to uninitialize and delete all snapshot's hard
10066 * disk attachments as they are no longer valid -- otherwise the
10067 * code in Machine::uninitDataAndChildObjects() will mistakenly
10068 * perform hard disk deassociation. */
10069 for (HDData::HDAttachmentList::iterator it = sm->mHDData->mHDAttachments.begin();
10070 it != sm->mHDData->mHDAttachments.end();)
10071 {
10072 (*it)->uninit();
10073 it = sm->mHDData->mHDAttachments.erase (it);
10074 }
10075
10076 ComObjPtr <Snapshot> parentSnapshot = aTask.snapshot->parent();
10077
10078 /// @todo (dmik):
10079 // when we introduce clones later, discarding the snapshot
10080 // will affect the current and first snapshots of clones, if they are
10081 // direct children of this snapshot. So we will need to lock machines
10082 // associated with child snapshots as well and update mCurrentSnapshot
10083 // and/or mFirstSnapshot fields.
10084
10085 if (aTask.snapshot == mData->mCurrentSnapshot)
10086 {
10087 /* currently, the parent snapshot must refer to the same machine */
10088 ComAssertBreak (
10089 !parentSnapshot ||
10090 parentSnapshot->data().mMachine->mData->mUuid == mData->mUuid,
10091 rc = E_FAIL);
10092 mData->mCurrentSnapshot = parentSnapshot;
10093 /* mark the current state as modified */
10094 mData->mCurrentStateModified = TRUE;
10095 }
10096
10097 if (aTask.snapshot == mData->mFirstSnapshot)
10098 {
10099 /*
10100 * the first snapshot must have only one child when discarded,
10101 * or no children at all
10102 */
10103 ComAssertBreak (aTask.snapshot->children().size() <= 1, rc = E_FAIL);
10104
10105 if (aTask.snapshot->children().size() == 1)
10106 {
10107 ComObjPtr <Snapshot> childSnapshot = aTask.snapshot->children().front();
10108 ComAssertBreak (
10109 childSnapshot->data().mMachine->mData->mUuid == mData->mUuid,
10110 rc = E_FAIL);
10111 mData->mFirstSnapshot = childSnapshot;
10112 }
10113 else
10114 mData->mFirstSnapshot.setNull();
10115 }
10116
10117 /// @todo (dmik)
10118 // if we implement some warning mechanism later, we'll have
10119 // to return a warning if the state file path cannot be deleted
10120 Bstr stateFilePath = aTask.snapshot->stateFilePath();
10121 if (stateFilePath)
10122 RTFileDelete (Utf8Str (stateFilePath));
10123
10124 aTask.snapshot->discard();
10125
10126 rc = saveSnapshotSettings (parentSnapshot,
10127 SaveSS_UpdateAllOp | SaveSS_UpdateCurrentId);
10128 }
10129 while (0);
10130
10131 /* restore the merge error if any (ErrorInfo will be restored
10132 * automatically) */
10133 if (FAILED (mergeRc))
10134 rc = mergeRc;
10135 }
10136 while (0);
10137
10138 if (!aTask.subTask || FAILED (rc))
10139 {
10140 if (!aTask.subTask)
10141 {
10142 /* preserve existing error info */
10143 ErrorInfoKeeper eik;
10144
10145 /* restore the machine state */
10146 setMachineState (aTask.state);
10147 updateMachineStateOnClient();
10148
10149 /*
10150 * save settings anyway, since we've already changed the current
10151 * machine configuration
10152 */
10153 if (aTask.settingsChanged)
10154 {
10155 saveSettings (true /* aMarkCurStateAsModified */,
10156 true /* aInformCallbacksAnyway */);
10157 }
10158 }
10159
10160 /* set the result (this will try to fetch current error info on failure) */
10161 aTask.progress->notifyComplete (rc);
10162 }
10163
10164 if (SUCCEEDED (rc))
10165 mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
10166
10167 LogFlowThisFunc (("Done discarding snapshot (rc=%08X)\n", rc));
10168 LogFlowThisFuncLeave();
10169}
10170
10171/**
10172 * Discard current state task handler.
10173 * Must be called only by DiscardCurrentStateTask::handler()!
10174 *
10175 * @note Locks mParent + this object for writing.
10176 */
10177void SessionMachine::discardCurrentStateHandler (DiscardCurrentStateTask &aTask)
10178{
10179 LogFlowThisFuncEnter();
10180
10181 AutoCaller autoCaller (this);
10182
10183 LogFlowThisFunc (("state=%d\n", autoCaller.state()));
10184 if (!autoCaller.isOk())
10185 {
10186 /*
10187 * we might have been uninitialized because the session was
10188 * accidentally closed by the client, so don't assert
10189 */
10190 aTask.progress->notifyComplete (
10191 E_FAIL, COM_IIDOF (IMachine), getComponentName(),
10192 tr ("The session has been accidentally closed"));
10193
10194 LogFlowThisFuncLeave();
10195 return;
10196 }
10197
10198 /* mParent is locked because of Progress::notifyComplete(), etc. */
10199 AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
10200
10201 /*
10202 * discard all current changes to mUserData (name, OSType etc.)
10203 * (note that the machine is powered off, so there is no need
10204 * to inform the direct session)
10205 */
10206 if (isModified())
10207 rollback (false /* aNotify */);
10208
10209 HRESULT rc = S_OK;
10210
10211 bool errorInSubtask = false;
10212 bool stateRestored = false;
10213
10214 const bool isLastSnapshot = mData->mCurrentSnapshot->parent().isNull();
10215
10216 do
10217 {
10218 /*
10219 * discard the saved state file if the machine was Saved prior
10220 * to this operation
10221 */
10222 if (aTask.state == MachineState_Saved)
10223 {
10224 Assert (!mSSData->mStateFilePath.isEmpty());
10225 RTFileDelete (Utf8Str (mSSData->mStateFilePath));
10226 mSSData->mStateFilePath.setNull();
10227 aTask.modifyLastState (MachineState_PoweredOff);
10228 rc = saveStateSettings (SaveSTS_StateFilePath);
10229 CheckComRCBreakRC (rc);
10230 }
10231
10232 if (aTask.discardCurrentSnapshot && !isLastSnapshot)
10233 {
10234 /*
10235 * the "discard current snapshot and state" task is in action,
10236 * the current snapshot is not the last one.
10237 * Discard the current snapshot first.
10238 */
10239
10240 DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
10241 subTask.subTask = true;
10242 discardSnapshotHandler (subTask);
10243 aTask.settingsChanged = subTask.settingsChanged;
10244 if (aTask.progress->completed())
10245 {
10246 /*
10247 * the progress can be completed by a subtask only if there was
10248 * a failure
10249 */
10250 Assert (FAILED (aTask.progress->resultCode()));
10251 errorInSubtask = true;
10252 rc = aTask.progress->resultCode();
10253 break;
10254 }
10255 }
10256
10257 LONG64 snapshotTimeStamp = 0;
10258
10259 {
10260 ComObjPtr <Snapshot> curSnapshot = mData->mCurrentSnapshot;
10261 AutoLock snapshotLock (curSnapshot);
10262
10263 /* remember the timestamp of the snapshot we're restoring from */
10264 snapshotTimeStamp = curSnapshot->data().mTimeStamp;
10265
10266 /* copy all hardware data from the current snapshot */
10267 copyFrom (curSnapshot->data().mMachine);
10268
10269 LogFlowThisFunc (("Restoring VDIs from the snapshot...\n"));
10270
10271 /* restore the attachmends from the snapshot */
10272 mHDData.backup();
10273 mHDData->mHDAttachments =
10274 curSnapshot->data().mMachine->mHDData->mHDAttachments;
10275
10276 snapshotLock.unlock();
10277 alock.leave();
10278 rc = createSnapshotDiffs (NULL, mUserData->mSnapshotFolderFull,
10279 aTask.progress,
10280 false /* aOnline */);
10281 alock.enter();
10282 snapshotLock.lock();
10283
10284 if (FAILED (rc))
10285 {
10286 /* here we can still safely rollback, so do it */
10287 /* preserve existing error info */
10288 ErrorInfoKeeper eik;
10289 /* undo all changes */
10290 rollback (false /* aNotify */);
10291 break;
10292 }
10293
10294 /*
10295 * note: old VDIs will be deassociated/deleted on #commit() called
10296 * either from #saveSettings() or directly at the end
10297 */
10298
10299 /* should not have a saved state file associated at this point */
10300 Assert (mSSData->mStateFilePath.isNull());
10301
10302 if (curSnapshot->stateFilePath())
10303 {
10304 Utf8Str snapStateFilePath = curSnapshot->stateFilePath();
10305
10306 Utf8Str stateFilePath = Utf8StrFmt ("%ls%c{%Vuuid}.sav",
10307 mUserData->mSnapshotFolderFull.raw(),
10308 RTPATH_DELIMITER, mData->mUuid.raw());
10309
10310 LogFlowThisFunc (("Copying saved state file from '%s' to '%s'...\n",
10311 snapStateFilePath.raw(), stateFilePath.raw()));
10312
10313 aTask.progress->advanceOperation (
10314 Bstr (tr ("Restoring the execution state")));
10315
10316 /* copy the state file */
10317 snapshotLock.unlock();
10318 alock.leave();
10319 int vrc = RTFileCopyEx (snapStateFilePath, stateFilePath,
10320 progressCallback, aTask.progress);
10321 alock.enter();
10322 snapshotLock.lock();
10323
10324 if (VBOX_SUCCESS (vrc))
10325 {
10326 mSSData->mStateFilePath = stateFilePath;
10327 }
10328 else
10329 {
10330 rc = setError (E_FAIL,
10331 tr ("Could not copy the state file '%s' to '%s' (%Vrc)"),
10332 snapStateFilePath.raw(), stateFilePath.raw(), vrc);
10333 break;
10334 }
10335 }
10336 }
10337
10338 bool informCallbacks = false;
10339
10340 if (aTask.discardCurrentSnapshot && isLastSnapshot)
10341 {
10342 /*
10343 * discard the current snapshot and state task is in action,
10344 * the current snapshot is the last one.
10345 * Discard the current snapshot after discarding the current state.
10346 */
10347
10348 /* commit changes to fixup hard disks before discarding */
10349 rc = commit();
10350 if (SUCCEEDED (rc))
10351 {
10352 DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
10353 subTask.subTask = true;
10354 discardSnapshotHandler (subTask);
10355 aTask.settingsChanged = subTask.settingsChanged;
10356 if (aTask.progress->completed())
10357 {
10358 /*
10359 * the progress can be completed by a subtask only if there
10360 * was a failure
10361 */
10362 Assert (FAILED (aTask.progress->resultCode()));
10363 errorInSubtask = true;
10364 rc = aTask.progress->resultCode();
10365 }
10366 }
10367
10368 /*
10369 * we've committed already, so inform callbacks anyway to ensure
10370 * they don't miss some change
10371 */
10372 informCallbacks = true;
10373 }
10374
10375 /*
10376 * we have already discarded the current state, so set the
10377 * execution state accordingly no matter of the discard snapshot result
10378 */
10379 if (mSSData->mStateFilePath)
10380 setMachineState (MachineState_Saved);
10381 else
10382 setMachineState (MachineState_PoweredOff);
10383
10384 updateMachineStateOnClient();
10385 stateRestored = true;
10386
10387 if (errorInSubtask)
10388 break;
10389
10390 /* assign the timestamp from the snapshot */
10391 Assert (snapshotTimeStamp != 0);
10392 mData->mLastStateChange = snapshotTimeStamp;
10393
10394 /* mark the current state as not modified */
10395 mData->mCurrentStateModified = FALSE;
10396
10397 /* save all settings and commit */
10398 rc = saveSettings (false /* aMarkCurStateAsModified */,
10399 informCallbacks);
10400 aTask.settingsChanged = false;
10401 }
10402 while (0);
10403
10404 if (FAILED (rc))
10405 {
10406 /* preserve existing error info */
10407 ErrorInfoKeeper eik;
10408
10409 if (!stateRestored)
10410 {
10411 /* restore the machine state */
10412 setMachineState (aTask.state);
10413 updateMachineStateOnClient();
10414 }
10415
10416 /*
10417 * save all settings and commit if still modified (there is no way to
10418 * rollback properly). Note that isModified() will return true after
10419 * copyFrom(). Also save the settings if requested by the subtask.
10420 */
10421 if (isModified() || aTask.settingsChanged)
10422 {
10423 if (aTask.settingsChanged)
10424 saveSettings (true /* aMarkCurStateAsModified */,
10425 true /* aInformCallbacksAnyway */);
10426 else
10427 saveSettings();
10428 }
10429 }
10430
10431 if (!errorInSubtask)
10432 {
10433 /* set the result (this will try to fetch current error info on failure) */
10434 aTask.progress->notifyComplete (rc);
10435 }
10436
10437 if (SUCCEEDED (rc))
10438 mParent->onSnapshotDiscarded (mData->mUuid, Guid());
10439
10440 LogFlowThisFunc (("Done discarding current state (rc=%08X)\n", rc));
10441
10442 LogFlowThisFuncLeave();
10443}
10444
10445/**
10446 * Helper to change the machine state (reimplementation).
10447 *
10448 * @note Locks this object for writing.
10449 */
10450HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
10451{
10452 LogFlowThisFuncEnter();
10453 LogFlowThisFunc (("aMachineState=%d\n", aMachineState));
10454
10455 AutoCaller autoCaller (this);
10456 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10457
10458 AutoLock alock (this);
10459
10460 MachineState_T oldMachineState = mData->mMachineState;
10461
10462 AssertMsgReturn (oldMachineState != aMachineState,
10463 ("oldMachineState=%d, aMachineState=%d\n",
10464 oldMachineState, aMachineState), E_FAIL);
10465
10466 HRESULT rc = S_OK;
10467
10468 int stsFlags = 0;
10469 bool deleteSavedState = false;
10470
10471 /* detect some state transitions */
10472
10473 if (oldMachineState < MachineState_Running &&
10474 aMachineState >= MachineState_Running &&
10475 aMachineState != MachineState_Discarding)
10476 {
10477 /*
10478 * the EMT thread is about to start, so mark attached HDDs as busy
10479 * and all its ancestors as being in use
10480 */
10481 for (HDData::HDAttachmentList::const_iterator it =
10482 mHDData->mHDAttachments.begin();
10483 it != mHDData->mHDAttachments.end();
10484 ++ it)
10485 {
10486 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
10487 AutoLock hdLock (hd);
10488 hd->setBusy();
10489 hd->addReaderOnAncestors();
10490 }
10491 }
10492 else
10493 if (oldMachineState >= MachineState_Running &&
10494 oldMachineState != MachineState_Discarding &&
10495 aMachineState < MachineState_Running)
10496 {
10497 /*
10498 * the EMT thread stopped, so mark attached HDDs as no more busy
10499 * and remove the in-use flag from all its ancestors
10500 */
10501 for (HDData::HDAttachmentList::const_iterator it =
10502 mHDData->mHDAttachments.begin();
10503 it != mHDData->mHDAttachments.end();
10504 ++ it)
10505 {
10506 ComObjPtr <HardDisk> hd = (*it)->hardDisk();
10507 AutoLock hdLock (hd);
10508 hd->releaseReaderOnAncestors();
10509 hd->clearBusy();
10510 }
10511 }
10512
10513 if (oldMachineState == MachineState_Restoring)
10514 {
10515 if (aMachineState != MachineState_Saved)
10516 {
10517 /*
10518 * delete the saved state file once the machine has finished
10519 * restoring from it (note that Console sets the state from
10520 * Restoring to Saved if the VM couldn't restore successfully,
10521 * to give the user an ability to fix an error and retry --
10522 * we keep the saved state file in this case)
10523 */
10524 deleteSavedState = true;
10525 }
10526 }
10527 else
10528 if (oldMachineState == MachineState_Saved &&
10529 (aMachineState == MachineState_PoweredOff ||
10530 aMachineState == MachineState_Aborted))
10531 {
10532 /*
10533 * delete the saved state after Console::DiscardSavedState() is called
10534 * or if the VM process (owning a direct VM session) crashed while the
10535 * VM was Saved
10536 */
10537
10538 /// @todo (dmik)
10539 // Not sure that deleting the saved state file just because of the
10540 // client death before it attempted to restore the VM is a good
10541 // thing. But when it crashes we need to go to the Aborted state
10542 // which cannot have the saved state file associated... The only
10543 // way to fix this is to make the Aborted condition not a VM state
10544 // but a bool flag: i.e., when a crash occurs, set it to true and
10545 // change the state to PoweredOff or Saved depending on the
10546 // saved state presence.
10547
10548 deleteSavedState = true;
10549 mData->mCurrentStateModified = TRUE;
10550 stsFlags |= SaveSTS_CurStateModified;
10551 }
10552
10553 if (aMachineState == MachineState_Starting ||
10554 aMachineState == MachineState_Restoring)
10555 {
10556 /*
10557 * set the current state modified flag to indicate that the
10558 * current state is no more identical to the state in the
10559 * current snapshot
10560 */
10561 if (!mData->mCurrentSnapshot.isNull())
10562 {
10563 mData->mCurrentStateModified = TRUE;
10564 stsFlags |= SaveSTS_CurStateModified;
10565 }
10566 }
10567
10568 if (deleteSavedState == true)
10569 {
10570 Assert (!mSSData->mStateFilePath.isEmpty());
10571 RTFileDelete (Utf8Str (mSSData->mStateFilePath));
10572 mSSData->mStateFilePath.setNull();
10573 stsFlags |= SaveSTS_StateFilePath;
10574 }
10575
10576 /* redirect to the underlying peer machine */
10577 mPeer->setMachineState (aMachineState);
10578
10579 if (aMachineState == MachineState_PoweredOff ||
10580 aMachineState == MachineState_Aborted ||
10581 aMachineState == MachineState_Saved)
10582 {
10583 /* the machine has stopped execution
10584 * (or the saved state file was adopted) */
10585 stsFlags |= SaveSTS_StateTimeStamp;
10586 }
10587
10588 if ((oldMachineState == MachineState_PoweredOff ||
10589 oldMachineState == MachineState_Aborted) &&
10590 aMachineState == MachineState_Saved)
10591 {
10592 /* the saved state file was adopted */
10593 Assert (!mSSData->mStateFilePath.isNull());
10594 stsFlags |= SaveSTS_StateFilePath;
10595 }
10596
10597 rc = saveStateSettings (stsFlags);
10598
10599 if ((oldMachineState != MachineState_PoweredOff &&
10600 oldMachineState != MachineState_Aborted) &&
10601 (aMachineState == MachineState_PoweredOff ||
10602 aMachineState == MachineState_Aborted))
10603 {
10604 /*
10605 * clear differencing hard disks based on immutable hard disks
10606 * once we've been shut down for any reason
10607 */
10608 rc = wipeOutImmutableDiffs();
10609 }
10610
10611 LogFlowThisFunc (("rc=%08X\n", rc));
10612 LogFlowThisFuncLeave();
10613 return rc;
10614}
10615
10616/**
10617 * Sends the current machine state value to the VM process.
10618 *
10619 * @note Locks this object for reading, then calls a client process.
10620 */
10621HRESULT SessionMachine::updateMachineStateOnClient()
10622{
10623 AutoCaller autoCaller (this);
10624 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10625
10626 ComPtr <IInternalSessionControl> directControl;
10627 {
10628 AutoReaderLock alock (this);
10629 AssertReturn (!!mData, E_FAIL);
10630 directControl = mData->mSession.mDirectControl;
10631
10632 /* directControl may be already set to NULL here in #OnSessionEnd()
10633 * called too early by the direct session process while there is still
10634 * some operation (like discarding the snapshot) in progress. The client
10635 * process in this case is waiting inside Session::close() for the
10636 * "end session" process object to complete, while #uninit() called by
10637 * #checkForDeath() on the Watcher thread is waiting for the pending
10638 * operation to complete. For now, we accept this inconsitent behavior
10639 * and simply do nothing here. */
10640
10641 if (mData->mSession.mState == SessionState_SessionClosing)
10642 return S_OK;
10643
10644 AssertReturn (!directControl.isNull(), E_FAIL);
10645 }
10646
10647 return directControl->UpdateMachineState (mData->mMachineState);
10648}
10649
10650/* static */
10651DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD thread, void *pvUser)
10652{
10653 AssertReturn (pvUser, VERR_INVALID_POINTER);
10654
10655 Task *task = static_cast <Task *> (pvUser);
10656 task->handler();
10657
10658 // it's our responsibility to delete the task
10659 delete task;
10660
10661 return 0;
10662}
10663
10664/////////////////////////////////////////////////////////////////////////////
10665// SnapshotMachine class
10666/////////////////////////////////////////////////////////////////////////////
10667
10668DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
10669
10670HRESULT SnapshotMachine::FinalConstruct()
10671{
10672 LogFlowThisFunc (("\n"));
10673
10674 /* set the proper type to indicate we're the SnapshotMachine instance */
10675 unconst (mType) = IsSnapshotMachine;
10676
10677 return S_OK;
10678}
10679
10680void SnapshotMachine::FinalRelease()
10681{
10682 LogFlowThisFunc (("\n"));
10683
10684 uninit();
10685}
10686
10687/**
10688 * Initializes the SnapshotMachine object when taking a snapshot.
10689 *
10690 * @param aSessionMachine machine to take a snapshot from
10691 * @param aSnapshotId snapshot ID of this snapshot machine
10692 * @param aStateFilePath file where the execution state will be later saved
10693 * (or NULL for the offline snapshot)
10694 *
10695 * @note Locks aSessionMachine object for reading.
10696 */
10697HRESULT SnapshotMachine::init (SessionMachine *aSessionMachine,
10698 INPTR GUIDPARAM aSnapshotId,
10699 INPTR BSTR aStateFilePath)
10700{
10701 LogFlowThisFuncEnter();
10702 LogFlowThisFunc (("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
10703
10704 AssertReturn (aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
10705
10706 /* Enclose the state transition NotReady->InInit->Ready */
10707 AutoInitSpan autoInitSpan (this);
10708 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
10709
10710 mSnapshotId = aSnapshotId;
10711
10712 AutoReaderLock alock (aSessionMachine);
10713
10714 /* memorize the primary Machine instance (i.e. not SessionMachine!) */
10715 unconst (mPeer) = aSessionMachine->mPeer;
10716 /* share the parent pointer */
10717 unconst (mParent) = mPeer->mParent;
10718
10719 /* take the pointer to Data to share */
10720 mData.share (mPeer->mData);
10721 /*
10722 * take the pointer to UserData to share
10723 * (our UserData must always be the same as Machine's data)
10724 */
10725 mUserData.share (mPeer->mUserData);
10726 /* make a private copy of all other data (recent changes from SessionMachine) */
10727 mHWData.attachCopy (aSessionMachine->mHWData);
10728 mHDData.attachCopy (aSessionMachine->mHDData);
10729
10730 /* SSData is always unique for SnapshotMachine */
10731 mSSData.allocate();
10732 mSSData->mStateFilePath = aStateFilePath;
10733
10734 /*
10735 * create copies of all shared folders (mHWData after attiching a copy
10736 * contains just references to original objects)
10737 */
10738 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
10739 it != mHWData->mSharedFolders.end();
10740 ++ it)
10741 {
10742 ComObjPtr <SharedFolder> folder;
10743 folder.createObject();
10744 HRESULT rc = folder->initCopy (this, *it);
10745 CheckComRCReturnRC (rc);
10746 *it = folder;
10747 }
10748
10749 /* create all other child objects that will be immutable private copies */
10750
10751 unconst (mBIOSSettings).createObject();
10752 mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
10753
10754#ifdef VBOX_VRDP
10755 unconst (mVRDPServer).createObject();
10756 mVRDPServer->initCopy (this, mPeer->mVRDPServer);
10757#endif
10758
10759 unconst (mDVDDrive).createObject();
10760 mDVDDrive->initCopy (this, mPeer->mDVDDrive);
10761
10762 unconst (mFloppyDrive).createObject();
10763 mFloppyDrive->initCopy (this, mPeer->mFloppyDrive);
10764
10765 unconst (mAudioAdapter).createObject();
10766 mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
10767
10768 unconst (mUSBController).createObject();
10769 mUSBController->initCopy (this, mPeer->mUSBController);
10770
10771 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
10772 {
10773 unconst (mNetworkAdapters [slot]).createObject();
10774 mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
10775 }
10776
10777 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
10778 {
10779 unconst (mSerialPorts [slot]).createObject();
10780 mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
10781 }
10782
10783 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
10784 {
10785 unconst (mParallelPorts [slot]).createObject();
10786 mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
10787 }
10788
10789 /* Confirm a successful initialization when it's the case */
10790 autoInitSpan.setSucceeded();
10791
10792 LogFlowThisFuncLeave();
10793 return S_OK;
10794}
10795
10796/**
10797 * Initializes the SnapshotMachine object when loading from the settings file.
10798 *
10799 * @param aMachine machine the snapshot belngs to
10800 * @param aHWNode <Hardware> node
10801 * @param aHDAsNode <HardDiskAttachments> node
10802 * @param aSnapshotId snapshot ID of this snapshot machine
10803 * @param aStateFilePath file where the execution state is saved
10804 * (or NULL for the offline snapshot)
10805 *
10806 * @note Locks aMachine object for reading.
10807 */
10808HRESULT SnapshotMachine::init (Machine *aMachine, CFGNODE aHWNode, CFGNODE aHDAsNode,
10809 INPTR GUIDPARAM aSnapshotId, INPTR BSTR aStateFilePath)
10810{
10811 LogFlowThisFuncEnter();
10812 LogFlowThisFunc (("mName={%ls}\n", aMachine->mUserData->mName.raw()));
10813
10814 AssertReturn (aMachine && aHWNode && aHDAsNode && !Guid (aSnapshotId).isEmpty(),
10815 E_INVALIDARG);
10816
10817 /* Enclose the state transition NotReady->InInit->Ready */
10818 AutoInitSpan autoInitSpan (this);
10819 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
10820
10821 mSnapshotId = aSnapshotId;
10822
10823 AutoReaderLock alock (aMachine);
10824
10825 /* memorize the primary Machine instance */
10826 unconst (mPeer) = aMachine;
10827 /* share the parent pointer */
10828 unconst (mParent) = mPeer->mParent;
10829
10830 /* take the pointer to Data to share */
10831 mData.share (mPeer->mData);
10832 /*
10833 * take the pointer to UserData to share
10834 * (our UserData must always be the same as Machine's data)
10835 */
10836 mUserData.share (mPeer->mUserData);
10837 /* allocate private copies of all other data (will be loaded from settings) */
10838 mHWData.allocate();
10839 mHDData.allocate();
10840
10841 /* SSData is always unique for SnapshotMachine */
10842 mSSData.allocate();
10843 mSSData->mStateFilePath = aStateFilePath;
10844
10845 /* create all other child objects that will be immutable private copies */
10846
10847 unconst (mBIOSSettings).createObject();
10848 mBIOSSettings->init (this);
10849
10850#ifdef VBOX_VRDP
10851 unconst (mVRDPServer).createObject();
10852 mVRDPServer->init (this);
10853#endif
10854
10855 unconst (mDVDDrive).createObject();
10856 mDVDDrive->init (this);
10857
10858 unconst (mFloppyDrive).createObject();
10859 mFloppyDrive->init (this);
10860
10861 unconst (mAudioAdapter).createObject();
10862 mAudioAdapter->init (this);
10863
10864 unconst (mUSBController).createObject();
10865 mUSBController->init (this);
10866
10867 for (ULONG slot = 0; slot < ELEMENTS (mNetworkAdapters); slot ++)
10868 {
10869 unconst (mNetworkAdapters [slot]).createObject();
10870 mNetworkAdapters [slot]->init (this, slot);
10871 }
10872
10873 for (ULONG slot = 0; slot < ELEMENTS (mSerialPorts); slot ++)
10874 {
10875 unconst (mSerialPorts [slot]).createObject();
10876 mSerialPorts [slot]->init (this, slot);
10877 }
10878
10879 for (ULONG slot = 0; slot < ELEMENTS (mParallelPorts); slot ++)
10880 {
10881 unconst (mParallelPorts [slot]).createObject();
10882 mParallelPorts [slot]->init (this, slot);
10883 }
10884
10885 /* load hardware and harddisk settings */
10886
10887 HRESULT rc = loadHardware (aHWNode);
10888 if (SUCCEEDED (rc))
10889 rc = loadHardDisks (aHDAsNode, true /* aRegistered */, &mSnapshotId);
10890
10891 if (SUCCEEDED (rc))
10892 {
10893 /* commit all changes made during the initialization */
10894 commit();
10895 }
10896
10897 /* Confirm a successful initialization when it's the case */
10898 if (SUCCEEDED (rc))
10899 autoInitSpan.setSucceeded();
10900
10901 LogFlowThisFuncLeave();
10902 return rc;
10903}
10904
10905/**
10906 * Uninitializes this SnapshotMachine object.
10907 */
10908void SnapshotMachine::uninit()
10909{
10910 LogFlowThisFuncEnter();
10911
10912 /* Enclose the state transition Ready->InUninit->NotReady */
10913 AutoUninitSpan autoUninitSpan (this);
10914 if (autoUninitSpan.uninitDone())
10915 return;
10916
10917 uninitDataAndChildObjects();
10918
10919 /* free the essential data structure last */
10920 mData.free();
10921
10922 unconst (mParent).setNull();
10923 unconst (mPeer).setNull();
10924
10925 LogFlowThisFuncLeave();
10926}
10927
10928// AutoLock::Lockable interface
10929////////////////////////////////////////////////////////////////////////////////
10930
10931/**
10932 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
10933 * with the primary Machine instance (mPeer).
10934 */
10935AutoLock::Handle *SnapshotMachine::lockHandle() const
10936{
10937 AssertReturn (!mPeer.isNull(), NULL);
10938 return mPeer->lockHandle();
10939}
10940
10941// public methods only for internal purposes
10942////////////////////////////////////////////////////////////////////////////////
10943
10944/**
10945 * Called by the snapshot object associated with this SnapshotMachine when
10946 * snapshot data such as name or description is changed.
10947 *
10948 * @note Locks this object for writing.
10949 */
10950HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
10951{
10952 AutoLock alock (this);
10953
10954 mPeer->saveSnapshotSettings (aSnapshot, SaveSS_UpdateAttrsOp);
10955
10956 /* inform callbacks */
10957 mParent->onSnapshotChange (mData->mUuid, aSnapshot->data().mId);
10958
10959 return S_OK;
10960}
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