VirtualBox

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

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

API/others: Renamed IConsole::discardSavedState to IConsole::forgetSavedState, added parameter. Deleted old IConsole::powerDown, renamed IConsole::powerDownAsync to IConsole::powerDown (as promised for 2.1). Implemented perl sample code for registering a hard disk. Cleaned up constant formatting in the API docs. Updated SDK changelog. Renamed com/errorprint2.h to com/errorprint.h, added a few assertion variants. Eliminated com/errorprint_legacy.h. Adjusted all files using the affected headers and APIs. Renamed tstHeadless2 to tstHeadless.

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

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