VirtualBox

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

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

Main: Shared Folders (#2130):

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

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