VirtualBox

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

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

Main: cosmetics

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

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