VirtualBox

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

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

Main: move Host::Get{DVD|Floppy}Drives implementation into implementation methods to eliminate useless conversions in mountMedium()

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

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