VirtualBox

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

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

Main & FEs: 3002: GUI/Main enhancements for 64 bits guests implemented.

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

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