VirtualBox

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

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

HostServices/GuestProperties and Main (Guest Properties): use the new iprt pattern matching APIs

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

© 2023 Oracle
ContactPrivacy policyTerms of Use