VirtualBox

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

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

Main: cleanup: merge VirtualBoxBase{WithTypedChildren}NEXT onto VirtualBoxBase{WithTypedChildren}, adjust Host and Snapshot implementations according to new parents (new locking scheme)

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

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