VirtualBox

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

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

Main: Better result codes for VIrtualBox::createMachine(), documentation. Assgined new UUIDs to changed interfaces (important!).

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