VirtualBox

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

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

valgrind warning

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

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