VirtualBox

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

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

Main: lock media correctly after creating an online snapshot (previously the new diff image was not locked for writing if the machine was running)

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

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