VirtualBox

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

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

Main: finish integration of Main lock validation with IPRT; only enabled with VBOX_WITH_STRICT_LOCKS=1 (do NOT enable unless you want Main to stop working now)

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