VirtualBox

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

Last change on this file since 16558 was 16558, checked in by vboxsync, 15 years ago

Main: back out r42503

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

© 2023 Oracle
ContactPrivacy policyTerms of Use