VirtualBox

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

Last change on this file since 5755 was 5755, checked in by vboxsync, 18 years ago

Added new type of network device 'e1000'. Define VBOX_WITH_E1000 to activate.

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

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