VirtualBox

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

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

Saved state screenshot Main API fix (xTracker 4364).

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