VirtualBox

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

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

Main: #3312: Fixed a wrong assertion in USBController triggered on a failure to revert to the current snapshot (hidden regression after rewriting USB support for Windows)

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