VirtualBox

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

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

medium: rename default IDE/FD storage controller names to IDE Controller and Floppy Controller

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