VirtualBox

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

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

Main: rip XML classes out of settings code and move them to independent files and new vboxxml namespace to make them useful outside of the settings context

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