VirtualBox

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

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

Main: make backups before overwriting old XML files; remove obsolete source file

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

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