VirtualBox

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

Last change on this file since 18158 was 18158, checked in by vboxsync, 16 years ago

fixed SEGFAULT in MachineImpl::rollback()

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