VirtualBox

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

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

SessionMachine::PushGuestProperty: Fixed state validation. mHWData.backup() seems to have to trouble with our mutable state when teleporting.

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

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