VirtualBox

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

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

Main: deleteSnapshot() fixes; should mostly work now

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

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