VirtualBox

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

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

FE/Qt4: Adapting 'New VM Wizard' & 'First Run Wizard' to new [Main] capabilities (multi-storage controllers support); Updating tool-tip for null medium items.

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