VirtualBox

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

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

Main (Guest Properties): another NULL pointer check

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

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