VirtualBox

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

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

build fix (msc doesn't like #ifdefs inside macro parameter lists).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 337.0 KB
Line 
1/* $Id: MachineImpl.cpp 22786 2009-09-04 14:31:30Z 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"),
2141 mData->mMachineState);
2142
2143 /* Check for an existing controller. */
2144 ComObjPtr<StorageController> ctl;
2145 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
2146 CheckComRCReturnRC(rc);
2147
2148 /* check that the port and device are not out of range. */
2149 ULONG portCount;
2150 ULONG devicesPerPort;
2151 rc = ctl->COMGETTER(PortCount)(&portCount);
2152 CheckComRCReturnRC(rc);
2153 rc = ctl->COMGETTER(MaxDevicesPerPortCount)(&devicesPerPort);
2154 CheckComRCReturnRC(rc);
2155
2156 if ( (aControllerPort < 0)
2157 || (aControllerPort >= (LONG)portCount)
2158 || (aDevice < 0)
2159 || (aDevice >= (LONG)devicesPerPort)
2160 )
2161 return setError(E_INVALIDARG,
2162 tr("The port and/or count parameter are out of range [%lu:%lu]"),
2163 portCount,
2164 devicesPerPort);
2165
2166 /* check if the device slot is already busy */
2167 HDData::AttachmentList::const_iterator it = std::find_if(mHDData->mAttachments.begin(),
2168 mHDData->mAttachments.end(),
2169 HardDiskAttachment::EqualsTo(aControllerName, aControllerPort, aDevice));
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
3121 if (SUCCEEDED(rc))
3122 {
3123 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3124 it != mHWData->mGuestProperties.end();
3125 ++it)
3126 if (it->strName == utf8Name)
3127 {
3128 property = *it;
3129 if (it->mFlags & (RDONLYHOST))
3130 rc = setError(E_ACCESSDENIED,
3131 tr("The property '%ls' cannot be changed by the host"),
3132 aName);
3133 else
3134 {
3135 mHWData.backup();
3136 /* The backup() operation invalidates our iterator, so
3137 * get a new one. */
3138 for (it = mHWData->mGuestProperties.begin();
3139 it->strName != utf8Name;
3140 ++it)
3141 ;
3142 mHWData->mGuestProperties.erase (it);
3143 }
3144 found = true;
3145 break;
3146 }
3147 }
3148 if (found && SUCCEEDED(rc))
3149 {
3150 if (*aValue)
3151 {
3152 RTTIMESPEC time;
3153 property.strValue = aValue;
3154 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3155 if (aFlags != NULL)
3156 property.mFlags = fFlags;
3157 mHWData->mGuestProperties.push_back (property);
3158 }
3159 }
3160 else if (SUCCEEDED(rc) && *aValue)
3161 {
3162 RTTIMESPEC time;
3163 mHWData.backup();
3164 property.strName = aName;
3165 property.strValue = aValue;
3166 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3167 property.mFlags = fFlags;
3168 mHWData->mGuestProperties.push_back (property);
3169 }
3170 if ( SUCCEEDED(rc)
3171 && ( matchAll
3172 || RTStrSimplePatternMultiMatch (utf8Patterns.raw(), RTSTR_MAX,
3173 utf8Name.raw(), RTSTR_MAX, NULL)
3174 )
3175 )
3176 mParent->onGuestPropertyChange (mData->mUuid, aName, aValue, aFlags);
3177 }
3178 else
3179 {
3180 ComPtr<IInternalSessionControl> directControl =
3181 mData->mSession.mDirectControl;
3182
3183 /* just be on the safe side when calling another process */
3184 alock.leave();
3185
3186 BSTR dummy = NULL;
3187 ULONG64 dummy64;
3188 if (!directControl)
3189 rc = E_FAIL;
3190 else
3191 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
3192 true /* isSetter */,
3193 &dummy, &dummy64, &dummy);
3194 }
3195 }
3196 catch (std::bad_alloc &)
3197 {
3198 rc = E_OUTOFMEMORY;
3199 }
3200
3201 return rc;
3202#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3203}
3204
3205STDMETHODIMP Machine::SetGuestPropertyValue (IN_BSTR aName, IN_BSTR aValue)
3206{
3207 return SetGuestProperty (aName, aValue, NULL);
3208}
3209
3210STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
3211 ComSafeArrayOut(BSTR, aNames),
3212 ComSafeArrayOut(BSTR, aValues),
3213 ComSafeArrayOut(ULONG64, aTimestamps),
3214 ComSafeArrayOut(BSTR, aFlags))
3215{
3216#if !defined (VBOX_WITH_GUEST_PROPS)
3217 ReturnComNotImplemented();
3218#else
3219 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
3220 return E_POINTER;
3221
3222 CheckComArgOutSafeArrayPointerValid(aNames);
3223 CheckComArgOutSafeArrayPointerValid(aValues);
3224 CheckComArgOutSafeArrayPointerValid(aTimestamps);
3225 CheckComArgOutSafeArrayPointerValid(aFlags);
3226
3227 AutoCaller autoCaller(this);
3228 CheckComRCReturnRC(autoCaller.rc());
3229
3230 AutoReadLock alock(this);
3231
3232 using namespace guestProp;
3233 HRESULT rc = E_FAIL;
3234
3235 Utf8Str strPatterns(aPatterns);
3236
3237 bool matchAll = false;
3238 if ((NULL == aPatterns) || (0 == aPatterns[0]))
3239 matchAll = true;
3240 if (!mHWData->mPropertyServiceActive)
3241 {
3242
3243 /*
3244 * Look for matching patterns and build up a list.
3245 */
3246 HWData::GuestPropertyList propList;
3247 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3248 it != mHWData->mGuestProperties.end();
3249 ++it)
3250 if ( matchAll
3251 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
3252 RTSTR_MAX,
3253 it->strName.raw(),
3254 RTSTR_MAX, NULL)
3255 )
3256 propList.push_back(*it);
3257
3258 /*
3259 * And build up the arrays for returning the property information.
3260 */
3261 size_t cEntries = propList.size();
3262 SafeArray<BSTR> names (cEntries);
3263 SafeArray<BSTR> values (cEntries);
3264 SafeArray<ULONG64> timestamps (cEntries);
3265 SafeArray<BSTR> flags (cEntries);
3266 size_t iProp = 0;
3267 for (HWData::GuestPropertyList::iterator it = propList.begin();
3268 it != propList.end();
3269 ++it)
3270 {
3271 char szFlags[MAX_FLAGS_LEN + 1];
3272 it->strName.cloneTo(&names[iProp]);
3273 it->strValue.cloneTo(&values[iProp]);
3274 timestamps[iProp] = it->mTimestamp;
3275 writeFlags(it->mFlags, szFlags);
3276 Bstr(szFlags).cloneTo(&flags[iProp]);
3277 ++iProp;
3278 }
3279 names.detachTo(ComSafeArrayOutArg(aNames));
3280 values.detachTo(ComSafeArrayOutArg(aValues));
3281 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
3282 flags.detachTo(ComSafeArrayOutArg(aFlags));
3283 rc = S_OK;
3284 }
3285 else
3286 {
3287 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
3288
3289 /* just be on the safe side when calling another process */
3290 alock.unlock();
3291
3292 if (!directControl)
3293 rc = E_FAIL;
3294 else
3295 rc = directControl->EnumerateGuestProperties(aPatterns,
3296 ComSafeArrayOutArg(aNames),
3297 ComSafeArrayOutArg(aValues),
3298 ComSafeArrayOutArg(aTimestamps),
3299 ComSafeArrayOutArg(aFlags));
3300 }
3301 return rc;
3302#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3303}
3304
3305STDMETHODIMP Machine:: GetHardDiskAttachmentsOfController(IN_BSTR aName,
3306 ComSafeArrayOut(IHardDiskAttachment*, aAttachments))
3307{
3308 HDData::AttachmentList atts;
3309
3310 HRESULT rc = getHardDiskAttachmentsOfController(aName, atts);
3311 CheckComRCReturnRC(rc);
3312
3313 SafeIfaceArray<IHardDiskAttachment> attachments (atts);
3314 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
3315
3316 return S_OK;
3317}
3318
3319STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
3320 StorageBus_T aConnectionType,
3321 IStorageController **controller)
3322{
3323 CheckComArgStrNotEmptyOrNull(aName);
3324
3325 if ( (aConnectionType <= StorageBus_Null)
3326 || (aConnectionType > StorageBus_SCSI))
3327 return setError (E_INVALIDARG,
3328 tr ("Invalid connection type: %d"),
3329 aConnectionType);
3330
3331 AutoCaller autoCaller(this);
3332 CheckComRCReturnRC(autoCaller.rc());
3333
3334 AutoWriteLock alock(this);
3335
3336 HRESULT rc = checkStateDependency(MutableStateDep);
3337 CheckComRCReturnRC(rc);
3338
3339 /* try to find one with the name first. */
3340 ComObjPtr<StorageController> ctrl;
3341
3342 rc = getStorageControllerByName (aName, ctrl, false /* aSetError */);
3343 if (SUCCEEDED(rc))
3344 return setError (VBOX_E_OBJECT_IN_USE,
3345 tr ("Storage controller named '%ls' already exists"), aName);
3346
3347 ctrl.createObject();
3348 rc = ctrl->init (this, aName, aConnectionType);
3349 CheckComRCReturnRC(rc);
3350
3351 mStorageControllers.backup();
3352 mStorageControllers->push_back (ctrl);
3353
3354 ctrl.queryInterfaceTo(controller);
3355
3356 /* inform the direct session if any */
3357 alock.leave();
3358 onStorageControllerChange();
3359
3360 return S_OK;
3361}
3362
3363STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
3364 IStorageController **aStorageController)
3365{
3366 CheckComArgStrNotEmptyOrNull(aName);
3367
3368 AutoCaller autoCaller(this);
3369 CheckComRCReturnRC(autoCaller.rc());
3370
3371 AutoReadLock alock(this);
3372
3373 ComObjPtr<StorageController> ctrl;
3374
3375 HRESULT rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3376 if (SUCCEEDED(rc))
3377 ctrl.queryInterfaceTo(aStorageController);
3378
3379 return rc;
3380}
3381
3382STDMETHODIMP Machine::
3383RemoveStorageController(IN_BSTR aName)
3384{
3385 CheckComArgStrNotEmptyOrNull(aName);
3386
3387 AutoCaller autoCaller(this);
3388 CheckComRCReturnRC(autoCaller.rc());
3389
3390 AutoWriteLock alock(this);
3391
3392 HRESULT rc = checkStateDependency(MutableStateDep);
3393 CheckComRCReturnRC(rc);
3394
3395 ComObjPtr<StorageController> ctrl;
3396 rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3397 CheckComRCReturnRC(rc);
3398
3399 /* We can remove the controller only if there is no device attached. */
3400 /* check if the device slot is already busy */
3401 for (HDData::AttachmentList::const_iterator
3402 it = mHDData->mAttachments.begin();
3403 it != mHDData->mAttachments.end();
3404 ++ it)
3405 {
3406 if (it != mHDData->mAttachments.end())
3407 {
3408 if ((*it)->controller() == aName)
3409 return setError(VBOX_E_OBJECT_IN_USE,
3410 tr("Storage controller named '%ls' has still devices attached"),
3411 aName);
3412 }
3413 }
3414
3415 /* We can remove it now. */
3416 mStorageControllers.backup();
3417
3418 ctrl->unshare();
3419
3420 mStorageControllers->remove (ctrl);
3421
3422 /* inform the direct session if any */
3423 alock.leave();
3424 onStorageControllerChange();
3425
3426 return S_OK;
3427}
3428
3429// public methods for internal purposes
3430/////////////////////////////////////////////////////////////////////////////
3431
3432/**
3433 * Saves the registry entry of this machine to the given configuration node.
3434 *
3435 * @param aEntryNode Node to save the registry entry to.
3436 *
3437 * @note locks this object for reading.
3438 */
3439HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
3440{
3441 AutoLimitedCaller autoCaller(this);
3442 AssertComRCReturnRC(autoCaller.rc());
3443
3444 AutoReadLock alock(this);
3445
3446 data.uuid = mData->mUuid;
3447 data.strSettingsFile = mData->m_strConfigFile;
3448
3449 return S_OK;
3450}
3451
3452/**
3453 * Calculates the absolute path of the given path taking the directory of the
3454 * machine settings file as the current directory.
3455 *
3456 * @param aPath Path to calculate the absolute path for.
3457 * @param aResult Where to put the result (used only on success, can be the
3458 * same Utf8Str instance as passed in @a aPath).
3459 * @return IPRT result.
3460 *
3461 * @note Locks this object for reading.
3462 */
3463int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3464{
3465 AutoCaller autoCaller(this);
3466 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3467
3468 AutoReadLock alock(this);
3469
3470 AssertReturn (!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
3471
3472 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
3473
3474 strSettingsDir.stripFilename();
3475 char folder[RTPATH_MAX];
3476 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3477 if (RT_SUCCESS(vrc))
3478 aResult = folder;
3479
3480 return vrc;
3481}
3482
3483/**
3484 * Tries to calculate the relative path of the given absolute path using the
3485 * directory of the machine settings file as the base directory.
3486 *
3487 * @param aPath Absolute path to calculate the relative path for.
3488 * @param aResult Where to put the result (used only when it's possible to
3489 * make a relative path from the given absolute path; otherwise
3490 * left untouched).
3491 *
3492 * @note Locks this object for reading.
3493 */
3494void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3495{
3496 AutoCaller autoCaller(this);
3497 AssertComRCReturn (autoCaller.rc(), (void) 0);
3498
3499 AutoReadLock alock(this);
3500
3501 AssertReturnVoid (!mData->m_strConfigFileFull.isEmpty());
3502
3503 Utf8Str settingsDir = mData->m_strConfigFileFull;
3504
3505 settingsDir.stripFilename();
3506 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3507 {
3508 /* when assigning, we create a separate Utf8Str instance because both
3509 * aPath and aResult can point to the same memory location when this
3510 * func is called (if we just do aResult = aPath, aResult will be freed
3511 * first, and since its the same as aPath, an attempt to copy garbage
3512 * will be made. */
3513 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3514 }
3515}
3516
3517/**
3518 * Returns the full path to the machine's log folder in the
3519 * \a aLogFolder argument.
3520 */
3521void Machine::getLogFolder (Utf8Str &aLogFolder)
3522{
3523 AutoCaller autoCaller(this);
3524 AssertComRCReturnVoid (autoCaller.rc());
3525
3526 AutoReadLock alock(this);
3527
3528 Utf8Str settingsDir;
3529 if (isInOwnDir (&settingsDir))
3530 {
3531 /* Log folder is <Machines>/<VM_Name>/Logs */
3532 aLogFolder = Utf8StrFmt ("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
3533 }
3534 else
3535 {
3536 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
3537 Assert (!mUserData->mSnapshotFolderFull.isEmpty());
3538 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
3539 RTPATH_DELIMITER);
3540 }
3541}
3542
3543/**
3544 * @note Locks this object for writing, calls the client process (outside the
3545 * lock).
3546 */
3547HRESULT Machine::openSession(IInternalSessionControl *aControl)
3548{
3549 LogFlowThisFuncEnter();
3550
3551 AssertReturn(aControl, E_FAIL);
3552
3553 AutoCaller autoCaller(this);
3554 CheckComRCReturnRC(autoCaller.rc());
3555
3556 AutoWriteLock alock(this);
3557
3558 if (!mData->mRegistered)
3559 return setError(E_UNEXPECTED,
3560 tr("The machine '%ls' is not registered"),
3561 mUserData->mName.raw());
3562
3563 LogFlowThisFunc(("mSession.mState=%d\n", mData->mSession.mState));
3564
3565 if (mData->mSession.mState == SessionState_Open ||
3566 mData->mSession.mState == SessionState_Closing)
3567 return setError(VBOX_E_INVALID_OBJECT_STATE,
3568 tr("A session for the machine '%ls' is currently open (or being closed)"),
3569 mUserData->mName.raw());
3570
3571 /* may not be busy */
3572 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
3573
3574 /* get the session PID */
3575 RTPROCESS pid = NIL_RTPROCESS;
3576 AssertCompile (sizeof (ULONG) == sizeof (RTPROCESS));
3577 aControl->GetPID ((ULONG *) &pid);
3578 Assert (pid != NIL_RTPROCESS);
3579
3580 if (mData->mSession.mState == SessionState_Spawning)
3581 {
3582 /* This machine is awaiting for a spawning session to be opened, so
3583 * reject any other open attempts from processes other than one
3584 * started by #openRemoteSession(). */
3585
3586 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
3587 mData->mSession.mPid, mData->mSession.mPid));
3588 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3589
3590 if (mData->mSession.mPid != pid)
3591 return setError(E_ACCESSDENIED,
3592 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
3593 "session with the machine named '%ls', while only a process "
3594 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
3595 pid, mUserData->mName.raw(), mData->mSession.mPid);
3596 }
3597
3598 /* create a SessionMachine object */
3599 ComObjPtr<SessionMachine> sessionMachine;
3600 sessionMachine.createObject();
3601 HRESULT rc = sessionMachine->init (this);
3602 AssertComRC (rc);
3603
3604 /* NOTE: doing return from this function after this point but
3605 * before the end is forbidden since it may call SessionMachine::uninit()
3606 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3607 * lock while still holding the Machine lock in alock so that a deadlock
3608 * is possible due to the wrong lock order. */
3609
3610 if (SUCCEEDED(rc))
3611 {
3612#ifdef VBOX_WITH_RESOURCE_USAGE_API
3613 registerMetrics (mParent->performanceCollector(), this, pid);
3614#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3615
3616 /*
3617 * Set the session state to Spawning to protect against subsequent
3618 * attempts to open a session and to unregister the machine after
3619 * we leave the lock.
3620 */
3621 SessionState_T origState = mData->mSession.mState;
3622 mData->mSession.mState = SessionState_Spawning;
3623
3624 /*
3625 * Leave the lock before calling the client process -- it will call
3626 * Machine/SessionMachine methods. Leaving the lock here is quite safe
3627 * because the state is Spawning, so that openRemotesession() and
3628 * openExistingSession() calls will fail. This method, called before we
3629 * enter the lock again, will fail because of the wrong PID.
3630 *
3631 * Note that mData->mSession.mRemoteControls accessed outside
3632 * the lock may not be modified when state is Spawning, so it's safe.
3633 */
3634 alock.leave();
3635
3636 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3637 rc = aControl->AssignMachine (sessionMachine);
3638 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3639
3640 /* The failure may occur w/o any error info (from RPC), so provide one */
3641 if (FAILED (rc))
3642 setError (VBOX_E_VM_ERROR,
3643 tr ("Failed to assign the machine to the session (%Rrc)"), rc);
3644
3645 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
3646 {
3647 /* complete the remote session initialization */
3648
3649 /* get the console from the direct session */
3650 ComPtr<IConsole> console;
3651 rc = aControl->GetRemoteConsole (console.asOutParam());
3652 ComAssertComRC (rc);
3653
3654 if (SUCCEEDED(rc) && !console)
3655 {
3656 ComAssert (!!console);
3657 rc = E_FAIL;
3658 }
3659
3660 /* assign machine & console to the remote session */
3661 if (SUCCEEDED(rc))
3662 {
3663 /*
3664 * after openRemoteSession(), the first and the only
3665 * entry in remoteControls is that remote session
3666 */
3667 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3668 rc = mData->mSession.mRemoteControls.front()->
3669 AssignRemoteMachine (sessionMachine, console);
3670 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3671
3672 /* The failure may occur w/o any error info (from RPC), so provide one */
3673 if (FAILED(rc))
3674 setError(VBOX_E_VM_ERROR,
3675 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3676 }
3677
3678 if (FAILED(rc))
3679 aControl->Uninitialize();
3680 }
3681
3682 /* enter the lock again */
3683 alock.enter();
3684
3685 /* Restore the session state */
3686 mData->mSession.mState = origState;
3687 }
3688
3689 /* finalize spawning anyway (this is why we don't return on errors above) */
3690 if (mData->mSession.mState == SessionState_Spawning)
3691 {
3692 /* Note that the progress object is finalized later */
3693
3694 /* We don't reset mSession.mPid here because it is necessary for
3695 * SessionMachine::uninit() to reap the child process later. */
3696
3697 if (FAILED(rc))
3698 {
3699 /* Close the remote session, remove the remote control from the list
3700 * and reset session state to Closed (@note keep the code in sync
3701 * with the relevant part in openSession()). */
3702
3703 Assert (mData->mSession.mRemoteControls.size() == 1);
3704 if (mData->mSession.mRemoteControls.size() == 1)
3705 {
3706 ErrorInfoKeeper eik;
3707 mData->mSession.mRemoteControls.front()->Uninitialize();
3708 }
3709
3710 mData->mSession.mRemoteControls.clear();
3711 mData->mSession.mState = SessionState_Closed;
3712 }
3713 }
3714 else
3715 {
3716 /* memorize PID of the directly opened session */
3717 if (SUCCEEDED(rc))
3718 mData->mSession.mPid = pid;
3719 }
3720
3721 if (SUCCEEDED(rc))
3722 {
3723 /* memorize the direct session control and cache IUnknown for it */
3724 mData->mSession.mDirectControl = aControl;
3725 mData->mSession.mState = SessionState_Open;
3726 /* associate the SessionMachine with this Machine */
3727 mData->mSession.mMachine = sessionMachine;
3728
3729 /* request an IUnknown pointer early from the remote party for later
3730 * identity checks (it will be internally cached within mDirectControl
3731 * at least on XPCOM) */
3732 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3733 NOREF (unk);
3734 }
3735
3736 if (mData->mSession.mProgress)
3737 {
3738 /* finalize the progress after setting the state, for consistency */
3739 mData->mSession.mProgress->notifyComplete (rc);
3740 mData->mSession.mProgress.setNull();
3741 }
3742
3743 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
3744 * would break the lock order */
3745 alock.leave();
3746
3747 /* uninitialize the created session machine on failure */
3748 if (FAILED(rc))
3749 sessionMachine->uninit();
3750
3751 LogFlowThisFunc(("rc=%08X\n", rc));
3752 LogFlowThisFuncLeave();
3753 return rc;
3754}
3755
3756/**
3757 * @note Locks this object for writing, calls the client process
3758 * (inside the lock).
3759 */
3760HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
3761 IN_BSTR aType,
3762 IN_BSTR aEnvironment,
3763 Progress *aProgress)
3764{
3765 LogFlowThisFuncEnter();
3766
3767 AssertReturn(aControl, E_FAIL);
3768 AssertReturn(aProgress, E_FAIL);
3769
3770 AutoCaller autoCaller(this);
3771 CheckComRCReturnRC(autoCaller.rc());
3772
3773 AutoWriteLock alock(this);
3774
3775 if (!mData->mRegistered)
3776 return setError(E_UNEXPECTED,
3777 tr("The machine '%ls' is not registered"),
3778 mUserData->mName.raw());
3779
3780 LogFlowThisFunc(("mSession.mState=%d\n", mData->mSession.mState));
3781
3782 if (mData->mSession.mState == SessionState_Open ||
3783 mData->mSession.mState == SessionState_Spawning ||
3784 mData->mSession.mState == SessionState_Closing)
3785 return setError(VBOX_E_INVALID_OBJECT_STATE,
3786 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
3787 mUserData->mName.raw());
3788
3789 /* may not be busy */
3790 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
3791
3792 /* get the path to the executable */
3793 char szPath[RTPATH_MAX];
3794 RTPathAppPrivateArch(szPath, RTPATH_MAX);
3795 size_t sz = strlen(szPath);
3796 szPath[sz++] = RTPATH_DELIMITER;
3797 szPath[sz] = 0;
3798 char *cmd = szPath + sz;
3799 sz = RTPATH_MAX - sz;
3800
3801 int vrc = VINF_SUCCESS;
3802 RTPROCESS pid = NIL_RTPROCESS;
3803
3804 RTENV env = RTENV_DEFAULT;
3805
3806 if (aEnvironment != NULL && *aEnvironment)
3807 {
3808 char *newEnvStr = NULL;
3809
3810 do
3811 {
3812 /* clone the current environment */
3813 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
3814 AssertRCBreakStmt(vrc2, vrc = vrc2);
3815
3816 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
3817 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
3818
3819 /* put new variables to the environment
3820 * (ignore empty variable names here since RTEnv API
3821 * intentionally doesn't do that) */
3822 char *var = newEnvStr;
3823 for (char *p = newEnvStr; *p; ++ p)
3824 {
3825 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
3826 {
3827 *p = '\0';
3828 if (*var)
3829 {
3830 char *val = strchr (var, '=');
3831 if (val)
3832 {
3833 *val++ = '\0';
3834 vrc2 = RTEnvSetEx (env, var, val);
3835 }
3836 else
3837 vrc2 = RTEnvUnsetEx (env, var);
3838 if (RT_FAILURE(vrc2))
3839 break;
3840 }
3841 var = p + 1;
3842 }
3843 }
3844 if (RT_SUCCESS(vrc2) && *var)
3845 vrc2 = RTEnvPutEx (env, var);
3846
3847 AssertRCBreakStmt (vrc2, vrc = vrc2);
3848 }
3849 while (0);
3850
3851 if (newEnvStr != NULL)
3852 RTStrFree(newEnvStr);
3853 }
3854
3855 Bstr type (aType);
3856
3857 /* Qt is default */
3858#ifdef VBOX_WITH_QTGUI
3859 if (type == "gui" || type == "GUI/Qt")
3860 {
3861# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
3862 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
3863# else
3864 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
3865# endif
3866 Assert (sz >= sizeof (VirtualBox_exe));
3867 strcpy (cmd, VirtualBox_exe);
3868
3869 Utf8Str idStr = mData->mUuid.toString();
3870# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
3871 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
3872# else
3873 Utf8Str name = mUserData->mName;
3874 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
3875# endif
3876 vrc = RTProcCreate(szPath, args, env, 0, &pid);
3877 }
3878#else /* !VBOX_WITH_QTGUI */
3879 if (0)
3880 ;
3881#endif /* VBOX_WITH_QTGUI */
3882
3883 else
3884
3885#ifdef VBOX_WITH_VBOXSDL
3886 if (type == "sdl" || type == "GUI/SDL")
3887 {
3888 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
3889 Assert (sz >= sizeof (VBoxSDL_exe));
3890 strcpy (cmd, VBoxSDL_exe);
3891
3892 Utf8Str idStr = mData->mUuid.toString();
3893# ifdef RT_OS_WINDOWS
3894 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
3895# else
3896 Utf8Str name = mUserData->mName;
3897 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
3898# endif
3899 vrc = RTProcCreate(szPath, args, env, 0, &pid);
3900 }
3901#else /* !VBOX_WITH_VBOXSDL */
3902 if (0)
3903 ;
3904#endif /* !VBOX_WITH_VBOXSDL */
3905
3906 else
3907
3908#ifdef VBOX_WITH_HEADLESS
3909 if ( type == "headless"
3910 || type == "capture"
3911#ifdef VBOX_WITH_VRDP
3912 || type == "vrdp"
3913#endif
3914 )
3915 {
3916 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
3917 Assert (sz >= sizeof (VBoxHeadless_exe));
3918 strcpy (cmd, VBoxHeadless_exe);
3919
3920 Utf8Str idStr = mData->mUuid.toString();
3921 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
3922# ifdef RT_OS_WINDOWS
3923 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
3924# else
3925 Utf8Str name = mUserData->mName;
3926 const char * args[] ={szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
3927# endif
3928#ifdef VBOX_WITH_VRDP
3929 if (type == "headless")
3930 {
3931 unsigned pos = RT_ELEMENTS(args) - 3;
3932 args[pos++] = "--vrdp";
3933 args[pos] = "off";
3934 }
3935#endif
3936 if (type == "capture")
3937 {
3938 unsigned pos = RT_ELEMENTS(args) - 3;
3939 args[pos] = "--capture";
3940 }
3941 vrc = RTProcCreate(szPath, args, env, 0, &pid);
3942 }
3943#else /* !VBOX_WITH_HEADLESS */
3944 if (0)
3945 ;
3946#endif /* !VBOX_WITH_HEADLESS */
3947 else
3948 {
3949 RTEnvDestroy (env);
3950 return setError (E_INVALIDARG,
3951 tr ("Invalid session type: '%ls'"), aType);
3952 }
3953
3954 RTEnvDestroy (env);
3955
3956 if (RT_FAILURE(vrc))
3957 return setError (VBOX_E_IPRT_ERROR,
3958 tr ("Could not launch a process for the machine '%ls' (%Rrc)"),
3959 mUserData->mName.raw(), vrc);
3960
3961 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
3962
3963 /*
3964 * Note that we don't leave the lock here before calling the client,
3965 * because it doesn't need to call us back if called with a NULL argument.
3966 * Leaving the lock herer is dangerous because we didn't prepare the
3967 * launch data yet, but the client we've just started may happen to be
3968 * too fast and call openSession() that will fail (because of PID, etc.),
3969 * so that the Machine will never get out of the Spawning session state.
3970 */
3971
3972 /* inform the session that it will be a remote one */
3973 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
3974 HRESULT rc = aControl->AssignMachine (NULL);
3975 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
3976
3977 if (FAILED(rc))
3978 {
3979 /* restore the session state */
3980 mData->mSession.mState = SessionState_Closed;
3981 /* The failure may occur w/o any error info (from RPC), so provide one */
3982 return setError(VBOX_E_VM_ERROR,
3983 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3984 }
3985
3986 /* attach launch data to the machine */
3987 Assert (mData->mSession.mPid == NIL_RTPROCESS);
3988 mData->mSession.mRemoteControls.push_back (aControl);
3989 mData->mSession.mProgress = aProgress;
3990 mData->mSession.mPid = pid;
3991 mData->mSession.mState = SessionState_Spawning;
3992 mData->mSession.mType = type;
3993
3994 LogFlowThisFuncLeave();
3995 return S_OK;
3996}
3997
3998/**
3999 * @note Locks this object for writing, calls the client process
4000 * (outside the lock).
4001 */
4002HRESULT Machine::openExistingSession (IInternalSessionControl *aControl)
4003{
4004 LogFlowThisFuncEnter();
4005
4006 AssertReturn(aControl, E_FAIL);
4007
4008 AutoCaller autoCaller(this);
4009 CheckComRCReturnRC(autoCaller.rc());
4010
4011 AutoWriteLock alock(this);
4012
4013 if (!mData->mRegistered)
4014 return setError (E_UNEXPECTED,
4015 tr ("The machine '%ls' is not registered"), mUserData->mName.raw());
4016
4017 LogFlowThisFunc(("mSession.state=%d\n", mData->mSession.mState));
4018
4019 if (mData->mSession.mState != SessionState_Open)
4020 return setError (VBOX_E_INVALID_SESSION_STATE,
4021 tr ("The machine '%ls' does not have an open session"),
4022 mUserData->mName.raw());
4023
4024 ComAssertRet (!mData->mSession.mDirectControl.isNull(), E_FAIL);
4025
4026 /*
4027 * Get the console from the direct session (note that we don't leave the
4028 * lock here because GetRemoteConsole must not call us back).
4029 */
4030 ComPtr<IConsole> console;
4031 HRESULT rc = mData->mSession.mDirectControl->
4032 GetRemoteConsole (console.asOutParam());
4033 if (FAILED (rc))
4034 {
4035 /* The failure may occur w/o any error info (from RPC), so provide one */
4036 return setError (VBOX_E_VM_ERROR,
4037 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
4038 }
4039
4040 ComAssertRet (!console.isNull(), E_FAIL);
4041
4042 ComObjPtr<SessionMachine> sessionMachine = mData->mSession.mMachine;
4043 AssertReturn(!sessionMachine.isNull(), E_FAIL);
4044
4045 /*
4046 * Leave the lock before calling the client process. It's safe here
4047 * since the only thing to do after we get the lock again is to add
4048 * the remote control to the list (which doesn't directly influence
4049 * anything).
4050 */
4051 alock.leave();
4052
4053 /* attach the remote session to the machine */
4054 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4055 rc = aControl->AssignRemoteMachine (sessionMachine, console);
4056 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4057
4058 /* The failure may occur w/o any error info (from RPC), so provide one */
4059 if (FAILED(rc))
4060 return setError(VBOX_E_VM_ERROR,
4061 tr("Failed to assign the machine to the session (%Rrc)"),
4062 rc);
4063
4064 alock.enter();
4065
4066 /* need to revalidate the state after entering the lock again */
4067 if (mData->mSession.mState != SessionState_Open)
4068 {
4069 aControl->Uninitialize();
4070
4071 return setError(VBOX_E_INVALID_SESSION_STATE,
4072 tr("The machine '%ls' does not have an open session"),
4073 mUserData->mName.raw());
4074 }
4075
4076 /* store the control in the list */
4077 mData->mSession.mRemoteControls.push_back (aControl);
4078
4079 LogFlowThisFuncLeave();
4080 return S_OK;
4081}
4082
4083/**
4084 * Returns @c true if the given machine has an open direct session and returns
4085 * the session machine instance and additional session data (on some platforms)
4086 * if so.
4087 *
4088 * Note that when the method returns @c false, the arguments remain unchanged.
4089 *
4090 * @param aMachine Session machine object.
4091 * @param aControl Direct session control object (optional).
4092 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
4093 *
4094 * @note locks this object for reading.
4095 */
4096#if defined (RT_OS_WINDOWS)
4097bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4098 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4099 HANDLE *aIPCSem /*= NULL*/,
4100 bool aAllowClosing /*= false*/)
4101#elif defined (RT_OS_OS2)
4102bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4103 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4104 HMTX *aIPCSem /*= NULL*/,
4105 bool aAllowClosing /*= false*/)
4106#else
4107bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4108 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4109 bool aAllowClosing /*= false*/)
4110#endif
4111{
4112 AutoLimitedCaller autoCaller(this);
4113 AssertComRCReturn (autoCaller.rc(), false);
4114
4115 /* just return false for inaccessible machines */
4116 if (autoCaller.state() != Ready)
4117 return false;
4118
4119 AutoReadLock alock(this);
4120
4121 if (mData->mSession.mState == SessionState_Open ||
4122 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
4123 {
4124 AssertReturn(!mData->mSession.mMachine.isNull(), false);
4125
4126 aMachine = mData->mSession.mMachine;
4127
4128 if (aControl != NULL)
4129 *aControl = mData->mSession.mDirectControl;
4130
4131#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4132 /* Additional session data */
4133 if (aIPCSem != NULL)
4134 *aIPCSem = aMachine->mIPCSem;
4135#endif
4136 return true;
4137 }
4138
4139 return false;
4140}
4141
4142/**
4143 * Returns @c true if the given machine has an spawning direct session and
4144 * returns and additional session data (on some platforms) if so.
4145 *
4146 * Note that when the method returns @c false, the arguments remain unchanged.
4147 *
4148 * @param aPID PID of the spawned direct session process.
4149 *
4150 * @note locks this object for reading.
4151 */
4152#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4153bool Machine::isSessionSpawning (RTPROCESS *aPID /*= NULL*/)
4154#else
4155bool Machine::isSessionSpawning()
4156#endif
4157{
4158 AutoLimitedCaller autoCaller(this);
4159 AssertComRCReturn (autoCaller.rc(), false);
4160
4161 /* just return false for inaccessible machines */
4162 if (autoCaller.state() != Ready)
4163 return false;
4164
4165 AutoReadLock alock(this);
4166
4167 if (mData->mSession.mState == SessionState_Spawning)
4168 {
4169#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4170 /* Additional session data */
4171 if (aPID != NULL)
4172 {
4173 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
4174 *aPID = mData->mSession.mPid;
4175 }
4176#endif
4177 return true;
4178 }
4179
4180 return false;
4181}
4182
4183/**
4184 * Called from the client watcher thread to check for unexpected client process
4185 * death during Session_Spawning state (e.g. before it successfully opened a
4186 * direct session).
4187 *
4188 * On Win32 and on OS/2, this method is called only when we've got the
4189 * direct client's process termination notification, so it always returns @c
4190 * true.
4191 *
4192 * On other platforms, this method returns @c true if the client process is
4193 * terminated and @c false if it's still alive.
4194 *
4195 * @note Locks this object for writing.
4196 */
4197bool Machine::checkForSpawnFailure()
4198{
4199 AutoCaller autoCaller(this);
4200 if (!autoCaller.isOk())
4201 {
4202 /* nothing to do */
4203 LogFlowThisFunc(("Already uninitialized!"));
4204 return true;
4205 }
4206
4207 /* VirtualBox::addProcessToReap() needs a write lock */
4208 AutoMultiWriteLock2 alock (mParent, this);
4209
4210 if (mData->mSession.mState != SessionState_Spawning)
4211 {
4212 /* nothing to do */
4213 LogFlowThisFunc(("Not spawning any more!"));
4214 return true;
4215 }
4216
4217 HRESULT rc = S_OK;
4218
4219#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4220
4221 /* the process was already unexpectedly terminated, we just need to set an
4222 * error and finalize session spawning */
4223 rc = setError(E_FAIL,
4224 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4225 name().raw());
4226#else
4227
4228 RTPROCSTATUS status;
4229 int vrc = ::RTProcWait (mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
4230 &status);
4231
4232 if (vrc != VERR_PROCESS_RUNNING)
4233 rc = setError(E_FAIL,
4234 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4235 name().raw());
4236#endif
4237
4238 if (FAILED(rc))
4239 {
4240 /* Close the remote session, remove the remote control from the list
4241 * and reset session state to Closed (@note keep the code in sync with
4242 * the relevant part in checkForSpawnFailure()). */
4243
4244 Assert (mData->mSession.mRemoteControls.size() == 1);
4245 if (mData->mSession.mRemoteControls.size() == 1)
4246 {
4247 ErrorInfoKeeper eik;
4248 mData->mSession.mRemoteControls.front()->Uninitialize();
4249 }
4250
4251 mData->mSession.mRemoteControls.clear();
4252 mData->mSession.mState = SessionState_Closed;
4253
4254 /* finalize the progress after setting the state, for consistency */
4255 mData->mSession.mProgress->notifyComplete (rc);
4256 mData->mSession.mProgress.setNull();
4257
4258 mParent->addProcessToReap (mData->mSession.mPid);
4259 mData->mSession.mPid = NIL_RTPROCESS;
4260
4261 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
4262 return true;
4263 }
4264
4265 return false;
4266}
4267
4268/**
4269 * Checks that the registered flag of the machine can be set according to
4270 * the argument and sets it. On success, commits and saves all settings.
4271 *
4272 * @note When this machine is inaccessible, the only valid value for \a
4273 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
4274 * inaccessible machines are not currently supported. Note that unregistering
4275 * an inaccessible machine will \b uninitialize this machine object. Therefore,
4276 * the caller must make sure there are no active Machine::addCaller() calls
4277 * on the current thread because this will block Machine::uninit().
4278 *
4279 * @note Must be called from mParent's write lock. Locks this object and
4280 * children for writing.
4281 */
4282HRESULT Machine::trySetRegistered (BOOL aRegistered)
4283{
4284 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
4285
4286 AutoLimitedCaller autoCaller(this);
4287 AssertComRCReturnRC(autoCaller.rc());
4288
4289 AutoWriteLock alock(this);
4290
4291 /* wait for state dependants to drop to zero */
4292 ensureNoStateDependencies();
4293
4294 ComAssertRet (mData->mRegistered != aRegistered, E_FAIL);
4295
4296 if (!mData->mAccessible)
4297 {
4298 /* A special case: the machine is not accessible. */
4299
4300 /* inaccessible machines can only be unregistered */
4301 AssertReturn(!aRegistered, E_FAIL);
4302
4303 /* Uninitialize ourselves here because currently there may be no
4304 * unregistered that are inaccessible (this state combination is not
4305 * supported). Note releasing the caller and leaving the lock before
4306 * calling uninit() */
4307
4308 alock.leave();
4309 autoCaller.release();
4310
4311 uninit();
4312
4313 return S_OK;
4314 }
4315
4316 AssertReturn(autoCaller.state() == Ready, E_FAIL);
4317
4318 /* we will probably modify these and want to prevent concurrent
4319 * modifications until we finish */
4320 AutoWriteLock dvdLock (mDVDDrive);
4321 AutoWriteLock floppyLock (mFloppyDrive);
4322
4323 if (aRegistered)
4324 {
4325 if (mData->mRegistered)
4326 return setError(VBOX_E_INVALID_OBJECT_STATE,
4327 tr("The machine '%ls' with UUID {%s} is already registered"),
4328 mUserData->mName.raw(),
4329 mData->mUuid.toString().raw());
4330 }
4331 else
4332 {
4333 if (mData->mMachineState == MachineState_Saved)
4334 return setError(VBOX_E_INVALID_VM_STATE,
4335 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
4336 mUserData->mName.raw());
4337
4338 size_t snapshotCount = 0;
4339 if (mData->mFirstSnapshot)
4340 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4341 if (snapshotCount)
4342 return setError(VBOX_E_INVALID_OBJECT_STATE,
4343 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
4344 mUserData->mName.raw(), snapshotCount);
4345
4346 if (mData->mSession.mState != SessionState_Closed)
4347 return setError(VBOX_E_INVALID_OBJECT_STATE,
4348 tr("Cannot unregister the machine '%ls' because it has an open session"),
4349 mUserData->mName.raw());
4350
4351 if (mHDData->mAttachments.size() != 0)
4352 return setError(VBOX_E_INVALID_OBJECT_STATE,
4353 tr("Cannot unregister the machine '%ls' because it has %d hard disks attached"),
4354 mUserData->mName.raw(),
4355 mHDData->mAttachments.size());
4356
4357 /* Note that we do not prevent unregistration of a DVD or Floppy image
4358 * is attached: as opposed to hard disks detaching such an image
4359 * implicitly in this method (which we will do below) won't have any
4360 * side effects (like detached orphan base and diff hard disks etc).*/
4361 }
4362
4363 HRESULT rc = S_OK;
4364
4365 /* Ensure the settings are saved. If we are going to be registered and
4366 * isConfigLocked() is FALSE then it means that no config file exists yet,
4367 * so create it by calling saveSettings() too. */
4368 if ( isModified()
4369 || (aRegistered && !mData->m_pMachineConfigFile->fileExists())
4370 )
4371 {
4372 rc = saveSettings();
4373 CheckComRCReturnRC(rc);
4374 }
4375
4376 /* Implicitly detach DVD/Floppy */
4377 rc = mDVDDrive->unmount();
4378 if (SUCCEEDED(rc))
4379 rc = mFloppyDrive->unmount();
4380
4381 if (SUCCEEDED(rc))
4382 {
4383 /* we may have had implicit modifications we want to fix on success */
4384 commit();
4385
4386 mData->mRegistered = aRegistered;
4387 }
4388 else
4389 {
4390 /* we may have had implicit modifications we want to cancel on failure*/
4391 rollback (false /* aNotify */);
4392 }
4393
4394 return rc;
4395}
4396
4397/**
4398 * Increases the number of objects dependent on the machine state or on the
4399 * registered state. Guarantees that these two states will not change at least
4400 * until #releaseStateDependency() is called.
4401 *
4402 * Depending on the @a aDepType value, additional state checks may be made.
4403 * These checks will set extended error info on failure. See
4404 * #checkStateDependency() for more info.
4405 *
4406 * If this method returns a failure, the dependency is not added and the caller
4407 * is not allowed to rely on any particular machine state or registration state
4408 * value and may return the failed result code to the upper level.
4409 *
4410 * @param aDepType Dependency type to add.
4411 * @param aState Current machine state (NULL if not interested).
4412 * @param aRegistered Current registered state (NULL if not interested).
4413 *
4414 * @note Locks this object for writing.
4415 */
4416HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
4417 MachineState_T *aState /* = NULL */,
4418 BOOL *aRegistered /* = NULL */)
4419{
4420 AutoCaller autoCaller(this);
4421 AssertComRCReturnRC(autoCaller.rc());
4422
4423 AutoWriteLock alock(this);
4424
4425 HRESULT rc = checkStateDependency(aDepType);
4426 CheckComRCReturnRC(rc);
4427
4428 {
4429 if (mData->mMachineStateChangePending != 0)
4430 {
4431 /* ensureNoStateDependencies() is waiting for state dependencies to
4432 * drop to zero so don't add more. It may make sense to wait a bit
4433 * and retry before reporting an error (since the pending state
4434 * transition should be really quick) but let's just assert for
4435 * now to see if it ever happens on practice. */
4436
4437 AssertFailed();
4438
4439 return setError(E_ACCESSDENIED,
4440 tr("Machine state change is in progress. Please retry the operation later."));
4441 }
4442
4443 ++ mData->mMachineStateDeps;
4444 Assert (mData->mMachineStateDeps != 0 /* overflow */);
4445 }
4446
4447 if (aState)
4448 *aState = mData->mMachineState;
4449 if (aRegistered)
4450 *aRegistered = mData->mRegistered;
4451
4452 return S_OK;
4453}
4454
4455/**
4456 * Decreases the number of objects dependent on the machine state.
4457 * Must always complete the #addStateDependency() call after the state
4458 * dependency is no more necessary.
4459 */
4460void Machine::releaseStateDependency()
4461{
4462 AutoCaller autoCaller(this);
4463 AssertComRCReturnVoid (autoCaller.rc());
4464
4465 AutoWriteLock alock(this);
4466
4467 AssertReturnVoid (mData->mMachineStateDeps != 0
4468 /* releaseStateDependency() w/o addStateDependency()? */);
4469 -- mData->mMachineStateDeps;
4470
4471 if (mData->mMachineStateDeps == 0)
4472 {
4473 /* inform ensureNoStateDependencies() that there are no more deps */
4474 if (mData->mMachineStateChangePending != 0)
4475 {
4476 Assert (mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
4477 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
4478 }
4479 }
4480}
4481
4482// protected methods
4483/////////////////////////////////////////////////////////////////////////////
4484
4485/**
4486 * Performs machine state checks based on the @a aDepType value. If a check
4487 * fails, this method will set extended error info, otherwise it will return
4488 * S_OK. It is supposed, that on failure, the caller will immedieately return
4489 * the return value of this method to the upper level.
4490 *
4491 * When @a aDepType is AnyStateDep, this method always returns S_OK.
4492 *
4493 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
4494 * current state of this machine object allows to change settings of the
4495 * machine (i.e. the machine is not registered, or registered but not running
4496 * and not saved). It is useful to call this method from Machine setters
4497 * before performing any change.
4498 *
4499 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
4500 * as for MutableStateDep except that if the machine is saved, S_OK is also
4501 * returned. This is useful in setters which allow changing machine
4502 * properties when it is in the saved state.
4503 *
4504 * @param aDepType Dependency type to check.
4505 *
4506 * @note Non Machine based classes should use #addStateDependency() and
4507 * #releaseStateDependency() methods or the smart AutoStateDependency
4508 * template.
4509 *
4510 * @note This method must be called from under this object's read or write
4511 * lock.
4512 */
4513HRESULT Machine::checkStateDependency(StateDependency aDepType)
4514{
4515 switch (aDepType)
4516 {
4517 case AnyStateDep:
4518 {
4519 break;
4520 }
4521 case MutableStateDep:
4522 {
4523 if (mData->mRegistered &&
4524 (mType != IsSessionMachine ||
4525 mData->mMachineState > MachineState_Paused ||
4526 mData->mMachineState == MachineState_Saved))
4527 return setError(VBOX_E_INVALID_VM_STATE,
4528 tr("The machine is not mutable (state is %d)"),
4529 mData->mMachineState);
4530 break;
4531 }
4532 case MutableOrSavedStateDep:
4533 {
4534 if (mData->mRegistered &&
4535 (mType != IsSessionMachine ||
4536 mData->mMachineState > MachineState_Paused))
4537 return setError(VBOX_E_INVALID_VM_STATE,
4538 tr("The machine is not mutable (state is %d)"),
4539 mData->mMachineState);
4540 break;
4541 }
4542 }
4543
4544 return S_OK;
4545}
4546
4547/**
4548 * Makes sure that there are no machine state dependants. If necessary, waits
4549 * for the number of dependants to drop to zero.
4550 *
4551 * Make sure this method is called from under this object's write lock to
4552 * guarantee that no new dependants may be added when this method returns
4553 * control to the caller.
4554 *
4555 * @note Locks this object for writing. The lock will be released while waiting
4556 * (if necessary).
4557 *
4558 * @warning To be used only in methods that change the machine state!
4559 */
4560void Machine::ensureNoStateDependencies()
4561{
4562 AssertReturnVoid (isWriteLockOnCurrentThread());
4563
4564 AutoWriteLock alock(this);
4565
4566 /* Wait for all state dependants if necessary */
4567 if (mData->mMachineStateDeps != 0)
4568 {
4569 /* lazy semaphore creation */
4570 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
4571 RTSemEventMultiCreate (&mData->mMachineStateDepsSem);
4572
4573 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
4574 mData->mMachineStateDeps));
4575
4576 ++ mData->mMachineStateChangePending;
4577
4578 /* reset the semaphore before waiting, the last dependant will signal
4579 * it */
4580 RTSemEventMultiReset (mData->mMachineStateDepsSem);
4581
4582 alock.leave();
4583
4584 RTSemEventMultiWait (mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
4585
4586 alock.enter();
4587
4588 -- mData->mMachineStateChangePending;
4589 }
4590}
4591
4592/**
4593 * Changes the machine state and informs callbacks.
4594 *
4595 * This method is not intended to fail so it either returns S_OK or asserts (and
4596 * returns a failure).
4597 *
4598 * @note Locks this object for writing.
4599 */
4600HRESULT Machine::setMachineState (MachineState_T aMachineState)
4601{
4602 LogFlowThisFuncEnter();
4603 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
4604
4605 AutoCaller autoCaller(this);
4606 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4607
4608 AutoWriteLock alock(this);
4609
4610 /* wait for state dependants to drop to zero */
4611 ensureNoStateDependencies();
4612
4613 if (mData->mMachineState != aMachineState)
4614 {
4615 mData->mMachineState = aMachineState;
4616
4617 RTTimeNow (&mData->mLastStateChange);
4618
4619 mParent->onMachineStateChange(mData->mUuid, aMachineState);
4620 }
4621
4622 LogFlowThisFuncLeave();
4623 return S_OK;
4624}
4625
4626/**
4627 * Searches for a shared folder with the given logical name
4628 * in the collection of shared folders.
4629 *
4630 * @param aName logical name of the shared folder
4631 * @param aSharedFolder where to return the found object
4632 * @param aSetError whether to set the error info if the folder is
4633 * not found
4634 * @return
4635 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
4636 *
4637 * @note
4638 * must be called from under the object's lock!
4639 */
4640HRESULT Machine::findSharedFolder (CBSTR aName,
4641 ComObjPtr<SharedFolder> &aSharedFolder,
4642 bool aSetError /* = false */)
4643{
4644 bool found = false;
4645 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
4646 !found && it != mHWData->mSharedFolders.end();
4647 ++ it)
4648 {
4649 AutoWriteLock alock(*it);
4650 found = (*it)->name() == aName;
4651 if (found)
4652 aSharedFolder = *it;
4653 }
4654
4655 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
4656
4657 if (aSetError && !found)
4658 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
4659
4660 return rc;
4661}
4662
4663/**
4664 * Loads all the VM settings by walking down the <Machine> node.
4665 *
4666 * @param aRegistered true when the machine is being loaded on VirtualBox
4667 * startup
4668 *
4669 * @note This method is intended to be called only from init(), so it assumes
4670 * all machine data fields have appropriate default values when it is called.
4671 *
4672 * @note Doesn't lock any objects.
4673 */
4674HRESULT Machine::loadSettings(bool aRegistered)
4675{
4676 LogFlowThisFuncEnter();
4677 AssertReturn(mType == IsMachine, E_FAIL);
4678
4679 AutoCaller autoCaller(this);
4680 AssertReturn(autoCaller.state() == InInit, E_FAIL);
4681
4682 HRESULT rc = S_OK;
4683
4684 try
4685 {
4686 Assert(mData->m_pMachineConfigFile == NULL);
4687
4688 // load and parse machine XML; this will throw on XML or logic errors
4689 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
4690
4691 /* If the stored UUID is not empty, it means the registered machine
4692 * is being loaded. Compare the loaded UUID with the stored one taken
4693 * from the global registry. */
4694 if (!mData->mUuid.isEmpty())
4695 {
4696 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
4697 {
4698 throw setError(E_FAIL,
4699 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
4700 mData->m_pMachineConfigFile->uuid.raw(),
4701 mData->m_strConfigFileFull.raw(),
4702 mData->mUuid.toString().raw(),
4703 mParent->settingsFilePath().raw());
4704 }
4705 }
4706 else
4707 unconst (mData->mUuid) = mData->m_pMachineConfigFile->uuid;
4708
4709 /* name (required) */
4710 mUserData->mName = mData->m_pMachineConfigFile->strName;
4711
4712 /* nameSync (optional, default is true) */
4713 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
4714
4715 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
4716
4717 // guest OS type
4718 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
4719 /* look up the object by Id to check it is valid */
4720 ComPtr<IGuestOSType> guestOSType;
4721 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
4722 guestOSType.asOutParam());
4723 CheckComRCThrowRC(rc);
4724
4725 // stateFile (optional)
4726 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
4727 mSSData->mStateFilePath.setNull();
4728 else
4729 {
4730 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
4731 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
4732 if (RT_FAILURE(vrc))
4733 throw setError(E_FAIL,
4734 tr("Invalid saved state file path '%s' (%Rrc)"),
4735 mData->m_pMachineConfigFile->strStateFile.raw(),
4736 vrc);
4737 mSSData->mStateFilePath = stateFilePathFull;
4738 }
4739
4740 /* snapshotFolder (optional) */
4741 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
4742 CheckComRCThrowRC(rc);
4743
4744 /* currentStateModified (optional, default is true) */
4745 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
4746
4747 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
4748
4749 /*
4750 * note: all mUserData members must be assigned prior this point because
4751 * we need to commit changes in order to let mUserData be shared by all
4752 * snapshot machine instances.
4753 */
4754 mUserData.commitCopy();
4755
4756 /* Snapshot node (optional) */
4757 if (mData->m_pMachineConfigFile->llFirstSnapshot.size())
4758 {
4759 // there can only be one root snapshot
4760 Assert(mData->m_pMachineConfigFile->llFirstSnapshot.size() == 1);
4761
4762 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
4763
4764 rc = loadSnapshot(snap,
4765 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
4766 NULL); // no parent == first snapshot
4767 CheckComRCThrowRC(rc);
4768 }
4769
4770 /* Hardware node (required) */
4771 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
4772 CheckComRCThrowRC(rc);
4773
4774 /* Load storage controllers */
4775 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
4776 CheckComRCThrowRC(rc);
4777
4778 /*
4779 * NOTE: the assignment below must be the last thing to do,
4780 * otherwise it will be not possible to change the settings
4781 * somewehere in the code above because all setters will be
4782 * blocked by checkStateDependency(MutableStateDep).
4783 */
4784
4785 /* set the machine state to Aborted or Saved when appropriate */
4786 if (mData->m_pMachineConfigFile->fAborted)
4787 {
4788 Assert(!mSSData->mStateFilePath);
4789 mSSData->mStateFilePath.setNull();
4790
4791 /* no need to use setMachineState() during init() */
4792 mData->mMachineState = MachineState_Aborted;
4793 }
4794 else if (mSSData->mStateFilePath)
4795 {
4796 /* no need to use setMachineState() during init() */
4797 mData->mMachineState = MachineState_Saved;
4798 }
4799 }
4800 catch (HRESULT err)
4801 {
4802 /* we assume that error info is set by the thrower */
4803 rc = err;
4804 }
4805 catch (...)
4806 {
4807 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4808 }
4809
4810 LogFlowThisFuncLeave();
4811 return rc;
4812}
4813
4814/**
4815 * Recursively loads all snapshots starting from the given.
4816 *
4817 * @param aNode <Snapshot> node.
4818 * @param aCurSnapshotId Current snapshot ID from the settings file.
4819 * @param aParentSnapshot Parent snapshot.
4820 */
4821HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
4822 const Guid &aCurSnapshotId,
4823 Snapshot *aParentSnapshot)
4824{
4825 AssertReturn (mType == IsMachine, E_FAIL);
4826
4827 HRESULT rc = S_OK;
4828
4829 Utf8Str strStateFile;
4830 if (!data.strStateFile.isEmpty())
4831 {
4832 /* optional */
4833 strStateFile = data.strStateFile;
4834 int vrc = calculateFullPath(strStateFile, strStateFile);
4835 if (RT_FAILURE(vrc))
4836 return setError(E_FAIL,
4837 tr("Invalid saved state file path '%s' (%Rrc)"),
4838 strStateFile.raw(),
4839 vrc);
4840 }
4841
4842 /* create a snapshot machine object */
4843 ComObjPtr<SnapshotMachine> pSnapshotMachine;
4844 pSnapshotMachine.createObject();
4845 rc = pSnapshotMachine->init(this,
4846 data.hardware,
4847 data.storage,
4848 data.uuid,
4849 strStateFile);
4850 CheckComRCReturnRC (rc);
4851
4852 /* create a snapshot object */
4853 ComObjPtr<Snapshot> pSnapshot;
4854 pSnapshot.createObject();
4855 /* initialize the snapshot */
4856 rc = pSnapshot->init(mParent, // VirtualBox object
4857 data.uuid,
4858 data.strName,
4859 data.strDescription,
4860 data.timestamp,
4861 pSnapshotMachine,
4862 aParentSnapshot);
4863 CheckComRCReturnRC (rc);
4864
4865 /* memorize the first snapshot if necessary */
4866 if (!mData->mFirstSnapshot)
4867 mData->mFirstSnapshot = pSnapshot;
4868
4869 /* memorize the current snapshot when appropriate */
4870 if ( !mData->mCurrentSnapshot
4871 && pSnapshot->getId() == aCurSnapshotId
4872 )
4873 mData->mCurrentSnapshot = pSnapshot;
4874
4875 // now create the children
4876 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
4877 it != data.llChildSnapshots.end();
4878 ++it)
4879 {
4880 const settings::Snapshot &childData = *it;
4881 // recurse
4882 rc = loadSnapshot(childData,
4883 aCurSnapshotId,
4884 pSnapshot); // parent = the one we created above
4885 CheckComRCBreakRC(rc);
4886 }
4887
4888 return rc;
4889}
4890
4891/**
4892 * @param aNode <Hardware> node.
4893 */
4894HRESULT Machine::loadHardware(const settings::Hardware &data)
4895{
4896 AssertReturn(mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
4897
4898 HRESULT rc = S_OK;
4899
4900 try
4901 {
4902 /* The hardware version attribute (optional). */
4903 mHWData->mHWVersion = data.strVersion;
4904
4905 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
4906 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
4907 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
4908 mHWData->mPAEEnabled = data.fPAE;
4909
4910 mHWData->mCPUCount = data.cCPUs;
4911
4912 mHWData->mMemorySize = data.ulMemorySizeMB;
4913
4914 // boot order
4915 for (size_t i = 0;
4916 i < RT_ELEMENTS(mHWData->mBootOrder);
4917 i++)
4918 {
4919 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
4920 if (it == data.mapBootOrder.end())
4921 mHWData->mBootOrder[i] = DeviceType_Null;
4922 else
4923 mHWData->mBootOrder[i] = it->second;
4924 }
4925
4926 mHWData->mVRAMSize = data.ulVRAMSizeMB;
4927 mHWData->mMonitorCount = data.cMonitors;
4928 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
4929 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
4930
4931#ifdef VBOX_WITH_VRDP
4932 /* RemoteDisplay */
4933 rc = mVRDPServer->loadSettings(data.vrdpSettings);
4934 CheckComRCReturnRC (rc);
4935#endif
4936
4937 /* BIOS */
4938 rc = mBIOSSettings->loadSettings(data.biosSettings);
4939 CheckComRCReturnRC (rc);
4940
4941 /* DVD drive */
4942 rc = mDVDDrive->loadSettings(data.dvdDrive);
4943 CheckComRCReturnRC (rc);
4944
4945 /* Floppy drive */
4946 rc = mFloppyDrive->loadSettings(data.floppyDrive);
4947 CheckComRCReturnRC (rc);
4948
4949 /* USB Controller */
4950 rc = mUSBController->loadSettings(data.usbController);
4951 CheckComRCReturnRC (rc);
4952
4953 // network adapters
4954 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
4955 it != data.llNetworkAdapters.end();
4956 ++it)
4957 {
4958 const settings::NetworkAdapter &nic = *it;
4959
4960 /* slot unicity is guaranteed by XML Schema */
4961 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
4962 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
4963 CheckComRCReturnRC (rc);
4964 }
4965
4966 // serial ports
4967 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
4968 it != data.llSerialPorts.end();
4969 ++it)
4970 {
4971 const settings::SerialPort &s = *it;
4972
4973 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
4974 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
4975 CheckComRCReturnRC (rc);
4976 }
4977
4978 // parallel ports (optional)
4979 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
4980 it != data.llParallelPorts.end();
4981 ++it)
4982 {
4983 const settings::ParallelPort &p = *it;
4984
4985 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
4986 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
4987 CheckComRCReturnRC (rc);
4988 }
4989
4990 /* AudioAdapter */
4991 rc = mAudioAdapter->loadSettings(data.audioAdapter);
4992 CheckComRCReturnRC (rc);
4993
4994 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
4995 it != data.llSharedFolders.end();
4996 ++it)
4997 {
4998 const settings::SharedFolder &sf = *it;
4999 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
5000 CheckComRCReturnRC (rc);
5001 }
5002
5003 // Clipboard
5004 mHWData->mClipboardMode = data.clipboardMode;
5005
5006 // guest settings
5007 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
5008 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
5009
5010#ifdef VBOX_WITH_GUEST_PROPS
5011 /* Guest properties (optional) */
5012 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
5013 it != data.llGuestProperties.end();
5014 ++it)
5015 {
5016 const settings::GuestProperty &prop = *it;
5017 uint32_t fFlags = guestProp::NILFLAG;
5018 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
5019 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
5020 mHWData->mGuestProperties.push_back(property);
5021 }
5022
5023 mHWData->mPropertyServiceActive = false;
5024 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
5025#endif /* VBOX_WITH_GUEST_PROPS defined */
5026 }
5027 catch(std::bad_alloc &)
5028 {
5029 return E_OUTOFMEMORY;
5030 }
5031
5032 AssertComRC(rc);
5033 return rc;
5034}
5035
5036 /**
5037 * @param aNode <StorageControllers> node.
5038 */
5039HRESULT Machine::loadStorageControllers(const settings::Storage &data,
5040 bool aRegistered,
5041 const Guid *aSnapshotId /* = NULL */)
5042{
5043 AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5044
5045 HRESULT rc = S_OK;
5046
5047 /* Make sure the attached hard disks don't get unregistered until we
5048 * associate them with tis machine (important for VMs loaded (opened) after
5049 * VirtualBox startup) */
5050 AutoReadLock vboxLock(mParent);
5051
5052 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
5053 it != data.llStorageControllers.end();
5054 ++it)
5055 {
5056 const settings::StorageController &ctlData = *it;
5057
5058 ComObjPtr<StorageController> pCtl;
5059 /* Try to find one with the name first. */
5060 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
5061 if (SUCCEEDED(rc))
5062 return setError(VBOX_E_OBJECT_IN_USE,
5063 tr("Storage controller named '%s' already exists"),
5064 ctlData.strName.raw());
5065
5066 pCtl.createObject();
5067 rc = pCtl->init(this,
5068 ctlData.strName,
5069 ctlData.storageBus);
5070 CheckComRCReturnRC (rc);
5071
5072 mStorageControllers->push_back(pCtl);
5073
5074 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
5075 CheckComRCReturnRC (rc);
5076
5077 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
5078 CheckComRCReturnRC (rc);
5079
5080 /* Set IDE emulation settings (only for AHCI controller). */
5081 if (ctlData.controllerType == StorageControllerType_IntelAhci)
5082 {
5083 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
5084 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
5085 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
5086 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
5087 )
5088 return rc;
5089 }
5090
5091 /* Load the attached devices now. */
5092 rc = loadStorageDevices(pCtl, ctlData,
5093 aRegistered,
5094 aSnapshotId);
5095 CheckComRCReturnRC (rc);
5096 }
5097
5098 return S_OK;
5099}
5100
5101/**
5102 * @param aNode <HardDiskAttachments> node.
5103 * @param aRegistered true when the machine is being loaded on VirtualBox
5104 * startup, or when a snapshot is being loaded (wchich
5105 * currently can happen on startup only)
5106 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
5107 *
5108 * @note Lock mParent for reading and hard disks for writing before calling.
5109 */
5110HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
5111 const settings::StorageController &data,
5112 bool aRegistered,
5113 const Guid *aSnapshotId /*= NULL*/)
5114{
5115 AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
5116 (mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
5117
5118 HRESULT rc = S_OK;
5119
5120 if (!aRegistered && data.llAttachedDevices.size() > 0)
5121 /* when the machine is being loaded (opened) from a file, it cannot
5122 * have hard disks attached (this should not happen normally,
5123 * because we don't allow to attach hard disks to an unregistered
5124 * VM at all */
5125 return setError(E_FAIL,
5126 tr("Unregistered machine '%ls' cannot have hard disks attached (found %d hard disk attachments)"),
5127 mUserData->mName.raw(),
5128 data.llAttachedDevices.size());
5129
5130 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5131 it != data.llAttachedDevices.end();
5132 ++it)
5133 {
5134 const settings::AttachedDevice &dev = *it;
5135
5136 /* find a hard disk by UUID */
5137 ComObjPtr<HardDisk> pDisk;
5138 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &pDisk);
5139 CheckComRCReturnRC (rc);
5140
5141 AutoWriteLock hdLock(pDisk);
5142
5143 if (pDisk->type() == HardDiskType_Immutable)
5144 {
5145 if (mType == IsSnapshotMachine)
5146 return setError(E_FAIL,
5147 tr("Immutable hard disk '%ls' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
5148 "of the virtual machine '%ls' ('%s')"),
5149 pDisk->locationFull().raw(),
5150 dev.uuid.raw(),
5151 aSnapshotId->raw(),
5152 mUserData->mName.raw(),
5153 mData->m_strConfigFileFull.raw());
5154
5155 return setError(E_FAIL,
5156 tr("Immutable hard disk '%ls' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
5157 pDisk->locationFull().raw(),
5158 dev.uuid.raw(),
5159 mUserData->mName.raw(),
5160 mData->m_strConfigFileFull.raw());
5161 }
5162
5163 if ( mType != IsSnapshotMachine
5164 && pDisk->children().size() != 0
5165 )
5166 return setError(E_FAIL,
5167 tr("Hard disk '%ls' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
5168 "because it has %d differencing child hard disks"),
5169 pDisk->locationFull().raw(),
5170 dev.uuid.raw(),
5171 mUserData->mName.raw(),
5172 mData->m_strConfigFileFull.raw(),
5173 pDisk->children().size());
5174
5175 if (std::find_if(mHDData->mAttachments.begin(),
5176 mHDData->mAttachments.end(),
5177 HardDiskAttachment::RefersTo(pDisk))
5178 != mHDData->mAttachments.end())
5179 {
5180 return setError(E_FAIL,
5181 tr("Hard disk '%ls' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
5182 pDisk->locationFull().raw(),
5183 dev.uuid.raw(),
5184 mUserData->mName.raw(),
5185 mData->m_strConfigFileFull.raw());
5186 }
5187
5188 const Bstr controllerName = aStorageController->name();
5189 ComObjPtr<HardDiskAttachment> pAttachment;
5190 pAttachment.createObject();
5191 rc = pAttachment->init(pDisk,
5192 controllerName,
5193 dev.lPort,
5194 dev.lDevice);
5195 CheckComRCBreakRC(rc);
5196
5197 /* associate the hard disk with this machine and snapshot */
5198 if (mType == IsSnapshotMachine)
5199 rc = pDisk->attachTo(mData->mUuid, *aSnapshotId);
5200 else
5201 rc = pDisk->attachTo(mData->mUuid);
5202
5203 AssertComRCBreakRC (rc);
5204
5205 /* backup mHDData to let registeredInit() properly rollback on failure
5206 * (= limited accessibility) */
5207
5208 mHDData.backup();
5209 mHDData->mAttachments.push_back(pAttachment);
5210 }
5211
5212 return rc;
5213}
5214
5215/**
5216 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
5217 *
5218 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
5219 * @param aSnapshot where to return the found snapshot
5220 * @param aSetError true to set extended error info on failure
5221 */
5222HRESULT Machine::findSnapshot(const Guid &aId,
5223 ComObjPtr<Snapshot> &aSnapshot,
5224 bool aSetError /* = false */)
5225{
5226 AutoReadLock chlock(snapshotsTreeLockHandle());
5227
5228 if (!mData->mFirstSnapshot)
5229 {
5230 if (aSetError)
5231 return setError(E_FAIL,
5232 tr("This machine does not have any snapshots"));
5233 return E_FAIL;
5234 }
5235
5236 if (aId.isEmpty())
5237 aSnapshot = mData->mFirstSnapshot;
5238 else
5239 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
5240
5241 if (!aSnapshot)
5242 {
5243 if (aSetError)
5244 return setError(E_FAIL,
5245 tr("Could not find a snapshot with UUID {%s}"),
5246 aId.toString().raw());
5247 return E_FAIL;
5248 }
5249
5250 return S_OK;
5251}
5252
5253/**
5254 * Returns the snapshot with the given name or fails of no such snapshot.
5255 *
5256 * @param aName snapshot name to find
5257 * @param aSnapshot where to return the found snapshot
5258 * @param aSetError true to set extended error info on failure
5259 */
5260HRESULT Machine::findSnapshot(IN_BSTR aName,
5261 ComObjPtr<Snapshot> &aSnapshot,
5262 bool aSetError /* = false */)
5263{
5264 AssertReturn(aName, E_INVALIDARG);
5265
5266 AutoReadLock chlock(snapshotsTreeLockHandle());
5267
5268 if (!mData->mFirstSnapshot)
5269 {
5270 if (aSetError)
5271 return setError(VBOX_E_OBJECT_NOT_FOUND,
5272 tr("This machine does not have any snapshots"));
5273 return VBOX_E_OBJECT_NOT_FOUND;
5274 }
5275
5276 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
5277
5278 if (!aSnapshot)
5279 {
5280 if (aSetError)
5281 return setError(VBOX_E_OBJECT_NOT_FOUND,
5282 tr("Could not find a snapshot named '%ls'"), aName);
5283 return VBOX_E_OBJECT_NOT_FOUND;
5284 }
5285
5286 return S_OK;
5287}
5288
5289/**
5290 * Returns a storage controller object with the given name.
5291 *
5292 * @param aName storage controller name to find
5293 * @param aStorageController where to return the found storage controller
5294 * @param aSetError true to set extended error info on failure
5295 */
5296HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
5297 ComObjPtr<StorageController> &aStorageController,
5298 bool aSetError /* = false */)
5299{
5300 AssertReturn (!aName.isEmpty(), E_INVALIDARG);
5301
5302 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5303 it != mStorageControllers->end();
5304 ++it)
5305 {
5306 if ((*it)->name() == aName)
5307 {
5308 aStorageController = (*it);
5309 return S_OK;
5310 }
5311 }
5312
5313 if (aSetError)
5314 return setError(VBOX_E_OBJECT_NOT_FOUND,
5315 tr("Could not find a storage controller named '%s'"),
5316 aName.raw());
5317 return VBOX_E_OBJECT_NOT_FOUND;
5318}
5319
5320HRESULT Machine::getHardDiskAttachmentsOfController(CBSTR aName,
5321 HDData::AttachmentList &atts)
5322{
5323 AutoCaller autoCaller(this);
5324 CheckComRCReturnRC(autoCaller.rc());
5325
5326 AutoReadLock alock(this);
5327
5328 for (HDData::AttachmentList::iterator it = mHDData->mAttachments.begin();
5329 it != mHDData->mAttachments.end(); ++it)
5330 {
5331 if ((*it)->controller() == aName)
5332 atts.push_back(*it);
5333 }
5334
5335 return S_OK;
5336}
5337
5338/**
5339 * Helper for #saveSettings. Cares about renaming the settings directory and
5340 * file if the machine name was changed and about creating a new settings file
5341 * if this is a new machine.
5342 *
5343 * @note Must be never called directly but only from #saveSettings().
5344 *
5345 * @param aRenamed receives |true| if the name was changed and the settings
5346 * file was renamed as a result, or |false| otherwise. The
5347 * value makes sense only on success.
5348 * @param aNew receives |true| if a virgin settings file was created.
5349 */
5350HRESULT Machine::prepareSaveSettings(bool &aRenamed,
5351 bool &aNew)
5352{
5353 /* Note: tecnhically, mParent needs to be locked only when the machine is
5354 * registered (see prepareSaveSettings() for details) but we don't
5355 * currently differentiate it in callers of saveSettings() so we don't
5356 * make difference here too. */
5357 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5358 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5359
5360 HRESULT rc = S_OK;
5361
5362 aRenamed = false;
5363
5364 /* if we're ready and isConfigLocked() is FALSE then it means
5365 * that no config file exists yet (we will create a virgin one) */
5366 aNew = !mData->m_pMachineConfigFile->fileExists();
5367
5368 /* attempt to rename the settings file if machine name is changed */
5369 if ( mUserData->mNameSync
5370 && mUserData.isBackedUp()
5371 && mUserData.backedUpData()->mName != mUserData->mName
5372 )
5373 {
5374 aRenamed = true;
5375
5376 bool dirRenamed = false;
5377 bool fileRenamed = false;
5378
5379 Utf8Str configFile, newConfigFile;
5380 Utf8Str configDir, newConfigDir;
5381
5382 do
5383 {
5384 int vrc = VINF_SUCCESS;
5385
5386 Utf8Str name = mUserData.backedUpData()->mName;
5387 Utf8Str newName = mUserData->mName;
5388
5389 configFile = mData->m_strConfigFileFull;
5390
5391 /* first, rename the directory if it matches the machine name */
5392 configDir = configFile;
5393 configDir.stripFilename();
5394 newConfigDir = configDir;
5395 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
5396 {
5397 newConfigDir.stripFilename();
5398 newConfigDir = Utf8StrFmt ("%s%c%s",
5399 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
5400 /* new dir and old dir cannot be equal here because of 'if'
5401 * above and because name != newName */
5402 Assert (configDir != newConfigDir);
5403 if (!aNew)
5404 {
5405 /* perform real rename only if the machine is not new */
5406 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
5407 if (RT_FAILURE(vrc))
5408 {
5409 rc = setError(E_FAIL,
5410 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
5411 configDir.raw(),
5412 newConfigDir.raw(),
5413 vrc);
5414 break;
5415 }
5416 dirRenamed = true;
5417 }
5418 }
5419
5420 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
5421 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
5422
5423 /* then try to rename the settings file itself */
5424 if (newConfigFile != configFile)
5425 {
5426 /* get the path to old settings file in renamed directory */
5427 configFile = Utf8StrFmt("%s%c%s",
5428 newConfigDir.raw(),
5429 RTPATH_DELIMITER,
5430 RTPathFilename(configFile.c_str()));
5431 if (!aNew)
5432 {
5433 /* perform real rename only if the machine is not new */
5434 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
5435 if (RT_FAILURE(vrc))
5436 {
5437 rc = setError(E_FAIL,
5438 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
5439 configFile.raw(),
5440 newConfigFile.raw(),
5441 vrc);
5442 break;
5443 }
5444 fileRenamed = true;
5445 }
5446 }
5447
5448 /* update m_strConfigFileFull amd mConfigFile */
5449 Utf8Str oldConfigFileFull = mData->m_strConfigFileFull;
5450 Utf8Str oldConfigFile = mData->m_strConfigFile;
5451 mData->m_strConfigFileFull = newConfigFile;
5452 /* try to get the relative path for mConfigFile */
5453 Utf8Str path = newConfigFile;
5454 mParent->calculateRelativePath (path, path);
5455 mData->m_strConfigFile = path;
5456
5457 /* last, try to update the global settings with the new path */
5458 if (mData->mRegistered)
5459 {
5460 rc = mParent->updateSettings(configDir.c_str(), newConfigDir.c_str());
5461 if (FAILED(rc))
5462 {
5463 /* revert to old values */
5464 mData->m_strConfigFileFull = oldConfigFileFull;
5465 mData->m_strConfigFile = oldConfigFile;
5466 break;
5467 }
5468 }
5469
5470 /* update the snapshot folder */
5471 path = mUserData->mSnapshotFolderFull;
5472 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
5473 {
5474 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
5475 path.raw() + configDir.length());
5476 mUserData->mSnapshotFolderFull = path;
5477 calculateRelativePath (path, path);
5478 mUserData->mSnapshotFolder = path;
5479 }
5480
5481 /* update the saved state file path */
5482 path = mSSData->mStateFilePath;
5483 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
5484 {
5485 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
5486 path.raw() + configDir.length());
5487 mSSData->mStateFilePath = path;
5488 }
5489
5490 /* Update saved state file paths of all online snapshots.
5491 * Note that saveSettings() will recognize name change
5492 * and will save all snapshots in this case. */
5493 if (mData->mFirstSnapshot)
5494 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
5495 newConfigDir.c_str());
5496 }
5497 while (0);
5498
5499 if (FAILED(rc))
5500 {
5501 /* silently try to rename everything back */
5502 if (fileRenamed)
5503 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
5504 if (dirRenamed)
5505 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
5506 }
5507
5508 CheckComRCReturnRC(rc);
5509 }
5510
5511 if (aNew)
5512 {
5513 /* create a virgin config file */
5514 int vrc = VINF_SUCCESS;
5515
5516 /* ensure the settings directory exists */
5517 Utf8Str path(mData->m_strConfigFileFull);
5518 path.stripFilename();
5519 if (!RTDirExists(path.c_str()))
5520 {
5521 vrc = RTDirCreateFullPath(path.c_str(), 0777);
5522 if (RT_FAILURE(vrc))
5523 {
5524 return setError(E_FAIL,
5525 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
5526 path.raw(),
5527 vrc);
5528 }
5529 }
5530
5531 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
5532 path = Utf8Str(mData->m_strConfigFileFull);
5533 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
5534 RTFILE_O_READWRITE | RTFILE_O_CREATE |
5535 RTFILE_O_DENY_WRITE);
5536 if (RT_FAILURE(vrc))
5537 {
5538 mData->mHandleCfgFile = NIL_RTFILE;
5539 return setError(E_FAIL,
5540 tr("Could not create the settings file '%s' (%Rrc)"),
5541 path.raw(),
5542 vrc);
5543 }
5544 RTFileClose(mData->mHandleCfgFile);
5545 }
5546
5547 return rc;
5548}
5549
5550/**
5551 * Saves and commits machine data, user data and hardware data.
5552 *
5553 * Note that on failure, the data remains uncommitted.
5554 *
5555 * @a aFlags may combine the following flags:
5556 *
5557 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
5558 * Used when saving settings after an operation that makes them 100%
5559 * correspond to the settings from the current snapshot.
5560 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
5561 * #isReallyModified() returns false. This is necessary for cases when we
5562 * change machine data diectly, not through the backup()/commit() mechanism.
5563 *
5564 * @note Must be called from under mParent write lock (sometimes needed by
5565 * #prepareSaveSettings()) and this object's write lock. Locks children for
5566 * writing. There is one exception when mParent is unused and therefore may be
5567 * left unlocked: if this machine is an unregistered one.
5568 */
5569HRESULT Machine::saveSettings(int aFlags /*= 0*/)
5570{
5571 LogFlowThisFuncEnter();
5572
5573 /* Note: tecnhically, mParent needs to be locked only when the machine is
5574 * registered (see prepareSaveSettings() for details) but we don't
5575 * currently differentiate it in callers of saveSettings() so we don't
5576 * make difference here too. */
5577 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5578 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5579
5580 /* make sure child objects are unable to modify the settings while we are
5581 * saving them */
5582 ensureNoStateDependencies();
5583
5584 AssertReturn(mType == IsMachine || mType == IsSessionMachine, E_FAIL);
5585
5586 BOOL currentStateModified = mData->mCurrentStateModified;
5587 bool settingsModified;
5588
5589 if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
5590 {
5591 /* We ignore changes to user data when setting mCurrentStateModified
5592 * because the current state will not differ from the current snapshot
5593 * if only user data has been changed (user data is shared by all
5594 * snapshots). */
5595 currentStateModified = isReallyModified (true /* aIgnoreUserData */);
5596 settingsModified = mUserData.hasActualChanges() || currentStateModified;
5597 }
5598 else
5599 {
5600 if (aFlags & SaveS_ResetCurStateModified)
5601 currentStateModified = FALSE;
5602 settingsModified = isReallyModified();
5603 }
5604
5605 HRESULT rc = S_OK;
5606
5607 /* First, prepare to save settings. It will care about renaming the
5608 * settings directory and file if the machine name was changed and about
5609 * creating a new settings file if this is a new machine. */
5610 bool isRenamed = false;
5611 bool isNew = false;
5612 rc = prepareSaveSettings(isRenamed, isNew);
5613 CheckComRCReturnRC(rc);
5614
5615 try
5616 {
5617 mData->m_pMachineConfigFile->uuid = mData->mUuid;
5618 mData->m_pMachineConfigFile->strName = mUserData->mName;
5619 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
5620 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
5621 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
5622
5623 if ( mData->mMachineState == MachineState_Saved
5624 || mData->mMachineState == MachineState_Restoring
5625 )
5626 {
5627 Assert(!mSSData->mStateFilePath.isEmpty());
5628 /* try to make the file name relative to the settings file dir */
5629 Utf8Str stateFilePath = mSSData->mStateFilePath;
5630 calculateRelativePath(stateFilePath, stateFilePath);
5631
5632 mData->m_pMachineConfigFile->strStateFile = stateFilePath;
5633 }
5634 else
5635 {
5636 Assert(mSSData->mStateFilePath.isNull());
5637 mData->m_pMachineConfigFile->strStateFile.setNull();
5638 }
5639
5640 if (mData->mCurrentSnapshot)
5641 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
5642 else
5643 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
5644
5645 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
5646 mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
5647 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
5648 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
5649
5650 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
5651 CheckComRCThrowRC(rc);
5652
5653 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
5654 CheckComRCThrowRC(rc);
5655
5656 // save snapshots
5657 rc = saveAllSnapshots();
5658 CheckComRCThrowRC(rc);
5659
5660 // now spit it all out
5661 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
5662 }
5663 catch (HRESULT err)
5664 {
5665 /* we assume that error info is set by the thrower */
5666 rc = err;
5667 }
5668 catch (...)
5669 {
5670 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
5671 }
5672
5673 if (SUCCEEDED(rc))
5674 {
5675 commit();
5676
5677 /* memorize the new modified state */
5678 mData->mCurrentStateModified = currentStateModified;
5679 }
5680
5681 if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
5682 {
5683 /* Fire the data change event, even on failure (since we've already
5684 * committed all data). This is done only for SessionMachines because
5685 * mutable Machine instances are always not registered (i.e. private
5686 * to the client process that creates them) and thus don't need to
5687 * inform callbacks. */
5688 if (mType == IsSessionMachine)
5689 mParent->onMachineDataChange(mData->mUuid);
5690 }
5691
5692 LogFlowThisFunc(("rc=%08X\n", rc));
5693 LogFlowThisFuncLeave();
5694 return rc;
5695}
5696
5697HRESULT Machine::saveAllSnapshots()
5698{
5699 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
5700
5701 HRESULT rc = S_OK;
5702
5703 try
5704 {
5705 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
5706
5707 if (mData->mFirstSnapshot)
5708 {
5709 settings::Snapshot snapNew;
5710 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
5711
5712 // get reference to the fresh copy of the snapshot on the list and
5713 // work on that copy directly to avoid excessive copying later
5714 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
5715
5716 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
5717 CheckComRCThrowRC(rc);
5718 }
5719
5720// if (mType == IsSessionMachine)
5721// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
5722
5723 }
5724 catch (HRESULT err)
5725 {
5726 /* we assume that error info is set by the thrower */
5727 rc = err;
5728 }
5729 catch (...)
5730 {
5731 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
5732 }
5733
5734 return rc;
5735}
5736
5737/**
5738 * Saves the VM hardware configuration. It is assumed that the
5739 * given node is empty.
5740 *
5741 * @param aNode <Hardware> node to save the VM hardware confguration to.
5742 */
5743HRESULT Machine::saveHardware(settings::Hardware &data)
5744{
5745 HRESULT rc = S_OK;
5746
5747 try
5748 {
5749 /* The hardware version attribute (optional).
5750 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
5751 if ( mHWData->mHWVersion == "1"
5752 && mSSData->mStateFilePath.isEmpty()
5753 )
5754 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. */
5755
5756 data.strVersion = mHWData->mHWVersion;
5757
5758 // CPU
5759 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
5760 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
5761 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
5762 data.fPAE = !!mHWData->mPAEEnabled;
5763
5764 data.cCPUs = mHWData->mCPUCount;
5765
5766 // memory
5767 data.ulMemorySizeMB = mHWData->mMemorySize;
5768
5769 // boot order
5770 data.mapBootOrder.clear();
5771 for (size_t i = 0;
5772 i < RT_ELEMENTS(mHWData->mBootOrder);
5773 ++i)
5774 data.mapBootOrder[i] = mHWData->mBootOrder[i];
5775
5776 // display
5777 data.ulVRAMSizeMB = mHWData->mVRAMSize;
5778 data.cMonitors = mHWData->mMonitorCount;
5779 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
5780 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
5781
5782#ifdef VBOX_WITH_VRDP
5783 /* VRDP settings (optional) */
5784 rc = mVRDPServer->saveSettings(data.vrdpSettings);
5785 CheckComRCThrowRC(rc);
5786#endif
5787
5788 /* BIOS (required) */
5789 rc = mBIOSSettings->saveSettings(data.biosSettings);
5790 CheckComRCThrowRC(rc);
5791
5792 /* DVD drive (required) */
5793 rc = mDVDDrive->saveSettings(data.dvdDrive);
5794 CheckComRCThrowRC(rc);
5795
5796 /* Flooppy drive (required) */
5797 rc = mFloppyDrive->saveSettings(data.floppyDrive);
5798 CheckComRCThrowRC(rc);
5799
5800 /* USB Controller (required) */
5801 rc = mUSBController->saveSettings(data.usbController);
5802 CheckComRCThrowRC(rc);
5803
5804 /* Network adapters (required) */
5805 data.llNetworkAdapters.clear();
5806 for (ULONG slot = 0;
5807 slot < RT_ELEMENTS(mNetworkAdapters);
5808 ++slot)
5809 {
5810 settings::NetworkAdapter nic;
5811 nic.ulSlot = slot;
5812 rc = mNetworkAdapters[slot]->saveSettings(nic);
5813 CheckComRCThrowRC(rc);
5814
5815 data.llNetworkAdapters.push_back(nic);
5816 }
5817
5818 /* Serial ports */
5819 data.llSerialPorts.clear();
5820 for (ULONG slot = 0;
5821 slot < RT_ELEMENTS(mSerialPorts);
5822 ++slot)
5823 {
5824 settings::SerialPort s;
5825 s.ulSlot = slot;
5826 rc = mSerialPorts[slot]->saveSettings(s);
5827 CheckComRCReturnRC (rc);
5828
5829 data.llSerialPorts.push_back(s);
5830 }
5831
5832 /* Parallel ports */
5833 data.llParallelPorts.clear();
5834 for (ULONG slot = 0;
5835 slot < RT_ELEMENTS(mParallelPorts);
5836 ++slot)
5837 {
5838 settings::ParallelPort p;
5839 p.ulSlot = slot;
5840 rc = mParallelPorts[slot]->saveSettings(p);
5841 CheckComRCReturnRC (rc);
5842
5843 data.llParallelPorts.push_back(p);
5844 }
5845
5846 /* Audio adapter */
5847 rc = mAudioAdapter->saveSettings(data.audioAdapter);
5848 CheckComRCReturnRC (rc);
5849
5850 /* Shared folders */
5851 data.llSharedFolders.clear();
5852 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
5853 it != mHWData->mSharedFolders.end();
5854 ++it)
5855 {
5856 ComObjPtr<SharedFolder> pFolder = *it;
5857 settings::SharedFolder sf;
5858 sf.strName = pFolder->name();
5859 sf.strHostPath = pFolder->hostPath();
5860 sf.fWritable = !!pFolder->writable();
5861
5862 data.llSharedFolders.push_back(sf);
5863 }
5864
5865 // clipboard
5866 data.clipboardMode = mHWData->mClipboardMode;
5867
5868 /* Guest */
5869 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
5870 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
5871
5872 // guest properties
5873 data.llGuestProperties.clear();
5874#ifdef VBOX_WITH_GUEST_PROPS
5875 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
5876 it != mHWData->mGuestProperties.end();
5877 ++it)
5878 {
5879 HWData::GuestProperty property = *it;
5880
5881 settings::GuestProperty prop;
5882 prop.strName = property.strName;
5883 prop.strValue = property.strValue;
5884 prop.timestamp = property.mTimestamp;
5885 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
5886 guestProp::writeFlags(property.mFlags, szFlags);
5887 prop.strFlags = szFlags;
5888
5889 data.llGuestProperties.push_back(prop);
5890 }
5891
5892 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
5893#endif /* VBOX_WITH_GUEST_PROPS defined */
5894 }
5895 catch(std::bad_alloc &)
5896 {
5897 return E_OUTOFMEMORY;
5898 }
5899
5900 AssertComRC(rc);
5901 return rc;
5902}
5903
5904/**
5905 * Saves the storage controller configuration.
5906 *
5907 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
5908 */
5909HRESULT Machine::saveStorageControllers(settings::Storage &data)
5910{
5911 data.llStorageControllers.clear();
5912
5913 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5914 it != mStorageControllers->end();
5915 ++it)
5916 {
5917 HRESULT rc;
5918 ComObjPtr<StorageController> pCtl = *it;
5919
5920 settings::StorageController ctl;
5921 ctl.strName = pCtl->name();
5922 ctl.controllerType = pCtl->controllerType();
5923 ctl.storageBus = pCtl->storageBus();
5924
5925 /* Save the port count. */
5926 ULONG portCount;
5927 rc = pCtl->COMGETTER(PortCount)(&portCount);
5928 ComAssertComRCRet(rc, rc);
5929 ctl.ulPortCount = portCount;
5930
5931 /* Save IDE emulation settings. */
5932 if (ctl.controllerType == StorageControllerType_IntelAhci)
5933 {
5934 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
5935 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
5936 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
5937 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
5938 )
5939 ComAssertComRCRet(rc, rc);
5940 }
5941
5942 /* save the devices now. */
5943 rc = saveStorageDevices(pCtl, ctl);
5944 ComAssertComRCRet(rc, rc);
5945
5946 data.llStorageControllers.push_back(ctl);
5947 }
5948
5949 return S_OK;
5950}
5951
5952/**
5953 * Saves the hard disk confguration.
5954 * It is assumed that the given node is empty.
5955 *
5956 * @param aNode <HardDiskAttachments> node to save the hard disk confguration to.
5957 */
5958HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
5959 settings::StorageController &data)
5960{
5961 using namespace settings;
5962
5963 HDData::AttachmentList atts;
5964
5965 HRESULT rc = getHardDiskAttachmentsOfController(Bstr(aStorageController->name()), atts);
5966 CheckComRCReturnRC (rc);
5967
5968 data.llAttachedDevices.clear();
5969 for (HDData::AttachmentList::const_iterator it = atts.begin();
5970 it != atts.end();
5971 ++it)
5972 {
5973 settings::AttachedDevice dev;
5974
5975 dev.type = settings::AttachedDevice::HardDisk;
5976 dev.lPort = (*it)->port();
5977 dev.lDevice = (*it)->device();
5978 dev.uuid = (*it)->hardDisk()->id();
5979
5980 data.llAttachedDevices.push_back(dev);
5981 }
5982
5983 return S_OK;
5984}
5985
5986/**
5987 * Saves machine state settings as defined by aFlags
5988 * (SaveSTS_* values).
5989 *
5990 * @param aFlags Combination of SaveSTS_* flags.
5991 *
5992 * @note Locks objects for writing.
5993 */
5994HRESULT Machine::saveStateSettings(int aFlags)
5995{
5996 if (aFlags == 0)
5997 return S_OK;
5998
5999 AutoCaller autoCaller (this);
6000 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6001
6002 /* This object's write lock is also necessary to serialize file access
6003 * (prevent concurrent reads and writes) */
6004 AutoWriteLock alock(this);
6005
6006 HRESULT rc = S_OK;
6007
6008 Assert(mData->m_pMachineConfigFile);
6009
6010 try
6011 {
6012 if (aFlags & SaveSTS_CurStateModified)
6013 mData->m_pMachineConfigFile->fCurrentStateModified = true;
6014
6015 if (aFlags & SaveSTS_StateFilePath)
6016 {
6017 if (mSSData->mStateFilePath)
6018 {
6019 /* try to make the file name relative to the settings file dir */
6020 Utf8Str stateFilePath = mSSData->mStateFilePath;
6021 calculateRelativePath(stateFilePath, stateFilePath);
6022 mData->m_pMachineConfigFile->strStateFile = stateFilePath;
6023 }
6024 else
6025 mData->m_pMachineConfigFile->strStateFile.setNull();
6026 }
6027
6028 if (aFlags & SaveSTS_StateTimeStamp)
6029 {
6030 Assert( mData->mMachineState != MachineState_Aborted
6031 || mSSData->mStateFilePath.isNull());
6032
6033 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6034
6035 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6036 }
6037
6038 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6039 }
6040 catch (...)
6041 {
6042 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6043 }
6044
6045 return rc;
6046}
6047
6048/**
6049 * Creates differencing hard disks for all normal hard disks attached to this
6050 * machine and a new set of attachments to refer to created disks.
6051 *
6052 * Used when taking a snapshot or when discarding the current state.
6053 *
6054 * This method assumes that mHDData contains the original hard disk attachments
6055 * it needs to create diffs for. On success, these attachments will be replaced
6056 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
6057 * called to delete created diffs which will also rollback mHDData and restore
6058 * whatever was backed up before calling this method.
6059 *
6060 * Attachments with non-normal hard disks are left as is.
6061 *
6062 * If @a aOnline is @c false then the original hard disks that require implicit
6063 * diffs will be locked for reading. Otherwise it is assumed that they are
6064 * already locked for writing (when the VM was started). Note that in the latter
6065 * case it is responsibility of the caller to lock the newly created diffs for
6066 * writing if this method succeeds.
6067 *
6068 * @param aFolder Folder where to create diff hard disks.
6069 * @param aProgress Progress object to run (must contain at least as
6070 * many operations left as the number of hard disks
6071 * attached).
6072 * @param aOnline Whether the VM was online prior to this operation.
6073 *
6074 * @note The progress object is not marked as completed, neither on success nor
6075 * on failure. This is a responsibility of the caller.
6076 *
6077 * @note Locks this object for writing.
6078 */
6079HRESULT Machine::createImplicitDiffs (const Bstr &aFolder,
6080 ComObjPtr<Progress> &aProgress,
6081 bool aOnline)
6082{
6083 AssertReturn(!aFolder.isEmpty(), E_FAIL);
6084
6085 AutoCaller autoCaller(this);
6086 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6087
6088 AutoWriteLock alock(this);
6089
6090 /* must be in a protective state because we leave the lock below */
6091 AssertReturn(mData->mMachineState == MachineState_Saving ||
6092 mData->mMachineState == MachineState_Discarding, E_FAIL);
6093
6094 HRESULT rc = S_OK;
6095
6096 typedef std::list< ComObjPtr<HardDisk> > LockedMedia;
6097 LockedMedia lockedMedia;
6098
6099 try
6100 {
6101 if (!aOnline)
6102 {
6103 /* lock all attached hard disks early to detect "in use"
6104 * situations before creating actual diffs */
6105 for (HDData::AttachmentList::const_iterator
6106 it = mHDData->mAttachments.begin();
6107 it != mHDData->mAttachments.end();
6108 ++ it)
6109 {
6110 ComObjPtr<HardDiskAttachment> hda = *it;
6111 ComObjPtr<HardDisk> hd = hda->hardDisk();
6112
6113 rc = hd->LockRead(NULL);
6114 CheckComRCThrowRC(rc);
6115
6116 lockedMedia.push_back(hd);
6117 }
6118 }
6119
6120 /* remember the current list (note that we don't use backup() since
6121 * mHDData may be already backed up) */
6122 HDData::AttachmentList atts = mHDData->mAttachments;
6123
6124 /* start from scratch */
6125 mHDData->mAttachments.clear();
6126
6127 /* go through remembered attachments and create diffs for normal hard
6128 * disks and attach them */
6129
6130 for (HDData::AttachmentList::const_iterator it = atts.begin();
6131 it != atts.end();
6132 ++it)
6133 {
6134 ComObjPtr<HardDiskAttachment> hda = *it;
6135 ComObjPtr<HardDisk> hd = hda->hardDisk();
6136
6137 /* type cannot be changed while attached => no need to lock */
6138 if (hd->type() != HardDiskType_Normal)
6139 {
6140 /* copy the attachment as is */
6141 Assert(hd->type() == HardDiskType_Writethrough);
6142
6143 rc = aProgress->setNextOperation(BstrFmt(tr("Skipping writethrough hard disk '%s'"),
6144 hd->root()->name().raw()),
6145 1); // weight
6146 CheckComRCThrowRC(rc);
6147
6148 mHDData->mAttachments.push_back(hda);
6149 continue;
6150 }
6151
6152 /* need a diff */
6153 rc = aProgress->setNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
6154 hd->root()->name().raw()),
6155 1); // weight
6156 CheckComRCThrowRC(rc);
6157
6158 ComObjPtr<HardDisk> diff;
6159 diff.createObject();
6160 rc = diff->init(mParent,
6161 hd->preferredDiffFormat(),
6162 BstrFmt("%ls"RTPATH_SLASH_STR,
6163 mUserData->mSnapshotFolderFull.raw()));
6164 CheckComRCThrowRC(rc);
6165
6166 /* leave the lock before the potentially lengthy operation */
6167 alock.leave();
6168
6169 LogFlowThisFunc(("Calling createDiffStorageAndWait() on hard disk '%s'\n", hd->root()->name().raw()));
6170
6171 rc = hd->createDiffStorageAndWait(diff,
6172 HardDiskVariant_Standard,
6173 &aProgress);
6174
6175 // at this point, the old image is still locked for writing, but instead
6176 // we need the new diff image locked for writing and lock the previously
6177 // current one for reading only
6178 if (aOnline)
6179 {
6180 diff->LockWrite(NULL);
6181 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IHardDisk>(diff), true));
6182 hd->UnlockWrite(NULL);
6183 hd->LockRead(NULL);
6184 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IHardDisk>(hd), false));
6185 }
6186
6187 alock.enter();
6188
6189 CheckComRCThrowRC(rc);
6190
6191 rc = diff->attachTo(mData->mUuid);
6192 AssertComRCThrowRC(rc);
6193
6194 /* add a new attachment */
6195 ComObjPtr<HardDiskAttachment> attachment;
6196 attachment.createObject();
6197 rc = attachment->init(diff,
6198 hda->controller(),
6199 hda->port(),
6200 hda->device(),
6201 true /* aImplicit */);
6202 CheckComRCThrowRC(rc);
6203
6204 mHDData->mAttachments.push_back(attachment);
6205 }
6206 }
6207 catch (HRESULT aRC) { rc = aRC; }
6208
6209 /* unlock all hard disks we locked */
6210 if (!aOnline)
6211 {
6212 ErrorInfoKeeper eik;
6213
6214 for (LockedMedia::const_iterator it = lockedMedia.begin();
6215 it != lockedMedia.end();
6216 ++it)
6217 {
6218 HRESULT rc2 = (*it)->UnlockRead(NULL);
6219 AssertComRC(rc2);
6220 }
6221 }
6222
6223 if (FAILED(rc))
6224 {
6225 MultiResultRef mrc (rc);
6226
6227 mrc = deleteImplicitDiffs();
6228 }
6229
6230 return rc;
6231}
6232
6233/**
6234 * Deletes implicit differencing hard disks created either by
6235 * #createImplicitDiffs() or by #AttachHardDisk() and rolls back mHDData.
6236 *
6237 * Note that to delete hard disks created by #AttachHardDisk() this method is
6238 * called from #fixupHardDisks() when the changes are rolled back.
6239 *
6240 * @note Locks this object for writing.
6241 */
6242HRESULT Machine::deleteImplicitDiffs()
6243{
6244 AutoCaller autoCaller(this);
6245 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6246
6247 AutoWriteLock alock(this);
6248
6249 AssertReturn(mHDData.isBackedUp(), E_FAIL);
6250
6251 HRESULT rc = S_OK;
6252
6253 HDData::AttachmentList implicitAtts;
6254
6255 const HDData::AttachmentList &oldAtts =
6256 mHDData.backedUpData()->mAttachments;
6257
6258 /* enumerate new attachments */
6259 for (HDData::AttachmentList::const_iterator
6260 it = mHDData->mAttachments.begin();
6261 it != mHDData->mAttachments.end(); ++ it)
6262 {
6263 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
6264
6265 if ((*it)->isImplicit())
6266 {
6267 /* deassociate and mark for deletion */
6268 rc = hd->detachFrom (mData->mUuid);
6269 AssertComRC(rc);
6270 implicitAtts.push_back (*it);
6271 continue;
6272 }
6273
6274 /* was this hard disk attached before? */
6275 HDData::AttachmentList::const_iterator oldIt =
6276 std::find_if(oldAtts.begin(), oldAtts.end(),
6277 HardDiskAttachment::RefersTo (hd));
6278 if (oldIt == oldAtts.end())
6279 {
6280 /* no: de-associate */
6281 rc = hd->detachFrom (mData->mUuid);
6282 AssertComRC(rc);
6283 continue;
6284 }
6285 }
6286
6287 /* rollback hard disk changes */
6288 mHDData.rollback();
6289
6290 MultiResult mrc (S_OK);
6291
6292 /* delete unused implicit diffs */
6293 if (implicitAtts.size() != 0)
6294 {
6295 /* will leave the lock before the potentially lengthy
6296 * operation, so protect with the special state (unless already
6297 * protected) */
6298 MachineState_T oldState = mData->mMachineState;
6299 if (oldState != MachineState_Saving &&
6300 oldState != MachineState_Discarding)
6301 {
6302 setMachineState (MachineState_SettingUp);
6303 }
6304
6305 alock.leave();
6306
6307 for (HDData::AttachmentList::const_iterator
6308 it = implicitAtts.begin();
6309 it != implicitAtts.end(); ++ it)
6310 {
6311 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
6312
6313 mrc = hd->deleteStorageAndWait();
6314 }
6315
6316 alock.enter();
6317
6318 if (mData->mMachineState == MachineState_SettingUp)
6319 {
6320 setMachineState (oldState);
6321 }
6322 }
6323
6324 return mrc;
6325}
6326
6327/**
6328 * Perform deferred hard disk detachments on success and deletion of implicitly
6329 * created diffs on failure.
6330 *
6331 * Does nothing if the hard disk attachment data (mHDData) is not changed (not
6332 * backed up).
6333 *
6334 * When the data is backed up, this method will commit mHDData if @a aCommit is
6335 * @c true and rollback it otherwise before returning.
6336 *
6337 * If @a aOnline is @c true then this method called with @a aCommit = @c true
6338 * will also unlock the old hard disks for which the new implicit diffs were
6339 * created and will lock these new diffs for writing. When @a aCommit is @c
6340 * false, this argument is ignored.
6341 *
6342 * @param aCommit @c true if called on success.
6343 * @param aOnline Whether the VM was online prior to this operation.
6344 *
6345 * @note Locks this object for writing!
6346 */
6347void Machine::fixupHardDisks(bool aCommit, bool aOnline /*= false*/)
6348{
6349 AutoCaller autoCaller(this);
6350 AssertComRCReturnVoid (autoCaller.rc());
6351
6352 AutoWriteLock alock(this);
6353
6354 /* no attach/detach operations -- nothing to do */
6355 if (!mHDData.isBackedUp())
6356 return;
6357
6358 HRESULT rc = S_OK;
6359
6360 if (aCommit)
6361 {
6362 HDData::AttachmentList &oldAtts =
6363 mHDData.backedUpData()->mAttachments;
6364
6365 /* enumerate new attachments */
6366 for (HDData::AttachmentList::const_iterator
6367 it = mHDData->mAttachments.begin();
6368 it != mHDData->mAttachments.end(); ++ it)
6369 {
6370 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
6371
6372 if ((*it)->isImplicit())
6373 {
6374 /* convert implicit attachment to normal */
6375 (*it)->setImplicit (false);
6376
6377 if (aOnline)
6378 {
6379 rc = hd->LockWrite (NULL);
6380 AssertComRC(rc);
6381
6382 mData->mSession.mLockedMedia.push_back (
6383 Data::Session::LockedMedia::value_type (
6384 ComPtr<IHardDisk> (hd), true));
6385
6386 /* also, relock the old hard disk which is a base for the
6387 * new diff for reading if the VM is online */
6388
6389 ComObjPtr<HardDisk> parent = hd->parent();
6390 /* make the relock atomic */
6391 AutoWriteLock parentLock (parent);
6392 rc = parent->UnlockWrite (NULL);
6393 AssertComRC(rc);
6394 rc = parent->LockRead (NULL);
6395 AssertComRC(rc);
6396
6397 /* XXX actually we should replace the old entry in that
6398 * vector (write lock => read lock) but this would take
6399 * some effort. So lets just ignore the error code in
6400 * SessionMachine::unlockMedia(). */
6401 mData->mSession.mLockedMedia.push_back (
6402 Data::Session::LockedMedia::value_type (
6403 ComPtr<IHardDisk> (parent), false));
6404 }
6405
6406 continue;
6407 }
6408
6409 /* was this hard disk attached before? */
6410 HDData::AttachmentList::iterator oldIt =
6411 std::find_if (oldAtts.begin(), oldAtts.end(),
6412 HardDiskAttachment::RefersTo (hd));
6413 if (oldIt != oldAtts.end())
6414 {
6415 /* yes: remove from old to avoid de-association */
6416 oldAtts.erase (oldIt);
6417 }
6418 }
6419
6420 /* enumerate remaining old attachments and de-associate from the
6421 * current machine state */
6422 for (HDData::AttachmentList::const_iterator it = oldAtts.begin();
6423 it != oldAtts.end(); ++ it)
6424 {
6425 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
6426
6427 /* now de-associate from the current machine state */
6428 rc = hd->detachFrom (mData->mUuid);
6429 AssertComRC(rc);
6430
6431 if (aOnline)
6432 {
6433 /* unlock since not used anymore */
6434 MediaState_T state;
6435 rc = hd->UnlockWrite (&state);
6436 /* the disk may be alredy relocked for reading above */
6437 Assert (SUCCEEDED(rc) || state == MediaState_LockedRead);
6438 }
6439 }
6440
6441 /* commit the hard disk changes */
6442 mHDData.commit();
6443
6444 if (mType == IsSessionMachine)
6445 {
6446 /* attach new data to the primary machine and reshare it */
6447 mPeer->mHDData.attach (mHDData);
6448 }
6449 }
6450 else
6451 {
6452 deleteImplicitDiffs();
6453 }
6454
6455 return;
6456}
6457
6458/**
6459 * Returns true if the settings file is located in the directory named exactly
6460 * as the machine. This will be true if the machine settings structure was
6461 * created by default in #openConfigLoader().
6462 *
6463 * @param aSettingsDir if not NULL, the full machine settings file directory
6464 * name will be assigned there.
6465 *
6466 * @note Doesn't lock anything.
6467 * @note Not thread safe (must be called from this object's lock).
6468 */
6469bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
6470{
6471 Utf8Str settingsDir = mData->m_strConfigFileFull;
6472 settingsDir.stripFilename();
6473 char *dirName = RTPathFilename(settingsDir.c_str());
6474
6475 AssertReturn(dirName, false);
6476
6477 /* if we don't rename anything on name change, return false shorlty */
6478 if (!mUserData->mNameSync)
6479 return false;
6480
6481 if (aSettingsDir)
6482 *aSettingsDir = settingsDir;
6483
6484 return Bstr (dirName) == mUserData->mName;
6485}
6486
6487/**
6488 * @note Locks objects for reading!
6489 */
6490bool Machine::isModified()
6491{
6492 AutoCaller autoCaller(this);
6493 AssertComRCReturn (autoCaller.rc(), false);
6494
6495 AutoReadLock alock(this);
6496
6497 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
6498 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
6499 return true;
6500
6501 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
6502 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
6503 return true;
6504
6505 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
6506 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
6507 return true;
6508
6509 if (!mStorageControllers.isNull())
6510 {
6511 for (StorageControllerList::const_iterator it =
6512 mStorageControllers->begin();
6513 it != mStorageControllers->end();
6514 ++ it)
6515 {
6516 if ((*it)->isModified())
6517 return true;
6518 }
6519 }
6520
6521 return
6522 mUserData.isBackedUp() ||
6523 mHWData.isBackedUp() ||
6524 mHDData.isBackedUp() ||
6525 mStorageControllers.isBackedUp() ||
6526#ifdef VBOX_WITH_VRDP
6527 (mVRDPServer && mVRDPServer->isModified()) ||
6528#endif
6529 (mDVDDrive && mDVDDrive->isModified()) ||
6530 (mFloppyDrive && mFloppyDrive->isModified()) ||
6531 (mAudioAdapter && mAudioAdapter->isModified()) ||
6532 (mUSBController && mUSBController->isModified()) ||
6533 (mBIOSSettings && mBIOSSettings->isModified());
6534}
6535
6536/**
6537 * Returns the logical OR of data.hasActualChanges() of this and all child
6538 * objects.
6539 *
6540 * @param aIgnoreUserData @c true to ignore changes to mUserData
6541 *
6542 * @note Locks objects for reading!
6543 */
6544bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
6545{
6546 AutoCaller autoCaller(this);
6547 AssertComRCReturn (autoCaller.rc(), false);
6548
6549 AutoReadLock alock(this);
6550
6551 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
6552 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
6553 return true;
6554
6555 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
6556 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
6557 return true;
6558
6559 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
6560 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
6561 return true;
6562
6563 if (!mStorageControllers.isBackedUp())
6564 {
6565 /* see whether any of the devices has changed its data */
6566 for (StorageControllerList::const_iterator
6567 it = mStorageControllers->begin();
6568 it != mStorageControllers->end();
6569 ++ it)
6570 {
6571 if ((*it)->isReallyModified())
6572 return true;
6573 }
6574 }
6575 else
6576 {
6577 if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
6578 return true;
6579 }
6580
6581 return
6582 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
6583 mHWData.hasActualChanges() ||
6584 mHDData.hasActualChanges() ||
6585 mStorageControllers.hasActualChanges() ||
6586#ifdef VBOX_WITH_VRDP
6587 (mVRDPServer && mVRDPServer->isReallyModified()) ||
6588#endif
6589 (mDVDDrive && mDVDDrive->isReallyModified()) ||
6590 (mFloppyDrive && mFloppyDrive->isReallyModified()) ||
6591 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
6592 (mUSBController && mUSBController->isReallyModified()) ||
6593 (mBIOSSettings && mBIOSSettings->isReallyModified());
6594}
6595
6596/**
6597 * Discards all changes to machine settings.
6598 *
6599 * @param aNotify Whether to notify the direct session about changes or not.
6600 *
6601 * @note Locks objects for writing!
6602 */
6603void Machine::rollback (bool aNotify)
6604{
6605 AutoCaller autoCaller(this);
6606 AssertComRCReturn (autoCaller.rc(), (void) 0);
6607
6608 AutoWriteLock alock(this);
6609
6610 /* check for changes in own data */
6611
6612 bool sharedFoldersChanged = false, storageChanged = false;
6613
6614 if (aNotify && mHWData.isBackedUp())
6615 {
6616 if (mHWData->mSharedFolders.size() !=
6617 mHWData.backedUpData()->mSharedFolders.size())
6618 sharedFoldersChanged = true;
6619 else
6620 {
6621 for (HWData::SharedFolderList::iterator rit =
6622 mHWData->mSharedFolders.begin();
6623 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
6624 ++ rit)
6625 {
6626 for (HWData::SharedFolderList::iterator cit =
6627 mHWData.backedUpData()->mSharedFolders.begin();
6628 cit != mHWData.backedUpData()->mSharedFolders.end();
6629 ++ cit)
6630 {
6631 if ((*cit)->name() != (*rit)->name() ||
6632 (*cit)->hostPath() != (*rit)->hostPath())
6633 {
6634 sharedFoldersChanged = true;
6635 break;
6636 }
6637 }
6638 }
6639 }
6640 }
6641
6642 if (!mStorageControllers.isNull())
6643 {
6644 if (mStorageControllers.isBackedUp())
6645 {
6646 /* unitialize all new devices (absent in the backed up list). */
6647 StorageControllerList::const_iterator it = mStorageControllers->begin();
6648 StorageControllerList *backedList = mStorageControllers.backedUpData();
6649 while (it != mStorageControllers->end())
6650 {
6651 if (std::find (backedList->begin(), backedList->end(), *it ) ==
6652 backedList->end())
6653 {
6654 (*it)->uninit();
6655 }
6656 ++ it;
6657 }
6658
6659 /* restore the list */
6660 mStorageControllers.rollback();
6661 }
6662
6663 /* rollback any changes to devices after restoring the list */
6664 StorageControllerList::const_iterator it = mStorageControllers->begin();
6665 while (it != mStorageControllers->end())
6666 {
6667 if ((*it)->isModified())
6668 (*it)->rollback();
6669
6670 ++ it;
6671 }
6672 }
6673
6674 mUserData.rollback();
6675
6676 mHWData.rollback();
6677
6678 if (mHDData.isBackedUp())
6679 fixupHardDisks(false /* aCommit */);
6680
6681 /* check for changes in child objects */
6682
6683 bool vrdpChanged = false, dvdChanged = false, floppyChanged = false,
6684 usbChanged = false;
6685
6686 ComPtr<INetworkAdapter> networkAdapters [RT_ELEMENTS (mNetworkAdapters)];
6687 ComPtr<ISerialPort> serialPorts [RT_ELEMENTS (mSerialPorts)];
6688 ComPtr<IParallelPort> parallelPorts [RT_ELEMENTS (mParallelPorts)];
6689
6690 if (mBIOSSettings)
6691 mBIOSSettings->rollback();
6692
6693#ifdef VBOX_WITH_VRDP
6694 if (mVRDPServer)
6695 vrdpChanged = mVRDPServer->rollback();
6696#endif
6697
6698 if (mDVDDrive)
6699 dvdChanged = mDVDDrive->rollback();
6700
6701 if (mFloppyDrive)
6702 floppyChanged = mFloppyDrive->rollback();
6703
6704 if (mAudioAdapter)
6705 mAudioAdapter->rollback();
6706
6707 if (mUSBController)
6708 usbChanged = mUSBController->rollback();
6709
6710 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
6711 if (mNetworkAdapters [slot])
6712 if (mNetworkAdapters [slot]->rollback())
6713 networkAdapters [slot] = mNetworkAdapters [slot];
6714
6715 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
6716 if (mSerialPorts [slot])
6717 if (mSerialPorts [slot]->rollback())
6718 serialPorts [slot] = mSerialPorts [slot];
6719
6720 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
6721 if (mParallelPorts [slot])
6722 if (mParallelPorts [slot]->rollback())
6723 parallelPorts [slot] = mParallelPorts [slot];
6724
6725 if (aNotify)
6726 {
6727 /* inform the direct session about changes */
6728
6729 ComObjPtr<Machine> that = this;
6730 alock.leave();
6731
6732 if (sharedFoldersChanged)
6733 that->onSharedFolderChange();
6734
6735 if (vrdpChanged)
6736 that->onVRDPServerChange();
6737 if (dvdChanged)
6738 that->onDVDDriveChange();
6739 if (floppyChanged)
6740 that->onFloppyDriveChange();
6741 if (usbChanged)
6742 that->onUSBControllerChange();
6743
6744 for (ULONG slot = 0; slot < RT_ELEMENTS (networkAdapters); slot ++)
6745 if (networkAdapters [slot])
6746 that->onNetworkAdapterChange (networkAdapters [slot], FALSE);
6747 for (ULONG slot = 0; slot < RT_ELEMENTS (serialPorts); slot ++)
6748 if (serialPorts [slot])
6749 that->onSerialPortChange (serialPorts [slot]);
6750 for (ULONG slot = 0; slot < RT_ELEMENTS (parallelPorts); slot ++)
6751 if (parallelPorts [slot])
6752 that->onParallelPortChange (parallelPorts [slot]);
6753
6754 if (storageChanged)
6755 that->onStorageControllerChange();
6756 }
6757}
6758
6759/**
6760 * Commits all the changes to machine settings.
6761 *
6762 * Note that this operation is supposed to never fail.
6763 *
6764 * @note Locks this object and children for writing.
6765 */
6766void Machine::commit()
6767{
6768 AutoCaller autoCaller(this);
6769 AssertComRCReturnVoid (autoCaller.rc());
6770
6771 AutoCaller peerCaller (mPeer);
6772 AssertComRCReturnVoid (peerCaller.rc());
6773
6774 AutoMultiWriteLock2 alock (mPeer, this);
6775
6776 /*
6777 * use safe commit to ensure Snapshot machines (that share mUserData)
6778 * will still refer to a valid memory location
6779 */
6780 mUserData.commitCopy();
6781
6782 mHWData.commit();
6783
6784 if (mHDData.isBackedUp())
6785 fixupHardDisks(true /* aCommit */);
6786
6787 mBIOSSettings->commit();
6788#ifdef VBOX_WITH_VRDP
6789 mVRDPServer->commit();
6790#endif
6791 mDVDDrive->commit();
6792 mFloppyDrive->commit();
6793 mAudioAdapter->commit();
6794 mUSBController->commit();
6795
6796 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
6797 mNetworkAdapters [slot]->commit();
6798 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
6799 mSerialPorts [slot]->commit();
6800 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
6801 mParallelPorts [slot]->commit();
6802
6803 bool commitStorageControllers = false;
6804
6805 if (mStorageControllers.isBackedUp())
6806 {
6807 mStorageControllers.commit();
6808
6809 if (mPeer)
6810 {
6811 AutoWriteLock peerlock (mPeer);
6812
6813 /* Commit all changes to new controllers (this will reshare data with
6814 * peers for thos who have peers) */
6815 StorageControllerList *newList = new StorageControllerList();
6816 StorageControllerList::const_iterator it = mStorageControllers->begin();
6817 while (it != mStorageControllers->end())
6818 {
6819 (*it)->commit();
6820
6821 /* look if this controller has a peer device */
6822 ComObjPtr<StorageController> peer = (*it)->peer();
6823 if (!peer)
6824 {
6825 /* no peer means the device is a newly created one;
6826 * create a peer owning data this device share it with */
6827 peer.createObject();
6828 peer->init (mPeer, *it, true /* aReshare */);
6829 }
6830 else
6831 {
6832 /* remove peer from the old list */
6833 mPeer->mStorageControllers->remove (peer);
6834 }
6835 /* and add it to the new list */
6836 newList->push_back(peer);
6837
6838 ++ it;
6839 }
6840
6841 /* uninit old peer's controllers that are left */
6842 it = mPeer->mStorageControllers->begin();
6843 while (it != mPeer->mStorageControllers->end())
6844 {
6845 (*it)->uninit();
6846 ++ it;
6847 }
6848
6849 /* attach new list of controllers to our peer */
6850 mPeer->mStorageControllers.attach (newList);
6851 }
6852 else
6853 {
6854 /* we have no peer (our parent is the newly created machine);
6855 * just commit changes to devices */
6856 commitStorageControllers = true;
6857 }
6858 }
6859 else
6860 {
6861 /* the list of controllers itself is not changed,
6862 * just commit changes to controllers themselves */
6863 commitStorageControllers = true;
6864 }
6865
6866 if (commitStorageControllers)
6867 {
6868 StorageControllerList::const_iterator it = mStorageControllers->begin();
6869 while (it != mStorageControllers->end())
6870 {
6871 (*it)->commit();
6872 ++ it;
6873 }
6874 }
6875
6876 if (mType == IsSessionMachine)
6877 {
6878 /* attach new data to the primary machine and reshare it */
6879 mPeer->mUserData.attach (mUserData);
6880 mPeer->mHWData.attach (mHWData);
6881 /* mHDData is reshared by fixupHardDisks */
6882 // mPeer->mHDData.attach (mHDData);
6883 Assert (mPeer->mHDData.data() == mHDData.data());
6884 }
6885}
6886
6887/**
6888 * Copies all the hardware data from the given machine.
6889 *
6890 * Currently, only called when the VM is being restored from a snapshot. In
6891 * particular, this implies that the VM is not running during this method's
6892 * call.
6893 *
6894 * @note This method must be called from under this object's lock.
6895 *
6896 * @note This method doesn't call #commit(), so all data remains backed up and
6897 * unsaved.
6898 */
6899void Machine::copyFrom (Machine *aThat)
6900{
6901 AssertReturnVoid (mType == IsMachine || mType == IsSessionMachine);
6902 AssertReturnVoid (aThat->mType == IsSnapshotMachine);
6903
6904 AssertReturnVoid (!Global::IsOnline (mData->mMachineState));
6905
6906 mHWData.assignCopy (aThat->mHWData);
6907
6908 // create copies of all shared folders (mHWData after attiching a copy
6909 // contains just references to original objects)
6910 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
6911 it != mHWData->mSharedFolders.end();
6912 ++ it)
6913 {
6914 ComObjPtr<SharedFolder> folder;
6915 folder.createObject();
6916 HRESULT rc = folder->initCopy (machine(), *it);
6917 AssertComRC (rc);
6918 *it = folder;
6919 }
6920
6921 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
6922#ifdef VBOX_WITH_VRDP
6923 mVRDPServer->copyFrom (aThat->mVRDPServer);
6924#endif
6925 mDVDDrive->copyFrom (aThat->mDVDDrive);
6926 mFloppyDrive->copyFrom (aThat->mFloppyDrive);
6927 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
6928 mUSBController->copyFrom (aThat->mUSBController);
6929
6930 /* create private copies of all controllers */
6931 mStorageControllers.backup();
6932 mStorageControllers->clear();
6933 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
6934 it != aThat->mStorageControllers->end();
6935 ++ it)
6936 {
6937 ComObjPtr<StorageController> ctrl;
6938 ctrl.createObject();
6939 ctrl->initCopy (this, *it);
6940 mStorageControllers->push_back(ctrl);
6941 }
6942
6943 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
6944 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
6945 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
6946 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
6947 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
6948 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
6949}
6950
6951#ifdef VBOX_WITH_RESOURCE_USAGE_API
6952void Machine::registerMetrics (PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
6953{
6954 pm::CollectorHAL *hal = aCollector->getHAL();
6955 /* Create sub metrics */
6956 pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User",
6957 "Percentage of processor time spent in user mode by VM process.");
6958 pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel",
6959 "Percentage of processor time spent in kernel mode by VM process.");
6960 pm::SubMetric *ramUsageUsed = new pm::SubMetric ("RAM/Usage/Used",
6961 "Size of resident portion of VM process in memory.");
6962 /* Create and register base metrics */
6963 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw (hal, aMachine, pid,
6964 cpuLoadUser, cpuLoadKernel);
6965 aCollector->registerBaseMetric (cpuLoad);
6966 pm::BaseMetric *ramUsage = new pm::MachineRamUsage (hal, aMachine, pid,
6967 ramUsageUsed);
6968 aCollector->registerBaseMetric (ramUsage);
6969
6970 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser, 0));
6971 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
6972 new pm::AggregateAvg()));
6973 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
6974 new pm::AggregateMin()));
6975 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
6976 new pm::AggregateMax()));
6977 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel, 0));
6978 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
6979 new pm::AggregateAvg()));
6980 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
6981 new pm::AggregateMin()));
6982 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
6983 new pm::AggregateMax()));
6984
6985 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed, 0));
6986 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
6987 new pm::AggregateAvg()));
6988 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
6989 new pm::AggregateMin()));
6990 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
6991 new pm::AggregateMax()));
6992};
6993
6994void Machine::unregisterMetrics (PerformanceCollector *aCollector, Machine *aMachine)
6995{
6996 aCollector->unregisterMetricsFor (aMachine);
6997 aCollector->unregisterBaseMetricsFor (aMachine);
6998};
6999#endif /* VBOX_WITH_RESOURCE_USAGE_API */
7000
7001
7002/////////////////////////////////////////////////////////////////////////////
7003// SessionMachine class
7004/////////////////////////////////////////////////////////////////////////////
7005
7006/** Task structure for asynchronous VM operations */
7007struct SessionMachine::Task
7008{
7009 Task (SessionMachine *m, Progress *p)
7010 : machine (m), progress (p)
7011 , state (m->mData->mMachineState) // save the current machine state
7012 , subTask (false)
7013 {}
7014
7015 void modifyLastState (MachineState_T s)
7016 {
7017 *const_cast <MachineState_T *> (&state) = s;
7018 }
7019
7020 virtual void handler() = 0;
7021
7022 ComObjPtr<SessionMachine> machine;
7023 ComObjPtr<Progress> progress;
7024 const MachineState_T state;
7025
7026 bool subTask : 1;
7027};
7028
7029/** Take snapshot task */
7030struct SessionMachine::TakeSnapshotTask
7031 : public SessionMachine::Task
7032{
7033 TakeSnapshotTask(SessionMachine *m)
7034 : Task(m, NULL)
7035 {}
7036
7037 void handler()
7038 {
7039 machine->takeSnapshotHandler(*this);
7040 }
7041};
7042
7043/** Discard snapshot task */
7044struct SessionMachine::DiscardSnapshotTask
7045 : public SessionMachine::Task
7046{
7047 DiscardSnapshotTask(SessionMachine *m, Progress *p, Snapshot *s)
7048 : Task (m, p),
7049 snapshot (s)
7050 {}
7051
7052 DiscardSnapshotTask (const Task &task, Snapshot *s)
7053 : Task (task)
7054 , snapshot (s)
7055 {}
7056
7057 void handler()
7058 {
7059 machine->discardSnapshotHandler(*this);
7060 }
7061
7062 ComObjPtr<Snapshot> snapshot;
7063};
7064
7065/** Discard current state task */
7066struct SessionMachine::DiscardCurrentStateTask
7067 : public SessionMachine::Task
7068{
7069 DiscardCurrentStateTask(SessionMachine *m, Progress *p, bool discardCurSnapshot)
7070 : Task(m, p),
7071 discardCurrentSnapshot(discardCurSnapshot)
7072 {}
7073
7074 void handler()
7075 {
7076 machine->discardCurrentStateHandler(*this);
7077 }
7078
7079 const bool discardCurrentSnapshot;
7080};
7081
7082////////////////////////////////////////////////////////////////////////////////
7083
7084DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
7085
7086HRESULT SessionMachine::FinalConstruct()
7087{
7088 LogFlowThisFunc(("\n"));
7089
7090 /* set the proper type to indicate we're the SessionMachine instance */
7091 unconst(mType) = IsSessionMachine;
7092
7093#if defined(RT_OS_WINDOWS)
7094 mIPCSem = NULL;
7095#elif defined(RT_OS_OS2)
7096 mIPCSem = NULLHANDLE;
7097#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7098 mIPCSem = -1;
7099#else
7100# error "Port me!"
7101#endif
7102
7103 return S_OK;
7104}
7105
7106void SessionMachine::FinalRelease()
7107{
7108 LogFlowThisFunc(("\n"));
7109
7110 uninit (Uninit::Unexpected);
7111}
7112
7113/**
7114 * @note Must be called only by Machine::openSession() from its own write lock.
7115 */
7116HRESULT SessionMachine::init (Machine *aMachine)
7117{
7118 LogFlowThisFuncEnter();
7119 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
7120
7121 AssertReturn(aMachine, E_INVALIDARG);
7122
7123 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
7124
7125 /* Enclose the state transition NotReady->InInit->Ready */
7126 AutoInitSpan autoInitSpan(this);
7127 AssertReturn(autoInitSpan.isOk(), E_FAIL);
7128
7129 /* create the interprocess semaphore */
7130#if defined(RT_OS_WINDOWS)
7131 mIPCSemName = aMachine->mData->m_strConfigFileFull;
7132 for (size_t i = 0; i < mIPCSemName.length(); i++)
7133 if (mIPCSemName[i] == '\\')
7134 mIPCSemName[i] = '/';
7135 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
7136 ComAssertMsgRet (mIPCSem,
7137 ("Cannot create IPC mutex '%ls', err=%d",
7138 mIPCSemName.raw(), ::GetLastError()),
7139 E_FAIL);
7140#elif defined(RT_OS_OS2)
7141 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%RTuuid}",
7142 aMachine->mData->mUuid.raw());
7143 mIPCSemName = ipcSem;
7144 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
7145 ComAssertMsgRet (arc == NO_ERROR,
7146 ("Cannot create IPC mutex '%s', arc=%ld",
7147 ipcSem.raw(), arc),
7148 E_FAIL);
7149#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7150# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
7151# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
7152 /** @todo Check that this still works correctly. */
7153 AssertCompileSize(key_t, 8);
7154# else
7155 AssertCompileSize(key_t, 4);
7156# endif
7157 key_t key;
7158 mIPCSem = -1;
7159 mIPCKey = "0";
7160 for (uint32_t i = 0; i < 1 << 24; i++)
7161 {
7162 key = ((uint32_t)'V' << 24) | i;
7163 int sem = ::semget (key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
7164 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
7165 {
7166 mIPCSem = sem;
7167 if (sem >= 0)
7168 mIPCKey = BstrFmt ("%u", key);
7169 break;
7170 }
7171 }
7172# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
7173 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
7174 char *pszSemName = NULL;
7175 RTStrUtf8ToCurrentCP (&pszSemName, semName);
7176 key_t key = ::ftok (pszSemName, 'V');
7177 RTStrFree (pszSemName);
7178
7179 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
7180# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
7181
7182 int errnoSave = errno;
7183 if (mIPCSem < 0 && errnoSave == ENOSYS)
7184 {
7185 setError(E_FAIL,
7186 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
7187 "support for SysV IPC. Check the host kernel configuration for "
7188 "CONFIG_SYSVIPC=y"));
7189 return E_FAIL;
7190 }
7191 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
7192 E_FAIL);
7193 /* set the initial value to 1 */
7194 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
7195 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
7196 E_FAIL);
7197#else
7198# error "Port me!"
7199#endif
7200
7201 /* memorize the peer Machine */
7202 unconst(mPeer) = aMachine;
7203 /* share the parent pointer */
7204 unconst(mParent) = aMachine->mParent;
7205
7206 /* take the pointers to data to share */
7207 mData.share (aMachine->mData);
7208 mSSData.share (aMachine->mSSData);
7209
7210 mUserData.share (aMachine->mUserData);
7211 mHWData.share (aMachine->mHWData);
7212 mHDData.share (aMachine->mHDData);
7213
7214 mStorageControllers.allocate();
7215 StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
7216 while (it != aMachine->mStorageControllers->end())
7217 {
7218 ComObjPtr<StorageController> ctl;
7219 ctl.createObject();
7220 ctl->init(this, *it);
7221 mStorageControllers->push_back (ctl);
7222 ++ it;
7223 }
7224
7225 unconst(mBIOSSettings).createObject();
7226 mBIOSSettings->init (this, aMachine->mBIOSSettings);
7227#ifdef VBOX_WITH_VRDP
7228 /* create another VRDPServer object that will be mutable */
7229 unconst(mVRDPServer).createObject();
7230 mVRDPServer->init (this, aMachine->mVRDPServer);
7231#endif
7232 /* create another DVD drive object that will be mutable */
7233 unconst(mDVDDrive).createObject();
7234 mDVDDrive->init (this, aMachine->mDVDDrive);
7235 /* create another floppy drive object that will be mutable */
7236 unconst(mFloppyDrive).createObject();
7237 mFloppyDrive->init (this, aMachine->mFloppyDrive);
7238 /* create another audio adapter object that will be mutable */
7239 unconst(mAudioAdapter).createObject();
7240 mAudioAdapter->init (this, aMachine->mAudioAdapter);
7241 /* create a list of serial ports that will be mutable */
7242 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7243 {
7244 unconst(mSerialPorts [slot]).createObject();
7245 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
7246 }
7247 /* create a list of parallel ports that will be mutable */
7248 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7249 {
7250 unconst(mParallelPorts [slot]).createObject();
7251 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
7252 }
7253 /* create another USB controller object that will be mutable */
7254 unconst(mUSBController).createObject();
7255 mUSBController->init (this, aMachine->mUSBController);
7256
7257 /* create a list of network adapters that will be mutable */
7258 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7259 {
7260 unconst(mNetworkAdapters [slot]).createObject();
7261 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
7262 }
7263
7264 /* default is to delete saved state on Saved -> PoweredOff transition */
7265 mRemoveSavedState = true;
7266
7267 /* Confirm a successful initialization when it's the case */
7268 autoInitSpan.setSucceeded();
7269
7270 LogFlowThisFuncLeave();
7271 return S_OK;
7272}
7273
7274/**
7275 * Uninitializes this session object. If the reason is other than
7276 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
7277 *
7278 * @param aReason uninitialization reason
7279 *
7280 * @note Locks mParent + this object for writing.
7281 */
7282void SessionMachine::uninit (Uninit::Reason aReason)
7283{
7284 LogFlowThisFuncEnter();
7285 LogFlowThisFunc(("reason=%d\n", aReason));
7286
7287 /*
7288 * Strongly reference ourselves to prevent this object deletion after
7289 * mData->mSession.mMachine.setNull() below (which can release the last
7290 * reference and call the destructor). Important: this must be done before
7291 * accessing any members (and before AutoUninitSpan that does it as well).
7292 * This self reference will be released as the very last step on return.
7293 */
7294 ComObjPtr<SessionMachine> selfRef = this;
7295
7296 /* Enclose the state transition Ready->InUninit->NotReady */
7297 AutoUninitSpan autoUninitSpan(this);
7298 if (autoUninitSpan.uninitDone())
7299 {
7300 LogFlowThisFunc(("Already uninitialized\n"));
7301 LogFlowThisFuncLeave();
7302 return;
7303 }
7304
7305 if (autoUninitSpan.initFailed())
7306 {
7307 /* We've been called by init() because it's failed. It's not really
7308 * necessary (nor it's safe) to perform the regular uninit sequense
7309 * below, the following is enough.
7310 */
7311 LogFlowThisFunc(("Initialization failed.\n"));
7312#if defined(RT_OS_WINDOWS)
7313 if (mIPCSem)
7314 ::CloseHandle (mIPCSem);
7315 mIPCSem = NULL;
7316#elif defined(RT_OS_OS2)
7317 if (mIPCSem != NULLHANDLE)
7318 ::DosCloseMutexSem (mIPCSem);
7319 mIPCSem = NULLHANDLE;
7320#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7321 if (mIPCSem >= 0)
7322 ::semctl (mIPCSem, 0, IPC_RMID);
7323 mIPCSem = -1;
7324# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
7325 mIPCKey = "0";
7326# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
7327#else
7328# error "Port me!"
7329#endif
7330 uninitDataAndChildObjects();
7331 mData.free();
7332 unconst(mParent).setNull();
7333 unconst(mPeer).setNull();
7334 LogFlowThisFuncLeave();
7335 return;
7336 }
7337
7338 /* We need to lock this object in uninit() because the lock is shared
7339 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
7340 * and others need mParent lock. */
7341 AutoMultiWriteLock2 alock (mParent, this);
7342
7343#ifdef VBOX_WITH_RESOURCE_USAGE_API
7344 unregisterMetrics (mParent->performanceCollector(), mPeer);
7345#endif /* VBOX_WITH_RESOURCE_USAGE_API */
7346
7347 MachineState_T lastState = mData->mMachineState;
7348 NOREF(lastState);
7349
7350 if (aReason == Uninit::Abnormal)
7351 {
7352 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
7353 Global::IsOnlineOrTransient (lastState)));
7354
7355 /* reset the state to Aborted */
7356 if (mData->mMachineState != MachineState_Aborted)
7357 setMachineState (MachineState_Aborted);
7358 }
7359
7360 if (isModified())
7361 {
7362 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
7363 rollback (false /* aNotify */);
7364 }
7365
7366 Assert (!mSnapshotData.mStateFilePath || !mSnapshotData.mSnapshot);
7367 if (mSnapshotData.mStateFilePath)
7368 {
7369 LogWarningThisFunc(("canceling failed save state request!\n"));
7370 endSavingState (FALSE /* aSuccess */);
7371 }
7372 else if (!mSnapshotData.mSnapshot.isNull())
7373 {
7374 LogWarningThisFunc(("canceling untaken snapshot!\n"));
7375 endTakingSnapshot (FALSE /* aSuccess */);
7376 }
7377
7378#ifdef VBOX_WITH_USB
7379 /* release all captured USB devices */
7380 if (aReason == Uninit::Abnormal && Global::IsOnline (lastState))
7381 {
7382 /* Console::captureUSBDevices() is called in the VM process only after
7383 * setting the machine state to Starting or Restoring.
7384 * Console::detachAllUSBDevices() will be called upon successful
7385 * termination. So, we need to release USB devices only if there was
7386 * an abnormal termination of a running VM.
7387 *
7388 * This is identical to SessionMachine::DetachAllUSBDevices except
7389 * for the aAbnormal argument. */
7390 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
7391 AssertComRC(rc);
7392 NOREF (rc);
7393
7394 USBProxyService *service = mParent->host()->usbProxyService();
7395 if (service)
7396 service->detachAllDevicesFromVM (this, true /* aDone */, true /* aAbnormal */);
7397 }
7398#endif /* VBOX_WITH_USB */
7399
7400 if (!mData->mSession.mType.isNull())
7401 {
7402 /* mType is not null when this machine's process has been started by
7403 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
7404 * need to queue the PID to reap the process (and avoid zombies on
7405 * Linux). */
7406 Assert (mData->mSession.mPid != NIL_RTPROCESS);
7407 mParent->addProcessToReap (mData->mSession.mPid);
7408 }
7409
7410 mData->mSession.mPid = NIL_RTPROCESS;
7411
7412 if (aReason == Uninit::Unexpected)
7413 {
7414 /* Uninitialization didn't come from #checkForDeath(), so tell the
7415 * client watcher thread to update the set of machines that have open
7416 * sessions. */
7417 mParent->updateClientWatcher();
7418 }
7419
7420 /* uninitialize all remote controls */
7421 if (mData->mSession.mRemoteControls.size())
7422 {
7423 LogFlowThisFunc(("Closing remote sessions (%d):\n",
7424 mData->mSession.mRemoteControls.size()));
7425
7426 Data::Session::RemoteControlList::iterator it =
7427 mData->mSession.mRemoteControls.begin();
7428 while (it != mData->mSession.mRemoteControls.end())
7429 {
7430 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
7431 HRESULT rc = (*it)->Uninitialize();
7432 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
7433 if (FAILED (rc))
7434 LogWarningThisFunc(("Forgot to close the remote session?\n"));
7435 ++ it;
7436 }
7437 mData->mSession.mRemoteControls.clear();
7438 }
7439
7440 /*
7441 * An expected uninitialization can come only from #checkForDeath().
7442 * Otherwise it means that something's got really wrong (for examlple,
7443 * the Session implementation has released the VirtualBox reference
7444 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
7445 * etc). However, it's also possible, that the client releases the IPC
7446 * semaphore correctly (i.e. before it releases the VirtualBox reference),
7447 * but the VirtualBox release event comes first to the server process.
7448 * This case is practically possible, so we should not assert on an
7449 * unexpected uninit, just log a warning.
7450 */
7451
7452 if ((aReason == Uninit::Unexpected))
7453 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
7454
7455 if (aReason != Uninit::Normal)
7456 {
7457 mData->mSession.mDirectControl.setNull();
7458 }
7459 else
7460 {
7461 /* this must be null here (see #OnSessionEnd()) */
7462 Assert (mData->mSession.mDirectControl.isNull());
7463 Assert (mData->mSession.mState == SessionState_Closing);
7464 Assert (!mData->mSession.mProgress.isNull());
7465
7466 mData->mSession.mProgress->notifyComplete (S_OK);
7467 mData->mSession.mProgress.setNull();
7468 }
7469
7470 /* remove the association between the peer machine and this session machine */
7471 Assert (mData->mSession.mMachine == this ||
7472 aReason == Uninit::Unexpected);
7473
7474 /* reset the rest of session data */
7475 mData->mSession.mMachine.setNull();
7476 mData->mSession.mState = SessionState_Closed;
7477 mData->mSession.mType.setNull();
7478
7479 /* close the interprocess semaphore before leaving the shared lock */
7480#if defined(RT_OS_WINDOWS)
7481 if (mIPCSem)
7482 ::CloseHandle (mIPCSem);
7483 mIPCSem = NULL;
7484#elif defined(RT_OS_OS2)
7485 if (mIPCSem != NULLHANDLE)
7486 ::DosCloseMutexSem (mIPCSem);
7487 mIPCSem = NULLHANDLE;
7488#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7489 if (mIPCSem >= 0)
7490 ::semctl (mIPCSem, 0, IPC_RMID);
7491 mIPCSem = -1;
7492# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
7493 mIPCKey = "0";
7494# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
7495#else
7496# error "Port me!"
7497#endif
7498
7499 /* fire an event */
7500 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
7501
7502 uninitDataAndChildObjects();
7503
7504 /* free the essential data structure last */
7505 mData.free();
7506
7507 /* leave the shared lock before setting the below two to NULL */
7508 alock.leave();
7509
7510 unconst(mParent).setNull();
7511 unconst(mPeer).setNull();
7512
7513 LogFlowThisFuncLeave();
7514}
7515
7516// util::Lockable interface
7517////////////////////////////////////////////////////////////////////////////////
7518
7519/**
7520 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
7521 * with the primary Machine instance (mPeer).
7522 */
7523RWLockHandle *SessionMachine::lockHandle() const
7524{
7525 AssertReturn(!mPeer.isNull(), NULL);
7526 return mPeer->lockHandle();
7527}
7528
7529// IInternalMachineControl methods
7530////////////////////////////////////////////////////////////////////////////////
7531
7532/**
7533 * @note Locks this object for writing.
7534 */
7535STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
7536{
7537 AutoCaller autoCaller(this);
7538 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7539
7540 AutoWriteLock alock(this);
7541
7542 mRemoveSavedState = aRemove;
7543
7544 return S_OK;
7545}
7546
7547/**
7548 * @note Locks the same as #setMachineState() does.
7549 */
7550STDMETHODIMP SessionMachine::UpdateState (MachineState_T aMachineState)
7551{
7552 return setMachineState (aMachineState);
7553}
7554
7555/**
7556 * @note Locks this object for reading.
7557 */
7558STDMETHODIMP SessionMachine::GetIPCId (BSTR *aId)
7559{
7560 AutoCaller autoCaller(this);
7561 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7562
7563 AutoReadLock alock(this);
7564
7565#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7566 mIPCSemName.cloneTo(aId);
7567 return S_OK;
7568#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7569# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
7570 mIPCKey.cloneTo(aId);
7571# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
7572 mData->m_strConfigFileFull.cloneTo(aId);
7573# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
7574 return S_OK;
7575#else
7576# error "Port me!"
7577#endif
7578}
7579
7580/**
7581 * Goes through the USB filters of the given machine to see if the given
7582 * device matches any filter or not.
7583 *
7584 * @note Locks the same as USBController::hasMatchingFilter() does.
7585 */
7586STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
7587 BOOL *aMatched,
7588 ULONG *aMaskedIfs)
7589{
7590 LogFlowThisFunc(("\n"));
7591
7592 CheckComArgNotNull (aUSBDevice);
7593 CheckComArgOutPointerValid(aMatched);
7594
7595 AutoCaller autoCaller(this);
7596 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7597
7598#ifdef VBOX_WITH_USB
7599 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice, aMaskedIfs);
7600#else
7601 NOREF(aUSBDevice);
7602 NOREF(aMaskedIfs);
7603 *aMatched = FALSE;
7604#endif
7605
7606 return S_OK;
7607}
7608
7609/**
7610 * @note Locks the same as Host::captureUSBDevice() does.
7611 */
7612STDMETHODIMP SessionMachine::CaptureUSBDevice (IN_BSTR aId)
7613{
7614 LogFlowThisFunc(("\n"));
7615
7616 AutoCaller autoCaller(this);
7617 AssertComRCReturnRC(autoCaller.rc());
7618
7619#ifdef VBOX_WITH_USB
7620 /* if captureDeviceForVM() fails, it must have set extended error info */
7621 MultiResult rc = mParent->host()->checkUSBProxyService();
7622 CheckComRCReturnRC(rc);
7623
7624 USBProxyService *service = mParent->host()->usbProxyService();
7625 AssertReturn(service, E_FAIL);
7626 return service->captureDeviceForVM (this, Guid(aId));
7627#else
7628 NOREF(aId);
7629 return E_NOTIMPL;
7630#endif
7631}
7632
7633/**
7634 * @note Locks the same as Host::detachUSBDevice() does.
7635 */
7636STDMETHODIMP SessionMachine::DetachUSBDevice (IN_BSTR aId, BOOL aDone)
7637{
7638 LogFlowThisFunc(("\n"));
7639
7640 AutoCaller autoCaller(this);
7641 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7642
7643#ifdef VBOX_WITH_USB
7644 USBProxyService *service = mParent->host()->usbProxyService();
7645 AssertReturn(service, E_FAIL);
7646 return service->detachDeviceFromVM (this, Guid(aId), !!aDone);
7647#else
7648 NOREF(aId);
7649 NOREF(aDone);
7650 return E_NOTIMPL;
7651#endif
7652}
7653
7654/**
7655 * Inserts all machine filters to the USB proxy service and then calls
7656 * Host::autoCaptureUSBDevices().
7657 *
7658 * Called by Console from the VM process upon VM startup.
7659 *
7660 * @note Locks what called methods lock.
7661 */
7662STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
7663{
7664 LogFlowThisFunc(("\n"));
7665
7666 AutoCaller autoCaller(this);
7667 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7668
7669#ifdef VBOX_WITH_USB
7670 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
7671 AssertComRC(rc);
7672 NOREF (rc);
7673
7674 USBProxyService *service = mParent->host()->usbProxyService();
7675 AssertReturn(service, E_FAIL);
7676 return service->autoCaptureDevicesForVM (this);
7677#else
7678 return S_OK;
7679#endif
7680}
7681
7682/**
7683 * Removes all machine filters from the USB proxy service and then calls
7684 * Host::detachAllUSBDevices().
7685 *
7686 * Called by Console from the VM process upon normal VM termination or by
7687 * SessionMachine::uninit() upon abnormal VM termination (from under the
7688 * Machine/SessionMachine lock).
7689 *
7690 * @note Locks what called methods lock.
7691 */
7692STDMETHODIMP SessionMachine::DetachAllUSBDevices (BOOL aDone)
7693{
7694 LogFlowThisFunc(("\n"));
7695
7696 AutoCaller autoCaller(this);
7697 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7698
7699#ifdef VBOX_WITH_USB
7700 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
7701 AssertComRC(rc);
7702 NOREF (rc);
7703
7704 USBProxyService *service = mParent->host()->usbProxyService();
7705 AssertReturn(service, E_FAIL);
7706 return service->detachAllDevicesFromVM (this, !!aDone, false /* aAbnormal */);
7707#else
7708 NOREF(aDone);
7709 return S_OK;
7710#endif
7711}
7712
7713/**
7714 * @note Locks this object for writing.
7715 */
7716STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
7717 IProgress **aProgress)
7718{
7719 LogFlowThisFuncEnter();
7720
7721 AssertReturn(aSession, E_INVALIDARG);
7722 AssertReturn(aProgress, E_INVALIDARG);
7723
7724 AutoCaller autoCaller(this);
7725
7726 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
7727 /*
7728 * We don't assert below because it might happen that a non-direct session
7729 * informs us it is closed right after we've been uninitialized -- it's ok.
7730 */
7731 CheckComRCReturnRC(autoCaller.rc());
7732
7733 /* get IInternalSessionControl interface */
7734 ComPtr<IInternalSessionControl> control (aSession);
7735
7736 ComAssertRet (!control.isNull(), E_INVALIDARG);
7737
7738 AutoWriteLock alock(this);
7739
7740 if (control.equalsTo (mData->mSession.mDirectControl))
7741 {
7742 ComAssertRet (aProgress, E_POINTER);
7743
7744 /* The direct session is being normally closed by the client process
7745 * ----------------------------------------------------------------- */
7746
7747 /* go to the closing state (essential for all open*Session() calls and
7748 * for #checkForDeath()) */
7749 Assert (mData->mSession.mState == SessionState_Open);
7750 mData->mSession.mState = SessionState_Closing;
7751
7752 /* set direct control to NULL to release the remote instance */
7753 mData->mSession.mDirectControl.setNull();
7754 LogFlowThisFunc(("Direct control is set to NULL\n"));
7755
7756 /* Create the progress object the client will use to wait until
7757 * #checkForDeath() is called to uninitialize this session object after
7758 * it releases the IPC semaphore. */
7759 ComObjPtr<Progress> progress;
7760 progress.createObject();
7761 progress->init (mParent, static_cast <IMachine *> (mPeer),
7762 Bstr (tr ("Closing session")), FALSE /* aCancelable */);
7763 progress.queryInterfaceTo(aProgress);
7764 mData->mSession.mProgress = progress;
7765 }
7766 else
7767 {
7768 /* the remote session is being normally closed */
7769 Data::Session::RemoteControlList::iterator it =
7770 mData->mSession.mRemoteControls.begin();
7771 while (it != mData->mSession.mRemoteControls.end())
7772 {
7773 if (control.equalsTo (*it))
7774 break;
7775 ++it;
7776 }
7777 BOOL found = it != mData->mSession.mRemoteControls.end();
7778 ComAssertMsgRet (found, ("The session is not found in the session list!"),
7779 E_INVALIDARG);
7780 mData->mSession.mRemoteControls.remove (*it);
7781 }
7782
7783 LogFlowThisFuncLeave();
7784 return S_OK;
7785}
7786
7787/**
7788 * @note Locks this object for writing.
7789 */
7790STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
7791{
7792 LogFlowThisFuncEnter();
7793
7794 AssertReturn(aProgress, E_INVALIDARG);
7795 AssertReturn(aStateFilePath, E_POINTER);
7796
7797 AutoCaller autoCaller(this);
7798 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7799
7800 AutoWriteLock alock(this);
7801
7802 AssertReturn(mData->mMachineState == MachineState_Paused &&
7803 mSnapshotData.mLastState == MachineState_Null &&
7804 mSnapshotData.mProgressId.isEmpty() &&
7805 mSnapshotData.mStateFilePath.isNull(),
7806 E_FAIL);
7807
7808 /* memorize the progress ID and add it to the global collection */
7809 Bstr progressId;
7810 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
7811 AssertComRCReturn (rc, rc);
7812 rc = mParent->addProgress (aProgress);
7813 AssertComRCReturn (rc, rc);
7814
7815 Bstr stateFilePath;
7816 /* stateFilePath is null when the machine is not running */
7817 if (mData->mMachineState == MachineState_Paused)
7818 {
7819 stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
7820 mUserData->mSnapshotFolderFull.raw(),
7821 RTPATH_DELIMITER, mData->mUuid.raw());
7822 }
7823
7824 /* fill in the snapshot data */
7825 mSnapshotData.mLastState = mData->mMachineState;
7826 mSnapshotData.mProgressId = Guid(progressId);
7827 mSnapshotData.mStateFilePath = stateFilePath;
7828
7829 /* set the state to Saving (this is expected by Console::SaveState()) */
7830 setMachineState (MachineState_Saving);
7831
7832 stateFilePath.cloneTo(aStateFilePath);
7833
7834 return S_OK;
7835}
7836
7837/**
7838 * @note Locks mParent + this object for writing.
7839 */
7840STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
7841{
7842 LogFlowThisFunc(("\n"));
7843
7844 AutoCaller autoCaller(this);
7845 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7846
7847 /* endSavingState() need mParent lock */
7848 AutoMultiWriteLock2 alock (mParent, this);
7849
7850 AssertReturn(mData->mMachineState == MachineState_Saving &&
7851 mSnapshotData.mLastState != MachineState_Null &&
7852 !mSnapshotData.mProgressId.isEmpty() &&
7853 !mSnapshotData.mStateFilePath.isNull(),
7854 E_FAIL);
7855
7856 /*
7857 * on success, set the state to Saved;
7858 * on failure, set the state to the state we had when BeginSavingState() was
7859 * called (this is expected by Console::SaveState() and
7860 * Console::saveStateThread())
7861 */
7862 if (aSuccess)
7863 setMachineState (MachineState_Saved);
7864 else
7865 setMachineState (mSnapshotData.mLastState);
7866
7867 return endSavingState (aSuccess);
7868}
7869
7870/**
7871 * @note Locks this object for writing.
7872 */
7873STDMETHODIMP SessionMachine::AdoptSavedState (IN_BSTR aSavedStateFile)
7874{
7875 LogFlowThisFunc(("\n"));
7876
7877 AssertReturn(aSavedStateFile, E_INVALIDARG);
7878
7879 AutoCaller autoCaller(this);
7880 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7881
7882 AutoWriteLock alock(this);
7883
7884 AssertReturn(mData->mMachineState == MachineState_PoweredOff ||
7885 mData->mMachineState == MachineState_Aborted,
7886 E_FAIL);
7887
7888 Utf8Str stateFilePathFull = aSavedStateFile;
7889 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
7890 if (RT_FAILURE(vrc))
7891 return setError(VBOX_E_FILE_ERROR,
7892 tr("Invalid saved state file path '%ls' (%Rrc)"),
7893 aSavedStateFile,
7894 vrc);
7895
7896 mSSData->mStateFilePath = stateFilePathFull;
7897
7898 /* The below setMachineState() will detect the state transition and will
7899 * update the settings file */
7900
7901 return setMachineState (MachineState_Saved);
7902}
7903
7904/**
7905 * @note Locks mParent + this object for writing.
7906 */
7907STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
7908 IN_BSTR aName,
7909 IN_BSTR aDescription,
7910 IProgress *aProgress,
7911 BSTR *aStateFilePath,
7912 IProgress **aServerProgress)
7913{
7914 LogFlowThisFuncEnter();
7915
7916 AssertReturn(aInitiator && aName, E_INVALIDARG);
7917 AssertReturn(aStateFilePath && aServerProgress, E_POINTER);
7918
7919 LogFlowThisFunc(("aName='%ls'\n", aName));
7920
7921 AutoCaller autoCaller(this);
7922 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7923
7924 /* saveSettings() needs mParent lock */
7925 AutoMultiWriteLock2 alock (mParent, this);
7926
7927#ifdef VBOX_WITH_LIVE_MIGRATION
7928 AssertReturn( ( !Global::IsOnlineOrTransient (mData->mMachineState)
7929 || mData->mMachineState == MachineState_Paused
7930 || mData->mMachineState == MachineState_Running
7931 )
7932 && mSnapshotData.mLastState == MachineState_Null
7933 && mSnapshotData.mSnapshot.isNull()
7934 && mSnapshotData.mServerProgress.isNull()
7935 && mSnapshotData.mCombinedProgress.isNull(), E_FAIL);
7936#else
7937 AssertReturn( ( !Global::IsOnlineOrTransient (mData->mMachineState)
7938 || mData->mMachineState == MachineState_Paused
7939 )
7940 && mSnapshotData.mLastState == MachineState_Null
7941 && mSnapshotData.mSnapshot.isNull()
7942 && mSnapshotData.mServerProgress.isNull()
7943 && mSnapshotData.mCombinedProgress.isNull(), E_FAIL);
7944#endif
7945
7946 bool const fTakingSnapshotOnline = ( mData->mMachineState == MachineState_Running
7947 || mData->mMachineState == MachineState_Paused);
7948
7949 if ( !fTakingSnapshotOnline
7950 && mData->mMachineState != MachineState_Saved)
7951 {
7952 /* save all current settings to ensure current changes are committed and
7953 * hard disks are fixed up */
7954 HRESULT rc = saveSettings();
7955 CheckComRCReturnRC(rc);
7956 }
7957
7958 /// @todo NEWMEDIA so far, we decided to allow for Writhethrough hard disks
7959 /// when taking snapshots putting all the responsibility to the user...
7960#if 0
7961 /* check that there are no Writethrough hard disks attached */
7962 for (HDData::AttachmentList::const_iterator
7963 it = mHDData->mAttachments.begin();
7964 it != mHDData->mAttachments.end();
7965 ++ it)
7966 {
7967 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
7968 AutoReadLock hdLock (hd);
7969 if (hd->type() == HardDiskType_Writethrough)
7970 return setError (E_FAIL,
7971 tr ("Cannot take a snapshot because the Writethrough hard disk "
7972 "'%ls' is attached to this virtual machine"),
7973 hd->locationFull().raw());
7974 }
7975#endif
7976
7977 AssertReturn(aProgress || !fTakingSnapshotOnline, E_FAIL);
7978
7979 /* create an ID for the snapshot */
7980 Guid snapshotId;
7981 snapshotId.create();
7982
7983 Bstr stateFilePath;
7984 /* stateFilePath is null when the machine is not online nor saved */
7985 if ( fTakingSnapshotOnline
7986 || mData->mMachineState == MachineState_Saved)
7987 stateFilePath = BstrFmt("%ls%c{%RTuuid}.sav",
7988 mUserData->mSnapshotFolderFull.raw(),
7989 RTPATH_DELIMITER,
7990 snapshotId.ptr());
7991
7992 /* ensure the directory for the saved state file exists */
7993 if (stateFilePath)
7994 {
7995 HRESULT rc = VirtualBox::ensureFilePathExists(Utf8Str(stateFilePath));
7996 CheckComRCReturnRC(rc);
7997 }
7998
7999 /* create a snapshot machine object */
8000 ComObjPtr<SnapshotMachine> snapshotMachine;
8001 snapshotMachine.createObject();
8002 HRESULT rc = snapshotMachine->init(this, snapshotId, stateFilePath);
8003 AssertComRCReturn (rc, rc);
8004
8005 Bstr progressDesc = BstrFmt(tr("Taking snapshot of virtual machine '%ls'"),
8006 mUserData->mName.raw());
8007 Bstr firstOpDesc = Bstr(tr("Preparing to take snapshot"));
8008
8009 /* create a server-side progress object (it will be descriptionless when we
8010 * need to combine it with the VM-side progress, i.e. when we're taking a
8011 * snapshot online). The number of operations is: 1 (preparing) + # of
8012 * hard disks + 1 (if the state is saved so we need to copy it)
8013 */
8014 ComObjPtr<Progress> serverProgress;
8015 serverProgress.createObject();
8016 {
8017 ULONG opCount = 1 + (ULONG)mHDData->mAttachments.size();
8018 if (mData->mMachineState == MachineState_Saved)
8019 opCount ++;
8020 if (fTakingSnapshotOnline)
8021 rc = serverProgress->init(FALSE, opCount, firstOpDesc);
8022 else
8023 rc = serverProgress->init(mParent, aInitiator, progressDesc, FALSE,
8024 opCount, firstOpDesc);
8025 AssertComRCReturn (rc, rc);
8026 }
8027
8028 /* create a combined server-side progress object when necessary */
8029 ComObjPtr<CombinedProgress> combinedProgress;
8030 if (fTakingSnapshotOnline)
8031 {
8032 combinedProgress.createObject();
8033 rc = combinedProgress->init (mParent, aInitiator, progressDesc,
8034 serverProgress, aProgress);
8035 AssertComRCReturn (rc, rc);
8036 }
8037
8038 /* create a snapshot object */
8039 RTTIMESPEC time;
8040 ComObjPtr<Snapshot> snapshot;
8041 snapshot.createObject();
8042 rc = snapshot->init(mParent,
8043 snapshotId,
8044 aName,
8045 aDescription,
8046 *RTTimeNow(&time),
8047 snapshotMachine,
8048 mData->mCurrentSnapshot);
8049 AssertComRCReturnRC(rc);
8050
8051 /* create and start the task on a separate thread (note that it will not
8052 * start working until we release alock) */
8053 TakeSnapshotTask *task = new TakeSnapshotTask(this);
8054 int vrc = RTThreadCreate(NULL,
8055 taskHandler,
8056 (void*)task,
8057 0,
8058 RTTHREADTYPE_MAIN_WORKER,
8059 0,
8060 "TakeSnapshot");
8061 if (RT_FAILURE(vrc))
8062 {
8063 snapshot->uninit();
8064 delete task;
8065 ComAssertRCRet (vrc, E_FAIL);
8066 }
8067
8068 /* fill in the snapshot data */
8069 mSnapshotData.mLastState = mData->mMachineState;
8070 mSnapshotData.mSnapshot = snapshot;
8071 mSnapshotData.mServerProgress = serverProgress;
8072 mSnapshotData.mCombinedProgress = combinedProgress;
8073
8074 /* set the state to Saving (this is expected by Console::TakeSnapshot()) */
8075 setMachineState(MachineState_Saving);
8076
8077 if (fTakingSnapshotOnline)
8078 stateFilePath.cloneTo(aStateFilePath);
8079 else
8080 *aStateFilePath = NULL;
8081
8082 serverProgress.queryInterfaceTo(aServerProgress);
8083
8084 LogFlowThisFuncLeave();
8085 return S_OK;
8086}
8087
8088/**
8089 * @note Locks this object for writing.
8090 */
8091STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
8092{
8093 LogFlowThisFunc(("\n"));
8094
8095 AutoCaller autoCaller(this);
8096 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8097
8098 AutoWriteLock alock(this);
8099
8100 AssertReturn(!aSuccess ||
8101 (mData->mMachineState == MachineState_Saving &&
8102 mSnapshotData.mLastState != MachineState_Null &&
8103 !mSnapshotData.mSnapshot.isNull() &&
8104 !mSnapshotData.mServerProgress.isNull() &&
8105 !mSnapshotData.mCombinedProgress.isNull()),
8106 E_FAIL);
8107
8108 /* set the state to the state we had when BeginTakingSnapshot() was called
8109 * (this is expected by Console::TakeSnapshot() and
8110 * Console::saveStateThread()) */
8111 setMachineState(mSnapshotData.mLastState);
8112
8113 return endTakingSnapshot(aSuccess);
8114}
8115
8116/**
8117 * @note Locks mParent + this + children objects for writing!
8118 */
8119STDMETHODIMP SessionMachine::DiscardSnapshot(IConsole *aInitiator,
8120 IN_BSTR aId,
8121 MachineState_T *aMachineState,
8122 IProgress **aProgress)
8123{
8124 LogFlowThisFunc(("\n"));
8125
8126 Guid id(aId);
8127 AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
8128 AssertReturn(aMachineState && aProgress, E_POINTER);
8129
8130 AutoCaller autoCaller(this);
8131 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8132
8133 /* saveSettings() needs mParent lock */
8134 AutoMultiWriteLock2 alock(mParent, this);
8135
8136 ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
8137
8138 AutoWriteLock treeLock(snapshotsTreeLockHandle());
8139
8140 ComObjPtr<Snapshot> snapshot;
8141 HRESULT rc = findSnapshot(id, snapshot, true /* aSetError */);
8142 CheckComRCReturnRC(rc);
8143
8144 AutoWriteLock snapshotLock(snapshot);
8145
8146 size_t childrenCount = snapshot->getChildrenCount();
8147 if (childrenCount > 1)
8148 return setError(VBOX_E_INVALID_OBJECT_STATE,
8149 tr("Snapshot '%s' of the machine '%ls' has more than one child snapshot (%d)"),
8150 snapshot->getName().c_str(),
8151 mUserData->mName.raw(),
8152 childrenCount);
8153
8154 /* If the snapshot being discarded is the current one, ensure current
8155 * settings are committed and saved.
8156 */
8157 if (snapshot == mData->mCurrentSnapshot)
8158 {
8159 if (isModified())
8160 {
8161 rc = saveSettings();
8162 CheckComRCReturnRC (rc);
8163 }
8164 }
8165
8166 /* create a progress object. The number of operations is:
8167 * 1 (preparing) + # of hard disks + 1 if the snapshot is online
8168 */
8169 ComObjPtr<Progress> progress;
8170 progress.createObject();
8171 rc = progress->init (mParent, aInitiator,
8172 Bstr(Utf8StrFmt(tr("Discarding snapshot '%s'"),
8173 snapshot->getName().c_str())),
8174 FALSE /* aCancelable */,
8175 1 + (ULONG)snapshot->getSnapshotMachine()->mHDData->mAttachments.size() +
8176 (snapshot->stateFilePath().isNull() ? 0 : 1),
8177 Bstr (tr ("Preparing to discard snapshot")));
8178 AssertComRCReturn (rc, rc);
8179
8180 /* create and start the task on a separate thread */
8181 DiscardSnapshotTask *task = new DiscardSnapshotTask (this, progress, snapshot);
8182 int vrc = RTThreadCreate (NULL, taskHandler,
8183 (void *) task,
8184 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardSnapshot");
8185 if (RT_FAILURE(vrc))
8186 delete task;
8187 ComAssertRCRet (vrc, E_FAIL);
8188
8189 /* set the proper machine state (note: after creating a Task instance) */
8190 setMachineState(MachineState_Discarding);
8191
8192 /* return the progress to the caller */
8193 progress.queryInterfaceTo(aProgress);
8194
8195 /* return the new state to the caller */
8196 *aMachineState = mData->mMachineState;
8197
8198 return S_OK;
8199}
8200
8201/**
8202 * @note Locks this + children objects for writing!
8203 */
8204STDMETHODIMP SessionMachine::DiscardCurrentState (
8205 IConsole *aInitiator, MachineState_T *aMachineState, IProgress **aProgress)
8206{
8207 LogFlowThisFunc(("\n"));
8208
8209 AssertReturn(aInitiator, E_INVALIDARG);
8210 AssertReturn(aMachineState && aProgress, E_POINTER);
8211
8212 AutoCaller autoCaller(this);
8213 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8214
8215 AutoWriteLock alock(this);
8216
8217 ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
8218
8219 if (mData->mCurrentSnapshot.isNull())
8220 return setError(VBOX_E_INVALID_OBJECT_STATE,
8221 tr("Could not discard the current state of the machine '%ls' because it doesn't have any snapshots"),
8222 mUserData->mName.raw());
8223
8224 /* create a progress object. The number of operations is: 1 (preparing) + #
8225 * of hard disks + 1 (if we need to copy the saved state file) */
8226 ComObjPtr<Progress> progress;
8227 progress.createObject();
8228 {
8229 ULONG opCount = 1 + (ULONG)mData->mCurrentSnapshot->getSnapshotMachine()->mHDData->mAttachments.size();
8230 if (mData->mCurrentSnapshot->stateFilePath())
8231 ++ opCount;
8232 progress->init (mParent, aInitiator,
8233 Bstr (tr ("Discarding current machine state")),
8234 FALSE /* aCancelable */, opCount,
8235 Bstr (tr ("Preparing to discard current state")));
8236 }
8237
8238 /* create and start the task on a separate thread (note that it will not
8239 * start working until we release alock) */
8240 DiscardCurrentStateTask *task =
8241 new DiscardCurrentStateTask (this, progress, false /* discardCurSnapshot */);
8242 int vrc = RTThreadCreate (NULL, taskHandler,
8243 (void *) task,
8244 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurState");
8245 if (RT_FAILURE(vrc))
8246 {
8247 delete task;
8248 ComAssertRCRet (vrc, E_FAIL);
8249 }
8250
8251 /* set the proper machine state (note: after creating a Task instance) */
8252 setMachineState (MachineState_Discarding);
8253
8254 /* return the progress to the caller */
8255 progress.queryInterfaceTo(aProgress);
8256
8257 /* return the new state to the caller */
8258 *aMachineState = mData->mMachineState;
8259
8260 return S_OK;
8261}
8262
8263/**
8264 * @note Locks thos object for writing!
8265 */
8266STDMETHODIMP SessionMachine::DiscardCurrentSnapshotAndState(IConsole *aInitiator,
8267 MachineState_T *aMachineState,
8268 IProgress **aProgress)
8269{
8270 LogFlowThisFunc(("\n"));
8271
8272 AssertReturn(aInitiator, E_INVALIDARG);
8273 AssertReturn(aMachineState && aProgress, E_POINTER);
8274
8275 AutoCaller autoCaller(this);
8276 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8277
8278 AutoWriteLock alock(this);
8279
8280 ComAssertRet (!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
8281
8282 if (mData->mCurrentSnapshot.isNull())
8283 return setError(VBOX_E_INVALID_OBJECT_STATE,
8284 tr("Could not discard the current state of the machine '%ls' "
8285 "because it doesn't have any snapshots"),
8286 mUserData->mName.raw());
8287
8288 /* create a progress object. The number of operations is:
8289 * 1 (preparing) + # of hard disks in the current snapshot +
8290 * # of hard disks in the previous snapshot +
8291 * 1 if we need to copy the saved state file of the previous snapshot +
8292 * 1 if the current snapshot is online
8293 * or (if there is no previous snapshot):
8294 * 1 (preparing) + # of hard disks in the current snapshot * 2 +
8295 * 1 if we need to copy the saved state file of the current snapshot * 2
8296 */
8297 ComObjPtr<Progress> progress;
8298 progress.createObject();
8299 {
8300 ComObjPtr<Snapshot> curSnapshot = mData->mCurrentSnapshot;
8301 ComObjPtr<Snapshot> prevSnapshot = mData->mCurrentSnapshot->parent();
8302
8303 ULONG opCount = 1;
8304 if (prevSnapshot)
8305 {
8306 opCount += (ULONG)curSnapshot->getSnapshotMachine()->mHDData->mAttachments.size();
8307 opCount += (ULONG)prevSnapshot->getSnapshotMachine()->mHDData->mAttachments.size();
8308 if (prevSnapshot->stateFilePath())
8309 ++opCount;
8310 if (curSnapshot->stateFilePath())
8311 ++opCount;
8312 }
8313 else
8314 {
8315 opCount +=
8316 (ULONG)curSnapshot->getSnapshotMachine()->mHDData->mAttachments.size() * 2;
8317 if (curSnapshot->stateFilePath())
8318 opCount += 2;
8319 }
8320
8321 progress->init(mParent, aInitiator,
8322 Bstr (tr ("Discarding current machine snapshot and state")),
8323 FALSE /* aCancelable */, opCount,
8324 Bstr (tr ("Preparing to discard current snapshot and state")));
8325 }
8326
8327 /* create and start the task on a separate thread */
8328 DiscardCurrentStateTask *task =
8329 new DiscardCurrentStateTask (this, progress, true /* discardCurSnapshot */);
8330 int vrc = RTThreadCreate(NULL, taskHandler,
8331 (void *) task,
8332 0, RTTHREADTYPE_MAIN_WORKER, 0, "DiscardCurStSnp");
8333 if (RT_FAILURE(vrc))
8334 {
8335 delete task;
8336 ComAssertRCRet(vrc, E_FAIL);
8337 }
8338
8339 /* set the proper machine state (note: after creating a Task instance) */
8340 setMachineState(MachineState_Discarding);
8341
8342 /* return the progress to the caller */
8343 progress.queryInterfaceTo(aProgress);
8344
8345 /* return the new state to the caller */
8346 *aMachineState = mData->mMachineState;
8347
8348 return S_OK;
8349}
8350
8351STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
8352 ComSafeArrayOut(BSTR, aValues),
8353 ComSafeArrayOut(ULONG64, aTimestamps),
8354 ComSafeArrayOut(BSTR, aFlags))
8355{
8356 LogFlowThisFunc(("\n"));
8357
8358#ifdef VBOX_WITH_GUEST_PROPS
8359 using namespace guestProp;
8360
8361 AutoCaller autoCaller(this);
8362 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8363
8364 AutoReadLock alock(this);
8365
8366 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
8367 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
8368 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
8369 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
8370
8371 size_t cEntries = mHWData->mGuestProperties.size();
8372 com::SafeArray<BSTR> names (cEntries);
8373 com::SafeArray<BSTR> values (cEntries);
8374 com::SafeArray<ULONG64> timestamps (cEntries);
8375 com::SafeArray<BSTR> flags (cEntries);
8376 unsigned i = 0;
8377 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
8378 it != mHWData->mGuestProperties.end();
8379 ++it)
8380 {
8381 char szFlags[MAX_FLAGS_LEN + 1];
8382 it->strName.cloneTo(&names[i]);
8383 it->strValue.cloneTo(&values[i]);
8384 timestamps[i] = it->mTimestamp;
8385 /* If it is NULL, keep it NULL. */
8386 if (it->mFlags)
8387 {
8388 writeFlags(it->mFlags, szFlags);
8389 Bstr(szFlags).cloneTo(&flags[i]);
8390 }
8391 else
8392 flags[i] = NULL;
8393 ++i;
8394 }
8395 names.detachTo(ComSafeArrayOutArg(aNames));
8396 values.detachTo(ComSafeArrayOutArg(aValues));
8397 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
8398 flags.detachTo(ComSafeArrayOutArg(aFlags));
8399 mHWData->mPropertyServiceActive = true;
8400 return S_OK;
8401#else
8402 ReturnComNotImplemented();
8403#endif
8404}
8405
8406STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn (IN_BSTR, aNames),
8407 ComSafeArrayIn (IN_BSTR, aValues),
8408 ComSafeArrayIn (ULONG64, aTimestamps),
8409 ComSafeArrayIn (IN_BSTR, aFlags))
8410{
8411 LogFlowThisFunc(("\n"));
8412
8413#ifdef VBOX_WITH_GUEST_PROPS
8414 using namespace guestProp;
8415
8416 AutoCaller autoCaller(this);
8417 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8418
8419 AutoWriteLock alock(this);
8420
8421 /* Temporarily reset the registered flag, so that our machine state
8422 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in
8423 * all setters will return FALSE for a Machine instance if mRegistered
8424 * is TRUE). This is copied from registeredInit(), and may or may not be
8425 * the right way to handle this. */
8426 mData->mRegistered = FALSE;
8427 HRESULT rc = checkStateDependency(MutableStateDep);
8428 LogRel (("checkStateDependency(MutableStateDep) returned 0x%x\n", rc));
8429 CheckComRCReturnRC(rc);
8430
8431 // ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
8432
8433 AssertReturn(!ComSafeArrayInIsNull (aNames), E_POINTER);
8434 AssertReturn(!ComSafeArrayInIsNull (aValues), E_POINTER);
8435 AssertReturn(!ComSafeArrayInIsNull (aTimestamps), E_POINTER);
8436 AssertReturn(!ComSafeArrayInIsNull (aFlags), E_POINTER);
8437
8438 com::SafeArray<IN_BSTR> names (ComSafeArrayInArg (aNames));
8439 com::SafeArray<IN_BSTR> values (ComSafeArrayInArg (aValues));
8440 com::SafeArray<ULONG64> timestamps (ComSafeArrayInArg (aTimestamps));
8441 com::SafeArray<IN_BSTR> flags (ComSafeArrayInArg (aFlags));
8442 DiscardSettings();
8443 mHWData.backup();
8444 mHWData->mGuestProperties.erase (mHWData->mGuestProperties.begin(),
8445 mHWData->mGuestProperties.end());
8446 for (unsigned i = 0; i < names.size(); ++i)
8447 {
8448 uint32_t fFlags = NILFLAG;
8449 validateFlags (Utf8Str (flags[i]).raw(), &fFlags);
8450 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
8451 mHWData->mGuestProperties.push_back (property);
8452 }
8453 mHWData->mPropertyServiceActive = false;
8454 alock.unlock();
8455 SaveSettings();
8456 /* Restore the mRegistered flag. */
8457 mData->mRegistered = TRUE;
8458 return S_OK;
8459#else
8460 ReturnComNotImplemented();
8461#endif
8462}
8463
8464STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
8465 IN_BSTR aValue,
8466 ULONG64 aTimestamp,
8467 IN_BSTR aFlags)
8468{
8469 LogFlowThisFunc(("\n"));
8470
8471#ifdef VBOX_WITH_GUEST_PROPS
8472 using namespace guestProp;
8473
8474 CheckComArgNotNull (aName);
8475 if ((aValue != NULL) && (!VALID_PTR (aValue) || !VALID_PTR (aFlags)))
8476 return E_POINTER; /* aValue can be NULL to indicate deletion */
8477
8478 try
8479 {
8480 Utf8Str utf8Name(aName);
8481 Utf8Str utf8Flags(aFlags);
8482 Utf8Str utf8Patterns(mHWData->mGuestPropertyNotificationPatterns);
8483
8484 uint32_t fFlags = NILFLAG;
8485 if ((aFlags != NULL) && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags)))
8486 return E_INVALIDARG;
8487
8488 bool matchAll = false;
8489 if (utf8Patterns.isEmpty())
8490 matchAll = true;
8491
8492 AutoCaller autoCaller(this);
8493 CheckComRCReturnRC(autoCaller.rc());
8494
8495 AutoWriteLock alock(this);
8496
8497 HRESULT rc = checkStateDependency(MutableStateDep);
8498 CheckComRCReturnRC(rc);
8499
8500 mHWData.backup();
8501 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
8502 iter != mHWData->mGuestProperties.end();
8503 ++iter)
8504 if (utf8Name == iter->strName)
8505 {
8506 mHWData->mGuestProperties.erase(iter);
8507 break;
8508 }
8509 if (aValue != NULL)
8510 {
8511 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
8512 mHWData->mGuestProperties.push_back (property);
8513 }
8514
8515 /* send a callback notification if appropriate */
8516 alock.leave();
8517 if ( matchAll
8518 || RTStrSimplePatternMultiMatch(utf8Patterns.raw(),
8519 RTSTR_MAX,
8520 utf8Name.raw(),
8521 RTSTR_MAX, NULL)
8522 )
8523 mParent->onGuestPropertyChange(mData->mUuid,
8524 aName,
8525 aValue,
8526 aFlags);
8527 }
8528 catch(std::bad_alloc &)
8529 {
8530 return E_OUTOFMEMORY;
8531 }
8532 return S_OK;
8533#else
8534 ReturnComNotImplemented();
8535#endif
8536}
8537
8538// public methods only for internal purposes
8539/////////////////////////////////////////////////////////////////////////////
8540
8541/**
8542 * Called from the client watcher thread to check for expected or unexpected
8543 * death of the client process that has a direct session to this machine.
8544 *
8545 * On Win32 and on OS/2, this method is called only when we've got the
8546 * mutex (i.e. the client has either died or terminated normally) so it always
8547 * returns @c true (the client is terminated, the session machine is
8548 * uninitialized).
8549 *
8550 * On other platforms, the method returns @c true if the client process has
8551 * terminated normally or abnormally and the session machine was uninitialized,
8552 * and @c false if the client process is still alive.
8553 *
8554 * @note Locks this object for writing.
8555 */
8556bool SessionMachine::checkForDeath()
8557{
8558 Uninit::Reason reason;
8559 bool terminated = false;
8560
8561 /* Enclose autoCaller with a block because calling uninit() from under it
8562 * will deadlock. */
8563 {
8564 AutoCaller autoCaller(this);
8565 if (!autoCaller.isOk())
8566 {
8567 /* return true if not ready, to cause the client watcher to exclude
8568 * the corresponding session from watching */
8569 LogFlowThisFunc(("Already uninitialized!"));
8570 return true;
8571 }
8572
8573 AutoWriteLock alock(this);
8574
8575 /* Determine the reason of death: if the session state is Closing here,
8576 * everything is fine. Otherwise it means that the client did not call
8577 * OnSessionEnd() before it released the IPC semaphore. This may happen
8578 * either because the client process has abnormally terminated, or
8579 * because it simply forgot to call ISession::Close() before exiting. We
8580 * threat the latter also as an abnormal termination (see
8581 * Session::uninit() for details). */
8582 reason = mData->mSession.mState == SessionState_Closing ?
8583 Uninit::Normal :
8584 Uninit::Abnormal;
8585
8586#if defined(RT_OS_WINDOWS)
8587
8588 AssertMsg (mIPCSem, ("semaphore must be created"));
8589
8590 /* release the IPC mutex */
8591 ::ReleaseMutex (mIPCSem);
8592
8593 terminated = true;
8594
8595#elif defined(RT_OS_OS2)
8596
8597 AssertMsg (mIPCSem, ("semaphore must be created"));
8598
8599 /* release the IPC mutex */
8600 ::DosReleaseMutexSem (mIPCSem);
8601
8602 terminated = true;
8603
8604#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8605
8606 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
8607
8608 int val = ::semctl (mIPCSem, 0, GETVAL);
8609 if (val > 0)
8610 {
8611 /* the semaphore is signaled, meaning the session is terminated */
8612 terminated = true;
8613 }
8614
8615#else
8616# error "Port me!"
8617#endif
8618
8619 } /* AutoCaller block */
8620
8621 if (terminated)
8622 uninit (reason);
8623
8624 return terminated;
8625}
8626
8627/**
8628 * @note Locks this object for reading.
8629 */
8630HRESULT SessionMachine::onDVDDriveChange()
8631{
8632 LogFlowThisFunc(("\n"));
8633
8634 AutoCaller autoCaller(this);
8635 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8636
8637 ComPtr<IInternalSessionControl> directControl;
8638 {
8639 AutoReadLock alock(this);
8640 directControl = mData->mSession.mDirectControl;
8641 }
8642
8643 /* ignore notifications sent after #OnSessionEnd() is called */
8644 if (!directControl)
8645 return S_OK;
8646
8647 return directControl->OnDVDDriveChange();
8648}
8649
8650/**
8651 * @note Locks this object for reading.
8652 */
8653HRESULT SessionMachine::onFloppyDriveChange()
8654{
8655 LogFlowThisFunc(("\n"));
8656
8657 AutoCaller autoCaller(this);
8658 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8659
8660 ComPtr<IInternalSessionControl> directControl;
8661 {
8662 AutoReadLock alock(this);
8663 directControl = mData->mSession.mDirectControl;
8664 }
8665
8666 /* ignore notifications sent after #OnSessionEnd() is called */
8667 if (!directControl)
8668 return S_OK;
8669
8670 return directControl->OnFloppyDriveChange();
8671}
8672
8673/**
8674 * @note Locks this object for reading.
8675 */
8676HRESULT SessionMachine::onNetworkAdapterChange (INetworkAdapter *networkAdapter, BOOL changeAdapter)
8677{
8678 LogFlowThisFunc(("\n"));
8679
8680 AutoCaller autoCaller(this);
8681 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8682
8683 ComPtr<IInternalSessionControl> directControl;
8684 {
8685 AutoReadLock alock(this);
8686 directControl = mData->mSession.mDirectControl;
8687 }
8688
8689 /* ignore notifications sent after #OnSessionEnd() is called */
8690 if (!directControl)
8691 return S_OK;
8692
8693 return directControl->OnNetworkAdapterChange (networkAdapter, changeAdapter);
8694}
8695
8696/**
8697 * @note Locks this object for reading.
8698 */
8699HRESULT SessionMachine::onSerialPortChange (ISerialPort *serialPort)
8700{
8701 LogFlowThisFunc(("\n"));
8702
8703 AutoCaller autoCaller(this);
8704 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8705
8706 ComPtr<IInternalSessionControl> directControl;
8707 {
8708 AutoReadLock alock(this);
8709 directControl = mData->mSession.mDirectControl;
8710 }
8711
8712 /* ignore notifications sent after #OnSessionEnd() is called */
8713 if (!directControl)
8714 return S_OK;
8715
8716 return directControl->OnSerialPortChange (serialPort);
8717}
8718
8719/**
8720 * @note Locks this object for reading.
8721 */
8722HRESULT SessionMachine::onParallelPortChange (IParallelPort *parallelPort)
8723{
8724 LogFlowThisFunc(("\n"));
8725
8726 AutoCaller autoCaller(this);
8727 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8728
8729 ComPtr<IInternalSessionControl> directControl;
8730 {
8731 AutoReadLock alock(this);
8732 directControl = mData->mSession.mDirectControl;
8733 }
8734
8735 /* ignore notifications sent after #OnSessionEnd() is called */
8736 if (!directControl)
8737 return S_OK;
8738
8739 return directControl->OnParallelPortChange (parallelPort);
8740}
8741
8742/**
8743 * @note Locks this object for reading.
8744 */
8745HRESULT SessionMachine::onStorageControllerChange ()
8746{
8747 LogFlowThisFunc(("\n"));
8748
8749 AutoCaller autoCaller(this);
8750 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8751
8752 ComPtr<IInternalSessionControl> directControl;
8753 {
8754 AutoReadLock alock(this);
8755 directControl = mData->mSession.mDirectControl;
8756 }
8757
8758 /* ignore notifications sent after #OnSessionEnd() is called */
8759 if (!directControl)
8760 return S_OK;
8761
8762 return directControl->OnStorageControllerChange ();
8763}
8764
8765/**
8766 * @note Locks this object for reading.
8767 */
8768HRESULT SessionMachine::onVRDPServerChange()
8769{
8770 LogFlowThisFunc(("\n"));
8771
8772 AutoCaller autoCaller(this);
8773 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8774
8775 ComPtr<IInternalSessionControl> directControl;
8776 {
8777 AutoReadLock alock(this);
8778 directControl = mData->mSession.mDirectControl;
8779 }
8780
8781 /* ignore notifications sent after #OnSessionEnd() is called */
8782 if (!directControl)
8783 return S_OK;
8784
8785 return directControl->OnVRDPServerChange();
8786}
8787
8788/**
8789 * @note Locks this object for reading.
8790 */
8791HRESULT SessionMachine::onUSBControllerChange()
8792{
8793 LogFlowThisFunc(("\n"));
8794
8795 AutoCaller autoCaller(this);
8796 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8797
8798 ComPtr<IInternalSessionControl> directControl;
8799 {
8800 AutoReadLock alock(this);
8801 directControl = mData->mSession.mDirectControl;
8802 }
8803
8804 /* ignore notifications sent after #OnSessionEnd() is called */
8805 if (!directControl)
8806 return S_OK;
8807
8808 return directControl->OnUSBControllerChange();
8809}
8810
8811/**
8812 * @note Locks this object for reading.
8813 */
8814HRESULT SessionMachine::onSharedFolderChange()
8815{
8816 LogFlowThisFunc(("\n"));
8817
8818 AutoCaller autoCaller(this);
8819 AssertComRCReturnRC(autoCaller.rc());
8820
8821 ComPtr<IInternalSessionControl> directControl;
8822 {
8823 AutoReadLock alock(this);
8824 directControl = mData->mSession.mDirectControl;
8825 }
8826
8827 /* ignore notifications sent after #OnSessionEnd() is called */
8828 if (!directControl)
8829 return S_OK;
8830
8831 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
8832}
8833
8834/**
8835 * Returns @c true if this machine's USB controller reports it has a matching
8836 * filter for the given USB device and @c false otherwise.
8837 *
8838 * @note Locks this object for reading.
8839 */
8840bool SessionMachine::hasMatchingUSBFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
8841{
8842 AutoCaller autoCaller(this);
8843 /* silently return if not ready -- this method may be called after the
8844 * direct machine session has been called */
8845 if (!autoCaller.isOk())
8846 return false;
8847
8848 AutoReadLock alock(this);
8849
8850#ifdef VBOX_WITH_USB
8851 switch (mData->mMachineState)
8852 {
8853 case MachineState_Starting:
8854 case MachineState_Restoring:
8855 case MachineState_Paused:
8856 case MachineState_Running:
8857 return mUSBController->hasMatchingFilter (aDevice, aMaskedIfs);
8858 default: break;
8859 }
8860#else
8861 NOREF(aDevice);
8862 NOREF(aMaskedIfs);
8863#endif
8864 return false;
8865}
8866
8867/**
8868 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
8869 */
8870HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
8871 IVirtualBoxErrorInfo *aError,
8872 ULONG aMaskedIfs)
8873{
8874 LogFlowThisFunc(("\n"));
8875
8876 AutoCaller autoCaller(this);
8877
8878 /* This notification may happen after the machine object has been
8879 * uninitialized (the session was closed), so don't assert. */
8880 CheckComRCReturnRC(autoCaller.rc());
8881
8882 ComPtr<IInternalSessionControl> directControl;
8883 {
8884 AutoReadLock alock(this);
8885 directControl = mData->mSession.mDirectControl;
8886 }
8887
8888 /* fail on notifications sent after #OnSessionEnd() is called, it is
8889 * expected by the caller */
8890 if (!directControl)
8891 return E_FAIL;
8892
8893 /* No locks should be held at this point. */
8894 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
8895 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
8896
8897 return directControl->OnUSBDeviceAttach (aDevice, aError, aMaskedIfs);
8898}
8899
8900/**
8901 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
8902 */
8903HRESULT SessionMachine::onUSBDeviceDetach (IN_BSTR aId,
8904 IVirtualBoxErrorInfo *aError)
8905{
8906 LogFlowThisFunc(("\n"));
8907
8908 AutoCaller autoCaller(this);
8909
8910 /* This notification may happen after the machine object has been
8911 * uninitialized (the session was closed), so don't assert. */
8912 CheckComRCReturnRC(autoCaller.rc());
8913
8914 ComPtr<IInternalSessionControl> directControl;
8915 {
8916 AutoReadLock alock(this);
8917 directControl = mData->mSession.mDirectControl;
8918 }
8919
8920 /* fail on notifications sent after #OnSessionEnd() is called, it is
8921 * expected by the caller */
8922 if (!directControl)
8923 return E_FAIL;
8924
8925 /* No locks should be held at this point. */
8926 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
8927 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
8928
8929 return directControl->OnUSBDeviceDetach (aId, aError);
8930}
8931
8932// protected methods
8933/////////////////////////////////////////////////////////////////////////////
8934
8935/**
8936 * Helper method to finalize saving the state.
8937 *
8938 * @note Must be called from under this object's lock.
8939 *
8940 * @param aSuccess TRUE if the snapshot has been taken successfully
8941 *
8942 * @note Locks mParent + this objects for writing.
8943 */
8944HRESULT SessionMachine::endSavingState (BOOL aSuccess)
8945{
8946 LogFlowThisFuncEnter();
8947
8948 AutoCaller autoCaller(this);
8949 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8950
8951 /* saveSettings() needs mParent lock */
8952 AutoMultiWriteLock2 alock (mParent, this);
8953
8954 HRESULT rc = S_OK;
8955
8956 if (aSuccess)
8957 {
8958 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
8959
8960 /* save all VM settings */
8961 rc = saveSettings();
8962 }
8963 else
8964 {
8965 /* delete the saved state file (it might have been already created) */
8966 RTFileDelete(Utf8Str(mSnapshotData.mStateFilePath).c_str());
8967 }
8968
8969 /* remove the completed progress object */
8970 mParent->removeProgress (mSnapshotData.mProgressId);
8971
8972 /* clear out the temporary saved state data */
8973 mSnapshotData.mLastState = MachineState_Null;
8974 mSnapshotData.mProgressId.clear();
8975 mSnapshotData.mStateFilePath.setNull();
8976
8977 LogFlowThisFuncLeave();
8978 return rc;
8979}
8980
8981/**
8982 * Helper method to finalize taking a snapshot. Gets called to finalize the
8983 * "take snapshot" procedure, either from the public SessionMachine::EndTakingSnapshot()
8984 * if taking the snapshot failed/was aborted or from the takeSnapshotHandler thread
8985 * when taking the snapshot succeeded.
8986 *
8987 * Expected to be called after completing *all* the tasks related to taking the
8988 * snapshot, either successfully or unsuccessfilly.
8989 *
8990 * @param aSuccess TRUE if the snapshot has been taken successfully.
8991 *
8992 * @note Locks this objects for writing.
8993 */
8994HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
8995{
8996 LogFlowThisFuncEnter();
8997
8998 AutoCaller autoCaller(this);
8999 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9000
9001 AutoMultiWriteLock2 alock(mParent, this);
9002 // saveSettings needs VirtualBox lock
9003
9004 AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL);
9005
9006 MultiResult rc (S_OK);
9007
9008 if (aSuccess)
9009 {
9010 /* the server progress must be completed on success */
9011 Assert(mSnapshotData.mServerProgress->completed());
9012
9013 mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
9014
9015 /* memorize the first snapshot if necessary */
9016 if (!mData->mFirstSnapshot)
9017 mData->mFirstSnapshot = mData->mCurrentSnapshot;
9018
9019 if (!Global::IsOnline(mSnapshotData.mLastState))
9020 /* the machine was powered off or saved when taking a snapshot, so
9021 * reset the mCurrentStateModified flag */
9022 mData->mCurrentStateModified = FALSE;
9023
9024 rc = saveSettings();
9025 }
9026
9027 if (aSuccess && SUCCEEDED(rc))
9028 {
9029 bool online = Global::IsOnline (mSnapshotData.mLastState);
9030
9031 /* associate old hard disks with the snapshot and do locking/unlocking*/
9032 fixupHardDisks(true /* aCommit */, online);
9033
9034 /* inform callbacks */
9035 mParent->onSnapshotTaken(mData->mUuid,
9036 mSnapshotData.mSnapshot->getId());
9037 }
9038 else
9039 {
9040 /* wait for the completion of the server progress (diff VDI creation) */
9041 /// @todo (dmik) later, we will definitely want to cancel it instead
9042 // (when the cancel function is implemented)
9043 mSnapshotData.mServerProgress->WaitForCompletion (-1);
9044
9045 /* delete all differencing hard disks created (this will also attach
9046 * their parents back by rolling back mHDData) */
9047 fixupHardDisks(false /* aCommit */);
9048
9049 /* delete the saved state file (it might have been already created) */
9050 if (mSnapshotData.mSnapshot->stateFilePath())
9051 RTFileDelete(Utf8Str(mSnapshotData.mSnapshot->stateFilePath()).c_str());
9052
9053 mSnapshotData.mSnapshot->uninit();
9054 }
9055
9056 /* clear out the snapshot data */
9057 mSnapshotData.mLastState = MachineState_Null;
9058 mSnapshotData.mSnapshot.setNull();
9059 mSnapshotData.mServerProgress.setNull();
9060
9061 /* uninitialize the combined progress (to remove it from the VBox collection) */
9062 if (!mSnapshotData.mCombinedProgress.isNull())
9063 {
9064 mSnapshotData.mCombinedProgress->uninit();
9065 mSnapshotData.mCombinedProgress.setNull();
9066 }
9067
9068 LogFlowThisFuncLeave();
9069 return rc;
9070}
9071
9072/**
9073 * Take snapshot task handler.
9074 * This gets called from TakeSnapshotTask::handler(), which got created by
9075 * SnapshotMachine::BeginTakingSnapshot().
9076 *
9077 * The sole purpose of this task is to asynchronously create differencing VDIs
9078 * and copy the saved state file (when necessary). The VM process will wait for
9079 * this task to complete using the mSnapshotData.mServerProgress returned to it.
9080 *
9081 * @note Locks this object for writing.
9082 */
9083void SessionMachine::takeSnapshotHandler(TakeSnapshotTask & /* aTask */)
9084{
9085 LogFlowThisFuncEnter();
9086
9087 AutoCaller autoCaller(this);
9088
9089 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
9090 if (!autoCaller.isOk())
9091 {
9092 /* we might have been uninitialized because the session was accidentally
9093 * closed by the client, so don't assert */
9094 LogFlowThisFuncLeave();
9095 return;
9096 }
9097
9098 AutoWriteLock alock(this);
9099
9100 HRESULT rc = S_OK;
9101
9102 bool online = Global::IsOnline(mSnapshotData.mLastState);
9103
9104 LogFlowThisFunc(("Creating differencing hard disks (online=%d)...\n",
9105 online));
9106
9107 mHDData.backup();
9108
9109 /* create new differencing hard disks and attach them to this machine */
9110 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
9111 mSnapshotData.mServerProgress,
9112 online);
9113
9114 if ( SUCCEEDED(rc)
9115 && mSnapshotData.mLastState == MachineState_Saved)
9116 {
9117 Utf8Str stateFrom = mSSData->mStateFilePath;
9118 Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
9119
9120 LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
9121 stateFrom.raw(), stateTo.raw()));
9122
9123 mSnapshotData.mServerProgress->setNextOperation(Bstr(tr("Copying the execution state")),
9124 1); // weight
9125
9126 /* Leave the lock before a lengthy operation (mMachineState is
9127 * MachineState_Saving here) */
9128 alock.leave();
9129
9130 /* copy the state file */
9131 int vrc = RTFileCopyEx(stateFrom.c_str(),
9132 stateTo.c_str(),
9133 0,
9134 progressCallback,
9135 static_cast<Progress*>(mSnapshotData.mServerProgress));
9136
9137 alock.enter();
9138
9139 if (RT_FAILURE(vrc))
9140 rc = setError(E_FAIL,
9141 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
9142 stateFrom.raw(),
9143 stateTo.raw(),
9144 vrc);
9145 }
9146
9147 /* we have to call endTakingSnapshot() ourselves if the snapshot was taken
9148 * offline because the VM process will not do it in this case
9149 */
9150 if (!online)
9151 {
9152 LogFlowThisFunc(("Finalizing the taken snapshot (rc=%Rhrc)...\n", rc));
9153
9154 {
9155 ErrorInfoKeeper eik;
9156
9157 setMachineState(mSnapshotData.mLastState);
9158 updateMachineStateOnClient();
9159 }
9160
9161 /* finalize the progress after setting the state, for consistency */
9162 mSnapshotData.mServerProgress->notifyComplete(rc);
9163
9164 endTakingSnapshot(SUCCEEDED(rc));
9165 }
9166 else
9167 {
9168 mSnapshotData.mServerProgress->notifyComplete (rc);
9169 }
9170
9171 LogFlowThisFuncLeave();
9172}
9173
9174/**
9175 * Helper struct for SessionMachine::discardSnapshotHandler().
9176 */
9177struct HardDiskDiscardRec
9178{
9179 HardDiskDiscardRec() : chain (NULL) {}
9180
9181 HardDiskDiscardRec (const ComObjPtr<HardDisk> &aHd,
9182 HardDisk::MergeChain *aChain = NULL)
9183 : hd (aHd), chain (aChain) {}
9184
9185 HardDiskDiscardRec (const ComObjPtr<HardDisk> &aHd,
9186 HardDisk::MergeChain *aChain,
9187 const ComObjPtr<HardDisk> &aReplaceHd,
9188 const ComObjPtr<HardDiskAttachment> &aReplaceHda,
9189 const Guid &aSnapshotId)
9190 : hd (aHd), chain (aChain)
9191 , replaceHd (aReplaceHd), replaceHda (aReplaceHda)
9192 , snapshotId (aSnapshotId) {}
9193
9194 ComObjPtr<HardDisk> hd;
9195 HardDisk::MergeChain *chain;
9196 /* these are for the replace hard disk case: */
9197 ComObjPtr<HardDisk> replaceHd;
9198 ComObjPtr<HardDiskAttachment> replaceHda;
9199 Guid snapshotId;
9200};
9201
9202typedef std::list <HardDiskDiscardRec> HardDiskDiscardRecList;
9203
9204/**
9205 * Discard snapshot task handler. Must be called only by
9206 * DiscardSnapshotTask::handler()!
9207 *
9208 * When aTask.subTask is true, the associated progress object is left
9209 * uncompleted on success. On failure, the progress is marked as completed
9210 * regardless of this parameter.
9211 *
9212 * @note Locks mParent + this + child objects for writing!
9213 */
9214void SessionMachine::discardSnapshotHandler(DiscardSnapshotTask &aTask)
9215{
9216 LogFlowThisFuncEnter();
9217
9218 AutoCaller autoCaller(this);
9219
9220 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
9221 if (!autoCaller.isOk())
9222 {
9223 /* we might have been uninitialized because the session was accidentally
9224 * closed by the client, so don't assert */
9225 aTask.progress->notifyComplete (
9226 E_FAIL, COM_IIDOF (IMachine), getComponentName(),
9227 tr ("The session has been accidentally closed"));
9228
9229 LogFlowThisFuncLeave();
9230 return;
9231 }
9232
9233 /* Locking order: */
9234 AutoMultiWriteLock3 alock(this->lockHandle(),
9235 this->snapshotsTreeLockHandle(),
9236 aTask.snapshot->lockHandle());
9237
9238 ComPtr<SnapshotMachine> sm = aTask.snapshot->getSnapshotMachine();
9239 /* no need to lock the snapshot machine since it is const by definiton */
9240
9241 HRESULT rc = S_OK;
9242
9243 /* save the snapshot ID (for callbacks) */
9244 Guid snapshotId = aTask.snapshot->getId();
9245
9246 HardDiskDiscardRecList toDiscard;
9247
9248 bool settingsChanged = false;
9249
9250 try
9251 {
9252 /* first pass: */
9253 LogFlowThisFunc(("1: Checking hard disk merge prerequisites...\n"));
9254
9255 for (HDData::AttachmentList::const_iterator it = sm->mHDData->mAttachments.begin();
9256 it != sm->mHDData->mAttachments.end();
9257 ++ it)
9258 {
9259 ComObjPtr<HardDiskAttachment> hda = *it;
9260 ComObjPtr<HardDisk> hd = hda->hardDisk();
9261
9262 /* HardDisk::prepareDiscard() reqiuires a write lock */
9263 AutoWriteLock hdLock(hd);
9264
9265 if (hd->type() != HardDiskType_Normal)
9266 {
9267 /* skip writethrough hard disks */
9268
9269 Assert (hd->type() == HardDiskType_Writethrough);
9270
9271 rc = aTask.progress->setNextOperation(BstrFmt(tr("Skipping writethrough hard disk '%s'"),
9272 hd->root()->name().raw()),
9273 1); // weight
9274 CheckComRCThrowRC(rc);
9275
9276 continue;
9277 }
9278
9279 HardDisk::MergeChain *chain = NULL;
9280
9281 /* needs to be discarded (merged with the child if any), check
9282 * prerequisites */
9283 rc = hd->prepareDiscard (chain);
9284 CheckComRCThrowRC(rc);
9285
9286 if (hd->parent().isNull() && chain != NULL)
9287 {
9288 /* it's a base hard disk so it will be a backward merge of its
9289 * only child to it (prepareDiscard() does necessary checks). We
9290 * need then to update the attachment that refers to the child
9291 * to refer to the parent insead. Don't forget to detach the
9292 * child (otherwise mergeTo() called by discard() will assert
9293 * because it will be going to delete the child) */
9294
9295 /* The below assert would be nice but I don't want to move
9296 * HardDisk::MergeChain to the header just for that
9297 * Assert (!chain->isForward()); */
9298
9299 Assert(hd->children().size() == 1);
9300
9301 ComObjPtr<HardDisk> replaceHd = hd->children().front();
9302
9303 Assert (replaceHd->backRefs().front().machineId == mData->mUuid);
9304 Assert (replaceHd->backRefs().front().snapshotIds.size() <= 1);
9305
9306 Guid snapshotId;
9307 if (replaceHd->backRefs().front().snapshotIds.size() == 1)
9308 snapshotId = replaceHd->backRefs().front().snapshotIds.front();
9309
9310 HRESULT rc2 = S_OK;
9311
9312 /* adjust back references */
9313 rc2 = replaceHd->detachFrom (mData->mUuid, snapshotId);
9314 AssertComRC(rc2);
9315
9316 rc2 = hd->attachTo (mData->mUuid, snapshotId);
9317 AssertComRC(rc2);
9318
9319 /* replace the hard disk in the attachment object */
9320 HDData::AttachmentList::iterator it;
9321 if (snapshotId.isEmpty())
9322 {
9323 /* in current state */
9324 it = std::find_if (mHDData->mAttachments.begin(),
9325 mHDData->mAttachments.end(),
9326 HardDiskAttachment::RefersTo (replaceHd));
9327 AssertBreak (it != mHDData->mAttachments.end());
9328 }
9329 else
9330 {
9331 /* in snapshot */
9332 ComObjPtr<Snapshot> snapshot;
9333 rc2 = findSnapshot (snapshotId, snapshot);
9334 AssertComRC (rc2);
9335
9336 /* don't lock the snapshot; cannot be modified outside */
9337 HDData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mHDData->mAttachments;
9338 it = std::find_if (snapAtts.begin(),
9339 snapAtts.end(),
9340 HardDiskAttachment::RefersTo (replaceHd));
9341 AssertBreak (it != snapAtts.end());
9342 }
9343
9344 AutoWriteLock attLock (*it);
9345 (*it)->updateHardDisk (hd, false /* aImplicit */);
9346
9347 toDiscard.push_back (HardDiskDiscardRec (hd, chain, replaceHd,
9348 *it, snapshotId));
9349 continue;
9350 }
9351
9352 toDiscard.push_back (HardDiskDiscardRec (hd, chain));
9353 }
9354
9355 /* Now we checked that we can successfully merge all normal hard disks
9356 * (unless a runtime error like end-of-disc happens). Prior to
9357 * performing the actual merge, we want to discard the snapshot itself
9358 * and remove it from the XML file to make sure that a possible merge
9359 * ruintime error will not make this snapshot inconsistent because of
9360 * the partially merged or corrupted hard disks */
9361
9362 /* second pass: */
9363 LogFlowThisFunc(("2: Discarding snapshot...\n"));
9364
9365 {
9366 ComObjPtr<Snapshot> parentSnapshot = aTask.snapshot->parent();
9367 Bstr stateFilePath = aTask.snapshot->stateFilePath();
9368
9369 /* Note that discarding the snapshot will deassociate it from the
9370 * hard disks which will allow the merge+delete operation for them*/
9371 aTask.snapshot->beginDiscard();
9372 aTask.snapshot->uninit();
9373
9374 rc = saveAllSnapshots();
9375 CheckComRCThrowRC(rc);
9376
9377 /// @todo (dmik)
9378 // if we implement some warning mechanism later, we'll have
9379 // to return a warning if the state file path cannot be deleted
9380 if (stateFilePath)
9381 {
9382 aTask.progress->setNextOperation(Bstr(tr("Discarding the execution state")),
9383 1); // weight
9384
9385 RTFileDelete(Utf8Str(stateFilePath).c_str());
9386 }
9387
9388 /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
9389 /// should restore the shapshot in the snapshot tree if
9390 /// saveSnapshotSettings fails. Actually, we may call
9391 /// #saveSnapshotSettings() with a special flag that will tell it to
9392 /// skip the given snapshot as if it would have been discarded and
9393 /// only actually discard it if the save operation succeeds.
9394 }
9395
9396 /* here we come when we've irrevesibly discarded the snapshot which
9397 * means that the VM settigns (our relevant changes to mData) need to be
9398 * saved too */
9399 /// @todo NEWMEDIA maybe save everything in one operation in place of
9400 /// saveSnapshotSettings() above
9401 settingsChanged = true;
9402
9403 /* third pass: */
9404 LogFlowThisFunc(("3: Performing actual hard disk merging...\n"));
9405
9406 /* leave the locks before the potentially lengthy operation */
9407 alock.leave();
9408
9409 /// @todo NEWMEDIA turn the following errors into warnings because the
9410 /// snapshot itself has been already deleted (and interpret these
9411 /// warnings properly on the GUI side)
9412
9413 for (HardDiskDiscardRecList::iterator it = toDiscard.begin();
9414 it != toDiscard.end();)
9415 {
9416 rc = it->hd->discard (aTask.progress, it->chain);
9417 CheckComRCBreakRC(rc);
9418
9419 /* prevent from calling cancelDiscard() */
9420 it = toDiscard.erase (it);
9421 }
9422
9423 alock.enter();
9424
9425 CheckComRCThrowRC(rc);
9426 }
9427 catch (HRESULT aRC) { rc = aRC; }
9428
9429 if (FAILED(rc))
9430 {
9431 HRESULT rc2 = S_OK;
9432
9433 /* un-prepare the remaining hard disks */
9434 for (HardDiskDiscardRecList::const_iterator it = toDiscard.begin();
9435 it != toDiscard.end(); ++ it)
9436 {
9437 it->hd->cancelDiscard (it->chain);
9438
9439 if (!it->replaceHd.isNull())
9440 {
9441 /* undo hard disk replacement */
9442
9443 rc2 = it->replaceHd->attachTo (mData->mUuid, it->snapshotId);
9444 AssertComRC(rc2);
9445
9446 rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
9447 AssertComRC(rc2);
9448
9449 AutoWriteLock attLock (it->replaceHda);
9450 it->replaceHda->updateHardDisk (it->replaceHd, false /* aImplicit */);
9451 }
9452 }
9453 }
9454
9455 if (!aTask.subTask || FAILED(rc))
9456 {
9457 if (!aTask.subTask)
9458 {
9459 /* saveSettings() below needs a VirtualBox write lock and we need to
9460 * leave this object's lock to do this to follow the {parent-child}
9461 * locking rule. This is the last chance to do that while we are
9462 * still in a protective state which allows us to temporarily leave
9463 * the lock */
9464 alock.unlock();
9465 AutoWriteLock vboxLock(mParent);
9466 alock.lock();
9467
9468 /* preserve existing error info */
9469 ErrorInfoKeeper eik;
9470
9471 /* restore the machine state */
9472 setMachineState(aTask.state);
9473 updateMachineStateOnClient();
9474
9475 if (settingsChanged)
9476 saveSettings(SaveS_InformCallbacksAnyway);
9477 }
9478
9479 /* set the result (this will try to fetch current error info on failure) */
9480 aTask.progress->notifyComplete (rc);
9481 }
9482
9483 if (SUCCEEDED(rc))
9484 mParent->onSnapshotDiscarded (mData->mUuid, snapshotId);
9485
9486 LogFlowThisFunc(("Done discarding snapshot (rc=%08X)\n", rc));
9487 LogFlowThisFuncLeave();
9488}
9489
9490/**
9491 * Discard current state task handler. Must be called only by
9492 * DiscardCurrentStateTask::handler()!
9493 *
9494 * @note Locks mParent + this object for writing.
9495 */
9496void SessionMachine::discardCurrentStateHandler (DiscardCurrentStateTask &aTask)
9497{
9498 LogFlowThisFuncEnter();
9499
9500 AutoCaller autoCaller(this);
9501
9502 LogFlowThisFunc(("state=%d\n", autoCaller.state()));
9503 if (!autoCaller.isOk())
9504 {
9505 /* we might have been uninitialized because the session was accidentally
9506 * closed by the client, so don't assert */
9507 aTask.progress->notifyComplete (
9508 E_FAIL, COM_IIDOF (IMachine), getComponentName(),
9509 tr ("The session has been accidentally closed"));
9510
9511 LogFlowThisFuncLeave();
9512 return;
9513 }
9514
9515 /* saveSettings() needs mParent lock */
9516 AutoWriteLock vboxLock (mParent);
9517
9518 /* @todo We don't need mParent lock so far so unlock() it. Better is to
9519 * provide an AutoWriteLock argument that lets create a non-locking
9520 * instance */
9521 vboxLock.unlock();
9522
9523 AutoWriteLock alock(this);
9524
9525 /* discard all current changes to mUserData (name, OSType etc.) (note that
9526 * the machine is powered off, so there is no need to inform the direct
9527 * session) */
9528 if (isModified())
9529 rollback (false /* aNotify */);
9530
9531 HRESULT rc = S_OK;
9532
9533 bool errorInSubtask = false;
9534 bool stateRestored = false;
9535
9536 const bool isLastSnapshot = mData->mCurrentSnapshot->parent().isNull();
9537
9538 try
9539 {
9540 /* discard the saved state file if the machine was Saved prior to this
9541 * operation */
9542 if (aTask.state == MachineState_Saved)
9543 {
9544 Assert (!mSSData->mStateFilePath.isEmpty());
9545 RTFileDelete(Utf8Str(mSSData->mStateFilePath).c_str());
9546 mSSData->mStateFilePath.setNull();
9547 aTask.modifyLastState (MachineState_PoweredOff);
9548 rc = saveStateSettings (SaveSTS_StateFilePath);
9549 CheckComRCThrowRC(rc);
9550 }
9551
9552 if (aTask.discardCurrentSnapshot && !isLastSnapshot)
9553 {
9554 /* the "discard current snapshot and state" task is in action, the
9555 * current snapshot is not the last one. Discard the current
9556 * snapshot first */
9557
9558 DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
9559 subTask.subTask = true;
9560 discardSnapshotHandler (subTask);
9561
9562 AutoCaller progressCaller (aTask.progress);
9563 AutoReadLock progressLock (aTask.progress);
9564 if (aTask.progress->completed())
9565 {
9566 /* the progress can be completed by a subtask only if there was
9567 * a failure */
9568 rc = aTask.progress->resultCode();
9569 Assert (FAILED(rc));
9570 errorInSubtask = true;
9571 throw rc;
9572 }
9573 }
9574
9575 RTTIMESPEC snapshotTimeStamp;
9576 RTTimeSpecSetMilli (&snapshotTimeStamp, 0);
9577
9578 {
9579 ComObjPtr<Snapshot> curSnapshot = mData->mCurrentSnapshot;
9580 AutoReadLock snapshotLock (curSnapshot);
9581
9582 /* remember the timestamp of the snapshot we're restoring from */
9583 snapshotTimeStamp = curSnapshot->getTimeStamp();
9584
9585 ComPtr<SnapshotMachine> pSnapshotMachine(curSnapshot->getSnapshotMachine());
9586
9587 /* copy all hardware data from the current snapshot */
9588 copyFrom(pSnapshotMachine);
9589
9590 LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));
9591
9592 /* restore the attachmends from the snapshot */
9593 mHDData.backup();
9594 mHDData->mAttachments =
9595 pSnapshotMachine->mHDData->mAttachments;
9596
9597 /* leave the locks before the potentially lengthy operation */
9598 snapshotLock.unlock();
9599 alock.leave();
9600
9601 rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
9602 aTask.progress,
9603 false /* aOnline */);
9604
9605 alock.enter();
9606 snapshotLock.lock();
9607
9608 CheckComRCThrowRC(rc);
9609
9610 /* Note: on success, current (old) hard disks will be
9611 * deassociated/deleted on #commit() called from #saveSettings() at
9612 * the end. On failure, newly created implicit diffs will be
9613 * deleted by #rollback() at the end. */
9614
9615 /* should not have a saved state file associated at this point */
9616 Assert (mSSData->mStateFilePath.isNull());
9617
9618 if (curSnapshot->stateFilePath())
9619 {
9620 Utf8Str snapStateFilePath = curSnapshot->stateFilePath();
9621
9622 Utf8Str stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
9623 mUserData->mSnapshotFolderFull.raw(),
9624 RTPATH_DELIMITER, mData->mUuid.raw());
9625
9626 LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
9627 snapStateFilePath.raw(), stateFilePath.raw()));
9628
9629 aTask.progress->setNextOperation(Bstr(tr("Restoring the execution state")),
9630 1); // weight
9631
9632 /* leave the lock before the potentially lengthy operation */
9633 snapshotLock.unlock();
9634 alock.leave();
9635
9636 /* copy the state file */
9637 int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
9638 stateFilePath.c_str(),
9639 0,
9640 progressCallback,
9641 aTask.progress);
9642
9643 alock.enter();
9644 snapshotLock.lock();
9645
9646 if (RT_SUCCESS(vrc))
9647 {
9648 mSSData->mStateFilePath = stateFilePath;
9649 }
9650 else
9651 {
9652 throw setError(E_FAIL,
9653 tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
9654 snapStateFilePath.raw(),
9655 stateFilePath.raw(),
9656 vrc);
9657 }
9658 }
9659 }
9660
9661 /* grab differencing hard disks from the old attachments that will
9662 * become unused and need to be auto-deleted */
9663
9664 std::list< ComObjPtr<HardDisk> > diffs;
9665
9666 for (HDData::AttachmentList::const_iterator
9667 it = mHDData.backedUpData()->mAttachments.begin();
9668 it != mHDData.backedUpData()->mAttachments.end(); ++ it)
9669 {
9670 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
9671
9672 /* while the hard disk is attached, the number of children or the
9673 * parent cannot change, so no lock */
9674 if (!hd->parent().isNull() && hd->children().size() == 0)
9675 diffs.push_back (hd);
9676 }
9677
9678 int saveFlags = 0;
9679
9680 if (aTask.discardCurrentSnapshot && isLastSnapshot)
9681 {
9682 /* commit changes to have unused diffs deassociated from this
9683 * machine before deletion (see below) */
9684 commit();
9685
9686 /* delete the unused diffs now (and uninit them) because discard
9687 * may fail otherwise (too many children of the hard disk to be
9688 * discarded) */
9689 for (std::list< ComObjPtr<HardDisk> >::const_iterator
9690 it = diffs.begin(); it != diffs.end(); ++ it)
9691 {
9692 /// @todo for now, we ignore errors since we've already
9693 /// and therefore cannot fail. Later, we may want to report a
9694 /// warning through the Progress object
9695 HRESULT rc2 = (*it)->deleteStorageAndWait();
9696 if (SUCCEEDED(rc2))
9697 (*it)->uninit();
9698 }
9699
9700 /* prevent further deletion */
9701 diffs.clear();
9702
9703 /* discard the current snapshot and state task is in action, the
9704 * current snapshot is the last one. Discard the current snapshot
9705 * after discarding the current state. */
9706
9707 DiscardSnapshotTask subTask (aTask, mData->mCurrentSnapshot);
9708 subTask.subTask = true;
9709 discardSnapshotHandler (subTask);
9710
9711 AutoCaller progressCaller (aTask.progress);
9712 AutoReadLock progressLock (aTask.progress);
9713 if (aTask.progress->completed())
9714 {
9715 /* the progress can be completed by a subtask only if there
9716 * was a failure */
9717 rc = aTask.progress->resultCode();
9718 Assert (FAILED(rc));
9719 errorInSubtask = true;
9720 }
9721
9722 /* we've committed already, so inform callbacks anyway to ensure
9723 * they don't miss some change */
9724 /// @todo NEWMEDIA check if we need this informCallbacks at all
9725 /// after updating discardCurrentSnapshot functionality
9726 saveFlags |= SaveS_InformCallbacksAnyway;
9727 }
9728
9729 /* @todo saveSettings() below needs a VirtualBox write lock and we need
9730 * to leave this object's lock to do this to follow the {parent-child}
9731 * locking rule. This is the last chance to do that while we are still
9732 * in a protective state which allows us to temporarily leave the lock*/
9733 alock.unlock();
9734 vboxLock.lock();
9735 alock.lock();
9736
9737 /* we have already discarded the current state, so set the execution
9738 * state accordingly no matter of the discard snapshot result */
9739 if (mSSData->mStateFilePath)
9740 setMachineState (MachineState_Saved);
9741 else
9742 setMachineState (MachineState_PoweredOff);
9743
9744 updateMachineStateOnClient();
9745 stateRestored = true;
9746
9747 /* assign the timestamp from the snapshot */
9748 Assert (RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
9749 mData->mLastStateChange = snapshotTimeStamp;
9750
9751 /* save all settings, reset the modified flag and commit. Note that we
9752 * do so even if the subtask failed (errorInSubtask=true) because we've
9753 * already committed machine data and deleted old diffs before
9754 * discarding the current snapshot so there is no way to rollback */
9755 HRESULT rc2 = saveSettings(SaveS_ResetCurStateModified | saveFlags);
9756
9757 /// @todo NEWMEDIA return multiple errors
9758 if (errorInSubtask)
9759 throw rc;
9760
9761 rc = rc2;
9762
9763 if (SUCCEEDED(rc))
9764 {
9765 /* now, delete the unused diffs (only on success!) and uninit them*/
9766 for (std::list< ComObjPtr<HardDisk> >::const_iterator
9767 it = diffs.begin(); it != diffs.end(); ++ it)
9768 {
9769 /// @todo for now, we ignore errors since we've already
9770 /// discarded and therefore cannot fail. Later, we may want to
9771 /// report a warning through the Progress object
9772 HRESULT rc2 = (*it)->deleteStorageAndWait();
9773 if (SUCCEEDED(rc2))
9774 (*it)->uninit();
9775 }
9776 }
9777 }
9778 catch (HRESULT aRC) { rc = aRC; }
9779
9780 if (FAILED (rc))
9781 {
9782 /* preserve existing error info */
9783 ErrorInfoKeeper eik;
9784
9785 if (!errorInSubtask)
9786 {
9787 /* undo all changes on failure unless the subtask has done so */
9788 rollback (false /* aNotify */);
9789 }
9790
9791 if (!stateRestored)
9792 {
9793 /* restore the machine state */
9794 setMachineState (aTask.state);
9795 updateMachineStateOnClient();
9796 }
9797 }
9798
9799 if (!errorInSubtask)
9800 {
9801 /* set the result (this will try to fetch current error info on failure) */
9802 aTask.progress->notifyComplete (rc);
9803 }
9804
9805 if (SUCCEEDED(rc))
9806 mParent->onSnapshotDiscarded (mData->mUuid, Guid());
9807
9808 LogFlowThisFunc(("Done discarding current state (rc=%08X)\n", rc));
9809
9810 LogFlowThisFuncLeave();
9811}
9812
9813/**
9814 * Locks the attached media.
9815 *
9816 * All attached hard disks are locked for writing and DVD/floppy are locked for
9817 * reading. Parents of attached hard disks (if any) are locked for reading.
9818 *
9819 * This method also performs accessibility check of all media it locks: if some
9820 * media is inaccessible, the method will return a failure and a bunch of
9821 * extended error info objects per each inaccessible medium.
9822 *
9823 * Note that this method is atomic: if it returns a success, all media are
9824 * locked as described above; on failure no media is locked at all (all
9825 * succeeded individual locks will be undone).
9826 *
9827 * This method is intended to be called when the machine is in Starting or
9828 * Restoring state and asserts otherwise.
9829 *
9830 * The locks made by this method must be undone by calling #unlockMedia() when
9831 * no more needed.
9832 */
9833HRESULT SessionMachine::lockMedia()
9834{
9835 AutoCaller autoCaller(this);
9836 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9837
9838 AutoWriteLock alock(this);
9839
9840 AssertReturn(mData->mMachineState == MachineState_Starting ||
9841 mData->mMachineState == MachineState_Restoring, E_FAIL);
9842
9843 typedef std::list <ComPtr<IMedium> > MediaList;
9844 MediaList mediaToCheck;
9845 MediaState_T mediaState;
9846
9847 try
9848 {
9849 HRESULT rc = S_OK;
9850
9851 /* lock hard disks */
9852 for (HDData::AttachmentList::const_iterator it =
9853 mHDData->mAttachments.begin();
9854 it != mHDData->mAttachments.end(); ++ it)
9855 {
9856 ComObjPtr<HardDisk> hd = (*it)->hardDisk();
9857
9858 bool first = true;
9859
9860 /** @todo split out the media locking, and put it into
9861 * HardDiskImpl.cpp, as it needs this functionality too. */
9862 while (!hd.isNull())
9863 {
9864 if (first)
9865 {
9866 rc = hd->LockWrite (&mediaState);
9867 CheckComRCThrowRC(rc);
9868
9869 mData->mSession.mLockedMedia.push_back (
9870 Data::Session::LockedMedia::value_type (
9871 ComPtr<IHardDisk> (hd), true));
9872
9873 first = false;
9874 }
9875 else
9876 {
9877 rc = hd->LockRead (&mediaState);
9878 CheckComRCThrowRC(rc);
9879
9880 mData->mSession.mLockedMedia.push_back (
9881 Data::Session::LockedMedia::value_type (
9882 ComPtr<IHardDisk> (hd), false));
9883 }
9884
9885 if (mediaState == MediaState_Inaccessible)
9886 mediaToCheck.push_back (ComPtr<IHardDisk> (hd));
9887
9888 /* no locks or callers here since there should be no way to
9889 * change the hard disk parent at this point (as it is still
9890 * attached to the machine) */
9891 hd = hd->parent();
9892 }
9893 }
9894
9895 /* lock the DVD image for reading if mounted */
9896 {
9897 AutoReadLock driveLock (mDVDDrive);
9898 if (mDVDDrive->data()->state == DriveState_ImageMounted)
9899 {
9900 ComObjPtr<DVDImage> image = mDVDDrive->data()->image;
9901
9902 rc = image->LockRead (&mediaState);
9903 CheckComRCThrowRC (rc);
9904
9905 mData->mSession.mLockedMedia.push_back (
9906 Data::Session::LockedMedia::value_type (
9907 ComPtr<IDVDImage> (image), false));
9908
9909 if (mediaState == MediaState_Inaccessible)
9910 mediaToCheck.push_back (ComPtr<IDVDImage> (image));
9911 }
9912 }
9913
9914 /* lock the floppy image for reading if mounted */
9915 {
9916 AutoReadLock driveLock (mFloppyDrive);
9917 if (mFloppyDrive->data()->state == DriveState_ImageMounted)
9918 {
9919 ComObjPtr<FloppyImage> image = mFloppyDrive->data()->image;
9920
9921 rc = image->LockRead (&mediaState);
9922 CheckComRCThrowRC (rc);
9923
9924 mData->mSession.mLockedMedia.push_back (
9925 Data::Session::LockedMedia::value_type (
9926 ComPtr<IFloppyImage> (image), false));
9927
9928 if (mediaState == MediaState_Inaccessible)
9929 mediaToCheck.push_back (ComPtr<IFloppyImage> (image));
9930 }
9931 }
9932
9933 /* SUCCEEDED locking all media, now check accessibility */
9934
9935 ErrorInfoKeeper eik (true /* aIsNull */);
9936 MultiResult mrc (S_OK);
9937
9938 /* perform a check of inaccessible media deferred above */
9939 for (MediaList::const_iterator
9940 it = mediaToCheck.begin();
9941 it != mediaToCheck.end(); ++ it)
9942 {
9943 MediaState_T mediaState;
9944 rc = (*it)->COMGETTER(State) (&mediaState);
9945 CheckComRCThrowRC(rc);
9946
9947 Assert (mediaState == MediaState_LockedRead ||
9948 mediaState == MediaState_LockedWrite);
9949
9950 /* Note that we locked the medium already, so use the error
9951 * value to see if there was an accessibility failure */
9952
9953 Bstr error;
9954 rc = (*it)->COMGETTER(LastAccessError) (error.asOutParam());
9955 CheckComRCThrowRC(rc);
9956
9957 if (!error.isEmpty())
9958 {
9959 Bstr loc;
9960 rc = (*it)->COMGETTER(Location) (loc.asOutParam());
9961 CheckComRCThrowRC(rc);
9962
9963 /* collect multiple errors */
9964 eik.restore();
9965
9966 /* be in sync with MediumBase::setStateError() */
9967 Assert (!error.isEmpty());
9968 mrc = setError(E_FAIL,
9969 tr("Medium '%ls' is not accessible. %ls"),
9970 loc.raw(),
9971 error.raw());
9972
9973 eik.fetch();
9974 }
9975 }
9976
9977 eik.restore();
9978 CheckComRCThrowRC((HRESULT) mrc);
9979 }
9980 catch (HRESULT aRC)
9981 {
9982 /* Unlock all locked media on failure */
9983 unlockMedia();
9984 return aRC;
9985 }
9986
9987 return S_OK;
9988}
9989
9990/**
9991 * Undoes the locks made by by #lockMedia().
9992 */
9993void SessionMachine::unlockMedia()
9994{
9995 AutoCaller autoCaller(this);
9996 AssertComRCReturnVoid (autoCaller.rc());
9997
9998 AutoWriteLock alock(this);
9999
10000 /* we may be holding important error info on the current thread;
10001 * preserve it */
10002 ErrorInfoKeeper eik;
10003
10004 HRESULT rc = S_OK;
10005
10006 for (Data::Session::LockedMedia::const_iterator
10007 it = mData->mSession.mLockedMedia.begin();
10008 it != mData->mSession.mLockedMedia.end(); ++ it)
10009 {
10010 MediaState_T state;
10011 if (it->second)
10012 rc = it->first->UnlockWrite (&state);
10013 else
10014 rc = it->first->UnlockRead (&state);
10015
10016 /* The second can happen if an object was re-locked in
10017 * Machine::fixupHardDisks(). The last can happen when e.g a DVD/Floppy
10018 * image was unmounted at runtime. */
10019 Assert (SUCCEEDED(rc) || state == MediaState_LockedRead || state == MediaState_Created);
10020 }
10021
10022 mData->mSession.mLockedMedia.clear();
10023}
10024
10025/**
10026 * Helper to change the machine state (reimplementation).
10027 *
10028 * @note Locks this object for writing.
10029 */
10030HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
10031{
10032 LogFlowThisFuncEnter();
10033 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
10034
10035 AutoCaller autoCaller(this);
10036 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10037
10038 AutoWriteLock alock(this);
10039
10040 MachineState_T oldMachineState = mData->mMachineState;
10041
10042 AssertMsgReturn (oldMachineState != aMachineState,
10043 ("oldMachineState=%d, aMachineState=%d\n",
10044 oldMachineState, aMachineState), E_FAIL);
10045
10046 HRESULT rc = S_OK;
10047
10048 int stsFlags = 0;
10049 bool deleteSavedState = false;
10050
10051 /* detect some state transitions */
10052
10053 if ((oldMachineState == MachineState_Saved &&
10054 aMachineState == MachineState_Restoring) ||
10055 (oldMachineState < MachineState_Running /* any other OFF state */ &&
10056 aMachineState == MachineState_Starting))
10057 {
10058 /* The EMT thread is about to start */
10059
10060 /* Nothing to do here for now... */
10061
10062 /// @todo NEWMEDIA don't let mDVDDrive and other children
10063 /// change anything when in the Starting/Restoring state
10064 }
10065 else
10066 if (oldMachineState >= MachineState_Running &&
10067 oldMachineState != MachineState_Discarding &&
10068 oldMachineState != MachineState_SettingUp &&
10069 aMachineState < MachineState_Running &&
10070 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10071 * snapshot */
10072 (mSnapshotData.mSnapshot.isNull() ||
10073 mSnapshotData.mLastState >= MachineState_Running))
10074 {
10075 /* The EMT thread has just stopped, unlock attached media. Note that as
10076 * opposed to locking that is done from Console, we do unlocking here
10077 * because the VM process may have aborted before having a chance to
10078 * properly unlock all media it locked. */
10079
10080 unlockMedia();
10081 }
10082
10083 if (oldMachineState == MachineState_Restoring)
10084 {
10085 if (aMachineState != MachineState_Saved)
10086 {
10087 /*
10088 * delete the saved state file once the machine has finished
10089 * restoring from it (note that Console sets the state from
10090 * Restoring to Saved if the VM couldn't restore successfully,
10091 * to give the user an ability to fix an error and retry --
10092 * we keep the saved state file in this case)
10093 */
10094 deleteSavedState = true;
10095 }
10096 }
10097 else
10098 if (oldMachineState == MachineState_Saved &&
10099 (aMachineState == MachineState_PoweredOff ||
10100 aMachineState == MachineState_Aborted))
10101 {
10102 /*
10103 * delete the saved state after Console::DiscardSavedState() is called
10104 * or if the VM process (owning a direct VM session) crashed while the
10105 * VM was Saved
10106 */
10107
10108 /// @todo (dmik)
10109 // Not sure that deleting the saved state file just because of the
10110 // client death before it attempted to restore the VM is a good
10111 // thing. But when it crashes we need to go to the Aborted state
10112 // which cannot have the saved state file associated... The only
10113 // way to fix this is to make the Aborted condition not a VM state
10114 // but a bool flag: i.e., when a crash occurs, set it to true and
10115 // change the state to PoweredOff or Saved depending on the
10116 // saved state presence.
10117
10118 deleteSavedState = true;
10119 mData->mCurrentStateModified = TRUE;
10120 stsFlags |= SaveSTS_CurStateModified;
10121 }
10122
10123 if (aMachineState == MachineState_Starting ||
10124 aMachineState == MachineState_Restoring)
10125 {
10126 /* set the current state modified flag to indicate that the current
10127 * state is no more identical to the state in the
10128 * current snapshot */
10129 if (!mData->mCurrentSnapshot.isNull())
10130 {
10131 mData->mCurrentStateModified = TRUE;
10132 stsFlags |= SaveSTS_CurStateModified;
10133 }
10134 }
10135
10136 if (deleteSavedState)
10137 {
10138 if (mRemoveSavedState)
10139 {
10140 Assert (!mSSData->mStateFilePath.isEmpty());
10141 RTFileDelete(Utf8Str(mSSData->mStateFilePath).c_str());
10142 }
10143 mSSData->mStateFilePath.setNull();
10144 stsFlags |= SaveSTS_StateFilePath;
10145 }
10146
10147 /* redirect to the underlying peer machine */
10148 mPeer->setMachineState (aMachineState);
10149
10150 if (aMachineState == MachineState_PoweredOff ||
10151 aMachineState == MachineState_Aborted ||
10152 aMachineState == MachineState_Saved)
10153 {
10154 /* the machine has stopped execution
10155 * (or the saved state file was adopted) */
10156 stsFlags |= SaveSTS_StateTimeStamp;
10157 }
10158
10159 if ((oldMachineState == MachineState_PoweredOff ||
10160 oldMachineState == MachineState_Aborted) &&
10161 aMachineState == MachineState_Saved)
10162 {
10163 /* the saved state file was adopted */
10164 Assert (!mSSData->mStateFilePath.isNull());
10165 stsFlags |= SaveSTS_StateFilePath;
10166 }
10167
10168 rc = saveStateSettings (stsFlags);
10169
10170 if ((oldMachineState != MachineState_PoweredOff &&
10171 oldMachineState != MachineState_Aborted) &&
10172 (aMachineState == MachineState_PoweredOff ||
10173 aMachineState == MachineState_Aborted))
10174 {
10175 /* we've been shut down for any reason */
10176 /* no special action so far */
10177 }
10178
10179 LogFlowThisFunc(("rc=%08X\n", rc));
10180 LogFlowThisFuncLeave();
10181 return rc;
10182}
10183
10184/**
10185 * Sends the current machine state value to the VM process.
10186 *
10187 * @note Locks this object for reading, then calls a client process.
10188 */
10189HRESULT SessionMachine::updateMachineStateOnClient()
10190{
10191 AutoCaller autoCaller(this);
10192 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10193
10194 ComPtr<IInternalSessionControl> directControl;
10195 {
10196 AutoReadLock alock(this);
10197 AssertReturn(!!mData, E_FAIL);
10198 directControl = mData->mSession.mDirectControl;
10199
10200 /* directControl may be already set to NULL here in #OnSessionEnd()
10201 * called too early by the direct session process while there is still
10202 * some operation (like discarding the snapshot) in progress. The client
10203 * process in this case is waiting inside Session::close() for the
10204 * "end session" process object to complete, while #uninit() called by
10205 * #checkForDeath() on the Watcher thread is waiting for the pending
10206 * operation to complete. For now, we accept this inconsitent behavior
10207 * and simply do nothing here. */
10208
10209 if (mData->mSession.mState == SessionState_Closing)
10210 return S_OK;
10211
10212 AssertReturn(!directControl.isNull(), E_FAIL);
10213 }
10214
10215 return directControl->UpdateMachineState (mData->mMachineState);
10216}
10217
10218/* static */
10219DECLCALLBACK(int) SessionMachine::taskHandler (RTTHREAD /* thread */, void *pvUser)
10220{
10221 AssertReturn(pvUser, VERR_INVALID_POINTER);
10222
10223 Task *task = static_cast <Task *> (pvUser);
10224 task->handler();
10225
10226 // it's our responsibility to delete the task
10227 delete task;
10228
10229 return 0;
10230}
10231
10232/////////////////////////////////////////////////////////////////////////////
10233// SnapshotMachine class
10234/////////////////////////////////////////////////////////////////////////////
10235
10236DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
10237
10238HRESULT SnapshotMachine::FinalConstruct()
10239{
10240 LogFlowThisFunc(("\n"));
10241
10242 /* set the proper type to indicate we're the SnapshotMachine instance */
10243 unconst(mType) = IsSnapshotMachine;
10244
10245 return S_OK;
10246}
10247
10248void SnapshotMachine::FinalRelease()
10249{
10250 LogFlowThisFunc(("\n"));
10251
10252 uninit();
10253}
10254
10255/**
10256 * Initializes the SnapshotMachine object when taking a snapshot.
10257 *
10258 * @param aSessionMachine machine to take a snapshot from
10259 * @param aSnapshotId snapshot ID of this snapshot machine
10260 * @param aStateFilePath file where the execution state will be later saved
10261 * (or NULL for the offline snapshot)
10262 *
10263 * @note The aSessionMachine must be locked for writing.
10264 */
10265HRESULT SnapshotMachine::init (SessionMachine *aSessionMachine,
10266 IN_GUID aSnapshotId,
10267 const Utf8Str &aStateFilePath)
10268{
10269 LogFlowThisFuncEnter();
10270 LogFlowThisFunc(("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
10271
10272 AssertReturn(aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
10273
10274 /* Enclose the state transition NotReady->InInit->Ready */
10275 AutoInitSpan autoInitSpan(this);
10276 AssertReturn(autoInitSpan.isOk(), E_FAIL);
10277
10278 AssertReturn(aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
10279
10280 mSnapshotId = aSnapshotId;
10281
10282 /* memorize the primary Machine instance (i.e. not SessionMachine!) */
10283 unconst(mPeer) = aSessionMachine->mPeer;
10284 /* share the parent pointer */
10285 unconst(mParent) = mPeer->mParent;
10286
10287 /* take the pointer to Data to share */
10288 mData.share (mPeer->mData);
10289
10290 /* take the pointer to UserData to share (our UserData must always be the
10291 * same as Machine's data) */
10292 mUserData.share (mPeer->mUserData);
10293 /* make a private copy of all other data (recent changes from SessionMachine) */
10294 mHWData.attachCopy (aSessionMachine->mHWData);
10295 mHDData.attachCopy (aSessionMachine->mHDData);
10296
10297 /* SSData is always unique for SnapshotMachine */
10298 mSSData.allocate();
10299 mSSData->mStateFilePath = aStateFilePath;
10300
10301 HRESULT rc = S_OK;
10302
10303 /* create copies of all shared folders (mHWData after attiching a copy
10304 * contains just references to original objects) */
10305 for (HWData::SharedFolderList::iterator
10306 it = mHWData->mSharedFolders.begin();
10307 it != mHWData->mSharedFolders.end();
10308 ++ it)
10309 {
10310 ComObjPtr<SharedFolder> folder;
10311 folder.createObject();
10312 rc = folder->initCopy (this, *it);
10313 CheckComRCReturnRC(rc);
10314 *it = folder;
10315 }
10316
10317 /* associate hard disks with the snapshot
10318 * (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
10319 for (HDData::AttachmentList::const_iterator
10320 it = mHDData->mAttachments.begin();
10321 it != mHDData->mAttachments.end();
10322 ++ it)
10323 {
10324 rc = (*it)->hardDisk()->attachTo (mData->mUuid, mSnapshotId);
10325 AssertComRC(rc);
10326 }
10327
10328 /* create copies of all storage controllers (mStorageControllerData
10329 * after attaching a copy contains just references to original objects) */
10330 mStorageControllers.allocate();
10331 for (StorageControllerList::const_iterator
10332 it = aSessionMachine->mStorageControllers->begin();
10333 it != aSessionMachine->mStorageControllers->end();
10334 ++ it)
10335 {
10336 ComObjPtr<StorageController> ctrl;
10337 ctrl.createObject();
10338 ctrl->initCopy (this, *it);
10339 mStorageControllers->push_back(ctrl);
10340 }
10341
10342 /* create all other child objects that will be immutable private copies */
10343
10344 unconst(mBIOSSettings).createObject();
10345 mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
10346
10347#ifdef VBOX_WITH_VRDP
10348 unconst(mVRDPServer).createObject();
10349 mVRDPServer->initCopy (this, mPeer->mVRDPServer);
10350#endif
10351
10352 unconst(mDVDDrive).createObject();
10353 mDVDDrive->initCopy (this, mPeer->mDVDDrive);
10354
10355 unconst(mFloppyDrive).createObject();
10356 mFloppyDrive->initCopy (this, mPeer->mFloppyDrive);
10357
10358 unconst(mAudioAdapter).createObject();
10359 mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
10360
10361 unconst(mUSBController).createObject();
10362 mUSBController->initCopy (this, mPeer->mUSBController);
10363
10364 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
10365 {
10366 unconst(mNetworkAdapters [slot]).createObject();
10367 mNetworkAdapters [slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
10368 }
10369
10370 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
10371 {
10372 unconst(mSerialPorts [slot]).createObject();
10373 mSerialPorts [slot]->initCopy (this, mPeer->mSerialPorts [slot]);
10374 }
10375
10376 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
10377 {
10378 unconst(mParallelPorts [slot]).createObject();
10379 mParallelPorts [slot]->initCopy (this, mPeer->mParallelPorts [slot]);
10380 }
10381
10382 /* Confirm a successful initialization when it's the case */
10383 autoInitSpan.setSucceeded();
10384
10385 LogFlowThisFuncLeave();
10386 return S_OK;
10387}
10388
10389/**
10390 * Initializes the SnapshotMachine object when loading from the settings file.
10391 *
10392 * @param aMachine machine the snapshot belngs to
10393 * @param aHWNode <Hardware> node
10394 * @param aHDAsNode <HardDiskAttachments> node
10395 * @param aSnapshotId snapshot ID of this snapshot machine
10396 * @param aStateFilePath file where the execution state is saved
10397 * (or NULL for the offline snapshot)
10398 *
10399 * @note Doesn't lock anything.
10400 */
10401HRESULT SnapshotMachine::init(Machine *aMachine,
10402 const settings::Hardware &hardware,
10403 const settings::Storage &storage,
10404 IN_GUID aSnapshotId,
10405 const Utf8Str &aStateFilePath)
10406{
10407 LogFlowThisFuncEnter();
10408 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
10409
10410 AssertReturn(aMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
10411
10412 /* Enclose the state transition NotReady->InInit->Ready */
10413 AutoInitSpan autoInitSpan(this);
10414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
10415
10416 /* Don't need to lock aMachine when VirtualBox is starting up */
10417
10418 mSnapshotId = aSnapshotId;
10419
10420 /* memorize the primary Machine instance */
10421 unconst(mPeer) = aMachine;
10422 /* share the parent pointer */
10423 unconst(mParent) = mPeer->mParent;
10424
10425 /* take the pointer to Data to share */
10426 mData.share (mPeer->mData);
10427 /*
10428 * take the pointer to UserData to share
10429 * (our UserData must always be the same as Machine's data)
10430 */
10431 mUserData.share (mPeer->mUserData);
10432 /* allocate private copies of all other data (will be loaded from settings) */
10433 mHWData.allocate();
10434 mHDData.allocate();
10435 mStorageControllers.allocate();
10436
10437 /* SSData is always unique for SnapshotMachine */
10438 mSSData.allocate();
10439 mSSData->mStateFilePath = aStateFilePath;
10440
10441 /* create all other child objects that will be immutable private copies */
10442
10443 unconst(mBIOSSettings).createObject();
10444 mBIOSSettings->init (this);
10445
10446#ifdef VBOX_WITH_VRDP
10447 unconst(mVRDPServer).createObject();
10448 mVRDPServer->init (this);
10449#endif
10450
10451 unconst(mDVDDrive).createObject();
10452 mDVDDrive->init (this);
10453
10454 unconst(mFloppyDrive).createObject();
10455 mFloppyDrive->init (this);
10456
10457 unconst(mAudioAdapter).createObject();
10458 mAudioAdapter->init (this);
10459
10460 unconst(mUSBController).createObject();
10461 mUSBController->init (this);
10462
10463 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
10464 {
10465 unconst(mNetworkAdapters [slot]).createObject();
10466 mNetworkAdapters [slot]->init (this, slot);
10467 }
10468
10469 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
10470 {
10471 unconst(mSerialPorts [slot]).createObject();
10472 mSerialPorts [slot]->init (this, slot);
10473 }
10474
10475 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
10476 {
10477 unconst(mParallelPorts [slot]).createObject();
10478 mParallelPorts [slot]->init (this, slot);
10479 }
10480
10481 /* load hardware and harddisk settings */
10482
10483 HRESULT rc = loadHardware(hardware);
10484 if (SUCCEEDED(rc))
10485 rc = loadStorageControllers(storage, true /* aRegistered */, &mSnapshotId);
10486
10487 if (SUCCEEDED(rc))
10488 /* commit all changes made during the initialization */
10489 commit();
10490
10491 /* Confirm a successful initialization when it's the case */
10492 if (SUCCEEDED(rc))
10493 autoInitSpan.setSucceeded();
10494
10495 LogFlowThisFuncLeave();
10496 return rc;
10497}
10498
10499/**
10500 * Uninitializes this SnapshotMachine object.
10501 */
10502void SnapshotMachine::uninit()
10503{
10504 LogFlowThisFuncEnter();
10505
10506 /* Enclose the state transition Ready->InUninit->NotReady */
10507 AutoUninitSpan autoUninitSpan(this);
10508 if (autoUninitSpan.uninitDone())
10509 return;
10510
10511 uninitDataAndChildObjects();
10512
10513 /* free the essential data structure last */
10514 mData.free();
10515
10516 unconst(mParent).setNull();
10517 unconst(mPeer).setNull();
10518
10519 LogFlowThisFuncLeave();
10520}
10521
10522// util::Lockable interface
10523////////////////////////////////////////////////////////////////////////////////
10524
10525/**
10526 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
10527 * with the primary Machine instance (mPeer).
10528 */
10529RWLockHandle *SnapshotMachine::lockHandle() const
10530{
10531 AssertReturn(!mPeer.isNull(), NULL);
10532 return mPeer->lockHandle();
10533}
10534
10535// public methods only for internal purposes
10536////////////////////////////////////////////////////////////////////////////////
10537
10538/**
10539 * Called by the snapshot object associated with this SnapshotMachine when
10540 * snapshot data such as name or description is changed.
10541 *
10542 * @note Locks this object for writing.
10543 */
10544HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
10545{
10546 AutoWriteLock alock(this);
10547
10548 // mPeer->saveAllSnapshots(); @todo
10549
10550 /* inform callbacks */
10551 mParent->onSnapshotChange(mData->mUuid, aSnapshot->getId());
10552
10553 return S_OK;
10554}
10555/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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