VirtualBox

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

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

MachineImpl/AttachDevice: the "smartness" applies only to hard disks. DVD/Floppy attachments should be immediate, and comparing with previous attachments will only cause trouble

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 325.8 KB
Line 
1/* $Id: MachineImpl.cpp 24765 2009-11-18 17:01:37Z 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 (aType == DeviceType_HardDisk && 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 ComObjPtr<Medium> oldmedium = pAttach->medium();
2729 DeviceType_T mediumType = pAttach->type();
2730
2731 if (pAttach->isImplicit())
2732 {
2733 /* attempt to implicitly delete the implicitly created diff */
2734
2735 /// @todo move the implicit flag from MediumAttachment to Medium
2736 /// and forbid any hard disk operation when it is implicit. Or maybe
2737 /// a special media state for it to make it even more simple.
2738
2739 Assert(mMediaData.isBackedUp());
2740
2741 /* will leave the lock before the potentially lengthy operation, so
2742 * protect with the special state */
2743 MachineState_T oldState = mData->mMachineState;
2744 setMachineState(MachineState_SettingUp);
2745
2746 alock.leave();
2747
2748 rc = oldmedium->deleteStorageAndWait();
2749
2750 alock.enter();
2751
2752 setMachineState(oldState);
2753
2754 CheckComRCReturnRC(rc);
2755 }
2756
2757 mMediaData.backup();
2758
2759 /* we cannot use erase (it) below because backup() above will create
2760 * a copy of the list and make this copy active, but the iterator
2761 * still refers to the original and is not valid for the copy */
2762 mMediaData->mAttachments.remove(pAttach);
2763
2764 /* For non-hard disk media, detach straight away. */
2765 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2766 oldmedium->detachFrom(mData->mUuid);
2767
2768 return S_OK;
2769}
2770
2771STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
2772 LONG aDevice, BOOL aPassthrough)
2773{
2774 CheckComArgNotNull(aControllerName);
2775
2776 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
2777 aControllerName, aControllerPort, aDevice, aPassthrough));
2778
2779 AutoCaller autoCaller(this);
2780 CheckComRCReturnRC(autoCaller.rc());
2781
2782 AutoWriteLock alock(this);
2783
2784 HRESULT rc = checkStateDependency(MutableStateDep);
2785 CheckComRCReturnRC(rc);
2786
2787 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2788
2789 if (Global::IsOnlineOrTransient(mData->mMachineState))
2790 return setError(VBOX_E_INVALID_VM_STATE,
2791 tr("Invalid machine state: %s"),
2792 Global::stringifyMachineState(mData->mMachineState));
2793
2794 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2795 aControllerName,
2796 aControllerPort,
2797 aDevice);
2798 if (!pAttach)
2799 return setError(VBOX_E_OBJECT_NOT_FOUND,
2800 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2801 aDevice, aControllerPort, aControllerName);
2802
2803
2804 mMediaData.backup();
2805
2806 AutoWriteLock attLock(pAttach);
2807
2808 if (pAttach->type() != DeviceType_DVD)
2809 return setError(E_INVALIDARG,
2810 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
2811 aDevice, aControllerPort, aControllerName);
2812 pAttach->updatePassthrough(!!aPassthrough);
2813
2814 return S_OK;
2815}
2816
2817STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
2818 LONG aControllerPort,
2819 LONG aDevice,
2820 IN_BSTR aId,
2821 BOOL aForce)
2822{
2823 int rc = S_OK;
2824 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
2825 aControllerName, aControllerPort, aDevice, aForce));
2826
2827 CheckComArgNotNull(aControllerName);
2828 CheckComArgNotNull(aId);
2829
2830 AutoCaller autoCaller(this);
2831 CheckComRCReturnRC(autoCaller.rc());
2832
2833 AutoWriteLock alock(this);
2834
2835 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2836 aControllerName,
2837 aControllerPort,
2838 aDevice);
2839 if (pAttach.isNull())
2840 return setError(VBOX_E_OBJECT_NOT_FOUND,
2841 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
2842 aDevice, aControllerPort, aControllerName);
2843
2844 /* Remember previously mounted medium. The medium before taking the
2845 * backup is not necessarily the same thing. */
2846 ComObjPtr<Medium> oldmedium;
2847 oldmedium = pAttach->medium();
2848
2849 Guid id(aId);
2850 ComObjPtr<Medium> medium;
2851 DeviceType_T mediumType = pAttach->type();
2852 switch (mediumType)
2853 {
2854 case DeviceType_DVD:
2855 if (!id.isEmpty())
2856 {
2857 /* find a DVD by host device UUID */
2858 SafeIfaceArray<IMedium> drivevec;
2859 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2860 if (SUCCEEDED(rc))
2861 {
2862 for (size_t i = 0; i < drivevec.size(); ++i)
2863 {
2864 /// @todo eliminate this conversion
2865 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2866 if (id == med->id())
2867 {
2868 medium = med;
2869 break;
2870 }
2871 }
2872 }
2873 /* find a DVD by UUID */
2874 if (medium.isNull())
2875 rc = mParent->findDVDImage(&id, NULL, true /* aDoSetError */, &medium);
2876 }
2877 CheckComRCReturnRC(rc);
2878 break;
2879 case DeviceType_Floppy:
2880 if (!id.isEmpty())
2881 {
2882 /* find a Floppy by host device UUID */
2883 SafeIfaceArray<IMedium> drivevec;
2884 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2885 if (SUCCEEDED(rc))
2886 {
2887 for (size_t i = 0; i < drivevec.size(); ++i)
2888 {
2889 /// @todo eliminate this conversion
2890 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2891 if (id == med->id())
2892 {
2893 medium = med;
2894 break;
2895 }
2896 }
2897 }
2898 /* find a Floppy by UUID */
2899 if (medium.isNull())
2900 rc = mParent->findFloppyImage(&id, NULL, true /* aDoSetError */, &medium);
2901 }
2902 CheckComRCReturnRC(rc);
2903 break;
2904 default:
2905 return setError(VBOX_E_INVALID_OBJECT_STATE,
2906 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
2907 aDevice, aControllerPort, aControllerName);
2908 }
2909
2910 if (SUCCEEDED(rc))
2911 {
2912
2913 mMediaData.backup();
2914 /* The backup operation makes the pAttach reference point to the
2915 * old settings. Re-get the correct reference. */
2916 pAttach = findAttachment(mMediaData->mAttachments,
2917 aControllerName,
2918 aControllerPort,
2919 aDevice);
2920 AutoWriteLock attLock(pAttach);
2921 /* For non-hard disk media, detach straight away. */
2922 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2923 oldmedium->detachFrom(mData->mUuid);
2924 if (!medium.isNull())
2925 medium->attachTo(mData->mUuid);
2926 pAttach->updateMedium(medium, false /* aImplicit */);
2927 }
2928
2929 alock.leave();
2930 rc = onMediumChange(pAttach, aForce);
2931 alock.enter();
2932
2933 /* On error roll back this change only. */
2934 if (FAILED(rc))
2935 {
2936 if (!medium.isNull())
2937 medium->detachFrom(mData->mUuid);
2938 pAttach = findAttachment(mMediaData->mAttachments,
2939 aControllerName,
2940 aControllerPort,
2941 aDevice);
2942 /* If the attachment is gone in the mean time, bail out. */
2943 if (pAttach.isNull())
2944 return rc;
2945 AutoWriteLock attLock(pAttach);
2946 /* For non-hard disk media, re-attach straight away. */
2947 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2948 oldmedium->attachTo(mData->mUuid);
2949 pAttach->updateMedium(oldmedium, false /* aImplicit */);
2950 }
2951
2952 return rc;
2953}
2954
2955STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
2956 LONG aControllerPort,
2957 LONG aDevice,
2958 IMedium **aMedium)
2959{
2960 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2961 aControllerName, aControllerPort, aDevice));
2962
2963 CheckComArgNotNull(aControllerName);
2964 CheckComArgOutPointerValid(aMedium);
2965
2966 AutoCaller autoCaller(this);
2967 CheckComRCReturnRC(autoCaller.rc());
2968
2969 AutoReadLock alock(this);
2970
2971 *aMedium = NULL;
2972
2973 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2974 aControllerName,
2975 aControllerPort,
2976 aDevice);
2977 if (pAttach.isNull())
2978 return setError(VBOX_E_OBJECT_NOT_FOUND,
2979 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2980 aDevice, aControllerPort, aControllerName);
2981
2982 pAttach->medium().queryInterfaceTo(aMedium);
2983
2984 return S_OK;
2985}
2986
2987STDMETHODIMP Machine::GetSerialPort (ULONG slot, ISerialPort **port)
2988{
2989 CheckComArgOutPointerValid(port);
2990 CheckComArgExpr (slot, slot < RT_ELEMENTS (mSerialPorts));
2991
2992 AutoCaller autoCaller(this);
2993 CheckComRCReturnRC(autoCaller.rc());
2994
2995 AutoReadLock alock(this);
2996
2997 mSerialPorts [slot].queryInterfaceTo(port);
2998
2999 return S_OK;
3000}
3001
3002STDMETHODIMP Machine::GetParallelPort (ULONG slot, IParallelPort **port)
3003{
3004 CheckComArgOutPointerValid(port);
3005 CheckComArgExpr (slot, slot < RT_ELEMENTS (mParallelPorts));
3006
3007 AutoCaller autoCaller(this);
3008 CheckComRCReturnRC(autoCaller.rc());
3009
3010 AutoReadLock alock(this);
3011
3012 mParallelPorts [slot].queryInterfaceTo(port);
3013
3014 return S_OK;
3015}
3016
3017STDMETHODIMP Machine::GetNetworkAdapter (ULONG slot, INetworkAdapter **adapter)
3018{
3019 CheckComArgOutPointerValid(adapter);
3020 CheckComArgExpr (slot, slot < RT_ELEMENTS (mNetworkAdapters));
3021
3022 AutoCaller autoCaller(this);
3023 CheckComRCReturnRC(autoCaller.rc());
3024
3025 AutoReadLock alock(this);
3026
3027 mNetworkAdapters[slot].queryInterfaceTo(adapter);
3028
3029 return S_OK;
3030}
3031
3032STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
3033{
3034 if (ComSafeArrayOutIsNull(aKeys))
3035 return E_POINTER;
3036
3037 AutoCaller autoCaller (this);
3038 CheckComRCReturnRC (autoCaller.rc());
3039
3040 AutoReadLock alock (this);
3041
3042 com::SafeArray<BSTR> saKeys(mData->m_pMachineConfigFile->mapExtraDataItems.size());
3043 int i = 0;
3044 for (settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.begin();
3045 it != mData->m_pMachineConfigFile->mapExtraDataItems.end();
3046 ++it, ++i)
3047 {
3048 const Utf8Str &strKey = it->first;
3049 strKey.cloneTo(&saKeys[i]);
3050 }
3051 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
3052
3053 return S_OK;
3054 }
3055
3056 /**
3057 * @note Locks this object for reading.
3058 */
3059STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
3060 BSTR *aValue)
3061{
3062 CheckComArgNotNull(aKey);
3063 CheckComArgOutPointerValid(aValue);
3064
3065 AutoCaller autoCaller (this);
3066 CheckComRCReturnRC (autoCaller.rc());
3067
3068 /* start with nothing found */
3069 Bstr bstrResult("");
3070
3071 AutoReadLock alock (this);
3072
3073 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
3074 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3075 // found:
3076 bstrResult = it->second; // source is a Utf8Str
3077
3078 /* return the result to caller (may be empty) */
3079 bstrResult.cloneTo(aValue);
3080
3081 return S_OK;
3082}
3083
3084 /**
3085 * @note Locks mParent for writing + this object for writing.
3086 */
3087STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
3088{
3089 CheckComArgNotNull(aKey);
3090
3091 AutoCaller autoCaller(this);
3092 CheckComRCReturnRC (autoCaller.rc());
3093
3094 Utf8Str strKey(aKey);
3095 Utf8Str strValue(aValue);
3096 Utf8Str strOldValue; // empty
3097
3098 // locking note: we only hold the read lock briefly to look up the old value,
3099 // then release it and call the onExtraCanChange callbacks. There is a small
3100 // chance of a race insofar as the callback might be called twice if two callers
3101 // change the same key at the same time, but that's a much better solution
3102 // than the deadlock we had here before. The actual changing of the extradata
3103 // is then performed under the write lock and race-free.
3104
3105 // look up the old value first; if nothing's changed then we need not do anything
3106 {
3107 AutoReadLock alock(this); // hold read lock only while looking up
3108 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(strKey);
3109 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3110 strOldValue = it->second;
3111 }
3112
3113 bool fChanged;
3114 if ((fChanged = (strOldValue != strValue)))
3115 {
3116 // ask for permission from all listeners outside the locks;
3117 // onExtraDataCanChange() only briefly requests the VirtualBox
3118 // lock to copy the list of callbacks to invoke
3119 Bstr error;
3120 Bstr bstrValue;
3121 if (aValue)
3122 bstrValue = aValue;
3123 else
3124 bstrValue = (const char *)"";
3125
3126 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
3127 {
3128 const char *sep = error.isEmpty() ? "" : ": ";
3129 CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
3130 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
3131 sep, err));
3132 return setError(E_ACCESSDENIED,
3133 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
3134 aKey,
3135 bstrValue.raw(),
3136 sep,
3137 err);
3138 }
3139
3140 // data is changing and change not vetoed: then write it out under the locks
3141
3142 // saveSettings() needs VirtualBox write lock
3143 AutoMultiWriteLock2 alock(mParent, this);
3144
3145 if (mType == IsSnapshotMachine)
3146 {
3147 HRESULT rc = checkStateDependency(MutableStateDep);
3148 CheckComRCReturnRC (rc);
3149 }
3150
3151 if (strValue.isEmpty())
3152 mData->m_pMachineConfigFile->mapExtraDataItems.erase(strKey);
3153 else
3154 mData->m_pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
3155 // creates a new key if needed
3156
3157 /* save settings on success */
3158 HRESULT rc = saveSettings();
3159 CheckComRCReturnRC (rc);
3160 }
3161
3162 // fire notification outside the lock
3163 if (fChanged)
3164 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
3165
3166 return S_OK;
3167}
3168
3169STDMETHODIMP Machine::SaveSettings()
3170{
3171 AutoCaller autoCaller(this);
3172 CheckComRCReturnRC(autoCaller.rc());
3173
3174 /* saveSettings() needs mParent lock */
3175 AutoMultiWriteLock2 alock(mParent, this);
3176
3177 /* when there was auto-conversion, we want to save the file even if
3178 * the VM is saved */
3179 HRESULT rc = checkStateDependency(MutableStateDep);
3180 CheckComRCReturnRC(rc);
3181
3182 /* the settings file path may never be null */
3183 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
3184
3185 /* save all VM data excluding snapshots */
3186 return saveSettings();
3187}
3188
3189STDMETHODIMP Machine::DiscardSettings()
3190{
3191 AutoCaller autoCaller(this);
3192 CheckComRCReturnRC(autoCaller.rc());
3193
3194 AutoWriteLock alock(this);
3195
3196 HRESULT rc = checkStateDependency(MutableStateDep);
3197 CheckComRCReturnRC(rc);
3198
3199 /*
3200 * during this rollback, the session will be notified if data has
3201 * been actually changed
3202 */
3203 rollback (true /* aNotify */);
3204
3205 return S_OK;
3206}
3207
3208STDMETHODIMP Machine::DeleteSettings()
3209{
3210 AutoCaller autoCaller(this);
3211 CheckComRCReturnRC(autoCaller.rc());
3212
3213 AutoWriteLock alock(this);
3214
3215 HRESULT rc = checkStateDependency(MutableStateDep);
3216 CheckComRCReturnRC(rc);
3217
3218 if (mData->mRegistered)
3219 return setError(VBOX_E_INVALID_VM_STATE,
3220 tr("Cannot delete settings of a registered machine"));
3221
3222 /* delete the settings only when the file actually exists */
3223 if (mData->m_pMachineConfigFile->fileExists())
3224 {
3225 int vrc = RTFileDelete(mData->m_strConfigFileFull.c_str());
3226 if (RT_FAILURE(vrc))
3227 return setError(VBOX_E_IPRT_ERROR,
3228 tr("Could not delete the settings file '%s' (%Rrc)"),
3229 mData->m_strConfigFileFull.raw(),
3230 vrc);
3231
3232 /* delete the Logs folder, nothing important should be left
3233 * there (we don't check for errors because the user might have
3234 * some private files there that we don't want to delete) */
3235 Utf8Str logFolder;
3236 getLogFolder(logFolder);
3237 Assert(logFolder.length());
3238 if (RTDirExists(logFolder.c_str()))
3239 {
3240 /* Delete all VBox.log[.N] files from the Logs folder
3241 * (this must be in sync with the rotation logic in
3242 * Console::powerUpThread()). Also, delete the VBox.png[.N]
3243 * files that may have been created by the GUI. */
3244 Utf8Str log = Utf8StrFmt("%s/VBox.log", logFolder.raw());
3245 RTFileDelete(log.c_str());
3246 log = Utf8StrFmt("%s/VBox.png", logFolder.raw());
3247 RTFileDelete(log.c_str());
3248 for (int i = 3; i >= 0; i--)
3249 {
3250 log = Utf8StrFmt("%s/VBox.log.%d", logFolder.raw(), i);
3251 RTFileDelete(log.c_str());
3252 log = Utf8StrFmt("%s/VBox.png.%d", logFolder.raw(), i);
3253 RTFileDelete(log.c_str());
3254 }
3255
3256 RTDirRemove(logFolder.c_str());
3257 }
3258
3259 /* delete the Snapshots folder, nothing important should be left
3260 * there (we don't check for errors because the user might have
3261 * some private files there that we don't want to delete) */
3262 Utf8Str snapshotFolder(mUserData->mSnapshotFolderFull);
3263 Assert(snapshotFolder.length());
3264 if (RTDirExists(snapshotFolder.c_str()))
3265 RTDirRemove(snapshotFolder.c_str());
3266
3267 /* delete the directory that contains the settings file, but only
3268 * if it matches the VM name (i.e. a structure created by default in
3269 * prepareSaveSettings()) */
3270 {
3271 Utf8Str settingsDir;
3272 if (isInOwnDir(&settingsDir))
3273 RTDirRemove(settingsDir.c_str());
3274 }
3275 }
3276
3277 return S_OK;
3278}
3279
3280STDMETHODIMP Machine::GetSnapshot (IN_BSTR aId, ISnapshot **aSnapshot)
3281{
3282 CheckComArgOutPointerValid(aSnapshot);
3283
3284 AutoCaller autoCaller(this);
3285 CheckComRCReturnRC(autoCaller.rc());
3286
3287 AutoReadLock alock(this);
3288
3289 Guid id(aId);
3290 ComObjPtr<Snapshot> snapshot;
3291
3292 HRESULT rc = findSnapshot (id, snapshot, true /* aSetError */);
3293 snapshot.queryInterfaceTo(aSnapshot);
3294
3295 return rc;
3296}
3297
3298STDMETHODIMP Machine::FindSnapshot (IN_BSTR aName, ISnapshot **aSnapshot)
3299{
3300 CheckComArgNotNull (aName);
3301 CheckComArgOutPointerValid(aSnapshot);
3302
3303 AutoCaller autoCaller(this);
3304 CheckComRCReturnRC(autoCaller.rc());
3305
3306 AutoReadLock alock(this);
3307
3308 ComObjPtr<Snapshot> snapshot;
3309
3310 HRESULT rc = findSnapshot(aName, snapshot, true /* aSetError */);
3311 snapshot.queryInterfaceTo(aSnapshot);
3312
3313 return rc;
3314}
3315
3316STDMETHODIMP Machine::SetCurrentSnapshot (IN_BSTR /* aId */)
3317{
3318 /// @todo (dmik) don't forget to set
3319 // mData->mCurrentStateModified to FALSE
3320
3321 return setError (E_NOTIMPL, "Not implemented");
3322}
3323
3324STDMETHODIMP Machine::CreateSharedFolder (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
3325{
3326 CheckComArgNotNull(aName);
3327 CheckComArgNotNull(aHostPath);
3328
3329 AutoCaller autoCaller(this);
3330 CheckComRCReturnRC(autoCaller.rc());
3331
3332 AutoWriteLock alock(this);
3333
3334 HRESULT rc = checkStateDependency(MutableStateDep);
3335 CheckComRCReturnRC(rc);
3336
3337 ComObjPtr<SharedFolder> sharedFolder;
3338 rc = findSharedFolder (aName, sharedFolder, false /* aSetError */);
3339 if (SUCCEEDED(rc))
3340 return setError(VBOX_E_OBJECT_IN_USE,
3341 tr("Shared folder named '%ls' already exists"),
3342 aName);
3343
3344 sharedFolder.createObject();
3345 rc = sharedFolder->init (machine(), aName, aHostPath, aWritable);
3346 CheckComRCReturnRC(rc);
3347
3348 mHWData.backup();
3349 mHWData->mSharedFolders.push_back (sharedFolder);
3350
3351 /* inform the direct session if any */
3352 alock.leave();
3353 onSharedFolderChange();
3354
3355 return S_OK;
3356}
3357
3358STDMETHODIMP Machine::RemoveSharedFolder (IN_BSTR aName)
3359{
3360 CheckComArgNotNull (aName);
3361
3362 AutoCaller autoCaller(this);
3363 CheckComRCReturnRC(autoCaller.rc());
3364
3365 AutoWriteLock alock(this);
3366
3367 HRESULT rc = checkStateDependency(MutableStateDep);
3368 CheckComRCReturnRC(rc);
3369
3370 ComObjPtr<SharedFolder> sharedFolder;
3371 rc = findSharedFolder (aName, sharedFolder, true /* aSetError */);
3372 CheckComRCReturnRC(rc);
3373
3374 mHWData.backup();
3375 mHWData->mSharedFolders.remove (sharedFolder);
3376
3377 /* inform the direct session if any */
3378 alock.leave();
3379 onSharedFolderChange();
3380
3381 return S_OK;
3382}
3383
3384STDMETHODIMP Machine::CanShowConsoleWindow (BOOL *aCanShow)
3385{
3386 CheckComArgOutPointerValid(aCanShow);
3387
3388 /* start with No */
3389 *aCanShow = FALSE;
3390
3391 AutoCaller autoCaller(this);
3392 AssertComRCReturnRC(autoCaller.rc());
3393
3394 ComPtr<IInternalSessionControl> directControl;
3395 {
3396 AutoReadLock alock(this);
3397
3398 if (mData->mSession.mState != SessionState_Open)
3399 return setError(VBOX_E_INVALID_VM_STATE,
3400 tr("Machine session is not open (session state: %s)"),
3401 Global::stringifySessionState(mData->mSession.mState));
3402
3403 directControl = mData->mSession.mDirectControl;
3404 }
3405
3406 /* ignore calls made after #OnSessionEnd() is called */
3407 if (!directControl)
3408 return S_OK;
3409
3410 ULONG64 dummy;
3411 return directControl->OnShowWindow (TRUE /* aCheck */, aCanShow, &dummy);
3412}
3413
3414STDMETHODIMP Machine::ShowConsoleWindow (ULONG64 *aWinId)
3415{
3416 CheckComArgOutPointerValid(aWinId);
3417
3418 AutoCaller autoCaller(this);
3419 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3420
3421 ComPtr<IInternalSessionControl> directControl;
3422 {
3423 AutoReadLock alock(this);
3424
3425 if (mData->mSession.mState != SessionState_Open)
3426 return setError(E_FAIL,
3427 tr("Machine session is not open (session state: %s)"),
3428 Global::stringifySessionState(mData->mSession.mState));
3429
3430 directControl = mData->mSession.mDirectControl;
3431 }
3432
3433 /* ignore calls made after #OnSessionEnd() is called */
3434 if (!directControl)
3435 return S_OK;
3436
3437 BOOL dummy;
3438 return directControl->OnShowWindow (FALSE /* aCheck */, &dummy, aWinId);
3439}
3440
3441STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
3442 BSTR *aValue,
3443 ULONG64 *aTimestamp,
3444 BSTR *aFlags)
3445{
3446#if !defined (VBOX_WITH_GUEST_PROPS)
3447 ReturnComNotImplemented();
3448#else
3449 CheckComArgNotNull(aName);
3450 CheckComArgOutPointerValid(aValue);
3451 CheckComArgOutPointerValid(aTimestamp);
3452 CheckComArgOutPointerValid(aFlags);
3453
3454 AutoCaller autoCaller(this);
3455 CheckComRCReturnRC(autoCaller.rc());
3456
3457 AutoReadLock alock(this);
3458
3459 using namespace guestProp;
3460 HRESULT rc = E_FAIL;
3461
3462 Utf8Str strName(aName);
3463
3464 if (!mHWData->mPropertyServiceActive)
3465 {
3466 bool found = false;
3467 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
3468 (it != mHWData->mGuestProperties.end()) && !found;
3469 ++it)
3470 {
3471 if (it->strName == strName)
3472 {
3473 char szFlags[MAX_FLAGS_LEN + 1];
3474 it->strValue.cloneTo(aValue);
3475 *aTimestamp = it->mTimestamp;
3476 writeFlags(it->mFlags, szFlags);
3477 Bstr(szFlags).cloneTo(aFlags);
3478 found = true;
3479 }
3480 }
3481 rc = S_OK;
3482 }
3483 else
3484 {
3485 ComPtr<IInternalSessionControl> directControl =
3486 mData->mSession.mDirectControl;
3487
3488 /* just be on the safe side when calling another process */
3489 alock.unlock();
3490
3491 /* fail if we were called after #OnSessionEnd() is called. This is a
3492 * silly race condition. */
3493
3494 if (!directControl)
3495 rc = E_FAIL;
3496 else
3497 rc = directControl->AccessGuestProperty (aName, NULL, NULL,
3498 false /* isSetter */,
3499 aValue, aTimestamp, aFlags);
3500 }
3501 return rc;
3502#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3503}
3504
3505STDMETHODIMP Machine::GetGuestPropertyValue (IN_BSTR aName, BSTR *aValue)
3506{
3507 ULONG64 dummyTimestamp;
3508 BSTR dummyFlags;
3509 return GetGuestProperty (aName, aValue, &dummyTimestamp, &dummyFlags);
3510}
3511
3512STDMETHODIMP Machine::GetGuestPropertyTimestamp (IN_BSTR aName, ULONG64 *aTimestamp)
3513{
3514 BSTR dummyValue;
3515 BSTR dummyFlags;
3516 return GetGuestProperty (aName, &dummyValue, aTimestamp, &dummyFlags);
3517}
3518
3519STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName,
3520 IN_BSTR aValue,
3521 IN_BSTR aFlags)
3522{
3523#if !defined (VBOX_WITH_GUEST_PROPS)
3524 ReturnComNotImplemented();
3525#else
3526 using namespace guestProp;
3527
3528 CheckComArgNotNull(aName);
3529 CheckComArgNotNull(aValue);
3530 if ((aFlags != NULL) && !VALID_PTR (aFlags))
3531 return E_INVALIDARG;
3532
3533 HRESULT rc = S_OK;
3534
3535 try
3536 {
3537 Utf8Str utf8Name(aName);
3538 Utf8Str utf8Flags(aFlags);
3539
3540 AutoCaller autoCaller(this);
3541 CheckComRCReturnRC(autoCaller.rc());
3542
3543 AutoWriteLock alock(this);
3544
3545 rc = checkStateDependency(MutableStateDep);
3546 CheckComRCReturnRC(rc);
3547
3548 rc = S_OK;
3549 uint32_t fFlags = NILFLAG;
3550 if ( (aFlags != NULL)
3551 && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags))
3552 )
3553 return setError(E_INVALIDARG,
3554 tr("Invalid flag values: '%ls'"),
3555 aFlags);
3556
3557 if (!mHWData->mPropertyServiceActive)
3558 {
3559 bool found = false;
3560 HWData::GuestProperty property;
3561 property.mFlags = NILFLAG;
3562
3563 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I know,
3564 * this is simple and do an OK job atm.) */
3565 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3566 it != mHWData->mGuestProperties.end();
3567 ++it)
3568 if (it->strName == utf8Name)
3569 {
3570 property = *it;
3571 if (it->mFlags & (RDONLYHOST))
3572 rc = setError(E_ACCESSDENIED,
3573 tr("The property '%ls' cannot be changed by the host"),
3574 aName);
3575 else
3576 {
3577 mHWData.backup();
3578 /* The backup() operation invalidates our iterator, so
3579 * get a new one. */
3580 for (it = mHWData->mGuestProperties.begin();
3581 it->strName != utf8Name;
3582 ++it)
3583 ;
3584 mHWData->mGuestProperties.erase (it);
3585 }
3586 found = true;
3587 break;
3588 }
3589 if (found && SUCCEEDED(rc))
3590 {
3591 if (*aValue)
3592 {
3593 RTTIMESPEC time;
3594 property.strValue = aValue;
3595 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3596 if (aFlags != NULL)
3597 property.mFlags = fFlags;
3598 mHWData->mGuestProperties.push_back (property);
3599 }
3600 }
3601 else if (SUCCEEDED(rc) && *aValue)
3602 {
3603 RTTIMESPEC time;
3604 mHWData.backup();
3605 property.strName = aName;
3606 property.strValue = aValue;
3607 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3608 property.mFlags = fFlags;
3609 mHWData->mGuestProperties.push_back (property);
3610 }
3611 if ( SUCCEEDED(rc)
3612 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
3613 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(), RTSTR_MAX,
3614 utf8Name.raw(), RTSTR_MAX, NULL) )
3615 )
3616 {
3617 /** @todo r=bird: Why aren't we leaving the lock here? The
3618 * same code in PushGuestProperty does... */
3619 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
3620 }
3621 }
3622 else
3623 {
3624 ComPtr<IInternalSessionControl> directControl =
3625 mData->mSession.mDirectControl;
3626
3627 /* just be on the safe side when calling another process */
3628 alock.leave();
3629
3630 BSTR dummy = NULL;
3631 ULONG64 dummy64;
3632 if (!directControl)
3633 rc = E_FAIL;
3634 else
3635 rc = directControl->AccessGuestProperty(aName,
3636 *aValue ? aValue : NULL, /** @todo Fix when adding DeleteGuestProperty(), see defect. */
3637 aFlags,
3638 true /* isSetter */,
3639 &dummy, &dummy64, &dummy);
3640 }
3641 }
3642 catch (std::bad_alloc &)
3643 {
3644 rc = E_OUTOFMEMORY;
3645 }
3646
3647 return rc;
3648#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3649}
3650
3651STDMETHODIMP Machine::SetGuestPropertyValue (IN_BSTR aName, IN_BSTR aValue)
3652{
3653 return SetGuestProperty (aName, aValue, NULL);
3654}
3655
3656STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
3657 ComSafeArrayOut(BSTR, aNames),
3658 ComSafeArrayOut(BSTR, aValues),
3659 ComSafeArrayOut(ULONG64, aTimestamps),
3660 ComSafeArrayOut(BSTR, aFlags))
3661{
3662#if !defined (VBOX_WITH_GUEST_PROPS)
3663 ReturnComNotImplemented();
3664#else
3665 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
3666 return E_POINTER;
3667
3668 CheckComArgOutSafeArrayPointerValid(aNames);
3669 CheckComArgOutSafeArrayPointerValid(aValues);
3670 CheckComArgOutSafeArrayPointerValid(aTimestamps);
3671 CheckComArgOutSafeArrayPointerValid(aFlags);
3672
3673 AutoCaller autoCaller(this);
3674 CheckComRCReturnRC(autoCaller.rc());
3675
3676 AutoReadLock alock(this);
3677
3678 using namespace guestProp;
3679 HRESULT rc = E_FAIL;
3680
3681 Utf8Str strPatterns(aPatterns);
3682
3683 if (!mHWData->mPropertyServiceActive)
3684 {
3685
3686 /*
3687 * Look for matching patterns and build up a list.
3688 */
3689 HWData::GuestPropertyList propList;
3690 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3691 it != mHWData->mGuestProperties.end();
3692 ++it)
3693 if ( strPatterns.isEmpty()
3694 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
3695 RTSTR_MAX,
3696 it->strName.raw(),
3697 RTSTR_MAX, NULL)
3698 )
3699 propList.push_back(*it);
3700
3701 /*
3702 * And build up the arrays for returning the property information.
3703 */
3704 size_t cEntries = propList.size();
3705 SafeArray<BSTR> names (cEntries);
3706 SafeArray<BSTR> values (cEntries);
3707 SafeArray<ULONG64> timestamps (cEntries);
3708 SafeArray<BSTR> flags (cEntries);
3709 size_t iProp = 0;
3710 for (HWData::GuestPropertyList::iterator it = propList.begin();
3711 it != propList.end();
3712 ++it)
3713 {
3714 char szFlags[MAX_FLAGS_LEN + 1];
3715 it->strName.cloneTo(&names[iProp]);
3716 it->strValue.cloneTo(&values[iProp]);
3717 timestamps[iProp] = it->mTimestamp;
3718 writeFlags(it->mFlags, szFlags);
3719 Bstr(szFlags).cloneTo(&flags[iProp]);
3720 ++iProp;
3721 }
3722 names.detachTo(ComSafeArrayOutArg(aNames));
3723 values.detachTo(ComSafeArrayOutArg(aValues));
3724 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
3725 flags.detachTo(ComSafeArrayOutArg(aFlags));
3726 rc = S_OK;
3727 }
3728 else
3729 {
3730 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
3731
3732 /* just be on the safe side when calling another process */
3733 alock.unlock();
3734
3735 if (!directControl)
3736 rc = E_FAIL;
3737 else
3738 rc = directControl->EnumerateGuestProperties(aPatterns,
3739 ComSafeArrayOutArg(aNames),
3740 ComSafeArrayOutArg(aValues),
3741 ComSafeArrayOutArg(aTimestamps),
3742 ComSafeArrayOutArg(aFlags));
3743 }
3744 return rc;
3745#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3746}
3747
3748STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
3749 ComSafeArrayOut(IMediumAttachment*, aAttachments))
3750{
3751 MediaData::AttachmentList atts;
3752
3753 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
3754 CheckComRCReturnRC(rc);
3755
3756 SafeIfaceArray<IMediumAttachment> attachments(atts);
3757 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
3758
3759 return S_OK;
3760}
3761
3762STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
3763 LONG aControllerPort,
3764 LONG aDevice,
3765 IMediumAttachment **aAttachment)
3766{
3767 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
3768 aControllerName, aControllerPort, aDevice));
3769
3770 CheckComArgNotNull(aControllerName);
3771 CheckComArgOutPointerValid(aAttachment);
3772
3773 AutoCaller autoCaller(this);
3774 CheckComRCReturnRC(autoCaller.rc());
3775
3776 AutoReadLock alock(this);
3777
3778 *aAttachment = NULL;
3779
3780 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3781 aControllerName,
3782 aControllerPort,
3783 aDevice);
3784 if (pAttach.isNull())
3785 return setError(VBOX_E_OBJECT_NOT_FOUND,
3786 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3787 aDevice, aControllerPort, aControllerName);
3788
3789 pAttach.queryInterfaceTo(aAttachment);
3790
3791 return S_OK;
3792}
3793
3794STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
3795 StorageBus_T aConnectionType,
3796 IStorageController **controller)
3797{
3798 CheckComArgStrNotEmptyOrNull(aName);
3799
3800 if ( (aConnectionType <= StorageBus_Null)
3801 || (aConnectionType > StorageBus_Floppy))
3802 return setError (E_INVALIDARG,
3803 tr ("Invalid connection type: %d"),
3804 aConnectionType);
3805
3806 AutoCaller autoCaller(this);
3807 CheckComRCReturnRC(autoCaller.rc());
3808
3809 AutoWriteLock alock(this);
3810
3811 HRESULT rc = checkStateDependency(MutableStateDep);
3812 CheckComRCReturnRC(rc);
3813
3814 /* try to find one with the name first. */
3815 ComObjPtr<StorageController> ctrl;
3816
3817 rc = getStorageControllerByName (aName, ctrl, false /* aSetError */);
3818 if (SUCCEEDED(rc))
3819 return setError (VBOX_E_OBJECT_IN_USE,
3820 tr ("Storage controller named '%ls' already exists"), aName);
3821
3822 ctrl.createObject();
3823
3824 /* get a new instance number for the storage controller */
3825 ULONG ulInstance = 0;
3826 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
3827 it != mStorageControllers->end();
3828 ++it)
3829 {
3830 if ((*it)->storageBus() == aConnectionType)
3831 {
3832 ULONG ulCurInst = (*it)->instance();
3833
3834 if (ulCurInst >= ulInstance)
3835 ulInstance = ulCurInst + 1;
3836 }
3837 }
3838
3839 rc = ctrl->init(this, aName, aConnectionType, ulInstance);
3840 CheckComRCReturnRC(rc);
3841
3842 mStorageControllers.backup();
3843 mStorageControllers->push_back (ctrl);
3844
3845 ctrl.queryInterfaceTo(controller);
3846
3847 /* inform the direct session if any */
3848 alock.leave();
3849 onStorageControllerChange();
3850
3851 return S_OK;
3852}
3853
3854STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
3855 IStorageController **aStorageController)
3856{
3857 CheckComArgStrNotEmptyOrNull(aName);
3858
3859 AutoCaller autoCaller(this);
3860 CheckComRCReturnRC(autoCaller.rc());
3861
3862 AutoReadLock alock(this);
3863
3864 ComObjPtr<StorageController> ctrl;
3865
3866 HRESULT rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3867 if (SUCCEEDED(rc))
3868 ctrl.queryInterfaceTo(aStorageController);
3869
3870 return rc;
3871}
3872
3873STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
3874 IStorageController **aStorageController)
3875{
3876 AutoCaller autoCaller(this);
3877 CheckComRCReturnRC(autoCaller.rc());
3878
3879 AutoReadLock alock(this);
3880
3881 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
3882 it != mStorageControllers->end();
3883 ++it)
3884 {
3885 if ((*it)->instance() == aInstance)
3886 {
3887 (*it).queryInterfaceTo(aStorageController);
3888 return S_OK;
3889 }
3890 }
3891
3892 return setError(VBOX_E_OBJECT_NOT_FOUND,
3893 tr("Could not find a storage controller with instance number '%lu'"),
3894 aInstance);
3895}
3896
3897STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
3898{
3899 CheckComArgStrNotEmptyOrNull(aName);
3900
3901 AutoCaller autoCaller(this);
3902 CheckComRCReturnRC(autoCaller.rc());
3903
3904 AutoWriteLock alock(this);
3905
3906 HRESULT rc = checkStateDependency(MutableStateDep);
3907 CheckComRCReturnRC(rc);
3908
3909 ComObjPtr<StorageController> ctrl;
3910 rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3911 CheckComRCReturnRC(rc);
3912
3913 /* We can remove the controller only if there is no device attached. */
3914 /* check if the device slot is already busy */
3915 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
3916 it != mMediaData->mAttachments.end();
3917 ++it)
3918 {
3919 if ((*it)->controllerName() == aName)
3920 return setError(VBOX_E_OBJECT_IN_USE,
3921 tr("Storage controller named '%ls' has still devices attached"),
3922 aName);
3923 }
3924
3925 /* We can remove it now. */
3926 mStorageControllers.backup();
3927
3928 ctrl->unshare();
3929
3930 mStorageControllers->remove (ctrl);
3931
3932 /* inform the direct session if any */
3933 alock.leave();
3934 onStorageControllerChange();
3935
3936 return S_OK;
3937}
3938
3939/* @todo where is the right place for this? */
3940#define sSSMDisplayScreenshotVer 0x00010001
3941
3942static int readSavedDisplayScreenshot(Utf8Str *pStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
3943{
3944 LogFlowFunc(("u32Type = %d [%s]\n", u32Type, pStateFilePath->raw()));
3945
3946 /* @todo cache read data */
3947 if (pStateFilePath->isEmpty())
3948 {
3949 /* No saved state data. */
3950 return VERR_NOT_SUPPORTED;
3951 }
3952
3953 uint8_t *pu8Data = NULL;
3954 uint32_t cbData = 0;
3955 uint32_t u32Width = 0;
3956 uint32_t u32Height = 0;
3957
3958 PSSMHANDLE pSSM;
3959 int rc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
3960 if (RT_SUCCESS(rc))
3961 {
3962 uint32_t uVersion;
3963 rc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
3964 if (RT_SUCCESS(rc))
3965 {
3966 if (uVersion == sSSMDisplayScreenshotVer)
3967 {
3968 uint32_t cBlocks;
3969 int rc = SSMR3GetU32(pSSM, &cBlocks);
3970 AssertRCReturn(rc, rc);
3971
3972 for (uint32_t i = 0; i < cBlocks; i++)
3973 {
3974 uint32_t cbBlock;
3975 rc = SSMR3GetU32(pSSM, &cbBlock);
3976 AssertRCBreak(rc);
3977
3978 uint32_t typeOfBlock;
3979 rc = SSMR3GetU32(pSSM, &typeOfBlock);
3980 AssertRCBreak(rc);
3981
3982 LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
3983
3984 if (typeOfBlock == u32Type)
3985 {
3986 if (cbBlock > 2 * sizeof (uint32_t))
3987 {
3988 cbData = cbBlock - 2 * sizeof (uint32_t);
3989 pu8Data = (uint8_t *)RTMemAlloc(cbData);
3990 if (pu8Data == NULL)
3991 {
3992 rc = VERR_NO_MEMORY;
3993 break;
3994 }
3995
3996 rc = SSMR3GetU32(pSSM, &u32Width);
3997 AssertRCBreak(rc);
3998 rc = SSMR3GetU32(pSSM, &u32Height);
3999 AssertRCBreak(rc);
4000 rc = SSMR3GetMem(pSSM, pu8Data, cbData);
4001 AssertRCBreak(rc);
4002 }
4003 else
4004 {
4005 /* No saved state data. */
4006 rc = VERR_NOT_SUPPORTED;
4007 }
4008
4009 break;
4010 }
4011 else
4012 {
4013 if (cbBlock != 0)
4014 {
4015 rc = SSMR3Skip(pSSM, cbBlock);
4016 AssertRCBreak(rc);
4017 }
4018 }
4019 }
4020 }
4021 else
4022 {
4023 rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4024 }
4025 }
4026
4027 SSMR3Close(pSSM);
4028 }
4029
4030 if (RT_SUCCESS(rc))
4031 {
4032 if (u32Type == 0 && cbData % 4 != 0)
4033 {
4034 /* Bitmap is 32bpp, so data is invalid. */
4035 rc = VERR_SSM_UNEXPECTED_DATA;
4036 }
4037 }
4038
4039 if (RT_SUCCESS(rc))
4040 {
4041 *ppu8Data = pu8Data;
4042 *pcbData = cbData;
4043 *pu32Width = u32Width;
4044 *pu32Height = u32Height;
4045 LogFlowFunc(("cbData %d, u32Width %d, u32Height %d\n", cbData, u32Width, u32Height));
4046 }
4047
4048 LogFlowFunc(("rc %Rrc\n", rc));
4049 return rc;
4050}
4051
4052static void freeSavedDisplayScreenshot(uint8_t *pu8Data)
4053{
4054 /* @todo not necessary when caching is implemented. */
4055 RTMemFree(pu8Data);
4056}
4057
4058STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4059{
4060 LogFlowThisFunc(("\n"));
4061
4062 CheckComArgNotNull(aSize);
4063 CheckComArgNotNull(aWidth);
4064 CheckComArgNotNull(aHeight);
4065
4066 AutoCaller autoCaller(this);
4067 CheckComRCReturnRC(autoCaller.rc());
4068
4069 AutoReadLock alock(this);
4070
4071 uint8_t *pu8Data = NULL;
4072 uint32_t cbData = 0;
4073 uint32_t u32Width = 0;
4074 uint32_t u32Height = 0;
4075
4076 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4077
4078 if (RT_FAILURE(vrc))
4079 return setError (VBOX_E_IPRT_ERROR,
4080 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4081
4082 *aSize = cbData;
4083 *aWidth = u32Width;
4084 *aHeight = u32Height;
4085
4086 freeSavedDisplayScreenshot(pu8Data);
4087
4088 return S_OK;
4089}
4090
4091STDMETHODIMP Machine::ReadSavedThumbnailToArray(BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4092{
4093 LogFlowThisFunc(("\n"));
4094
4095 CheckComArgNotNull(aWidth);
4096 CheckComArgNotNull(aHeight);
4097 CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4098
4099 AutoCaller autoCaller(this);
4100 CheckComRCReturnRC(autoCaller.rc());
4101
4102 AutoReadLock alock(this);
4103
4104 uint8_t *pu8Data = NULL;
4105 uint32_t cbData = 0;
4106 uint32_t u32Width = 0;
4107 uint32_t u32Height = 0;
4108
4109 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4110
4111 if (RT_FAILURE(vrc))
4112 return setError (VBOX_E_IPRT_ERROR,
4113 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4114
4115 *aWidth = u32Width;
4116 *aHeight = u32Height;
4117
4118 com::SafeArray<BYTE> bitmap(cbData);
4119 /* Convert pixels to format expected by the API caller. */
4120 if (aBGR)
4121 {
4122 /* [0] B, [1] G, [2] R, [3] A. */
4123 for (unsigned i = 0; i < cbData; i += 4)
4124 {
4125 bitmap[i] = pu8Data[i];
4126 bitmap[i + 1] = pu8Data[i + 1];
4127 bitmap[i + 2] = pu8Data[i + 2];
4128 bitmap[i + 3] = 0xff;
4129 }
4130 }
4131 else
4132 {
4133 /* [0] R, [1] G, [2] B, [3] A. */
4134 for (unsigned i = 0; i < cbData; i += 4)
4135 {
4136 bitmap[i] = pu8Data[i + 2];
4137 bitmap[i + 1] = pu8Data[i + 1];
4138 bitmap[i + 2] = pu8Data[i];
4139 bitmap[i + 3] = 0xff;
4140 }
4141 }
4142 bitmap.detachTo(ComSafeArrayOutArg(aData));
4143
4144 freeSavedDisplayScreenshot(pu8Data);
4145
4146 return S_OK;
4147}
4148
4149STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4150{
4151 LogFlowThisFunc(("\n"));
4152
4153 CheckComArgNotNull(aSize);
4154 CheckComArgNotNull(aWidth);
4155 CheckComArgNotNull(aHeight);
4156
4157 AutoCaller autoCaller(this);
4158 CheckComRCReturnRC(autoCaller.rc());
4159
4160 AutoReadLock alock(this);
4161
4162 uint8_t *pu8Data = NULL;
4163 uint32_t cbData = 0;
4164 uint32_t u32Width = 0;
4165 uint32_t u32Height = 0;
4166
4167 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4168
4169 if (RT_FAILURE(vrc))
4170 return setError (VBOX_E_IPRT_ERROR,
4171 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4172
4173 *aSize = cbData;
4174 *aWidth = u32Width;
4175 *aHeight = u32Height;
4176
4177 freeSavedDisplayScreenshot(pu8Data);
4178
4179 return S_OK;
4180}
4181
4182STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4183{
4184 LogFlowThisFunc(("\n"));
4185
4186 CheckComArgNotNull(aWidth);
4187 CheckComArgNotNull(aHeight);
4188 CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4189
4190 AutoCaller autoCaller(this);
4191 CheckComRCReturnRC(autoCaller.rc());
4192
4193 AutoReadLock alock(this);
4194
4195 uint8_t *pu8Data = NULL;
4196 uint32_t cbData = 0;
4197 uint32_t u32Width = 0;
4198 uint32_t u32Height = 0;
4199
4200 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4201
4202 if (RT_FAILURE(vrc))
4203 return setError (VBOX_E_IPRT_ERROR,
4204 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4205
4206 *aWidth = u32Width;
4207 *aHeight = u32Height;
4208
4209 com::SafeArray<BYTE> png(cbData);
4210 for (unsigned i = 0; i < cbData; i++)
4211 png[i] = pu8Data[i];
4212 png.detachTo(ComSafeArrayOutArg(aData));
4213
4214 freeSavedDisplayScreenshot(pu8Data);
4215
4216 return S_OK;
4217}
4218
4219// public methods for internal purposes
4220/////////////////////////////////////////////////////////////////////////////
4221
4222/**
4223 * Saves the registry entry of this machine to the given configuration node.
4224 *
4225 * @param aEntryNode Node to save the registry entry to.
4226 *
4227 * @note locks this object for reading.
4228 */
4229HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
4230{
4231 AutoLimitedCaller autoCaller(this);
4232 AssertComRCReturnRC(autoCaller.rc());
4233
4234 AutoReadLock alock(this);
4235
4236 data.uuid = mData->mUuid;
4237 data.strSettingsFile = mData->m_strConfigFile;
4238
4239 return S_OK;
4240}
4241
4242/**
4243 * Calculates the absolute path of the given path taking the directory of the
4244 * machine settings file as the current directory.
4245 *
4246 * @param aPath Path to calculate the absolute path for.
4247 * @param aResult Where to put the result (used only on success, can be the
4248 * same Utf8Str instance as passed in @a aPath).
4249 * @return IPRT result.
4250 *
4251 * @note Locks this object for reading.
4252 */
4253int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4254{
4255 AutoCaller autoCaller(this);
4256 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4257
4258 AutoReadLock alock(this);
4259
4260 AssertReturn (!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
4261
4262 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
4263
4264 strSettingsDir.stripFilename();
4265 char folder[RTPATH_MAX];
4266 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
4267 if (RT_SUCCESS(vrc))
4268 aResult = folder;
4269
4270 return vrc;
4271}
4272
4273/**
4274 * Tries to calculate the relative path of the given absolute path using the
4275 * directory of the machine settings file as the base directory.
4276 *
4277 * @param aPath Absolute path to calculate the relative path for.
4278 * @param aResult Where to put the result (used only when it's possible to
4279 * make a relative path from the given absolute path; otherwise
4280 * left untouched).
4281 *
4282 * @note Locks this object for reading.
4283 */
4284void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
4285{
4286 AutoCaller autoCaller(this);
4287 AssertComRCReturn (autoCaller.rc(), (void) 0);
4288
4289 AutoReadLock alock(this);
4290
4291 AssertReturnVoid (!mData->m_strConfigFileFull.isEmpty());
4292
4293 Utf8Str settingsDir = mData->m_strConfigFileFull;
4294
4295 settingsDir.stripFilename();
4296 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
4297 {
4298 /* when assigning, we create a separate Utf8Str instance because both
4299 * aPath and aResult can point to the same memory location when this
4300 * func is called (if we just do aResult = aPath, aResult will be freed
4301 * first, and since its the same as aPath, an attempt to copy garbage
4302 * will be made. */
4303 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
4304 }
4305}
4306
4307/**
4308 * Returns the full path to the machine's log folder in the
4309 * \a aLogFolder argument.
4310 */
4311void Machine::getLogFolder (Utf8Str &aLogFolder)
4312{
4313 AutoCaller autoCaller(this);
4314 AssertComRCReturnVoid (autoCaller.rc());
4315
4316 AutoReadLock alock(this);
4317
4318 Utf8Str settingsDir;
4319 if (isInOwnDir (&settingsDir))
4320 {
4321 /* Log folder is <Machines>/<VM_Name>/Logs */
4322 aLogFolder = Utf8StrFmt ("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
4323 }
4324 else
4325 {
4326 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
4327 Assert (!mUserData->mSnapshotFolderFull.isEmpty());
4328 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
4329 RTPATH_DELIMITER);
4330 }
4331}
4332
4333/**
4334 * @note Locks this object for writing, calls the client process (outside the
4335 * lock).
4336 */
4337HRESULT Machine::openSession(IInternalSessionControl *aControl)
4338{
4339 LogFlowThisFuncEnter();
4340
4341 AssertReturn(aControl, E_FAIL);
4342
4343 AutoCaller autoCaller(this);
4344 CheckComRCReturnRC(autoCaller.rc());
4345
4346 AutoWriteLock alock(this);
4347
4348 if (!mData->mRegistered)
4349 return setError(E_UNEXPECTED,
4350 tr("The machine '%ls' is not registered"),
4351 mUserData->mName.raw());
4352
4353 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4354
4355 /* Hack: in case the session is closing and there is a progress object
4356 * which allows waiting for the session to be closed, take the opportunity
4357 * and do a limited wait (max. 1 second). This helps a lot when the system
4358 * is busy and thus session closing can take a little while. */
4359 if ( mData->mSession.mState == SessionState_Closing
4360 && mData->mSession.mProgress)
4361 {
4362 alock.leave();
4363 mData->mSession.mProgress->WaitForCompletion(1000);
4364 alock.enter();
4365 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4366 }
4367
4368 if (mData->mSession.mState == SessionState_Open ||
4369 mData->mSession.mState == SessionState_Closing)
4370 return setError(VBOX_E_INVALID_OBJECT_STATE,
4371 tr("A session for the machine '%ls' is currently open (or being closed)"),
4372 mUserData->mName.raw());
4373
4374 /* may not be busy */
4375 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
4376
4377 /* get the session PID */
4378 RTPROCESS pid = NIL_RTPROCESS;
4379 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
4380 aControl->GetPID((ULONG *) &pid);
4381 Assert(pid != NIL_RTPROCESS);
4382
4383 if (mData->mSession.mState == SessionState_Spawning)
4384 {
4385 /* This machine is awaiting for a spawning session to be opened, so
4386 * reject any other open attempts from processes other than one
4387 * started by #openRemoteSession(). */
4388
4389 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
4390 mData->mSession.mPid, mData->mSession.mPid));
4391 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
4392
4393 if (mData->mSession.mPid != pid)
4394 return setError(E_ACCESSDENIED,
4395 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
4396 "session with the machine named '%ls', while only a process "
4397 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
4398 pid, mUserData->mName.raw(), mData->mSession.mPid);
4399 }
4400
4401 /* create a SessionMachine object */
4402 ComObjPtr<SessionMachine> sessionMachine;
4403 sessionMachine.createObject();
4404 HRESULT rc = sessionMachine->init(this);
4405 AssertComRC(rc);
4406
4407 /* NOTE: doing return from this function after this point but
4408 * before the end is forbidden since it may call SessionMachine::uninit()
4409 * (through the ComObjPtr's destructor) which requests the VirtualBox write
4410 * lock while still holding the Machine lock in alock so that a deadlock
4411 * is possible due to the wrong lock order. */
4412
4413 if (SUCCEEDED(rc))
4414 {
4415#ifdef VBOX_WITH_RESOURCE_USAGE_API
4416 registerMetrics(mParent->performanceCollector(), this, pid);
4417#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4418
4419 /*
4420 * Set the session state to Spawning to protect against subsequent
4421 * attempts to open a session and to unregister the machine after
4422 * we leave the lock.
4423 */
4424 SessionState_T origState = mData->mSession.mState;
4425 mData->mSession.mState = SessionState_Spawning;
4426
4427 /*
4428 * Leave the lock before calling the client process -- it will call
4429 * Machine/SessionMachine methods. Leaving the lock here is quite safe
4430 * because the state is Spawning, so that openRemotesession() and
4431 * openExistingSession() calls will fail. This method, called before we
4432 * enter the lock again, will fail because of the wrong PID.
4433 *
4434 * Note that mData->mSession.mRemoteControls accessed outside
4435 * the lock may not be modified when state is Spawning, so it's safe.
4436 */
4437 alock.leave();
4438
4439 LogFlowThisFunc(("Calling AssignMachine()...\n"));
4440 rc = aControl->AssignMachine(sessionMachine);
4441 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
4442
4443 /* The failure may occur w/o any error info (from RPC), so provide one */
4444 if (FAILED(rc))
4445 setError(VBOX_E_VM_ERROR,
4446 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4447
4448 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
4449 {
4450 /* complete the remote session initialization */
4451
4452 /* get the console from the direct session */
4453 ComPtr<IConsole> console;
4454 rc = aControl->GetRemoteConsole(console.asOutParam());
4455 ComAssertComRC(rc);
4456
4457 if (SUCCEEDED(rc) && !console)
4458 {
4459 ComAssert(!!console);
4460 rc = E_FAIL;
4461 }
4462
4463 /* assign machine & console to the remote session */
4464 if (SUCCEEDED(rc))
4465 {
4466 /*
4467 * after openRemoteSession(), the first and the only
4468 * entry in remoteControls is that remote session
4469 */
4470 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4471 rc = mData->mSession.mRemoteControls.front()->
4472 AssignRemoteMachine(sessionMachine, console);
4473 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4474
4475 /* The failure may occur w/o any error info (from RPC), so provide one */
4476 if (FAILED(rc))
4477 setError(VBOX_E_VM_ERROR,
4478 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
4479 }
4480
4481 if (FAILED(rc))
4482 aControl->Uninitialize();
4483 }
4484
4485 /* enter the lock again */
4486 alock.enter();
4487
4488 /* Restore the session state */
4489 mData->mSession.mState = origState;
4490 }
4491
4492 /* finalize spawning anyway (this is why we don't return on errors above) */
4493 if (mData->mSession.mState == SessionState_Spawning)
4494 {
4495 /* Note that the progress object is finalized later */
4496
4497 /* We don't reset mSession.mPid here because it is necessary for
4498 * SessionMachine::uninit() to reap the child process later. */
4499
4500 if (FAILED(rc))
4501 {
4502 /* Close the remote session, remove the remote control from the list
4503 * and reset session state to Closed (@note keep the code in sync
4504 * with the relevant part in openSession()). */
4505
4506 Assert (mData->mSession.mRemoteControls.size() == 1);
4507 if (mData->mSession.mRemoteControls.size() == 1)
4508 {
4509 ErrorInfoKeeper eik;
4510 mData->mSession.mRemoteControls.front()->Uninitialize();
4511 }
4512
4513 mData->mSession.mRemoteControls.clear();
4514 mData->mSession.mState = SessionState_Closed;
4515 }
4516 }
4517 else
4518 {
4519 /* memorize PID of the directly opened session */
4520 if (SUCCEEDED(rc))
4521 mData->mSession.mPid = pid;
4522 }
4523
4524 if (SUCCEEDED(rc))
4525 {
4526 /* memorize the direct session control and cache IUnknown for it */
4527 mData->mSession.mDirectControl = aControl;
4528 mData->mSession.mState = SessionState_Open;
4529 /* associate the SessionMachine with this Machine */
4530 mData->mSession.mMachine = sessionMachine;
4531
4532 /* request an IUnknown pointer early from the remote party for later
4533 * identity checks (it will be internally cached within mDirectControl
4534 * at least on XPCOM) */
4535 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
4536 NOREF(unk);
4537 }
4538
4539 if (mData->mSession.mProgress)
4540 {
4541 /* finalize the progress after setting the state, for consistency */
4542 mData->mSession.mProgress->notifyComplete(rc);
4543 mData->mSession.mProgress.setNull();
4544 }
4545
4546 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
4547 * would break the lock order */
4548 alock.leave();
4549
4550 /* uninitialize the created session machine on failure */
4551 if (FAILED(rc))
4552 sessionMachine->uninit();
4553
4554 LogFlowThisFunc(("rc=%08X\n", rc));
4555 LogFlowThisFuncLeave();
4556 return rc;
4557}
4558
4559/**
4560 * @note Locks this object for writing, calls the client process
4561 * (inside the lock).
4562 */
4563HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
4564 IN_BSTR aType,
4565 IN_BSTR aEnvironment,
4566 Progress *aProgress)
4567{
4568 LogFlowThisFuncEnter();
4569
4570 AssertReturn(aControl, E_FAIL);
4571 AssertReturn(aProgress, E_FAIL);
4572
4573 AutoCaller autoCaller(this);
4574 CheckComRCReturnRC(autoCaller.rc());
4575
4576 AutoWriteLock alock(this);
4577
4578 if (!mData->mRegistered)
4579 return setError(E_UNEXPECTED,
4580 tr("The machine '%ls' is not registered"),
4581 mUserData->mName.raw());
4582
4583 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4584
4585 if (mData->mSession.mState == SessionState_Open ||
4586 mData->mSession.mState == SessionState_Spawning ||
4587 mData->mSession.mState == SessionState_Closing)
4588 return setError(VBOX_E_INVALID_OBJECT_STATE,
4589 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
4590 mUserData->mName.raw());
4591
4592 /* may not be busy */
4593 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
4594
4595 /* get the path to the executable */
4596 char szPath[RTPATH_MAX];
4597 RTPathAppPrivateArch(szPath, RTPATH_MAX);
4598 size_t sz = strlen(szPath);
4599 szPath[sz++] = RTPATH_DELIMITER;
4600 szPath[sz] = 0;
4601 char *cmd = szPath + sz;
4602 sz = RTPATH_MAX - sz;
4603
4604 int vrc = VINF_SUCCESS;
4605 RTPROCESS pid = NIL_RTPROCESS;
4606
4607 RTENV env = RTENV_DEFAULT;
4608
4609 if (aEnvironment != NULL && *aEnvironment)
4610 {
4611 char *newEnvStr = NULL;
4612
4613 do
4614 {
4615 /* clone the current environment */
4616 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
4617 AssertRCBreakStmt(vrc2, vrc = vrc2);
4618
4619 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
4620 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
4621
4622 /* put new variables to the environment
4623 * (ignore empty variable names here since RTEnv API
4624 * intentionally doesn't do that) */
4625 char *var = newEnvStr;
4626 for (char *p = newEnvStr; *p; ++p)
4627 {
4628 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
4629 {
4630 *p = '\0';
4631 if (*var)
4632 {
4633 char *val = strchr (var, '=');
4634 if (val)
4635 {
4636 *val++ = '\0';
4637 vrc2 = RTEnvSetEx (env, var, val);
4638 }
4639 else
4640 vrc2 = RTEnvUnsetEx (env, var);
4641 if (RT_FAILURE(vrc2))
4642 break;
4643 }
4644 var = p + 1;
4645 }
4646 }
4647 if (RT_SUCCESS(vrc2) && *var)
4648 vrc2 = RTEnvPutEx (env, var);
4649
4650 AssertRCBreakStmt (vrc2, vrc = vrc2);
4651 }
4652 while (0);
4653
4654 if (newEnvStr != NULL)
4655 RTStrFree(newEnvStr);
4656 }
4657
4658 Bstr type (aType);
4659
4660 /* Qt is default */
4661#ifdef VBOX_WITH_QTGUI
4662 if (type == "gui" || type == "GUI/Qt")
4663 {
4664# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
4665 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
4666# else
4667 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
4668# endif
4669 Assert (sz >= sizeof (VirtualBox_exe));
4670 strcpy (cmd, VirtualBox_exe);
4671
4672 Utf8Str idStr = mData->mUuid.toString();
4673# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
4674 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4675# else
4676 Utf8Str name = mUserData->mName;
4677 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4678# endif
4679 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4680 }
4681#else /* !VBOX_WITH_QTGUI */
4682 if (0)
4683 ;
4684#endif /* VBOX_WITH_QTGUI */
4685
4686 else
4687
4688#ifdef VBOX_WITH_VBOXSDL
4689 if (type == "sdl" || type == "GUI/SDL")
4690 {
4691 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
4692 Assert (sz >= sizeof (VBoxSDL_exe));
4693 strcpy (cmd, VBoxSDL_exe);
4694
4695 Utf8Str idStr = mData->mUuid.toString();
4696# ifdef RT_OS_WINDOWS
4697 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4698# else
4699 Utf8Str name = mUserData->mName;
4700 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4701# endif
4702 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4703 }
4704#else /* !VBOX_WITH_VBOXSDL */
4705 if (0)
4706 ;
4707#endif /* !VBOX_WITH_VBOXSDL */
4708
4709 else
4710
4711#ifdef VBOX_WITH_HEADLESS
4712 if ( type == "headless"
4713 || type == "capture"
4714#ifdef VBOX_WITH_VRDP
4715 || type == "vrdp"
4716#endif
4717 )
4718 {
4719 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
4720 Assert (sz >= sizeof (VBoxHeadless_exe));
4721 strcpy (cmd, VBoxHeadless_exe);
4722
4723 Utf8Str idStr = mData->mUuid.toString();
4724 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
4725# ifdef RT_OS_WINDOWS
4726 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
4727# else
4728 Utf8Str name = mUserData->mName;
4729 const char * args[] ={szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
4730# endif
4731#ifdef VBOX_WITH_VRDP
4732 if (type == "headless")
4733 {
4734 unsigned pos = RT_ELEMENTS(args) - 3;
4735 args[pos++] = "--vrdp";
4736 args[pos] = "off";
4737 }
4738#endif
4739 if (type == "capture")
4740 {
4741 unsigned pos = RT_ELEMENTS(args) - 3;
4742 args[pos] = "--capture";
4743 }
4744 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4745 }
4746#else /* !VBOX_WITH_HEADLESS */
4747 if (0)
4748 ;
4749#endif /* !VBOX_WITH_HEADLESS */
4750 else
4751 {
4752 RTEnvDestroy (env);
4753 return setError (E_INVALIDARG,
4754 tr ("Invalid session type: '%ls'"), aType);
4755 }
4756
4757 RTEnvDestroy (env);
4758
4759 if (RT_FAILURE(vrc))
4760 return setError (VBOX_E_IPRT_ERROR,
4761 tr ("Could not launch a process for the machine '%ls' (%Rrc)"),
4762 mUserData->mName.raw(), vrc);
4763
4764 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
4765
4766 /*
4767 * Note that we don't leave the lock here before calling the client,
4768 * because it doesn't need to call us back if called with a NULL argument.
4769 * Leaving the lock herer is dangerous because we didn't prepare the
4770 * launch data yet, but the client we've just started may happen to be
4771 * too fast and call openSession() that will fail (because of PID, etc.),
4772 * so that the Machine will never get out of the Spawning session state.
4773 */
4774
4775 /* inform the session that it will be a remote one */
4776 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
4777 HRESULT rc = aControl->AssignMachine (NULL);
4778 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
4779
4780 if (FAILED(rc))
4781 {
4782 /* restore the session state */
4783 mData->mSession.mState = SessionState_Closed;
4784 /* The failure may occur w/o any error info (from RPC), so provide one */
4785 return setError(VBOX_E_VM_ERROR,
4786 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4787 }
4788
4789 /* attach launch data to the machine */
4790 Assert (mData->mSession.mPid == NIL_RTPROCESS);
4791 mData->mSession.mRemoteControls.push_back (aControl);
4792 mData->mSession.mProgress = aProgress;
4793 mData->mSession.mPid = pid;
4794 mData->mSession.mState = SessionState_Spawning;
4795 mData->mSession.mType = type;
4796
4797 LogFlowThisFuncLeave();
4798 return S_OK;
4799}
4800
4801/**
4802 * @note Locks this object for writing, calls the client process
4803 * (outside the lock).
4804 */
4805HRESULT Machine::openExistingSession (IInternalSessionControl *aControl)
4806{
4807 LogFlowThisFuncEnter();
4808
4809 AssertReturn(aControl, E_FAIL);
4810
4811 AutoCaller autoCaller(this);
4812 CheckComRCReturnRC(autoCaller.rc());
4813
4814 AutoWriteLock alock(this);
4815
4816 if (!mData->mRegistered)
4817 return setError (E_UNEXPECTED,
4818 tr ("The machine '%ls' is not registered"), mUserData->mName.raw());
4819
4820 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4821
4822 if (mData->mSession.mState != SessionState_Open)
4823 return setError (VBOX_E_INVALID_SESSION_STATE,
4824 tr ("The machine '%ls' does not have an open session"),
4825 mUserData->mName.raw());
4826
4827 ComAssertRet (!mData->mSession.mDirectControl.isNull(), E_FAIL);
4828
4829 /*
4830 * Get the console from the direct session (note that we don't leave the
4831 * lock here because GetRemoteConsole must not call us back).
4832 */
4833 ComPtr<IConsole> console;
4834 HRESULT rc = mData->mSession.mDirectControl->
4835 GetRemoteConsole (console.asOutParam());
4836 if (FAILED (rc))
4837 {
4838 /* The failure may occur w/o any error info (from RPC), so provide one */
4839 return setError (VBOX_E_VM_ERROR,
4840 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
4841 }
4842
4843 ComAssertRet (!console.isNull(), E_FAIL);
4844
4845 ComObjPtr<SessionMachine> sessionMachine = mData->mSession.mMachine;
4846 AssertReturn(!sessionMachine.isNull(), E_FAIL);
4847
4848 /*
4849 * Leave the lock before calling the client process. It's safe here
4850 * since the only thing to do after we get the lock again is to add
4851 * the remote control to the list (which doesn't directly influence
4852 * anything).
4853 */
4854 alock.leave();
4855
4856 /* attach the remote session to the machine */
4857 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4858 rc = aControl->AssignRemoteMachine (sessionMachine, console);
4859 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4860
4861 /* The failure may occur w/o any error info (from RPC), so provide one */
4862 if (FAILED(rc))
4863 return setError(VBOX_E_VM_ERROR,
4864 tr("Failed to assign the machine to the session (%Rrc)"),
4865 rc);
4866
4867 alock.enter();
4868
4869 /* need to revalidate the state after entering the lock again */
4870 if (mData->mSession.mState != SessionState_Open)
4871 {
4872 aControl->Uninitialize();
4873
4874 return setError(VBOX_E_INVALID_SESSION_STATE,
4875 tr("The machine '%ls' does not have an open session"),
4876 mUserData->mName.raw());
4877 }
4878
4879 /* store the control in the list */
4880 mData->mSession.mRemoteControls.push_back (aControl);
4881
4882 LogFlowThisFuncLeave();
4883 return S_OK;
4884}
4885
4886/**
4887 * Returns @c true if the given machine has an open direct session and returns
4888 * the session machine instance and additional session data (on some platforms)
4889 * if so.
4890 *
4891 * Note that when the method returns @c false, the arguments remain unchanged.
4892 *
4893 * @param aMachine Session machine object.
4894 * @param aControl Direct session control object (optional).
4895 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
4896 *
4897 * @note locks this object for reading.
4898 */
4899#if defined (RT_OS_WINDOWS)
4900bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4901 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4902 HANDLE *aIPCSem /*= NULL*/,
4903 bool aAllowClosing /*= false*/)
4904#elif defined (RT_OS_OS2)
4905bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4906 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4907 HMTX *aIPCSem /*= NULL*/,
4908 bool aAllowClosing /*= false*/)
4909#else
4910bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4911 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4912 bool aAllowClosing /*= false*/)
4913#endif
4914{
4915 AutoLimitedCaller autoCaller(this);
4916 AssertComRCReturn (autoCaller.rc(), false);
4917
4918 /* just return false for inaccessible machines */
4919 if (autoCaller.state() != Ready)
4920 return false;
4921
4922 AutoReadLock alock(this);
4923
4924 if (mData->mSession.mState == SessionState_Open ||
4925 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
4926 {
4927 AssertReturn(!mData->mSession.mMachine.isNull(), false);
4928
4929 aMachine = mData->mSession.mMachine;
4930
4931 if (aControl != NULL)
4932 *aControl = mData->mSession.mDirectControl;
4933
4934#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4935 /* Additional session data */
4936 if (aIPCSem != NULL)
4937 *aIPCSem = aMachine->mIPCSem;
4938#endif
4939 return true;
4940 }
4941
4942 return false;
4943}
4944
4945/**
4946 * Returns @c true if the given machine has an spawning direct session and
4947 * returns and additional session data (on some platforms) if so.
4948 *
4949 * Note that when the method returns @c false, the arguments remain unchanged.
4950 *
4951 * @param aPID PID of the spawned direct session process.
4952 *
4953 * @note locks this object for reading.
4954 */
4955#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4956bool Machine::isSessionSpawning (RTPROCESS *aPID /*= NULL*/)
4957#else
4958bool Machine::isSessionSpawning()
4959#endif
4960{
4961 AutoLimitedCaller autoCaller(this);
4962 AssertComRCReturn (autoCaller.rc(), false);
4963
4964 /* just return false for inaccessible machines */
4965 if (autoCaller.state() != Ready)
4966 return false;
4967
4968 AutoReadLock alock(this);
4969
4970 if (mData->mSession.mState == SessionState_Spawning)
4971 {
4972#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4973 /* Additional session data */
4974 if (aPID != NULL)
4975 {
4976 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
4977 *aPID = mData->mSession.mPid;
4978 }
4979#endif
4980 return true;
4981 }
4982
4983 return false;
4984}
4985
4986/**
4987 * Called from the client watcher thread to check for unexpected client process
4988 * death during Session_Spawning state (e.g. before it successfully opened a
4989 * direct session).
4990 *
4991 * On Win32 and on OS/2, this method is called only when we've got the
4992 * direct client's process termination notification, so it always returns @c
4993 * true.
4994 *
4995 * On other platforms, this method returns @c true if the client process is
4996 * terminated and @c false if it's still alive.
4997 *
4998 * @note Locks this object for writing.
4999 */
5000bool Machine::checkForSpawnFailure()
5001{
5002 AutoCaller autoCaller(this);
5003 if (!autoCaller.isOk())
5004 {
5005 /* nothing to do */
5006 LogFlowThisFunc(("Already uninitialized!\n"));
5007 return true;
5008 }
5009
5010 /* VirtualBox::addProcessToReap() needs a write lock */
5011 AutoMultiWriteLock2 alock(mParent, this);
5012
5013 if (mData->mSession.mState != SessionState_Spawning)
5014 {
5015 /* nothing to do */
5016 LogFlowThisFunc(("Not spawning any more!\n"));
5017 return true;
5018 }
5019
5020 HRESULT rc = S_OK;
5021
5022#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5023
5024 /* the process was already unexpectedly terminated, we just need to set an
5025 * error and finalize session spawning */
5026 rc = setError(E_FAIL,
5027 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5028 name().raw());
5029#else
5030
5031 /* PID not yet initialized, skip check. */
5032 if (mData->mSession.mPid == NIL_RTPROCESS)
5033 return false;
5034
5035 RTPROCSTATUS status;
5036 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
5037 &status);
5038
5039 if (vrc != VERR_PROCESS_RUNNING)
5040 rc = setError(E_FAIL,
5041 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5042 name().raw());
5043#endif
5044
5045 if (FAILED(rc))
5046 {
5047 /* Close the remote session, remove the remote control from the list
5048 * and reset session state to Closed (@note keep the code in sync with
5049 * the relevant part in checkForSpawnFailure()). */
5050
5051 Assert(mData->mSession.mRemoteControls.size() == 1);
5052 if (mData->mSession.mRemoteControls.size() == 1)
5053 {
5054 ErrorInfoKeeper eik;
5055 mData->mSession.mRemoteControls.front()->Uninitialize();
5056 }
5057
5058 mData->mSession.mRemoteControls.clear();
5059 mData->mSession.mState = SessionState_Closed;
5060
5061 /* finalize the progress after setting the state, for consistency */
5062 if (!mData->mSession.mProgress.isNull())
5063 {
5064 mData->mSession.mProgress->notifyComplete(rc);
5065 mData->mSession.mProgress.setNull();
5066 }
5067
5068 mParent->addProcessToReap(mData->mSession.mPid);
5069 mData->mSession.mPid = NIL_RTPROCESS;
5070
5071 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
5072 return true;
5073 }
5074
5075 return false;
5076}
5077
5078/**
5079 * Checks that the registered flag of the machine can be set according to
5080 * the argument and sets it. On success, commits and saves all settings.
5081 *
5082 * @note When this machine is inaccessible, the only valid value for \a
5083 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
5084 * inaccessible machines are not currently supported. Note that unregistering
5085 * an inaccessible machine will \b uninitialize this machine object. Therefore,
5086 * the caller must make sure there are no active Machine::addCaller() calls
5087 * on the current thread because this will block Machine::uninit().
5088 *
5089 * @note Must be called from mParent's write lock. Locks this object and
5090 * children for writing.
5091 */
5092HRESULT Machine::trySetRegistered (BOOL aRegistered)
5093{
5094 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5095
5096 AutoLimitedCaller autoCaller(this);
5097 AssertComRCReturnRC(autoCaller.rc());
5098
5099 AutoWriteLock alock(this);
5100
5101 /* wait for state dependants to drop to zero */
5102 ensureNoStateDependencies();
5103
5104 ComAssertRet (mData->mRegistered != aRegistered, E_FAIL);
5105
5106 if (!mData->mAccessible)
5107 {
5108 /* A special case: the machine is not accessible. */
5109
5110 /* inaccessible machines can only be unregistered */
5111 AssertReturn(!aRegistered, E_FAIL);
5112
5113 /* Uninitialize ourselves here because currently there may be no
5114 * unregistered that are inaccessible (this state combination is not
5115 * supported). Note releasing the caller and leaving the lock before
5116 * calling uninit() */
5117
5118 alock.leave();
5119 autoCaller.release();
5120
5121 uninit();
5122
5123 return S_OK;
5124 }
5125
5126 AssertReturn(autoCaller.state() == Ready, E_FAIL);
5127
5128 if (aRegistered)
5129 {
5130 if (mData->mRegistered)
5131 return setError(VBOX_E_INVALID_OBJECT_STATE,
5132 tr("The machine '%ls' with UUID {%s} is already registered"),
5133 mUserData->mName.raw(),
5134 mData->mUuid.toString().raw());
5135 }
5136 else
5137 {
5138 if (mData->mMachineState == MachineState_Saved)
5139 return setError(VBOX_E_INVALID_VM_STATE,
5140 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
5141 mUserData->mName.raw());
5142
5143 size_t snapshotCount = 0;
5144 if (mData->mFirstSnapshot)
5145 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5146 if (snapshotCount)
5147 return setError(VBOX_E_INVALID_OBJECT_STATE,
5148 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
5149 mUserData->mName.raw(), snapshotCount);
5150
5151 if (mData->mSession.mState != SessionState_Closed)
5152 return setError(VBOX_E_INVALID_OBJECT_STATE,
5153 tr("Cannot unregister the machine '%ls' because it has an open session"),
5154 mUserData->mName.raw());
5155
5156 if (mMediaData->mAttachments.size() != 0)
5157 return setError(VBOX_E_INVALID_OBJECT_STATE,
5158 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
5159 mUserData->mName.raw(),
5160 mMediaData->mAttachments.size());
5161
5162 /* Note that we do not prevent unregistration of a DVD or Floppy image
5163 * is attached: as opposed to hard disks detaching such an image
5164 * implicitly in this method (which we will do below) won't have any
5165 * side effects (like detached orphan base and diff hard disks etc).*/
5166 }
5167
5168 HRESULT rc = S_OK;
5169
5170 /* Ensure the settings are saved. If we are going to be registered and
5171 * isConfigLocked() is FALSE then it means that no config file exists yet,
5172 * so create it by calling saveSettings() too. */
5173 if ( isModified()
5174 || (aRegistered && !mData->m_pMachineConfigFile->fileExists())
5175 )
5176 {
5177 rc = saveSettings();
5178 CheckComRCReturnRC(rc);
5179 }
5180
5181 /* more config checking goes here */
5182
5183 if (SUCCEEDED(rc))
5184 {
5185 /* we may have had implicit modifications we want to fix on success */
5186 commit();
5187
5188 mData->mRegistered = aRegistered;
5189 }
5190 else
5191 {
5192 /* we may have had implicit modifications we want to cancel on failure*/
5193 rollback (false /* aNotify */);
5194 }
5195
5196 return rc;
5197}
5198
5199/**
5200 * Increases the number of objects dependent on the machine state or on the
5201 * registered state. Guarantees that these two states will not change at least
5202 * until #releaseStateDependency() is called.
5203 *
5204 * Depending on the @a aDepType value, additional state checks may be made.
5205 * These checks will set extended error info on failure. See
5206 * #checkStateDependency() for more info.
5207 *
5208 * If this method returns a failure, the dependency is not added and the caller
5209 * is not allowed to rely on any particular machine state or registration state
5210 * value and may return the failed result code to the upper level.
5211 *
5212 * @param aDepType Dependency type to add.
5213 * @param aState Current machine state (NULL if not interested).
5214 * @param aRegistered Current registered state (NULL if not interested).
5215 *
5216 * @note Locks this object for writing.
5217 */
5218HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
5219 MachineState_T *aState /* = NULL */,
5220 BOOL *aRegistered /* = NULL */)
5221{
5222 AutoCaller autoCaller(this);
5223 AssertComRCReturnRC(autoCaller.rc());
5224
5225 AutoWriteLock alock(this);
5226
5227 HRESULT rc = checkStateDependency(aDepType);
5228 CheckComRCReturnRC(rc);
5229
5230 {
5231 if (mData->mMachineStateChangePending != 0)
5232 {
5233 /* ensureNoStateDependencies() is waiting for state dependencies to
5234 * drop to zero so don't add more. It may make sense to wait a bit
5235 * and retry before reporting an error (since the pending state
5236 * transition should be really quick) but let's just assert for
5237 * now to see if it ever happens on practice. */
5238
5239 AssertFailed();
5240
5241 return setError(E_ACCESSDENIED,
5242 tr("Machine state change is in progress. Please retry the operation later."));
5243 }
5244
5245 ++mData->mMachineStateDeps;
5246 Assert (mData->mMachineStateDeps != 0 /* overflow */);
5247 }
5248
5249 if (aState)
5250 *aState = mData->mMachineState;
5251 if (aRegistered)
5252 *aRegistered = mData->mRegistered;
5253
5254 return S_OK;
5255}
5256
5257/**
5258 * Decreases the number of objects dependent on the machine state.
5259 * Must always complete the #addStateDependency() call after the state
5260 * dependency is no more necessary.
5261 */
5262void Machine::releaseStateDependency()
5263{
5264 AutoCaller autoCaller(this);
5265 AssertComRCReturnVoid (autoCaller.rc());
5266
5267 AutoWriteLock alock(this);
5268
5269 AssertReturnVoid (mData->mMachineStateDeps != 0
5270 /* releaseStateDependency() w/o addStateDependency()? */);
5271 -- mData->mMachineStateDeps;
5272
5273 if (mData->mMachineStateDeps == 0)
5274 {
5275 /* inform ensureNoStateDependencies() that there are no more deps */
5276 if (mData->mMachineStateChangePending != 0)
5277 {
5278 Assert (mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
5279 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
5280 }
5281 }
5282}
5283
5284// protected methods
5285/////////////////////////////////////////////////////////////////////////////
5286
5287/**
5288 * Performs machine state checks based on the @a aDepType value. If a check
5289 * fails, this method will set extended error info, otherwise it will return
5290 * S_OK. It is supposed, that on failure, the caller will immedieately return
5291 * the return value of this method to the upper level.
5292 *
5293 * When @a aDepType is AnyStateDep, this method always returns S_OK.
5294 *
5295 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
5296 * current state of this machine object allows to change settings of the
5297 * machine (i.e. the machine is not registered, or registered but not running
5298 * and not saved). It is useful to call this method from Machine setters
5299 * before performing any change.
5300 *
5301 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
5302 * as for MutableStateDep except that if the machine is saved, S_OK is also
5303 * returned. This is useful in setters which allow changing machine
5304 * properties when it is in the saved state.
5305 *
5306 * @param aDepType Dependency type to check.
5307 *
5308 * @note Non Machine based classes should use #addStateDependency() and
5309 * #releaseStateDependency() methods or the smart AutoStateDependency
5310 * template.
5311 *
5312 * @note This method must be called from under this object's read or write
5313 * lock.
5314 */
5315HRESULT Machine::checkStateDependency(StateDependency aDepType)
5316{
5317 switch (aDepType)
5318 {
5319 case AnyStateDep:
5320 {
5321 break;
5322 }
5323 case MutableStateDep:
5324 {
5325 if ( mData->mRegistered
5326 && ( mType != IsSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5327 || ( mData->mMachineState != MachineState_Paused
5328 && mData->mMachineState != MachineState_Running
5329 && mData->mMachineState != MachineState_Aborted
5330 && mData->mMachineState != MachineState_Teleported
5331 && mData->mMachineState != MachineState_PoweredOff
5332 )
5333 )
5334 )
5335 return setError(VBOX_E_INVALID_VM_STATE,
5336 tr("The machine is not mutable (state is %s)"),
5337 Global::stringifyMachineState(mData->mMachineState));
5338 break;
5339 }
5340 case MutableOrSavedStateDep:
5341 {
5342 if ( mData->mRegistered
5343 && ( mType != IsSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5344 || ( mData->mMachineState != MachineState_Paused
5345 && mData->mMachineState != MachineState_Running
5346 && mData->mMachineState != MachineState_Aborted
5347 && mData->mMachineState != MachineState_Teleported
5348 && mData->mMachineState != MachineState_Saved
5349 && mData->mMachineState != MachineState_PoweredOff
5350 )
5351 )
5352 )
5353 return setError(VBOX_E_INVALID_VM_STATE,
5354 tr("The machine is not mutable (state is %s)"),
5355 Global::stringifyMachineState(mData->mMachineState));
5356 break;
5357 }
5358 }
5359
5360 return S_OK;
5361}
5362
5363/**
5364 * Helper to initialize all associated child objects and allocate data
5365 * structures.
5366 *
5367 * This method must be called as a part of the object's initialization procedure
5368 * (usually done in the #init() method).
5369 *
5370 * @note Must be called only from #init() or from #registeredInit().
5371 */
5372HRESULT Machine::initDataAndChildObjects()
5373{
5374 AutoCaller autoCaller(this);
5375 AssertComRCReturnRC(autoCaller.rc());
5376 AssertComRCReturn (autoCaller.state() == InInit ||
5377 autoCaller.state() == Limited, E_FAIL);
5378
5379 AssertReturn(!mData->mAccessible, E_FAIL);
5380
5381 /* allocate data structures */
5382 mSSData.allocate();
5383 mUserData.allocate();
5384 mHWData.allocate();
5385 mMediaData.allocate();
5386 mStorageControllers.allocate();
5387
5388 /* initialize mOSTypeId */
5389 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
5390
5391 /* create associated BIOS settings object */
5392 unconst(mBIOSSettings).createObject();
5393 mBIOSSettings->init (this);
5394
5395#ifdef VBOX_WITH_VRDP
5396 /* create an associated VRDPServer object (default is disabled) */
5397 unconst(mVRDPServer).createObject();
5398 mVRDPServer->init (this);
5399#endif
5400
5401 /* create associated serial port objects */
5402 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5403 {
5404 unconst(mSerialPorts [slot]).createObject();
5405 mSerialPorts [slot]->init (this, slot);
5406 }
5407
5408 /* create associated parallel port objects */
5409 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5410 {
5411 unconst(mParallelPorts [slot]).createObject();
5412 mParallelPorts [slot]->init (this, slot);
5413 }
5414
5415 /* create the audio adapter object (always present, default is disabled) */
5416 unconst(mAudioAdapter).createObject();
5417 mAudioAdapter->init (this);
5418
5419 /* create the USB controller object (always present, default is disabled) */
5420 unconst(mUSBController).createObject();
5421 mUSBController->init (this);
5422
5423 /* create associated network adapter objects */
5424 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5425 {
5426 unconst(mNetworkAdapters [slot]).createObject();
5427 mNetworkAdapters [slot]->init (this, slot);
5428 }
5429
5430 return S_OK;
5431}
5432
5433/**
5434 * Helper to uninitialize all associated child objects and to free all data
5435 * structures.
5436 *
5437 * This method must be called as a part of the object's uninitialization
5438 * procedure (usually done in the #uninit() method).
5439 *
5440 * @note Must be called only from #uninit() or from #registeredInit().
5441 */
5442void Machine::uninitDataAndChildObjects()
5443{
5444 AutoCaller autoCaller(this);
5445 AssertComRCReturnVoid (autoCaller.rc());
5446 AssertComRCReturnVoid (autoCaller.state() == InUninit ||
5447 autoCaller.state() == Limited);
5448
5449 /* uninit all children using addDependentChild()/removeDependentChild()
5450 * in their init()/uninit() methods */
5451 uninitDependentChildren();
5452
5453 /* tell all our other child objects we've been uninitialized */
5454
5455 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5456 {
5457 if (mNetworkAdapters [slot])
5458 {
5459 mNetworkAdapters [slot]->uninit();
5460 unconst(mNetworkAdapters [slot]).setNull();
5461 }
5462 }
5463
5464 if (mUSBController)
5465 {
5466 mUSBController->uninit();
5467 unconst(mUSBController).setNull();
5468 }
5469
5470 if (mAudioAdapter)
5471 {
5472 mAudioAdapter->uninit();
5473 unconst(mAudioAdapter).setNull();
5474 }
5475
5476 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5477 {
5478 if (mParallelPorts [slot])
5479 {
5480 mParallelPorts [slot]->uninit();
5481 unconst(mParallelPorts [slot]).setNull();
5482 }
5483 }
5484
5485 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5486 {
5487 if (mSerialPorts [slot])
5488 {
5489 mSerialPorts [slot]->uninit();
5490 unconst(mSerialPorts [slot]).setNull();
5491 }
5492 }
5493
5494#ifdef VBOX_WITH_VRDP
5495 if (mVRDPServer)
5496 {
5497 mVRDPServer->uninit();
5498 unconst(mVRDPServer).setNull();
5499 }
5500#endif
5501
5502 if (mBIOSSettings)
5503 {
5504 mBIOSSettings->uninit();
5505 unconst(mBIOSSettings).setNull();
5506 }
5507
5508 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
5509 * instance is uninitialized; SessionMachine instances refer to real
5510 * Machine hard disks). This is necessary for a clean re-initialization of
5511 * the VM after successfully re-checking the accessibility state. Note
5512 * that in case of normal Machine or SnapshotMachine uninitialization (as
5513 * a result of unregistering or discarding the snapshot), outdated hard
5514 * disk attachments will already be uninitialized and deleted, so this
5515 * code will not affect them. */
5516 if (!!mMediaData && (mType == IsMachine || mType == IsSnapshotMachine))
5517 {
5518 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5519 it != mMediaData->mAttachments.end();
5520 ++it)
5521 {
5522 ComObjPtr<Medium> hd = (*it)->medium();
5523 if (hd.isNull())
5524 continue;
5525 HRESULT rc = hd->detachFrom(mData->mUuid, snapshotId());
5526 AssertComRC (rc);
5527 }
5528 }
5529
5530 if (mType == IsMachine)
5531 {
5532 /* reset some important fields of mData */
5533 mData->mCurrentSnapshot.setNull();
5534 mData->mFirstSnapshot.setNull();
5535 }
5536
5537 /* free data structures (the essential mData structure is not freed here
5538 * since it may be still in use) */
5539 mMediaData.free();
5540 mStorageControllers.free();
5541 mHWData.free();
5542 mUserData.free();
5543 mSSData.free();
5544}
5545
5546/**
5547 * Makes sure that there are no machine state dependants. If necessary, waits
5548 * for the number of dependants to drop to zero.
5549 *
5550 * Make sure this method is called from under this object's write lock to
5551 * guarantee that no new dependants may be added when this method returns
5552 * control to the caller.
5553 *
5554 * @note Locks this object for writing. The lock will be released while waiting
5555 * (if necessary).
5556 *
5557 * @warning To be used only in methods that change the machine state!
5558 */
5559void Machine::ensureNoStateDependencies()
5560{
5561 AssertReturnVoid (isWriteLockOnCurrentThread());
5562
5563 AutoWriteLock alock(this);
5564
5565 /* Wait for all state dependants if necessary */
5566 if (mData->mMachineStateDeps != 0)
5567 {
5568 /* lazy semaphore creation */
5569 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
5570 RTSemEventMultiCreate (&mData->mMachineStateDepsSem);
5571
5572 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
5573 mData->mMachineStateDeps));
5574
5575 ++mData->mMachineStateChangePending;
5576
5577 /* reset the semaphore before waiting, the last dependant will signal
5578 * it */
5579 RTSemEventMultiReset (mData->mMachineStateDepsSem);
5580
5581 alock.leave();
5582
5583 RTSemEventMultiWait (mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
5584
5585 alock.enter();
5586
5587 -- mData->mMachineStateChangePending;
5588 }
5589}
5590
5591/**
5592 * Changes the machine state and informs callbacks.
5593 *
5594 * This method is not intended to fail so it either returns S_OK or asserts (and
5595 * returns a failure).
5596 *
5597 * @note Locks this object for writing.
5598 */
5599HRESULT Machine::setMachineState (MachineState_T aMachineState)
5600{
5601 LogFlowThisFuncEnter();
5602 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
5603
5604 AutoCaller autoCaller(this);
5605 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
5606
5607 AutoWriteLock alock(this);
5608
5609 /* wait for state dependants to drop to zero */
5610 ensureNoStateDependencies();
5611
5612 if (mData->mMachineState != aMachineState)
5613 {
5614 mData->mMachineState = aMachineState;
5615
5616 RTTimeNow (&mData->mLastStateChange);
5617
5618 mParent->onMachineStateChange(mData->mUuid, aMachineState);
5619 }
5620
5621 LogFlowThisFuncLeave();
5622 return S_OK;
5623}
5624
5625/**
5626 * Searches for a shared folder with the given logical name
5627 * in the collection of shared folders.
5628 *
5629 * @param aName logical name of the shared folder
5630 * @param aSharedFolder where to return the found object
5631 * @param aSetError whether to set the error info if the folder is
5632 * not found
5633 * @return
5634 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
5635 *
5636 * @note
5637 * must be called from under the object's lock!
5638 */
5639HRESULT Machine::findSharedFolder (CBSTR aName,
5640 ComObjPtr<SharedFolder> &aSharedFolder,
5641 bool aSetError /* = false */)
5642{
5643 bool found = false;
5644 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
5645 !found && it != mHWData->mSharedFolders.end();
5646 ++it)
5647 {
5648 AutoWriteLock alock(*it);
5649 found = (*it)->name() == aName;
5650 if (found)
5651 aSharedFolder = *it;
5652 }
5653
5654 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
5655
5656 if (aSetError && !found)
5657 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
5658
5659 return rc;
5660}
5661
5662/**
5663 * Loads all the VM settings by walking down the <Machine> node.
5664 *
5665 * @param aRegistered true when the machine is being loaded on VirtualBox
5666 * startup
5667 *
5668 * @note This method is intended to be called only from init(), so it assumes
5669 * all machine data fields have appropriate default values when it is called.
5670 *
5671 * @note Doesn't lock any objects.
5672 */
5673HRESULT Machine::loadSettings(bool aRegistered)
5674{
5675 LogFlowThisFuncEnter();
5676 AssertReturn(mType == IsMachine, E_FAIL);
5677
5678 AutoCaller autoCaller(this);
5679 AssertReturn(autoCaller.state() == InInit, E_FAIL);
5680
5681 HRESULT rc = S_OK;
5682
5683 try
5684 {
5685 Assert(mData->m_pMachineConfigFile == NULL);
5686
5687 // load and parse machine XML; this will throw on XML or logic errors
5688 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
5689
5690 /* If the stored UUID is not empty, it means the registered machine
5691 * is being loaded. Compare the loaded UUID with the stored one taken
5692 * from the global registry. */
5693 if (!mData->mUuid.isEmpty())
5694 {
5695 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
5696 {
5697 throw setError(E_FAIL,
5698 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
5699 mData->m_pMachineConfigFile->uuid.raw(),
5700 mData->m_strConfigFileFull.raw(),
5701 mData->mUuid.toString().raw(),
5702 mParent->settingsFilePath().raw());
5703 }
5704 }
5705 else
5706 unconst (mData->mUuid) = mData->m_pMachineConfigFile->uuid;
5707
5708 /* name (required) */
5709 mUserData->mName = mData->m_pMachineConfigFile->strName;
5710
5711 /* nameSync (optional, default is true) */
5712 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
5713
5714 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
5715
5716 // guest OS type
5717 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
5718 /* look up the object by Id to check it is valid */
5719 ComPtr<IGuestOSType> guestOSType;
5720 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
5721 guestOSType.asOutParam());
5722 CheckComRCThrowRC(rc);
5723
5724 // stateFile (optional)
5725 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
5726 mSSData->mStateFilePath.setNull();
5727 else
5728 {
5729 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
5730 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
5731 if (RT_FAILURE(vrc))
5732 throw setError(E_FAIL,
5733 tr("Invalid saved state file path '%s' (%Rrc)"),
5734 mData->m_pMachineConfigFile->strStateFile.raw(),
5735 vrc);
5736 mSSData->mStateFilePath = stateFilePathFull;
5737 }
5738
5739 /* snapshotFolder (optional) */
5740 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
5741 CheckComRCThrowRC(rc);
5742
5743 /* currentStateModified (optional, default is true) */
5744 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
5745
5746 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
5747
5748 /* teleportation */
5749 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
5750 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
5751 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
5752 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
5753
5754 /*
5755 * note: all mUserData members must be assigned prior this point because
5756 * we need to commit changes in order to let mUserData be shared by all
5757 * snapshot machine instances.
5758 */
5759 mUserData.commitCopy();
5760
5761 /* Snapshot node (optional) */
5762 if (mData->m_pMachineConfigFile->llFirstSnapshot.size())
5763 {
5764 // there can only be one root snapshot
5765 Assert(mData->m_pMachineConfigFile->llFirstSnapshot.size() == 1);
5766
5767 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
5768
5769 rc = loadSnapshot(snap,
5770 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
5771 NULL); // no parent == first snapshot
5772 CheckComRCThrowRC(rc);
5773 }
5774
5775 /* Hardware node (required) */
5776 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
5777 CheckComRCThrowRC(rc);
5778
5779 /* Load storage controllers */
5780 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
5781 CheckComRCThrowRC(rc);
5782
5783 /*
5784 * NOTE: the assignment below must be the last thing to do,
5785 * otherwise it will be not possible to change the settings
5786 * somewehere in the code above because all setters will be
5787 * blocked by checkStateDependency(MutableStateDep).
5788 */
5789
5790 /* set the machine state to Aborted or Saved when appropriate */
5791 if (mData->m_pMachineConfigFile->fAborted)
5792 {
5793 Assert(!mSSData->mStateFilePath.isEmpty());
5794 mSSData->mStateFilePath.setNull();
5795
5796 /* no need to use setMachineState() during init() */
5797 mData->mMachineState = MachineState_Aborted;
5798 }
5799 else if (!mSSData->mStateFilePath.isEmpty())
5800 {
5801 /* no need to use setMachineState() during init() */
5802 mData->mMachineState = MachineState_Saved;
5803 }
5804 }
5805 catch (HRESULT err)
5806 {
5807 /* we assume that error info is set by the thrower */
5808 rc = err;
5809 }
5810 catch (...)
5811 {
5812 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
5813 }
5814
5815 LogFlowThisFuncLeave();
5816 return rc;
5817}
5818
5819/**
5820 * Recursively loads all snapshots starting from the given.
5821 *
5822 * @param aNode <Snapshot> node.
5823 * @param aCurSnapshotId Current snapshot ID from the settings file.
5824 * @param aParentSnapshot Parent snapshot.
5825 */
5826HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
5827 const Guid &aCurSnapshotId,
5828 Snapshot *aParentSnapshot)
5829{
5830 AssertReturn (mType == IsMachine, E_FAIL);
5831
5832 HRESULT rc = S_OK;
5833
5834 Utf8Str strStateFile;
5835 if (!data.strStateFile.isEmpty())
5836 {
5837 /* optional */
5838 strStateFile = data.strStateFile;
5839 int vrc = calculateFullPath(strStateFile, strStateFile);
5840 if (RT_FAILURE(vrc))
5841 return setError(E_FAIL,
5842 tr("Invalid saved state file path '%s' (%Rrc)"),
5843 strStateFile.raw(),
5844 vrc);
5845 }
5846
5847 /* create a snapshot machine object */
5848 ComObjPtr<SnapshotMachine> pSnapshotMachine;
5849 pSnapshotMachine.createObject();
5850 rc = pSnapshotMachine->init(this,
5851 data.hardware,
5852 data.storage,
5853 data.uuid,
5854 strStateFile);
5855 CheckComRCReturnRC(rc);
5856
5857 /* create a snapshot object */
5858 ComObjPtr<Snapshot> pSnapshot;
5859 pSnapshot.createObject();
5860 /* initialize the snapshot */
5861 rc = pSnapshot->init(mParent, // VirtualBox object
5862 data.uuid,
5863 data.strName,
5864 data.strDescription,
5865 data.timestamp,
5866 pSnapshotMachine,
5867 aParentSnapshot);
5868 CheckComRCReturnRC (rc);
5869
5870 /* memorize the first snapshot if necessary */
5871 if (!mData->mFirstSnapshot)
5872 mData->mFirstSnapshot = pSnapshot;
5873
5874 /* memorize the current snapshot when appropriate */
5875 if ( !mData->mCurrentSnapshot
5876 && pSnapshot->getId() == aCurSnapshotId
5877 )
5878 mData->mCurrentSnapshot = pSnapshot;
5879
5880 // now create the children
5881 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
5882 it != data.llChildSnapshots.end();
5883 ++it)
5884 {
5885 const settings::Snapshot &childData = *it;
5886 // recurse
5887 rc = loadSnapshot(childData,
5888 aCurSnapshotId,
5889 pSnapshot); // parent = the one we created above
5890 CheckComRCReturnRC(rc);
5891 }
5892
5893 return rc;
5894}
5895
5896/**
5897 * @param aNode <Hardware> node.
5898 */
5899HRESULT Machine::loadHardware(const settings::Hardware &data)
5900{
5901 AssertReturn(mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5902
5903 HRESULT rc = S_OK;
5904
5905 try
5906 {
5907 /* The hardware version attribute (optional). */
5908 mHWData->mHWVersion = data.strVersion;
5909 mHWData->mHardwareUUID = data.uuid;
5910
5911 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
5912 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
5913 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
5914 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
5915 mHWData->mPAEEnabled = data.fPAE;
5916 mHWData->mSyntheticCpu = data.fSyntheticCpu;
5917
5918 mHWData->mCPUCount = data.cCPUs;
5919
5920 // cpuid leafs
5921 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
5922 it != data.llCpuIdLeafs.end();
5923 ++it)
5924 {
5925 const settings::CpuIdLeaf &leaf = *it;
5926
5927 switch (leaf.ulId)
5928 {
5929 case 0x0:
5930 case 0x1:
5931 case 0x2:
5932 case 0x3:
5933 case 0x4:
5934 case 0x5:
5935 case 0x6:
5936 case 0x7:
5937 case 0x8:
5938 case 0x9:
5939 case 0xA:
5940 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
5941 break;
5942
5943 case 0x80000000:
5944 case 0x80000001:
5945 case 0x80000002:
5946 case 0x80000003:
5947 case 0x80000004:
5948 case 0x80000005:
5949 case 0x80000006:
5950 case 0x80000007:
5951 case 0x80000008:
5952 case 0x80000009:
5953 case 0x8000000A:
5954 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
5955 break;
5956
5957 default:
5958 /* just ignore */
5959 break;
5960 }
5961 }
5962
5963 mHWData->mMemorySize = data.ulMemorySizeMB;
5964
5965 // boot order
5966 for (size_t i = 0;
5967 i < RT_ELEMENTS(mHWData->mBootOrder);
5968 i++)
5969 {
5970 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
5971 if (it == data.mapBootOrder.end())
5972 mHWData->mBootOrder[i] = DeviceType_Null;
5973 else
5974 mHWData->mBootOrder[i] = it->second;
5975 }
5976
5977 mHWData->mVRAMSize = data.ulVRAMSizeMB;
5978 mHWData->mMonitorCount = data.cMonitors;
5979 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
5980 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
5981 mHWData->mFirmwareType = data.firmwareType;
5982
5983#ifdef VBOX_WITH_VRDP
5984 /* RemoteDisplay */
5985 rc = mVRDPServer->loadSettings(data.vrdpSettings);
5986 CheckComRCReturnRC (rc);
5987#endif
5988
5989 /* BIOS */
5990 rc = mBIOSSettings->loadSettings(data.biosSettings);
5991 CheckComRCReturnRC (rc);
5992
5993 /* USB Controller */
5994 rc = mUSBController->loadSettings(data.usbController);
5995 CheckComRCReturnRC (rc);
5996
5997 // network adapters
5998 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
5999 it != data.llNetworkAdapters.end();
6000 ++it)
6001 {
6002 const settings::NetworkAdapter &nic = *it;
6003
6004 /* slot unicity is guaranteed by XML Schema */
6005 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
6006 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
6007 CheckComRCReturnRC (rc);
6008 }
6009
6010 // serial ports
6011 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
6012 it != data.llSerialPorts.end();
6013 ++it)
6014 {
6015 const settings::SerialPort &s = *it;
6016
6017 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
6018 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
6019 CheckComRCReturnRC (rc);
6020 }
6021
6022 // parallel ports (optional)
6023 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
6024 it != data.llParallelPorts.end();
6025 ++it)
6026 {
6027 const settings::ParallelPort &p = *it;
6028
6029 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
6030 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
6031 CheckComRCReturnRC (rc);
6032 }
6033
6034 /* AudioAdapter */
6035 rc = mAudioAdapter->loadSettings(data.audioAdapter);
6036 CheckComRCReturnRC (rc);
6037
6038 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
6039 it != data.llSharedFolders.end();
6040 ++it)
6041 {
6042 const settings::SharedFolder &sf = *it;
6043 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
6044 CheckComRCReturnRC (rc);
6045 }
6046
6047 // Clipboard
6048 mHWData->mClipboardMode = data.clipboardMode;
6049
6050 // guest settings
6051 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
6052 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
6053
6054#ifdef VBOX_WITH_GUEST_PROPS
6055 /* Guest properties (optional) */
6056 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
6057 it != data.llGuestProperties.end();
6058 ++it)
6059 {
6060 const settings::GuestProperty &prop = *it;
6061 uint32_t fFlags = guestProp::NILFLAG;
6062 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
6063 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
6064 mHWData->mGuestProperties.push_back(property);
6065 }
6066
6067 mHWData->mPropertyServiceActive = false;
6068 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
6069#endif /* VBOX_WITH_GUEST_PROPS defined */
6070 }
6071 catch(std::bad_alloc &)
6072 {
6073 return E_OUTOFMEMORY;
6074 }
6075
6076 AssertComRC(rc);
6077 return rc;
6078}
6079
6080 /**
6081 * @param aNode <StorageControllers> node.
6082 */
6083HRESULT Machine::loadStorageControllers(const settings::Storage &data,
6084 bool aRegistered,
6085 const Guid *aSnapshotId /* = NULL */)
6086{
6087 AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
6088
6089 HRESULT rc = S_OK;
6090
6091 /* Make sure the attached hard disks don't get unregistered until we
6092 * associate them with tis machine (important for VMs loaded (opened) after
6093 * VirtualBox startup) */
6094 AutoReadLock vboxLock(mParent);
6095
6096 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
6097 it != data.llStorageControllers.end();
6098 ++it)
6099 {
6100 const settings::StorageController &ctlData = *it;
6101
6102 ComObjPtr<StorageController> pCtl;
6103 /* Try to find one with the name first. */
6104 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
6105 if (SUCCEEDED(rc))
6106 return setError(VBOX_E_OBJECT_IN_USE,
6107 tr("Storage controller named '%s' already exists"),
6108 ctlData.strName.raw());
6109
6110 pCtl.createObject();
6111 rc = pCtl->init(this,
6112 ctlData.strName,
6113 ctlData.storageBus,
6114 ctlData.ulInstance);
6115 CheckComRCReturnRC (rc);
6116
6117 mStorageControllers->push_back(pCtl);
6118
6119 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
6120 CheckComRCReturnRC (rc);
6121
6122 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
6123 CheckComRCReturnRC (rc);
6124
6125 /* Set IDE emulation settings (only for AHCI controller). */
6126 if (ctlData.controllerType == StorageControllerType_IntelAhci)
6127 {
6128 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
6129 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
6130 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
6131 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
6132 )
6133 return rc;
6134 }
6135
6136 /* Load the attached devices now. */
6137 rc = loadStorageDevices(pCtl,
6138 ctlData,
6139 aRegistered,
6140 aSnapshotId);
6141 CheckComRCReturnRC (rc);
6142 }
6143
6144 return S_OK;
6145}
6146
6147/**
6148 * @param aNode <HardDiskAttachments> node.
6149 * @param aRegistered true when the machine is being loaded on VirtualBox
6150 * startup, or when a snapshot is being loaded (wchich
6151 * currently can happen on startup only)
6152 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
6153 *
6154 * @note Lock mParent for reading and hard disks for writing before calling.
6155 */
6156HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
6157 const settings::StorageController &data,
6158 bool aRegistered,
6159 const Guid *aSnapshotId /*= NULL*/)
6160{
6161 AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
6162 (mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
6163
6164 HRESULT rc = S_OK;
6165
6166 if (!aRegistered && data.llAttachedDevices.size() > 0)
6167 /* when the machine is being loaded (opened) from a file, it cannot
6168 * have hard disks attached (this should not happen normally,
6169 * because we don't allow to attach hard disks to an unregistered
6170 * VM at all */
6171 return setError(E_FAIL,
6172 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
6173 mUserData->mName.raw(),
6174 data.llAttachedDevices.size());
6175
6176 /* paranoia: detect duplicate attachments */
6177 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6178 it != data.llAttachedDevices.end();
6179 ++it)
6180 {
6181 for (settings::AttachedDevicesList::const_iterator it2 = it;
6182 it2 != data.llAttachedDevices.end();
6183 ++it2)
6184 {
6185 if (it == it2)
6186 continue;
6187
6188 if ( (*it).lPort == (*it2).lPort
6189 && (*it).lDevice == (*it2).lDevice)
6190 {
6191 return setError(E_FAIL,
6192 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
6193 aStorageController->name().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
6194 }
6195 }
6196 }
6197
6198 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6199 it != data.llAttachedDevices.end();
6200 ++it)
6201 {
6202 const settings::AttachedDevice &dev = *it;
6203 ComObjPtr<Medium> medium;
6204
6205 switch (dev.deviceType)
6206 {
6207 case DeviceType_Floppy:
6208 /* find a floppy by UUID */
6209 if (!dev.uuid.isEmpty())
6210 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6211 /* find a floppy by host device name */
6212 else if (!dev.strHostDriveSrc.isEmpty())
6213 {
6214 SafeIfaceArray<IMedium> drivevec;
6215 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
6216 if (SUCCEEDED(rc))
6217 {
6218 for (size_t i = 0; i < drivevec.size(); ++i)
6219 {
6220 /// @todo eliminate this conversion
6221 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6222 if ( dev.strHostDriveSrc == med->name()
6223 || dev.strHostDriveSrc == med->location())
6224 {
6225 medium = med;
6226 break;
6227 }
6228 }
6229 }
6230 }
6231 break;
6232
6233 case DeviceType_DVD:
6234 /* find a DVD by UUID */
6235 if (!dev.uuid.isEmpty())
6236 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6237 /* find a DVD by host device name */
6238 else if (!dev.strHostDriveSrc.isEmpty())
6239 {
6240 SafeIfaceArray<IMedium> drivevec;
6241 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
6242 if (SUCCEEDED(rc))
6243 {
6244 for (size_t i = 0; i < drivevec.size(); ++i)
6245 {
6246 Bstr hostDriveSrc(dev.strHostDriveSrc);
6247 /// @todo eliminate this conversion
6248 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6249 if ( hostDriveSrc == med->name()
6250 || hostDriveSrc == med->location())
6251 {
6252 medium = med;
6253 break;
6254 }
6255 }
6256 }
6257 }
6258 break;
6259
6260 case DeviceType_HardDisk:
6261 {
6262 /* find a hard disk by UUID */
6263 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6264 if (FAILED(rc))
6265 {
6266 if (mType == IsSnapshotMachine)
6267 {
6268 // wrap another error message around the "cannot find hard disk" set by findHardDisk
6269 // so the user knows that the bad disk is in a snapshot somewhere
6270 com::ErrorInfo info;
6271 return setError(E_FAIL,
6272 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
6273 aSnapshotId->raw(),
6274 info.getText().raw());
6275 }
6276 else
6277 return rc;
6278 }
6279
6280 AutoWriteLock hdLock(medium);
6281
6282 if (medium->type() == MediumType_Immutable)
6283 {
6284 if (mType == IsSnapshotMachine)
6285 return setError(E_FAIL,
6286 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
6287 "of the virtual machine '%ls' ('%s')"),
6288 medium->locationFull().raw(),
6289 dev.uuid.raw(),
6290 aSnapshotId->raw(),
6291 mUserData->mName.raw(),
6292 mData->m_strConfigFileFull.raw());
6293
6294 return setError(E_FAIL,
6295 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
6296 medium->locationFull().raw(),
6297 dev.uuid.raw(),
6298 mUserData->mName.raw(),
6299 mData->m_strConfigFileFull.raw());
6300 }
6301
6302 if ( mType != IsSnapshotMachine
6303 && medium->children().size() != 0
6304 )
6305 return setError(E_FAIL,
6306 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
6307 "because it has %d differencing child hard disks"),
6308 medium->locationFull().raw(),
6309 dev.uuid.raw(),
6310 mUserData->mName.raw(),
6311 mData->m_strConfigFileFull.raw(),
6312 medium->children().size());
6313
6314 if (findAttachment(mMediaData->mAttachments,
6315 medium))
6316 return setError(E_FAIL,
6317 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
6318 medium->locationFull().raw(),
6319 dev.uuid.raw(),
6320 mUserData->mName.raw(),
6321 mData->m_strConfigFileFull.raw());
6322
6323 break;
6324 }
6325
6326 default:
6327 return setError(E_FAIL,
6328 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
6329 medium->locationFull().raw(),
6330 mUserData->mName.raw(),
6331 mData->m_strConfigFileFull.raw());
6332 }
6333
6334 if (FAILED(rc))
6335 break;
6336
6337 const Bstr controllerName = aStorageController->name();
6338 ComObjPtr<MediumAttachment> pAttachment;
6339 pAttachment.createObject();
6340 rc = pAttachment->init(this,
6341 medium,
6342 controllerName,
6343 dev.lPort,
6344 dev.lDevice,
6345 dev.deviceType);
6346 CheckComRCBreakRC(rc);
6347
6348 /* associate the medium with this machine and snapshot */
6349 if (!medium.isNull())
6350 {
6351 if (mType == IsSnapshotMachine)
6352 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
6353 else
6354 rc = medium->attachTo(mData->mUuid);
6355 }
6356 if (FAILED(rc))
6357 break;
6358
6359 /* backup mMediaData to let registeredInit() properly rollback on failure
6360 * (= limited accessibility) */
6361
6362 mMediaData.backup();
6363 mMediaData->mAttachments.push_back(pAttachment);
6364 }
6365
6366 return rc;
6367}
6368
6369/**
6370 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
6371 *
6372 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
6373 * @param aSnapshot where to return the found snapshot
6374 * @param aSetError true to set extended error info on failure
6375 */
6376HRESULT Machine::findSnapshot(const Guid &aId,
6377 ComObjPtr<Snapshot> &aSnapshot,
6378 bool aSetError /* = false */)
6379{
6380 AutoReadLock chlock(snapshotsTreeLockHandle());
6381
6382 if (!mData->mFirstSnapshot)
6383 {
6384 if (aSetError)
6385 return setError(E_FAIL,
6386 tr("This machine does not have any snapshots"));
6387 return E_FAIL;
6388 }
6389
6390 if (aId.isEmpty())
6391 aSnapshot = mData->mFirstSnapshot;
6392 else
6393 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
6394
6395 if (!aSnapshot)
6396 {
6397 if (aSetError)
6398 return setError(E_FAIL,
6399 tr("Could not find a snapshot with UUID {%s}"),
6400 aId.toString().raw());
6401 return E_FAIL;
6402 }
6403
6404 return S_OK;
6405}
6406
6407/**
6408 * Returns the snapshot with the given name or fails of no such snapshot.
6409 *
6410 * @param aName snapshot name to find
6411 * @param aSnapshot where to return the found snapshot
6412 * @param aSetError true to set extended error info on failure
6413 */
6414HRESULT Machine::findSnapshot(IN_BSTR aName,
6415 ComObjPtr<Snapshot> &aSnapshot,
6416 bool aSetError /* = false */)
6417{
6418 AssertReturn(aName, E_INVALIDARG);
6419
6420 AutoReadLock chlock(snapshotsTreeLockHandle());
6421
6422 if (!mData->mFirstSnapshot)
6423 {
6424 if (aSetError)
6425 return setError(VBOX_E_OBJECT_NOT_FOUND,
6426 tr("This machine does not have any snapshots"));
6427 return VBOX_E_OBJECT_NOT_FOUND;
6428 }
6429
6430 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
6431
6432 if (!aSnapshot)
6433 {
6434 if (aSetError)
6435 return setError(VBOX_E_OBJECT_NOT_FOUND,
6436 tr("Could not find a snapshot named '%ls'"), aName);
6437 return VBOX_E_OBJECT_NOT_FOUND;
6438 }
6439
6440 return S_OK;
6441}
6442
6443/**
6444 * Returns a storage controller object with the given name.
6445 *
6446 * @param aName storage controller name to find
6447 * @param aStorageController where to return the found storage controller
6448 * @param aSetError true to set extended error info on failure
6449 */
6450HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
6451 ComObjPtr<StorageController> &aStorageController,
6452 bool aSetError /* = false */)
6453{
6454 AssertReturn (!aName.isEmpty(), E_INVALIDARG);
6455
6456 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6457 it != mStorageControllers->end();
6458 ++it)
6459 {
6460 if ((*it)->name() == aName)
6461 {
6462 aStorageController = (*it);
6463 return S_OK;
6464 }
6465 }
6466
6467 if (aSetError)
6468 return setError(VBOX_E_OBJECT_NOT_FOUND,
6469 tr("Could not find a storage controller named '%s'"),
6470 aName.raw());
6471 return VBOX_E_OBJECT_NOT_FOUND;
6472}
6473
6474HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
6475 MediaData::AttachmentList &atts)
6476{
6477 AutoCaller autoCaller(this);
6478 CheckComRCReturnRC(autoCaller.rc());
6479
6480 AutoReadLock alock(this);
6481
6482 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
6483 it != mMediaData->mAttachments.end();
6484 ++it)
6485 {
6486 if ((*it)->controllerName() == aName)
6487 atts.push_back(*it);
6488 }
6489
6490 return S_OK;
6491}
6492
6493/**
6494 * Helper for #saveSettings. Cares about renaming the settings directory and
6495 * file if the machine name was changed and about creating a new settings file
6496 * if this is a new machine.
6497 *
6498 * @note Must be never called directly but only from #saveSettings().
6499 *
6500 * @param aRenamed receives |true| if the name was changed and the settings
6501 * file was renamed as a result, or |false| otherwise. The
6502 * value makes sense only on success.
6503 * @param aNew receives |true| if a virgin settings file was created.
6504 */
6505HRESULT Machine::prepareSaveSettings(bool &aRenamed,
6506 bool &aNew)
6507{
6508 /* Note: tecnhically, mParent needs to be locked only when the machine is
6509 * registered (see prepareSaveSettings() for details) but we don't
6510 * currently differentiate it in callers of saveSettings() so we don't
6511 * make difference here too. */
6512 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6513 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6514
6515 HRESULT rc = S_OK;
6516
6517 aRenamed = false;
6518
6519 /* if we're ready and isConfigLocked() is FALSE then it means
6520 * that no config file exists yet (we will create a virgin one) */
6521 aNew = !mData->m_pMachineConfigFile->fileExists();
6522
6523 /* attempt to rename the settings file if machine name is changed */
6524 if ( mUserData->mNameSync
6525 && mUserData.isBackedUp()
6526 && mUserData.backedUpData()->mName != mUserData->mName
6527 )
6528 {
6529 aRenamed = true;
6530
6531 bool dirRenamed = false;
6532 bool fileRenamed = false;
6533
6534 Utf8Str configFile, newConfigFile;
6535 Utf8Str configDir, newConfigDir;
6536
6537 do
6538 {
6539 int vrc = VINF_SUCCESS;
6540
6541 Utf8Str name = mUserData.backedUpData()->mName;
6542 Utf8Str newName = mUserData->mName;
6543
6544 configFile = mData->m_strConfigFileFull;
6545
6546 /* first, rename the directory if it matches the machine name */
6547 configDir = configFile;
6548 configDir.stripFilename();
6549 newConfigDir = configDir;
6550 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
6551 {
6552 newConfigDir.stripFilename();
6553 newConfigDir = Utf8StrFmt ("%s%c%s",
6554 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6555 /* new dir and old dir cannot be equal here because of 'if'
6556 * above and because name != newName */
6557 Assert (configDir != newConfigDir);
6558 if (!aNew)
6559 {
6560 /* perform real rename only if the machine is not new */
6561 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
6562 if (RT_FAILURE(vrc))
6563 {
6564 rc = setError(E_FAIL,
6565 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
6566 configDir.raw(),
6567 newConfigDir.raw(),
6568 vrc);
6569 break;
6570 }
6571 dirRenamed = true;
6572 }
6573 }
6574
6575 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
6576 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6577
6578 /* then try to rename the settings file itself */
6579 if (newConfigFile != configFile)
6580 {
6581 /* get the path to old settings file in renamed directory */
6582 configFile = Utf8StrFmt("%s%c%s",
6583 newConfigDir.raw(),
6584 RTPATH_DELIMITER,
6585 RTPathFilename(configFile.c_str()));
6586 if (!aNew)
6587 {
6588 /* perform real rename only if the machine is not new */
6589 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
6590 if (RT_FAILURE(vrc))
6591 {
6592 rc = setError(E_FAIL,
6593 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
6594 configFile.raw(),
6595 newConfigFile.raw(),
6596 vrc);
6597 break;
6598 }
6599 fileRenamed = true;
6600 }
6601 }
6602
6603 /* update m_strConfigFileFull amd mConfigFile */
6604 Utf8Str oldConfigFileFull = mData->m_strConfigFileFull;
6605 Utf8Str oldConfigFile = mData->m_strConfigFile;
6606 mData->m_strConfigFileFull = newConfigFile;
6607 /* try to get the relative path for mConfigFile */
6608 Utf8Str path = newConfigFile;
6609 mParent->calculateRelativePath (path, path);
6610 mData->m_strConfigFile = path;
6611
6612 /* last, try to update the global settings with the new path */
6613 if (mData->mRegistered)
6614 {
6615 rc = mParent->updateSettings(configDir.c_str(), newConfigDir.c_str());
6616 if (FAILED(rc))
6617 {
6618 /* revert to old values */
6619 mData->m_strConfigFileFull = oldConfigFileFull;
6620 mData->m_strConfigFile = oldConfigFile;
6621 break;
6622 }
6623 }
6624
6625 /* update the snapshot folder */
6626 path = mUserData->mSnapshotFolderFull;
6627 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6628 {
6629 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6630 path.raw() + configDir.length());
6631 mUserData->mSnapshotFolderFull = path;
6632 calculateRelativePath (path, path);
6633 mUserData->mSnapshotFolder = path;
6634 }
6635
6636 /* update the saved state file path */
6637 path = mSSData->mStateFilePath;
6638 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6639 {
6640 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6641 path.raw() + configDir.length());
6642 mSSData->mStateFilePath = path;
6643 }
6644
6645 /* Update saved state file paths of all online snapshots.
6646 * Note that saveSettings() will recognize name change
6647 * and will save all snapshots in this case. */
6648 if (mData->mFirstSnapshot)
6649 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
6650 newConfigDir.c_str());
6651 }
6652 while (0);
6653
6654 if (FAILED(rc))
6655 {
6656 /* silently try to rename everything back */
6657 if (fileRenamed)
6658 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
6659 if (dirRenamed)
6660 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
6661 }
6662
6663 CheckComRCReturnRC(rc);
6664 }
6665
6666 if (aNew)
6667 {
6668 /* create a virgin config file */
6669 int vrc = VINF_SUCCESS;
6670
6671 /* ensure the settings directory exists */
6672 Utf8Str path(mData->m_strConfigFileFull);
6673 path.stripFilename();
6674 if (!RTDirExists(path.c_str()))
6675 {
6676 vrc = RTDirCreateFullPath(path.c_str(), 0777);
6677 if (RT_FAILURE(vrc))
6678 {
6679 return setError(E_FAIL,
6680 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
6681 path.raw(),
6682 vrc);
6683 }
6684 }
6685
6686 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
6687 path = Utf8Str(mData->m_strConfigFileFull);
6688 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
6689 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
6690 if (RT_FAILURE(vrc))
6691 {
6692 mData->mHandleCfgFile = NIL_RTFILE;
6693 return setError(E_FAIL,
6694 tr("Could not create the settings file '%s' (%Rrc)"),
6695 path.raw(),
6696 vrc);
6697 }
6698 RTFileClose(mData->mHandleCfgFile);
6699 }
6700
6701 return rc;
6702}
6703
6704/**
6705 * Saves and commits machine data, user data and hardware data.
6706 *
6707 * Note that on failure, the data remains uncommitted.
6708 *
6709 * @a aFlags may combine the following flags:
6710 *
6711 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
6712 * Used when saving settings after an operation that makes them 100%
6713 * correspond to the settings from the current snapshot.
6714 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
6715 * #isReallyModified() returns false. This is necessary for cases when we
6716 * change machine data diectly, not through the backup()/commit() mechanism.
6717 *
6718 * @note Must be called from under mParent write lock (sometimes needed by
6719 * #prepareSaveSettings()) and this object's write lock. Locks children for
6720 * writing. There is one exception when mParent is unused and therefore may be
6721 * left unlocked: if this machine is an unregistered one.
6722 */
6723HRESULT Machine::saveSettings(int aFlags /*= 0*/)
6724{
6725 LogFlowThisFuncEnter();
6726
6727 /* Note: tecnhically, mParent needs to be locked only when the machine is
6728 * registered (see prepareSaveSettings() for details) but we don't
6729 * currently differentiate it in callers of saveSettings() so we don't
6730 * make difference here too. */
6731 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6732 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6733
6734 /* make sure child objects are unable to modify the settings while we are
6735 * saving them */
6736 ensureNoStateDependencies();
6737
6738 AssertReturn(mType == IsMachine || mType == IsSessionMachine, E_FAIL);
6739
6740 BOOL currentStateModified = mData->mCurrentStateModified;
6741 bool settingsModified;
6742
6743 if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
6744 {
6745 /* We ignore changes to user data when setting mCurrentStateModified
6746 * because the current state will not differ from the current snapshot
6747 * if only user data has been changed (user data is shared by all
6748 * snapshots). */
6749 currentStateModified = isReallyModified (true /* aIgnoreUserData */);
6750 settingsModified = mUserData.hasActualChanges() || currentStateModified;
6751 }
6752 else
6753 {
6754 if (aFlags & SaveS_ResetCurStateModified)
6755 currentStateModified = FALSE;
6756 settingsModified = isReallyModified();
6757 }
6758
6759 HRESULT rc = S_OK;
6760
6761 /* First, prepare to save settings. It will care about renaming the
6762 * settings directory and file if the machine name was changed and about
6763 * creating a new settings file if this is a new machine. */
6764 bool isRenamed = false;
6765 bool isNew = false;
6766 rc = prepareSaveSettings(isRenamed, isNew);
6767 CheckComRCReturnRC(rc);
6768
6769 try
6770 {
6771 mData->m_pMachineConfigFile->uuid = mData->mUuid;
6772 mData->m_pMachineConfigFile->strName = mUserData->mName;
6773 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
6774 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
6775 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
6776
6777 if ( mData->mMachineState == MachineState_Saved
6778 || mData->mMachineState == MachineState_Restoring
6779 // when deleting a snapshot we may or may not have a saved state in the current state,
6780 // so let's not assert here please
6781 || ( (mData->mMachineState == MachineState_DeletingSnapshot)
6782 && (!mSSData->mStateFilePath.isEmpty())
6783 )
6784 )
6785 {
6786 Assert(!mSSData->mStateFilePath.isEmpty());
6787 /* try to make the file name relative to the settings file dir */
6788 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
6789 }
6790 else
6791 {
6792 Assert(mSSData->mStateFilePath.isEmpty());
6793 mData->m_pMachineConfigFile->strStateFile.setNull();
6794 }
6795
6796 if (mData->mCurrentSnapshot)
6797 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
6798 else
6799 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
6800
6801 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
6802 mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
6803 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6804 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6805/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
6806
6807 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
6808 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
6809 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
6810 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
6811
6812 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
6813 CheckComRCThrowRC(rc);
6814
6815 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
6816 CheckComRCThrowRC(rc);
6817
6818 // save snapshots
6819 rc = saveAllSnapshots();
6820 CheckComRCThrowRC(rc);
6821
6822 // now spit it all out
6823 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6824 }
6825 catch (HRESULT err)
6826 {
6827 /* we assume that error info is set by the thrower */
6828 rc = err;
6829 }
6830 catch (...)
6831 {
6832 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6833 }
6834
6835 if (SUCCEEDED(rc))
6836 {
6837 commit();
6838
6839 /* memorize the new modified state */
6840 mData->mCurrentStateModified = currentStateModified;
6841 }
6842
6843 if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
6844 {
6845 /* Fire the data change event, even on failure (since we've already
6846 * committed all data). This is done only for SessionMachines because
6847 * mutable Machine instances are always not registered (i.e. private
6848 * to the client process that creates them) and thus don't need to
6849 * inform callbacks. */
6850 if (mType == IsSessionMachine)
6851 mParent->onMachineDataChange(mData->mUuid);
6852 }
6853
6854 LogFlowThisFunc(("rc=%08X\n", rc));
6855 LogFlowThisFuncLeave();
6856 return rc;
6857}
6858
6859HRESULT Machine::saveAllSnapshots()
6860{
6861 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
6862
6863 HRESULT rc = S_OK;
6864
6865 try
6866 {
6867 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
6868
6869 if (mData->mFirstSnapshot)
6870 {
6871 settings::Snapshot snapNew;
6872 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
6873
6874 // get reference to the fresh copy of the snapshot on the list and
6875 // work on that copy directly to avoid excessive copying later
6876 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6877
6878 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
6879 CheckComRCThrowRC(rc);
6880 }
6881
6882// if (mType == IsSessionMachine)
6883// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
6884
6885 }
6886 catch (HRESULT err)
6887 {
6888 /* we assume that error info is set by the thrower */
6889 rc = err;
6890 }
6891 catch (...)
6892 {
6893 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6894 }
6895
6896 return rc;
6897}
6898
6899/**
6900 * Saves the VM hardware configuration. It is assumed that the
6901 * given node is empty.
6902 *
6903 * @param aNode <Hardware> node to save the VM hardware confguration to.
6904 */
6905HRESULT Machine::saveHardware(settings::Hardware &data)
6906{
6907 HRESULT rc = S_OK;
6908
6909 try
6910 {
6911 /* The hardware version attribute (optional).
6912 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
6913 if ( mHWData->mHWVersion == "1"
6914 && mSSData->mStateFilePath.isEmpty()
6915 )
6916 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. */
6917
6918 data.strVersion = mHWData->mHWVersion;
6919 data.uuid = mHWData->mHardwareUUID;
6920
6921 // CPU
6922 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
6923 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
6924 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
6925 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
6926 data.fPAE = !!mHWData->mPAEEnabled;
6927 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
6928
6929 /* Standard and Extended CPUID leafs. */
6930 data.llCpuIdLeafs.clear();
6931 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
6932 {
6933 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
6934 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
6935 }
6936 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
6937 {
6938 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
6939 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
6940 }
6941
6942 data.cCPUs = mHWData->mCPUCount;
6943
6944 // memory
6945 data.ulMemorySizeMB = mHWData->mMemorySize;
6946
6947 // firmware
6948 data.firmwareType = mHWData->mFirmwareType;
6949
6950 // boot order
6951 data.mapBootOrder.clear();
6952 for (size_t i = 0;
6953 i < RT_ELEMENTS(mHWData->mBootOrder);
6954 ++i)
6955 data.mapBootOrder[i] = mHWData->mBootOrder[i];
6956
6957 // display
6958 data.ulVRAMSizeMB = mHWData->mVRAMSize;
6959 data.cMonitors = mHWData->mMonitorCount;
6960 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
6961 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
6962
6963#ifdef VBOX_WITH_VRDP
6964 /* VRDP settings (optional) */
6965 rc = mVRDPServer->saveSettings(data.vrdpSettings);
6966 CheckComRCThrowRC(rc);
6967#endif
6968
6969 /* BIOS (required) */
6970 rc = mBIOSSettings->saveSettings(data.biosSettings);
6971 CheckComRCThrowRC(rc);
6972
6973 /* USB Controller (required) */
6974 rc = mUSBController->saveSettings(data.usbController);
6975 CheckComRCThrowRC(rc);
6976
6977 /* Network adapters (required) */
6978 data.llNetworkAdapters.clear();
6979 for (ULONG slot = 0;
6980 slot < RT_ELEMENTS(mNetworkAdapters);
6981 ++slot)
6982 {
6983 settings::NetworkAdapter nic;
6984 nic.ulSlot = slot;
6985 rc = mNetworkAdapters[slot]->saveSettings(nic);
6986 CheckComRCThrowRC(rc);
6987
6988 data.llNetworkAdapters.push_back(nic);
6989 }
6990
6991 /* Serial ports */
6992 data.llSerialPorts.clear();
6993 for (ULONG slot = 0;
6994 slot < RT_ELEMENTS(mSerialPorts);
6995 ++slot)
6996 {
6997 settings::SerialPort s;
6998 s.ulSlot = slot;
6999 rc = mSerialPorts[slot]->saveSettings(s);
7000 CheckComRCReturnRC (rc);
7001
7002 data.llSerialPorts.push_back(s);
7003 }
7004
7005 /* Parallel ports */
7006 data.llParallelPorts.clear();
7007 for (ULONG slot = 0;
7008 slot < RT_ELEMENTS(mParallelPorts);
7009 ++slot)
7010 {
7011 settings::ParallelPort p;
7012 p.ulSlot = slot;
7013 rc = mParallelPorts[slot]->saveSettings(p);
7014 CheckComRCReturnRC (rc);
7015
7016 data.llParallelPorts.push_back(p);
7017 }
7018
7019 /* Audio adapter */
7020 rc = mAudioAdapter->saveSettings(data.audioAdapter);
7021 CheckComRCReturnRC (rc);
7022
7023 /* Shared folders */
7024 data.llSharedFolders.clear();
7025 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7026 it != mHWData->mSharedFolders.end();
7027 ++it)
7028 {
7029 ComObjPtr<SharedFolder> pFolder = *it;
7030 settings::SharedFolder sf;
7031 sf.strName = pFolder->name();
7032 sf.strHostPath = pFolder->hostPath();
7033 sf.fWritable = !!pFolder->writable();
7034
7035 data.llSharedFolders.push_back(sf);
7036 }
7037
7038 // clipboard
7039 data.clipboardMode = mHWData->mClipboardMode;
7040
7041 /* Guest */
7042 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7043 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
7044
7045 // guest properties
7046 data.llGuestProperties.clear();
7047#ifdef VBOX_WITH_GUEST_PROPS
7048 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
7049 it != mHWData->mGuestProperties.end();
7050 ++it)
7051 {
7052 HWData::GuestProperty property = *it;
7053
7054 settings::GuestProperty prop;
7055 prop.strName = property.strName;
7056 prop.strValue = property.strValue;
7057 prop.timestamp = property.mTimestamp;
7058 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7059 guestProp::writeFlags(property.mFlags, szFlags);
7060 prop.strFlags = szFlags;
7061
7062 data.llGuestProperties.push_back(prop);
7063 }
7064
7065 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7066#endif /* VBOX_WITH_GUEST_PROPS defined */
7067 }
7068 catch(std::bad_alloc &)
7069 {
7070 return E_OUTOFMEMORY;
7071 }
7072
7073 AssertComRC(rc);
7074 return rc;
7075}
7076
7077/**
7078 * Saves the storage controller configuration.
7079 *
7080 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
7081 */
7082HRESULT Machine::saveStorageControllers(settings::Storage &data)
7083{
7084 data.llStorageControllers.clear();
7085
7086 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7087 it != mStorageControllers->end();
7088 ++it)
7089 {
7090 HRESULT rc;
7091 ComObjPtr<StorageController> pCtl = *it;
7092
7093 settings::StorageController ctl;
7094 ctl.strName = pCtl->name();
7095 ctl.controllerType = pCtl->controllerType();
7096 ctl.storageBus = pCtl->storageBus();
7097 ctl.ulInstance = pCtl->instance();
7098
7099 /* Save the port count. */
7100 ULONG portCount;
7101 rc = pCtl->COMGETTER(PortCount)(&portCount);
7102 ComAssertComRCRet(rc, rc);
7103 ctl.ulPortCount = portCount;
7104
7105 /* Save IDE emulation settings. */
7106 if (ctl.controllerType == StorageControllerType_IntelAhci)
7107 {
7108 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
7109 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
7110 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
7111 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
7112 )
7113 ComAssertComRCRet(rc, rc);
7114 }
7115
7116 /* save the devices now. */
7117 rc = saveStorageDevices(pCtl, ctl);
7118 ComAssertComRCRet(rc, rc);
7119
7120 data.llStorageControllers.push_back(ctl);
7121 }
7122
7123 return S_OK;
7124}
7125
7126/**
7127 * Saves the hard disk confguration.
7128 */
7129HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
7130 settings::StorageController &data)
7131{
7132 MediaData::AttachmentList atts;
7133
7134 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->name()), atts);
7135 CheckComRCReturnRC (rc);
7136
7137 data.llAttachedDevices.clear();
7138 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7139 it != atts.end();
7140 ++it)
7141 {
7142 settings::AttachedDevice dev;
7143
7144 MediumAttachment *pAttach = *it;
7145 Medium *pMedium = pAttach->medium();
7146
7147 dev.deviceType = pAttach->type();
7148 dev.lPort = pAttach->port();
7149 dev.lDevice = pAttach->device();
7150 if (pMedium)
7151 {
7152 BOOL fHostDrive = false;
7153 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
7154 if (FAILED(rc))
7155 return rc;
7156 if (fHostDrive)
7157 dev.strHostDriveSrc = pMedium->location();
7158 else
7159 dev.uuid = pMedium->id();
7160 dev.fPassThrough = pAttach->passthrough();
7161 }
7162
7163 data.llAttachedDevices.push_back(dev);
7164 }
7165
7166 return S_OK;
7167}
7168
7169/**
7170 * Saves machine state settings as defined by aFlags
7171 * (SaveSTS_* values).
7172 *
7173 * @param aFlags Combination of SaveSTS_* flags.
7174 *
7175 * @note Locks objects for writing.
7176 */
7177HRESULT Machine::saveStateSettings(int aFlags)
7178{
7179 if (aFlags == 0)
7180 return S_OK;
7181
7182 AutoCaller autoCaller (this);
7183 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7184
7185 /* This object's write lock is also necessary to serialize file access
7186 * (prevent concurrent reads and writes) */
7187 AutoWriteLock alock(this);
7188
7189 HRESULT rc = S_OK;
7190
7191 Assert(mData->m_pMachineConfigFile);
7192
7193 try
7194 {
7195 if (aFlags & SaveSTS_CurStateModified)
7196 mData->m_pMachineConfigFile->fCurrentStateModified = true;
7197
7198 if (aFlags & SaveSTS_StateFilePath)
7199 {
7200 if (!mSSData->mStateFilePath.isEmpty())
7201 /* try to make the file name relative to the settings file dir */
7202 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7203 else
7204 mData->m_pMachineConfigFile->strStateFile.setNull();
7205 }
7206
7207 if (aFlags & SaveSTS_StateTimeStamp)
7208 {
7209 Assert( mData->mMachineState != MachineState_Aborted
7210 || mSSData->mStateFilePath.isEmpty());
7211
7212 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7213
7214 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7215//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7216 }
7217
7218 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7219 }
7220 catch (...)
7221 {
7222 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
7223 }
7224
7225 return rc;
7226}
7227
7228/**
7229 * Creates differencing hard disks for all normal hard disks attached to this
7230 * machine and a new set of attachments to refer to created disks.
7231 *
7232 * Used when taking a snapshot or when discarding the current state.
7233 *
7234 * This method assumes that mMediaData contains the original hard disk attachments
7235 * it needs to create diffs for. On success, these attachments will be replaced
7236 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
7237 * called to delete created diffs which will also rollback mMediaData and restore
7238 * whatever was backed up before calling this method.
7239 *
7240 * Attachments with non-normal hard disks are left as is.
7241 *
7242 * If @a aOnline is @c false then the original hard disks that require implicit
7243 * diffs will be locked for reading. Otherwise it is assumed that they are
7244 * already locked for writing (when the VM was started). Note that in the latter
7245 * case it is responsibility of the caller to lock the newly created diffs for
7246 * writing if this method succeeds.
7247 *
7248 * @param aFolder Folder where to create diff hard disks.
7249 * @param aProgress Progress object to run (must contain at least as
7250 * many operations left as the number of hard disks
7251 * attached).
7252 * @param aOnline Whether the VM was online prior to this operation.
7253 *
7254 * @note The progress object is not marked as completed, neither on success nor
7255 * on failure. This is a responsibility of the caller.
7256 *
7257 * @note Locks this object for writing.
7258 */
7259HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
7260 IProgress *aProgress,
7261 ULONG aWeight,
7262 bool aOnline)
7263{
7264 AssertReturn(!aFolder.isEmpty(), E_FAIL);
7265
7266 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
7267
7268 AutoCaller autoCaller(this);
7269 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7270
7271 AutoWriteLock alock(this);
7272
7273 /* must be in a protective state because we leave the lock below */
7274 AssertReturn( mData->mMachineState == MachineState_Saving
7275 || mData->mMachineState == MachineState_LiveSnapshotting
7276 || mData->mMachineState == MachineState_RestoringSnapshot
7277 || mData->mMachineState == MachineState_DeletingSnapshot
7278 , E_FAIL);
7279
7280 HRESULT rc = S_OK;
7281
7282 typedef std::list< ComObjPtr<Medium> > LockedMedia;
7283 LockedMedia lockedMedia;
7284
7285 try
7286 {
7287 if (!aOnline)
7288 {
7289 /* lock all attached hard disks early to detect "in use"
7290 * situations before creating actual diffs */
7291 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7292 it != mMediaData->mAttachments.end();
7293 ++it)
7294 {
7295 MediumAttachment* pAtt = *it;
7296 if (pAtt->type() == DeviceType_HardDisk)
7297 {
7298 Medium* pHD = pAtt->medium();
7299 Assert(pHD);
7300 rc = pHD->LockRead (NULL);
7301 CheckComRCThrowRC(rc);
7302 lockedMedia.push_back(pHD);
7303 }
7304 }
7305 }
7306
7307 /* remember the current list (note that we don't use backup() since
7308 * mMediaData may be already backed up) */
7309 MediaData::AttachmentList atts = mMediaData->mAttachments;
7310
7311 /* start from scratch */
7312 mMediaData->mAttachments.clear();
7313
7314 /* go through remembered attachments and create diffs for normal hard
7315 * disks and attach them */
7316 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7317 it != atts.end();
7318 ++it)
7319 {
7320 MediumAttachment* pAtt = *it;
7321
7322 DeviceType_T devType = pAtt->type();
7323 Medium* medium = pAtt->medium();
7324
7325 if ( devType != DeviceType_HardDisk
7326 || medium == NULL
7327 || medium->type() != MediumType_Normal)
7328 {
7329 /* copy the attachment as is */
7330
7331 /** @todo the progress object created in Console::TakeSnaphot
7332 * only expects operations for hard disks. Later other
7333 * device types need to show up in the progress as well. */
7334 if (devType == DeviceType_HardDisk)
7335 {
7336 if (medium == NULL)
7337 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
7338 aWeight); // weight
7339 else
7340 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
7341 medium->base()->name().raw()),
7342 aWeight); // weight
7343 }
7344
7345 mMediaData->mAttachments.push_back(pAtt);
7346 continue;
7347 }
7348
7349 /* need a diff */
7350 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
7351 medium->base()->name().raw()),
7352 aWeight); // weight
7353
7354 ComObjPtr<Medium> diff;
7355 diff.createObject();
7356 rc = diff->init(mParent,
7357 medium->preferredDiffFormat().raw(),
7358 BstrFmt("%ls"RTPATH_SLASH_STR,
7359 mUserData->mSnapshotFolderFull.raw()).raw());
7360 CheckComRCThrowRC(rc);
7361
7362 /* leave the lock before the potentially lengthy operation */
7363 alock.leave();
7364
7365 rc = medium->createDiffStorageAndWait(diff,
7366 MediumVariant_Standard,
7367 NULL);
7368
7369 // at this point, the old image is still locked for writing, but instead
7370 // we need the new diff image locked for writing and lock the previously
7371 // current one for reading only
7372 if (aOnline)
7373 {
7374 diff->LockWrite(NULL);
7375 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7376 medium->UnlockWrite(NULL);
7377 medium->LockRead(NULL);
7378 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7379 }
7380
7381 alock.enter();
7382
7383 CheckComRCThrowRC(rc);
7384
7385 rc = diff->attachTo(mData->mUuid);
7386 AssertComRCThrowRC(rc);
7387
7388 /* add a new attachment */
7389 ComObjPtr<MediumAttachment> attachment;
7390 attachment.createObject();
7391 rc = attachment->init(this,
7392 diff,
7393 pAtt->controllerName(),
7394 pAtt->port(),
7395 pAtt->device(),
7396 DeviceType_HardDisk,
7397 true /* aImplicit */);
7398 CheckComRCThrowRC(rc);
7399
7400 mMediaData->mAttachments.push_back(attachment);
7401 }
7402 }
7403 catch (HRESULT aRC) { rc = aRC; }
7404
7405 /* unlock all hard disks we locked */
7406 if (!aOnline)
7407 {
7408 ErrorInfoKeeper eik;
7409
7410 for (LockedMedia::const_iterator it = lockedMedia.begin();
7411 it != lockedMedia.end();
7412 ++it)
7413 {
7414 HRESULT rc2 = (*it)->UnlockRead(NULL);
7415 AssertComRC(rc2);
7416 }
7417 }
7418
7419 if (FAILED(rc))
7420 {
7421 MultiResultRef mrc (rc);
7422
7423 mrc = deleteImplicitDiffs();
7424 }
7425
7426 return rc;
7427}
7428
7429/**
7430 * Deletes implicit differencing hard disks created either by
7431 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
7432 *
7433 * Note that to delete hard disks created by #AttachMedium() this method is
7434 * called from #fixupMedia() when the changes are rolled back.
7435 *
7436 * @note Locks this object for writing.
7437 */
7438HRESULT Machine::deleteImplicitDiffs()
7439{
7440 AutoCaller autoCaller(this);
7441 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7442
7443 AutoWriteLock alock(this);
7444
7445 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
7446
7447 HRESULT rc = S_OK;
7448
7449 MediaData::AttachmentList implicitAtts;
7450
7451 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7452
7453 /* enumerate new attachments */
7454 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7455 it != mMediaData->mAttachments.end();
7456 ++it)
7457 {
7458 ComObjPtr<Medium> hd = (*it)->medium();
7459 if (hd.isNull())
7460 continue;
7461
7462 if ((*it)->isImplicit())
7463 {
7464 /* deassociate and mark for deletion */
7465 rc = hd->detachFrom(mData->mUuid);
7466 AssertComRC(rc);
7467 implicitAtts.push_back (*it);
7468 continue;
7469 }
7470
7471 /* was this hard disk attached before? */
7472 if (!findAttachment(oldAtts, hd))
7473 {
7474 /* no: de-associate */
7475 rc = hd->detachFrom(mData->mUuid);
7476 AssertComRC(rc);
7477 continue;
7478 }
7479 }
7480
7481 /* rollback hard disk changes */
7482 mMediaData.rollback();
7483
7484 MultiResult mrc (S_OK);
7485
7486 /* delete unused implicit diffs */
7487 if (implicitAtts.size() != 0)
7488 {
7489 /* will leave the lock before the potentially lengthy
7490 * operation, so protect with the special state (unless already
7491 * protected) */
7492 MachineState_T oldState = mData->mMachineState;
7493 if ( oldState != MachineState_Saving
7494 && oldState != MachineState_LiveSnapshotting
7495 && oldState != MachineState_RestoringSnapshot
7496 && oldState != MachineState_DeletingSnapshot
7497 )
7498 setMachineState (MachineState_SettingUp);
7499
7500 alock.leave();
7501
7502 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
7503 it != implicitAtts.end();
7504 ++it)
7505 {
7506 ComObjPtr<Medium> hd = (*it)->medium();
7507 mrc = hd->deleteStorageAndWait();
7508 }
7509
7510 alock.enter();
7511
7512 if (mData->mMachineState == MachineState_SettingUp)
7513 {
7514 setMachineState (oldState);
7515 }
7516 }
7517
7518 return mrc;
7519}
7520
7521/**
7522 * Looks through the given list of media attachments for one with the given parameters
7523 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7524 * can be searched as well if needed.
7525 *
7526 * @param list
7527 * @param aControllerName
7528 * @param aControllerPort
7529 * @param aDevice
7530 * @return
7531 */
7532MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7533 IN_BSTR aControllerName,
7534 LONG aControllerPort,
7535 LONG aDevice)
7536{
7537 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7538 it != ll.end();
7539 ++it)
7540 {
7541 MediumAttachment *pAttach = *it;
7542 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
7543 return pAttach;
7544 }
7545
7546 return NULL;
7547}
7548
7549/**
7550 * Looks through the given list of media attachments for one with the given parameters
7551 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7552 * can be searched as well if needed.
7553 *
7554 * @param list
7555 * @param aControllerName
7556 * @param aControllerPort
7557 * @param aDevice
7558 * @return
7559 */
7560MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7561 ComObjPtr<Medium> pMedium)
7562{
7563 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7564 it != ll.end();
7565 ++it)
7566 {
7567 MediumAttachment *pAttach = *it;
7568 ComObjPtr<Medium> pMediumThis = pAttach->medium();
7569 if (pMediumThis.equalsTo(pMedium))
7570 return pAttach;
7571 }
7572
7573 return NULL;
7574}
7575
7576/**
7577 * Looks through the given list of media attachments for one with the given parameters
7578 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7579 * can be searched as well if needed.
7580 *
7581 * @param list
7582 * @param aControllerName
7583 * @param aControllerPort
7584 * @param aDevice
7585 * @return
7586 */
7587MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7588 Guid &id)
7589{
7590 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7591 it != ll.end();
7592 ++it)
7593 {
7594 MediumAttachment *pAttach = *it;
7595 ComObjPtr<Medium> pMediumThis = pAttach->medium();
7596 if (pMediumThis->id() == id)
7597 return pAttach;
7598 }
7599
7600 return NULL;
7601}
7602
7603/**
7604 * Perform deferred hard disk detachments on success and deletion of implicitly
7605 * created diffs on failure.
7606 *
7607 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
7608 * backed up).
7609 *
7610 * When the data is backed up, this method will commit mMediaData if @a aCommit is
7611 * @c true and rollback it otherwise before returning.
7612 *
7613 * If @a aOnline is @c true then this method called with @a aCommit = @c true
7614 * will also unlock the old hard disks for which the new implicit diffs were
7615 * created and will lock these new diffs for writing. When @a aCommit is @c
7616 * false, this argument is ignored.
7617 *
7618 * @param aCommit @c true if called on success.
7619 * @param aOnline Whether the VM was online prior to this operation.
7620 *
7621 * @note Locks this object for writing!
7622 */
7623void Machine::fixupMedia(bool aCommit, bool aOnline /*= false*/)
7624{
7625 AutoCaller autoCaller(this);
7626 AssertComRCReturnVoid (autoCaller.rc());
7627
7628 AutoWriteLock alock(this);
7629
7630 LogFlowThisFunc(("Entering, aCommit=%d, aOnline=%d\n", aCommit, aOnline));
7631
7632 HRESULT rc = S_OK;
7633
7634 /* no attach/detach operations -- nothing to do */
7635 if (!mMediaData.isBackedUp())
7636 return;
7637
7638 if (aCommit)
7639 {
7640 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7641
7642 /* enumerate new attachments */
7643 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7644 it != mMediaData->mAttachments.end();
7645 ++it)
7646 {
7647 MediumAttachment *pAttach = *it;
7648
7649 pAttach->commit();
7650
7651 Medium* pMedium = pAttach->medium();
7652 bool fImplicit = pAttach->isImplicit();
7653
7654 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
7655 (pMedium) ? pMedium->name().raw() : "NULL",
7656 fImplicit));
7657
7658 /** @todo convert all this Machine-based voodoo to MediumAttachment
7659 * based commit logic. */
7660 if (fImplicit)
7661 {
7662 /* convert implicit attachment to normal */
7663 pAttach->setImplicit(false);
7664
7665 if ( aOnline
7666 && pMedium
7667 && pAttach->type() == DeviceType_HardDisk
7668 )
7669 {
7670 rc = pMedium->LockWrite(NULL);
7671 AssertComRC(rc);
7672
7673 mData->mSession.mLockedMedia.push_back(
7674 Data::Session::LockedMedia::value_type(
7675 ComPtr<IMedium>(pMedium), true));
7676
7677 /* also, relock the old hard disk which is a base for the
7678 * new diff for reading if the VM is online */
7679
7680 ComObjPtr<Medium> parent = pMedium->parent();
7681 /* make the relock atomic */
7682 AutoWriteLock parentLock (parent);
7683 rc = parent->UnlockWrite(NULL);
7684 AssertComRC(rc);
7685 rc = parent->LockRead(NULL);
7686 AssertComRC(rc);
7687
7688 /* XXX actually we should replace the old entry in that
7689 * vector (write lock => read lock) but this would take
7690 * some effort. So lets just ignore the error code in
7691 * SessionMachine::unlockMedia(). */
7692 mData->mSession.mLockedMedia.push_back(
7693 Data::Session::LockedMedia::value_type (
7694 ComPtr<IMedium>(parent), false));
7695 }
7696
7697 continue;
7698 }
7699
7700 if (pMedium)
7701 {
7702 /* was this medium attached before? */
7703 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
7704 oldIt != oldAtts.end();
7705 ++oldIt)
7706 {
7707 MediumAttachment *pOldAttach = *oldIt;
7708 if (pOldAttach->medium().equalsTo(pMedium))
7709 {
7710 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->name().raw()));
7711
7712 /* yes: remove from old to avoid de-association */
7713 oldAtts.erase(oldIt);
7714 break;
7715 }
7716 }
7717 }
7718 }
7719
7720 /* enumerate remaining old attachments and de-associate from the
7721 * current machine state */
7722 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
7723 it != oldAtts.end();
7724 ++it)
7725 {
7726 MediumAttachment *pAttach = *it;
7727 Medium* pMedium = pAttach->medium();
7728
7729 /* Detach only hard disks, since DVD/floppy media is detached
7730 * instantly in MountMedium. */
7731 if (pAttach->type() == DeviceType_HardDisk && pMedium)
7732 {
7733 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->name().raw()));
7734
7735 /* now de-associate from the current machine state */
7736 rc = pMedium->detachFrom(mData->mUuid);
7737 AssertComRC(rc);
7738
7739 if ( aOnline
7740 && pAttach->type() == DeviceType_HardDisk)
7741 {
7742 /* unlock since not used anymore */
7743 MediumState_T state;
7744 rc = pMedium->UnlockWrite(&state);
7745 /* the disk may be alredy relocked for reading above */
7746 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead);
7747 }
7748 }
7749 }
7750
7751 /* commit the hard disk changes */
7752 mMediaData.commit();
7753
7754 if (mType == IsSessionMachine)
7755 {
7756 /* attach new data to the primary machine and reshare it */
7757 mPeer->mMediaData.attach(mMediaData);
7758 }
7759 }
7760 else
7761 {
7762 /* enumerate new attachments */
7763 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7764 it != mMediaData->mAttachments.end();
7765 ++it)
7766 {
7767 MediumAttachment *pAttach = *it;
7768 /* Fix up the backrefs for DVD/floppy media. */
7769 if (pAttach->type() != DeviceType_HardDisk)
7770 {
7771 Medium* pMedium = pAttach->medium();
7772 if (pMedium)
7773 {
7774 rc = pMedium->detachFrom(mData->mUuid);
7775 AssertComRC(rc);
7776 }
7777 }
7778
7779 (*it)->rollback();
7780
7781 pAttach = *it;
7782 /* Fix up the backrefs for DVD/floppy media. */
7783 if (pAttach->type() != DeviceType_HardDisk)
7784 {
7785 Medium* pMedium = pAttach->medium();
7786 if (pMedium)
7787 {
7788 rc = pMedium->attachTo(mData->mUuid);
7789 AssertComRC(rc);
7790 }
7791 }
7792 }
7793
7794 /** @todo convert all this Machine-based voodoo to MediumAttachment
7795 * based rollback logic. */
7796 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
7797 // which gets called if Machine::registeredInit() fails...
7798 deleteImplicitDiffs();
7799 }
7800
7801 return;
7802}
7803
7804/**
7805 * Returns true if the settings file is located in the directory named exactly
7806 * as the machine. This will be true if the machine settings structure was
7807 * created by default in #openConfigLoader().
7808 *
7809 * @param aSettingsDir if not NULL, the full machine settings file directory
7810 * name will be assigned there.
7811 *
7812 * @note Doesn't lock anything.
7813 * @note Not thread safe (must be called from this object's lock).
7814 */
7815bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
7816{
7817 Utf8Str settingsDir = mData->m_strConfigFileFull;
7818 settingsDir.stripFilename();
7819 char *dirName = RTPathFilename(settingsDir.c_str());
7820
7821 AssertReturn(dirName, false);
7822
7823 /* if we don't rename anything on name change, return false shorlty */
7824 if (!mUserData->mNameSync)
7825 return false;
7826
7827 if (aSettingsDir)
7828 *aSettingsDir = settingsDir;
7829
7830 return Bstr (dirName) == mUserData->mName;
7831}
7832
7833/**
7834 * @note Locks objects for reading!
7835 */
7836bool Machine::isModified()
7837{
7838 AutoCaller autoCaller(this);
7839 AssertComRCReturn (autoCaller.rc(), false);
7840
7841 AutoReadLock alock(this);
7842
7843 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7844 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
7845 return true;
7846
7847 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7848 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
7849 return true;
7850
7851 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7852 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
7853 return true;
7854
7855 if (!mStorageControllers.isNull())
7856 {
7857 for (StorageControllerList::const_iterator it =
7858 mStorageControllers->begin();
7859 it != mStorageControllers->end();
7860 ++it)
7861 {
7862 if ((*it)->isModified())
7863 return true;
7864 }
7865 }
7866
7867 return
7868 mUserData.isBackedUp() ||
7869 mHWData.isBackedUp() ||
7870 mMediaData.isBackedUp() ||
7871 mStorageControllers.isBackedUp() ||
7872#ifdef VBOX_WITH_VRDP
7873 (mVRDPServer && mVRDPServer->isModified()) ||
7874#endif
7875 (mAudioAdapter && mAudioAdapter->isModified()) ||
7876 (mUSBController && mUSBController->isModified()) ||
7877 (mBIOSSettings && mBIOSSettings->isModified());
7878}
7879
7880/**
7881 * Returns the logical OR of data.hasActualChanges() of this and all child
7882 * objects.
7883 *
7884 * @param aIgnoreUserData @c true to ignore changes to mUserData
7885 *
7886 * @note Locks objects for reading!
7887 */
7888bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
7889{
7890 AutoCaller autoCaller(this);
7891 AssertComRCReturn (autoCaller.rc(), false);
7892
7893 AutoReadLock alock(this);
7894
7895 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7896 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
7897 return true;
7898
7899 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7900 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
7901 return true;
7902
7903 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7904 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
7905 return true;
7906
7907 if (!mStorageControllers.isBackedUp())
7908 {
7909 /* see whether any of the devices has changed its data */
7910 for (StorageControllerList::const_iterator
7911 it = mStorageControllers->begin();
7912 it != mStorageControllers->end();
7913 ++it)
7914 {
7915 if ((*it)->isReallyModified())
7916 return true;
7917 }
7918 }
7919 else
7920 {
7921 if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
7922 return true;
7923 }
7924
7925 return
7926 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
7927 mHWData.hasActualChanges() ||
7928 mMediaData.hasActualChanges() ||
7929 mStorageControllers.hasActualChanges() ||
7930#ifdef VBOX_WITH_VRDP
7931 (mVRDPServer && mVRDPServer->isReallyModified()) ||
7932#endif
7933 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
7934 (mUSBController && mUSBController->isReallyModified()) ||
7935 (mBIOSSettings && mBIOSSettings->isReallyModified());
7936}
7937
7938/**
7939 * Discards all changes to machine settings.
7940 *
7941 * @param aNotify Whether to notify the direct session about changes or not.
7942 *
7943 * @note Locks objects for writing!
7944 */
7945void Machine::rollback (bool aNotify)
7946{
7947 AutoCaller autoCaller(this);
7948 AssertComRCReturn (autoCaller.rc(), (void) 0);
7949
7950 AutoWriteLock alock(this);
7951
7952 /* check for changes in own data */
7953
7954 bool sharedFoldersChanged = false, storageChanged = false;
7955
7956 if (aNotify && mHWData.isBackedUp())
7957 {
7958 if (mHWData->mSharedFolders.size() !=
7959 mHWData.backedUpData()->mSharedFolders.size())
7960 sharedFoldersChanged = true;
7961 else
7962 {
7963 for (HWData::SharedFolderList::iterator rit =
7964 mHWData->mSharedFolders.begin();
7965 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
7966 ++rit)
7967 {
7968 for (HWData::SharedFolderList::iterator cit =
7969 mHWData.backedUpData()->mSharedFolders.begin();
7970 cit != mHWData.backedUpData()->mSharedFolders.end();
7971 ++cit)
7972 {
7973 if ((*cit)->name() != (*rit)->name() ||
7974 (*cit)->hostPath() != (*rit)->hostPath())
7975 {
7976 sharedFoldersChanged = true;
7977 break;
7978 }
7979 }
7980 }
7981 }
7982 }
7983
7984 if (!mStorageControllers.isNull())
7985 {
7986 if (mStorageControllers.isBackedUp())
7987 {
7988 /* unitialize all new devices (absent in the backed up list). */
7989 StorageControllerList::const_iterator it = mStorageControllers->begin();
7990 StorageControllerList *backedList = mStorageControllers.backedUpData();
7991 while (it != mStorageControllers->end())
7992 {
7993 if (std::find (backedList->begin(), backedList->end(), *it ) ==
7994 backedList->end())
7995 {
7996 (*it)->uninit();
7997 }
7998 ++it;
7999 }
8000
8001 /* restore the list */
8002 mStorageControllers.rollback();
8003 }
8004
8005 /* rollback any changes to devices after restoring the list */
8006 StorageControllerList::const_iterator it = mStorageControllers->begin();
8007 while (it != mStorageControllers->end())
8008 {
8009 if ((*it)->isModified())
8010 (*it)->rollback();
8011
8012 ++it;
8013 }
8014 }
8015
8016 mUserData.rollback();
8017
8018 mHWData.rollback();
8019
8020 if (mMediaData.isBackedUp())
8021 fixupMedia(false /* aCommit */);
8022
8023 /* check for changes in child objects */
8024
8025 bool vrdpChanged = false, usbChanged = false;
8026
8027 ComPtr<INetworkAdapter> networkAdapters [RT_ELEMENTS (mNetworkAdapters)];
8028 ComPtr<ISerialPort> serialPorts [RT_ELEMENTS (mSerialPorts)];
8029 ComPtr<IParallelPort> parallelPorts [RT_ELEMENTS (mParallelPorts)];
8030
8031 if (mBIOSSettings)
8032 mBIOSSettings->rollback();
8033
8034#ifdef VBOX_WITH_VRDP
8035 if (mVRDPServer)
8036 vrdpChanged = mVRDPServer->rollback();
8037#endif
8038
8039 if (mAudioAdapter)
8040 mAudioAdapter->rollback();
8041
8042 if (mUSBController)
8043 usbChanged = mUSBController->rollback();
8044
8045 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8046 if (mNetworkAdapters [slot])
8047 if (mNetworkAdapters [slot]->rollback())
8048 networkAdapters [slot] = mNetworkAdapters [slot];
8049
8050 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8051 if (mSerialPorts [slot])
8052 if (mSerialPorts [slot]->rollback())
8053 serialPorts [slot] = mSerialPorts [slot];
8054
8055 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8056 if (mParallelPorts [slot])
8057 if (mParallelPorts [slot]->rollback())
8058 parallelPorts [slot] = mParallelPorts [slot];
8059
8060 if (aNotify)
8061 {
8062 /* inform the direct session about changes */
8063
8064 ComObjPtr<Machine> that = this;
8065 alock.leave();
8066
8067 if (sharedFoldersChanged)
8068 that->onSharedFolderChange();
8069
8070 if (vrdpChanged)
8071 that->onVRDPServerChange();
8072 if (usbChanged)
8073 that->onUSBControllerChange();
8074
8075 for (ULONG slot = 0; slot < RT_ELEMENTS (networkAdapters); slot ++)
8076 if (networkAdapters [slot])
8077 that->onNetworkAdapterChange (networkAdapters [slot], FALSE);
8078 for (ULONG slot = 0; slot < RT_ELEMENTS (serialPorts); slot ++)
8079 if (serialPorts [slot])
8080 that->onSerialPortChange (serialPorts [slot]);
8081 for (ULONG slot = 0; slot < RT_ELEMENTS (parallelPorts); slot ++)
8082 if (parallelPorts [slot])
8083 that->onParallelPortChange (parallelPorts [slot]);
8084
8085 if (storageChanged)
8086 that->onStorageControllerChange();
8087 }
8088}
8089
8090/**
8091 * Commits all the changes to machine settings.
8092 *
8093 * Note that this operation is supposed to never fail.
8094 *
8095 * @note Locks this object and children for writing.
8096 */
8097void Machine::commit()
8098{
8099 AutoCaller autoCaller(this);
8100 AssertComRCReturnVoid (autoCaller.rc());
8101
8102 AutoCaller peerCaller (mPeer);
8103 AssertComRCReturnVoid (peerCaller.rc());
8104
8105 AutoMultiWriteLock2 alock (mPeer, this);
8106
8107 /*
8108 * use safe commit to ensure Snapshot machines (that share mUserData)
8109 * will still refer to a valid memory location
8110 */
8111 mUserData.commitCopy();
8112
8113 mHWData.commit();
8114
8115 if (mMediaData.isBackedUp())
8116 fixupMedia(true /* aCommit */);
8117
8118 mBIOSSettings->commit();
8119#ifdef VBOX_WITH_VRDP
8120 mVRDPServer->commit();
8121#endif
8122 mAudioAdapter->commit();
8123 mUSBController->commit();
8124
8125 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8126 mNetworkAdapters [slot]->commit();
8127 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8128 mSerialPorts [slot]->commit();
8129 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8130 mParallelPorts [slot]->commit();
8131
8132 bool commitStorageControllers = false;
8133
8134 if (mStorageControllers.isBackedUp())
8135 {
8136 mStorageControllers.commit();
8137
8138 if (mPeer)
8139 {
8140 AutoWriteLock peerlock (mPeer);
8141
8142 /* Commit all changes to new controllers (this will reshare data with
8143 * peers for thos who have peers) */
8144 StorageControllerList *newList = new StorageControllerList();
8145 StorageControllerList::const_iterator it = mStorageControllers->begin();
8146 while (it != mStorageControllers->end())
8147 {
8148 (*it)->commit();
8149
8150 /* look if this controller has a peer device */
8151 ComObjPtr<StorageController> peer = (*it)->peer();
8152 if (!peer)
8153 {
8154 /* no peer means the device is a newly created one;
8155 * create a peer owning data this device share it with */
8156 peer.createObject();
8157 peer->init (mPeer, *it, true /* aReshare */);
8158 }
8159 else
8160 {
8161 /* remove peer from the old list */
8162 mPeer->mStorageControllers->remove (peer);
8163 }
8164 /* and add it to the new list */
8165 newList->push_back(peer);
8166
8167 ++it;
8168 }
8169
8170 /* uninit old peer's controllers that are left */
8171 it = mPeer->mStorageControllers->begin();
8172 while (it != mPeer->mStorageControllers->end())
8173 {
8174 (*it)->uninit();
8175 ++it;
8176 }
8177
8178 /* attach new list of controllers to our peer */
8179 mPeer->mStorageControllers.attach (newList);
8180 }
8181 else
8182 {
8183 /* we have no peer (our parent is the newly created machine);
8184 * just commit changes to devices */
8185 commitStorageControllers = true;
8186 }
8187 }
8188 else
8189 {
8190 /* the list of controllers itself is not changed,
8191 * just commit changes to controllers themselves */
8192 commitStorageControllers = true;
8193 }
8194
8195 if (commitStorageControllers)
8196 {
8197 StorageControllerList::const_iterator it = mStorageControllers->begin();
8198 while (it != mStorageControllers->end())
8199 {
8200 (*it)->commit();
8201 ++it;
8202 }
8203 }
8204
8205 if (mType == IsSessionMachine)
8206 {
8207 /* attach new data to the primary machine and reshare it */
8208 mPeer->mUserData.attach (mUserData);
8209 mPeer->mHWData.attach (mHWData);
8210 /* mMediaData is reshared by fixupMedia */
8211 // mPeer->mMediaData.attach(mMediaData);
8212 Assert(mPeer->mMediaData.data() == mMediaData.data());
8213 }
8214}
8215
8216/**
8217 * Copies all the hardware data from the given machine.
8218 *
8219 * Currently, only called when the VM is being restored from a snapshot. In
8220 * particular, this implies that the VM is not running during this method's
8221 * call.
8222 *
8223 * @note This method must be called from under this object's lock.
8224 *
8225 * @note This method doesn't call #commit(), so all data remains backed up and
8226 * unsaved.
8227 */
8228void Machine::copyFrom (Machine *aThat)
8229{
8230 AssertReturnVoid (mType == IsMachine || mType == IsSessionMachine);
8231 AssertReturnVoid (aThat->mType == IsSnapshotMachine);
8232
8233 AssertReturnVoid (!Global::IsOnline (mData->mMachineState));
8234
8235 mHWData.assignCopy (aThat->mHWData);
8236
8237 // create copies of all shared folders (mHWData after attiching a copy
8238 // contains just references to original objects)
8239 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
8240 it != mHWData->mSharedFolders.end();
8241 ++it)
8242 {
8243 ComObjPtr<SharedFolder> folder;
8244 folder.createObject();
8245 HRESULT rc = folder->initCopy (machine(), *it);
8246 AssertComRC (rc);
8247 *it = folder;
8248 }
8249
8250 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
8251#ifdef VBOX_WITH_VRDP
8252 mVRDPServer->copyFrom (aThat->mVRDPServer);
8253#endif
8254 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
8255 mUSBController->copyFrom (aThat->mUSBController);
8256
8257 /* create private copies of all controllers */
8258 mStorageControllers.backup();
8259 mStorageControllers->clear();
8260 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
8261 it != aThat->mStorageControllers->end();
8262 ++it)
8263 {
8264 ComObjPtr<StorageController> ctrl;
8265 ctrl.createObject();
8266 ctrl->initCopy (this, *it);
8267 mStorageControllers->push_back(ctrl);
8268 }
8269
8270 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8271 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
8272 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8273 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
8274 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8275 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
8276}
8277
8278#ifdef VBOX_WITH_RESOURCE_USAGE_API
8279void Machine::registerMetrics (PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
8280{
8281 pm::CollectorHAL *hal = aCollector->getHAL();
8282 /* Create sub metrics */
8283 pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User",
8284 "Percentage of processor time spent in user mode by VM process.");
8285 pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel",
8286 "Percentage of processor time spent in kernel mode by VM process.");
8287 pm::SubMetric *ramUsageUsed = new pm::SubMetric ("RAM/Usage/Used",
8288 "Size of resident portion of VM process in memory.");
8289 /* Create and register base metrics */
8290 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw (hal, aMachine, pid,
8291 cpuLoadUser, cpuLoadKernel);
8292 aCollector->registerBaseMetric (cpuLoad);
8293 pm::BaseMetric *ramUsage = new pm::MachineRamUsage (hal, aMachine, pid,
8294 ramUsageUsed);
8295 aCollector->registerBaseMetric (ramUsage);
8296
8297 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser, 0));
8298 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
8299 new pm::AggregateAvg()));
8300 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
8301 new pm::AggregateMin()));
8302 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
8303 new pm::AggregateMax()));
8304 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel, 0));
8305 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
8306 new pm::AggregateAvg()));
8307 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
8308 new pm::AggregateMin()));
8309 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
8310 new pm::AggregateMax()));
8311
8312 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed, 0));
8313 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
8314 new pm::AggregateAvg()));
8315 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
8316 new pm::AggregateMin()));
8317 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
8318 new pm::AggregateMax()));
8319};
8320
8321void Machine::unregisterMetrics (PerformanceCollector *aCollector, Machine *aMachine)
8322{
8323 aCollector->unregisterMetricsFor (aMachine);
8324 aCollector->unregisterBaseMetricsFor (aMachine);
8325};
8326#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8327
8328
8329////////////////////////////////////////////////////////////////////////////////
8330
8331DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
8332
8333HRESULT SessionMachine::FinalConstruct()
8334{
8335 LogFlowThisFunc(("\n"));
8336
8337 /* set the proper type to indicate we're the SessionMachine instance */
8338 unconst(mType) = IsSessionMachine;
8339
8340#if defined(RT_OS_WINDOWS)
8341 mIPCSem = NULL;
8342#elif defined(RT_OS_OS2)
8343 mIPCSem = NULLHANDLE;
8344#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8345 mIPCSem = -1;
8346#else
8347# error "Port me!"
8348#endif
8349
8350 return S_OK;
8351}
8352
8353void SessionMachine::FinalRelease()
8354{
8355 LogFlowThisFunc(("\n"));
8356
8357 uninit (Uninit::Unexpected);
8358}
8359
8360/**
8361 * @note Must be called only by Machine::openSession() from its own write lock.
8362 */
8363HRESULT SessionMachine::init (Machine *aMachine)
8364{
8365 LogFlowThisFuncEnter();
8366 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8367
8368 AssertReturn(aMachine, E_INVALIDARG);
8369
8370 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
8371
8372 /* Enclose the state transition NotReady->InInit->Ready */
8373 AutoInitSpan autoInitSpan(this);
8374 AssertReturn(autoInitSpan.isOk(), E_FAIL);
8375
8376 /* create the interprocess semaphore */
8377#if defined(RT_OS_WINDOWS)
8378 mIPCSemName = aMachine->mData->m_strConfigFileFull;
8379 for (size_t i = 0; i < mIPCSemName.length(); i++)
8380 if (mIPCSemName[i] == '\\')
8381 mIPCSemName[i] = '/';
8382 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
8383 ComAssertMsgRet (mIPCSem,
8384 ("Cannot create IPC mutex '%ls', err=%d",
8385 mIPCSemName.raw(), ::GetLastError()),
8386 E_FAIL);
8387#elif defined(RT_OS_OS2)
8388 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%RTuuid}",
8389 aMachine->mData->mUuid.raw());
8390 mIPCSemName = ipcSem;
8391 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
8392 ComAssertMsgRet (arc == NO_ERROR,
8393 ("Cannot create IPC mutex '%s', arc=%ld",
8394 ipcSem.raw(), arc),
8395 E_FAIL);
8396#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8397# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8398# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8399 /** @todo Check that this still works correctly. */
8400 AssertCompileSize(key_t, 8);
8401# else
8402 AssertCompileSize(key_t, 4);
8403# endif
8404 key_t key;
8405 mIPCSem = -1;
8406 mIPCKey = "0";
8407 for (uint32_t i = 0; i < 1 << 24; i++)
8408 {
8409 key = ((uint32_t)'V' << 24) | i;
8410 int sem = ::semget (key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8411 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8412 {
8413 mIPCSem = sem;
8414 if (sem >= 0)
8415 mIPCKey = BstrFmt ("%u", key);
8416 break;
8417 }
8418 }
8419# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8420 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8421 char *pszSemName = NULL;
8422 RTStrUtf8ToCurrentCP (&pszSemName, semName);
8423 key_t key = ::ftok (pszSemName, 'V');
8424 RTStrFree (pszSemName);
8425
8426 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8427# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8428
8429 int errnoSave = errno;
8430 if (mIPCSem < 0 && errnoSave == ENOSYS)
8431 {
8432 setError(E_FAIL,
8433 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8434 "support for SysV IPC. Check the host kernel configuration for "
8435 "CONFIG_SYSVIPC=y"));
8436 return E_FAIL;
8437 }
8438 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8439 * the IPC semaphores */
8440 if (mIPCSem < 0 && errnoSave == ENOSPC)
8441 {
8442#ifdef RT_OS_LINUX
8443 setError(E_FAIL,
8444 tr("Cannot create IPC semaphore because the system limit for the "
8445 "maximum number of semaphore sets (SEMMNI), or the system wide "
8446 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8447 "current set of SysV IPC semaphores can be determined from "
8448 "the file /proc/sysvipc/sem"));
8449#else
8450 setError(E_FAIL,
8451 tr("Cannot create IPC semaphore because the system-imposed limit "
8452 "on the maximum number of allowed semaphores or semaphore "
8453 "identifiers system-wide would be exceeded"));
8454#endif
8455 return E_FAIL;
8456 }
8457 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8458 E_FAIL);
8459 /* set the initial value to 1 */
8460 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
8461 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8462 E_FAIL);
8463#else
8464# error "Port me!"
8465#endif
8466
8467 /* memorize the peer Machine */
8468 unconst(mPeer) = aMachine;
8469 /* share the parent pointer */
8470 unconst(mParent) = aMachine->mParent;
8471
8472 /* take the pointers to data to share */
8473 mData.share (aMachine->mData);
8474 mSSData.share (aMachine->mSSData);
8475
8476 mUserData.share (aMachine->mUserData);
8477 mHWData.share (aMachine->mHWData);
8478 mMediaData.share(aMachine->mMediaData);
8479
8480 mStorageControllers.allocate();
8481 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
8482 it != aMachine->mStorageControllers->end();
8483 ++it)
8484 {
8485 ComObjPtr<StorageController> ctl;
8486 ctl.createObject();
8487 ctl->init(this, *it);
8488 mStorageControllers->push_back (ctl);
8489 }
8490
8491 unconst(mBIOSSettings).createObject();
8492 mBIOSSettings->init (this, aMachine->mBIOSSettings);
8493#ifdef VBOX_WITH_VRDP
8494 /* create another VRDPServer object that will be mutable */
8495 unconst(mVRDPServer).createObject();
8496 mVRDPServer->init (this, aMachine->mVRDPServer);
8497#endif
8498 /* create another audio adapter object that will be mutable */
8499 unconst(mAudioAdapter).createObject();
8500 mAudioAdapter->init (this, aMachine->mAudioAdapter);
8501 /* create a list of serial ports that will be mutable */
8502 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8503 {
8504 unconst(mSerialPorts [slot]).createObject();
8505 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
8506 }
8507 /* create a list of parallel ports that will be mutable */
8508 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8509 {
8510 unconst(mParallelPorts [slot]).createObject();
8511 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
8512 }
8513 /* create another USB controller object that will be mutable */
8514 unconst(mUSBController).createObject();
8515 mUSBController->init (this, aMachine->mUSBController);
8516
8517 /* create a list of network adapters that will be mutable */
8518 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8519 {
8520 unconst(mNetworkAdapters [slot]).createObject();
8521 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
8522 }
8523
8524 /* default is to delete saved state on Saved -> PoweredOff transition */
8525 mRemoveSavedState = true;
8526
8527 /* Confirm a successful initialization when it's the case */
8528 autoInitSpan.setSucceeded();
8529
8530 LogFlowThisFuncLeave();
8531 return S_OK;
8532}
8533
8534/**
8535 * Uninitializes this session object. If the reason is other than
8536 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
8537 *
8538 * @param aReason uninitialization reason
8539 *
8540 * @note Locks mParent + this object for writing.
8541 */
8542void SessionMachine::uninit (Uninit::Reason aReason)
8543{
8544 LogFlowThisFuncEnter();
8545 LogFlowThisFunc(("reason=%d\n", aReason));
8546
8547 /*
8548 * Strongly reference ourselves to prevent this object deletion after
8549 * mData->mSession.mMachine.setNull() below (which can release the last
8550 * reference and call the destructor). Important: this must be done before
8551 * accessing any members (and before AutoUninitSpan that does it as well).
8552 * This self reference will be released as the very last step on return.
8553 */
8554 ComObjPtr<SessionMachine> selfRef = this;
8555
8556 /* Enclose the state transition Ready->InUninit->NotReady */
8557 AutoUninitSpan autoUninitSpan(this);
8558 if (autoUninitSpan.uninitDone())
8559 {
8560 LogFlowThisFunc(("Already uninitialized\n"));
8561 LogFlowThisFuncLeave();
8562 return;
8563 }
8564
8565 if (autoUninitSpan.initFailed())
8566 {
8567 /* We've been called by init() because it's failed. It's not really
8568 * necessary (nor it's safe) to perform the regular uninit sequense
8569 * below, the following is enough.
8570 */
8571 LogFlowThisFunc(("Initialization failed.\n"));
8572#if defined(RT_OS_WINDOWS)
8573 if (mIPCSem)
8574 ::CloseHandle (mIPCSem);
8575 mIPCSem = NULL;
8576#elif defined(RT_OS_OS2)
8577 if (mIPCSem != NULLHANDLE)
8578 ::DosCloseMutexSem (mIPCSem);
8579 mIPCSem = NULLHANDLE;
8580#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8581 if (mIPCSem >= 0)
8582 ::semctl (mIPCSem, 0, IPC_RMID);
8583 mIPCSem = -1;
8584# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8585 mIPCKey = "0";
8586# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8587#else
8588# error "Port me!"
8589#endif
8590 uninitDataAndChildObjects();
8591 mData.free();
8592 unconst(mParent).setNull();
8593 unconst(mPeer).setNull();
8594 LogFlowThisFuncLeave();
8595 return;
8596 }
8597
8598 /* We need to lock this object in uninit() because the lock is shared
8599 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
8600 * and others need mParent lock. */
8601 AutoMultiWriteLock2 alock (mParent, this);
8602
8603#ifdef VBOX_WITH_RESOURCE_USAGE_API
8604 unregisterMetrics (mParent->performanceCollector(), mPeer);
8605#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8606
8607 MachineState_T lastState = mData->mMachineState;
8608 NOREF(lastState);
8609
8610 if (aReason == Uninit::Abnormal)
8611 {
8612 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
8613 Global::IsOnlineOrTransient (lastState)));
8614
8615 /* reset the state to Aborted */
8616 if (mData->mMachineState != MachineState_Aborted)
8617 setMachineState (MachineState_Aborted);
8618 }
8619
8620 if (isModified())
8621 {
8622 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
8623 rollback (false /* aNotify */);
8624 }
8625
8626 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
8627 if (!mSnapshotData.mStateFilePath.isEmpty())
8628 {
8629 LogWarningThisFunc(("canceling failed save state request!\n"));
8630 endSavingState (FALSE /* aSuccess */);
8631 }
8632 else if (!mSnapshotData.mSnapshot.isNull())
8633 {
8634 LogWarningThisFunc(("canceling untaken snapshot!\n"));
8635 endTakingSnapshot (FALSE /* aSuccess */);
8636 }
8637
8638#ifdef VBOX_WITH_USB
8639 /* release all captured USB devices */
8640 if (aReason == Uninit::Abnormal && Global::IsOnline (lastState))
8641 {
8642 /* Console::captureUSBDevices() is called in the VM process only after
8643 * setting the machine state to Starting or Restoring.
8644 * Console::detachAllUSBDevices() will be called upon successful
8645 * termination. So, we need to release USB devices only if there was
8646 * an abnormal termination of a running VM.
8647 *
8648 * This is identical to SessionMachine::DetachAllUSBDevices except
8649 * for the aAbnormal argument. */
8650 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8651 AssertComRC(rc);
8652 NOREF (rc);
8653
8654 USBProxyService *service = mParent->host()->usbProxyService();
8655 if (service)
8656 service->detachAllDevicesFromVM (this, true /* aDone */, true /* aAbnormal */);
8657 }
8658#endif /* VBOX_WITH_USB */
8659
8660 if (!mData->mSession.mType.isNull())
8661 {
8662 /* mType is not null when this machine's process has been started by
8663 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
8664 * need to queue the PID to reap the process (and avoid zombies on
8665 * Linux). */
8666 Assert (mData->mSession.mPid != NIL_RTPROCESS);
8667 mParent->addProcessToReap (mData->mSession.mPid);
8668 }
8669
8670 mData->mSession.mPid = NIL_RTPROCESS;
8671
8672 if (aReason == Uninit::Unexpected)
8673 {
8674 /* Uninitialization didn't come from #checkForDeath(), so tell the
8675 * client watcher thread to update the set of machines that have open
8676 * sessions. */
8677 mParent->updateClientWatcher();
8678 }
8679
8680 /* uninitialize all remote controls */
8681 if (mData->mSession.mRemoteControls.size())
8682 {
8683 LogFlowThisFunc(("Closing remote sessions (%d):\n",
8684 mData->mSession.mRemoteControls.size()));
8685
8686 Data::Session::RemoteControlList::iterator it =
8687 mData->mSession.mRemoteControls.begin();
8688 while (it != mData->mSession.mRemoteControls.end())
8689 {
8690 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
8691 HRESULT rc = (*it)->Uninitialize();
8692 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
8693 if (FAILED (rc))
8694 LogWarningThisFunc(("Forgot to close the remote session?\n"));
8695 ++it;
8696 }
8697 mData->mSession.mRemoteControls.clear();
8698 }
8699
8700 /*
8701 * An expected uninitialization can come only from #checkForDeath().
8702 * Otherwise it means that something's got really wrong (for examlple,
8703 * the Session implementation has released the VirtualBox reference
8704 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
8705 * etc). However, it's also possible, that the client releases the IPC
8706 * semaphore correctly (i.e. before it releases the VirtualBox reference),
8707 * but the VirtualBox release event comes first to the server process.
8708 * This case is practically possible, so we should not assert on an
8709 * unexpected uninit, just log a warning.
8710 */
8711
8712 if ((aReason == Uninit::Unexpected))
8713 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
8714
8715 if (aReason != Uninit::Normal)
8716 {
8717 mData->mSession.mDirectControl.setNull();
8718 }
8719 else
8720 {
8721 /* this must be null here (see #OnSessionEnd()) */
8722 Assert (mData->mSession.mDirectControl.isNull());
8723 Assert (mData->mSession.mState == SessionState_Closing);
8724 Assert (!mData->mSession.mProgress.isNull());
8725
8726 mData->mSession.mProgress->notifyComplete (S_OK);
8727 mData->mSession.mProgress.setNull();
8728 }
8729
8730 /* remove the association between the peer machine and this session machine */
8731 Assert (mData->mSession.mMachine == this ||
8732 aReason == Uninit::Unexpected);
8733
8734 /* reset the rest of session data */
8735 mData->mSession.mMachine.setNull();
8736 mData->mSession.mState = SessionState_Closed;
8737 mData->mSession.mType.setNull();
8738
8739 /* close the interprocess semaphore before leaving the exclusive lock */
8740#if defined(RT_OS_WINDOWS)
8741 if (mIPCSem)
8742 ::CloseHandle (mIPCSem);
8743 mIPCSem = NULL;
8744#elif defined(RT_OS_OS2)
8745 if (mIPCSem != NULLHANDLE)
8746 ::DosCloseMutexSem (mIPCSem);
8747 mIPCSem = NULLHANDLE;
8748#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8749 if (mIPCSem >= 0)
8750 ::semctl (mIPCSem, 0, IPC_RMID);
8751 mIPCSem = -1;
8752# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8753 mIPCKey = "0";
8754# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8755#else
8756# error "Port me!"
8757#endif
8758
8759 /* fire an event */
8760 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
8761
8762 uninitDataAndChildObjects();
8763
8764 /* free the essential data structure last */
8765 mData.free();
8766
8767 /* leave the exclusive lock before setting the below two to NULL */
8768 alock.leave();
8769
8770 unconst(mParent).setNull();
8771 unconst(mPeer).setNull();
8772
8773 LogFlowThisFuncLeave();
8774}
8775
8776// util::Lockable interface
8777////////////////////////////////////////////////////////////////////////////////
8778
8779/**
8780 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
8781 * with the primary Machine instance (mPeer).
8782 */
8783RWLockHandle *SessionMachine::lockHandle() const
8784{
8785 AssertReturn(!mPeer.isNull(), NULL);
8786 return mPeer->lockHandle();
8787}
8788
8789// IInternalMachineControl methods
8790////////////////////////////////////////////////////////////////////////////////
8791
8792/**
8793 * @note Locks this object for writing.
8794 */
8795STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
8796{
8797 AutoCaller autoCaller(this);
8798 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8799
8800 AutoWriteLock alock(this);
8801
8802 mRemoveSavedState = aRemove;
8803
8804 return S_OK;
8805}
8806
8807/**
8808 * @note Locks the same as #setMachineState() does.
8809 */
8810STDMETHODIMP SessionMachine::UpdateState (MachineState_T aMachineState)
8811{
8812 return setMachineState (aMachineState);
8813}
8814
8815/**
8816 * @note Locks this object for reading.
8817 */
8818STDMETHODIMP SessionMachine::GetIPCId (BSTR *aId)
8819{
8820 AutoCaller autoCaller(this);
8821 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8822
8823 AutoReadLock alock(this);
8824
8825#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8826 mIPCSemName.cloneTo(aId);
8827 return S_OK;
8828#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8829# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8830 mIPCKey.cloneTo(aId);
8831# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8832 mData->m_strConfigFileFull.cloneTo(aId);
8833# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8834 return S_OK;
8835#else
8836# error "Port me!"
8837#endif
8838}
8839
8840/**
8841 * Goes through the USB filters of the given machine to see if the given
8842 * device matches any filter or not.
8843 *
8844 * @note Locks the same as USBController::hasMatchingFilter() does.
8845 */
8846STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
8847 BOOL *aMatched,
8848 ULONG *aMaskedIfs)
8849{
8850 LogFlowThisFunc(("\n"));
8851
8852 CheckComArgNotNull (aUSBDevice);
8853 CheckComArgOutPointerValid(aMatched);
8854
8855 AutoCaller autoCaller(this);
8856 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8857
8858#ifdef VBOX_WITH_USB
8859 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice, aMaskedIfs);
8860#else
8861 NOREF(aUSBDevice);
8862 NOREF(aMaskedIfs);
8863 *aMatched = FALSE;
8864#endif
8865
8866 return S_OK;
8867}
8868
8869/**
8870 * @note Locks the same as Host::captureUSBDevice() does.
8871 */
8872STDMETHODIMP SessionMachine::CaptureUSBDevice (IN_BSTR aId)
8873{
8874 LogFlowThisFunc(("\n"));
8875
8876 AutoCaller autoCaller(this);
8877 AssertComRCReturnRC(autoCaller.rc());
8878
8879#ifdef VBOX_WITH_USB
8880 /* if captureDeviceForVM() fails, it must have set extended error info */
8881 MultiResult rc = mParent->host()->checkUSBProxyService();
8882 CheckComRCReturnRC(rc);
8883
8884 USBProxyService *service = mParent->host()->usbProxyService();
8885 AssertReturn(service, E_FAIL);
8886 return service->captureDeviceForVM (this, Guid(aId));
8887#else
8888 NOREF(aId);
8889 return E_NOTIMPL;
8890#endif
8891}
8892
8893/**
8894 * @note Locks the same as Host::detachUSBDevice() does.
8895 */
8896STDMETHODIMP SessionMachine::DetachUSBDevice (IN_BSTR aId, BOOL aDone)
8897{
8898 LogFlowThisFunc(("\n"));
8899
8900 AutoCaller autoCaller(this);
8901 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8902
8903#ifdef VBOX_WITH_USB
8904 USBProxyService *service = mParent->host()->usbProxyService();
8905 AssertReturn(service, E_FAIL);
8906 return service->detachDeviceFromVM (this, Guid(aId), !!aDone);
8907#else
8908 NOREF(aId);
8909 NOREF(aDone);
8910 return E_NOTIMPL;
8911#endif
8912}
8913
8914/**
8915 * Inserts all machine filters to the USB proxy service and then calls
8916 * Host::autoCaptureUSBDevices().
8917 *
8918 * Called by Console from the VM process upon VM startup.
8919 *
8920 * @note Locks what called methods lock.
8921 */
8922STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
8923{
8924 LogFlowThisFunc(("\n"));
8925
8926 AutoCaller autoCaller(this);
8927 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8928
8929#ifdef VBOX_WITH_USB
8930 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
8931 AssertComRC(rc);
8932 NOREF (rc);
8933
8934 USBProxyService *service = mParent->host()->usbProxyService();
8935 AssertReturn(service, E_FAIL);
8936 return service->autoCaptureDevicesForVM (this);
8937#else
8938 return S_OK;
8939#endif
8940}
8941
8942/**
8943 * Removes all machine filters from the USB proxy service and then calls
8944 * Host::detachAllUSBDevices().
8945 *
8946 * Called by Console from the VM process upon normal VM termination or by
8947 * SessionMachine::uninit() upon abnormal VM termination (from under the
8948 * Machine/SessionMachine lock).
8949 *
8950 * @note Locks what called methods lock.
8951 */
8952STDMETHODIMP SessionMachine::DetachAllUSBDevices (BOOL aDone)
8953{
8954 LogFlowThisFunc(("\n"));
8955
8956 AutoCaller autoCaller(this);
8957 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8958
8959#ifdef VBOX_WITH_USB
8960 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8961 AssertComRC(rc);
8962 NOREF (rc);
8963
8964 USBProxyService *service = mParent->host()->usbProxyService();
8965 AssertReturn(service, E_FAIL);
8966 return service->detachAllDevicesFromVM (this, !!aDone, false /* aAbnormal */);
8967#else
8968 NOREF(aDone);
8969 return S_OK;
8970#endif
8971}
8972
8973/**
8974 * @note Locks this object for writing.
8975 */
8976STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
8977 IProgress **aProgress)
8978{
8979 LogFlowThisFuncEnter();
8980
8981 AssertReturn(aSession, E_INVALIDARG);
8982 AssertReturn(aProgress, E_INVALIDARG);
8983
8984 AutoCaller autoCaller(this);
8985
8986 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
8987 /*
8988 * We don't assert below because it might happen that a non-direct session
8989 * informs us it is closed right after we've been uninitialized -- it's ok.
8990 */
8991 CheckComRCReturnRC(autoCaller.rc());
8992
8993 /* get IInternalSessionControl interface */
8994 ComPtr<IInternalSessionControl> control (aSession);
8995
8996 ComAssertRet (!control.isNull(), E_INVALIDARG);
8997
8998 /* Creating a Progress object requires the VirtualBox children lock, and
8999 * thus locking it here is required by the lock order rules. */
9000 AutoMultiWriteLock2 alock(mParent->childrenLock(), this->lockHandle());
9001
9002 if (control.equalsTo (mData->mSession.mDirectControl))
9003 {
9004 ComAssertRet (aProgress, E_POINTER);
9005
9006 /* The direct session is being normally closed by the client process
9007 * ----------------------------------------------------------------- */
9008
9009 /* go to the closing state (essential for all open*Session() calls and
9010 * for #checkForDeath()) */
9011 Assert (mData->mSession.mState == SessionState_Open);
9012 mData->mSession.mState = SessionState_Closing;
9013
9014 /* set direct control to NULL to release the remote instance */
9015 mData->mSession.mDirectControl.setNull();
9016 LogFlowThisFunc(("Direct control is set to NULL\n"));
9017
9018 /* Create the progress object the client will use to wait until
9019 * #checkForDeath() is called to uninitialize this session object after
9020 * it releases the IPC semaphore. */
9021 ComObjPtr<Progress> progress;
9022 progress.createObject();
9023 progress->init (mParent, static_cast <IMachine *> (mPeer),
9024 Bstr (tr ("Closing session")), FALSE /* aCancelable */);
9025 progress.queryInterfaceTo(aProgress);
9026 mData->mSession.mProgress = progress;
9027 }
9028 else
9029 {
9030 /* the remote session is being normally closed */
9031 Data::Session::RemoteControlList::iterator it =
9032 mData->mSession.mRemoteControls.begin();
9033 while (it != mData->mSession.mRemoteControls.end())
9034 {
9035 if (control.equalsTo (*it))
9036 break;
9037 ++it;
9038 }
9039 BOOL found = it != mData->mSession.mRemoteControls.end();
9040 ComAssertMsgRet (found, ("The session is not found in the session list!"),
9041 E_INVALIDARG);
9042 mData->mSession.mRemoteControls.remove (*it);
9043 }
9044
9045 LogFlowThisFuncLeave();
9046 return S_OK;
9047}
9048
9049/**
9050 * @note Locks this object for writing.
9051 */
9052STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
9053{
9054 LogFlowThisFuncEnter();
9055
9056 AssertReturn(aProgress, E_INVALIDARG);
9057 AssertReturn(aStateFilePath, E_POINTER);
9058
9059 AutoCaller autoCaller(this);
9060 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9061
9062 AutoWriteLock alock(this);
9063
9064 AssertReturn( mData->mMachineState == MachineState_Paused
9065 && mSnapshotData.mLastState == MachineState_Null
9066 && mSnapshotData.mProgressId.isEmpty()
9067 && mSnapshotData.mStateFilePath.isEmpty(),
9068 E_FAIL);
9069
9070 /* memorize the progress ID and add it to the global collection */
9071 Bstr progressId;
9072 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
9073 AssertComRCReturn (rc, rc);
9074 rc = mParent->addProgress (aProgress);
9075 AssertComRCReturn (rc, rc);
9076
9077 Bstr stateFilePath;
9078 /* stateFilePath is null when the machine is not running */
9079 if (mData->mMachineState == MachineState_Paused)
9080 {
9081 stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
9082 mUserData->mSnapshotFolderFull.raw(),
9083 RTPATH_DELIMITER, mData->mUuid.raw());
9084 }
9085
9086 /* fill in the snapshot data */
9087 mSnapshotData.mLastState = mData->mMachineState;
9088 mSnapshotData.mProgressId = Guid(progressId);
9089 mSnapshotData.mStateFilePath = stateFilePath;
9090
9091 /* set the state to Saving (this is expected by Console::SaveState()) */
9092 setMachineState (MachineState_Saving);
9093
9094 stateFilePath.cloneTo(aStateFilePath);
9095
9096 return S_OK;
9097}
9098
9099/**
9100 * @note Locks mParent + this object for writing.
9101 */
9102STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
9103{
9104 LogFlowThisFunc(("\n"));
9105
9106 AutoCaller autoCaller(this);
9107 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9108
9109 /* endSavingState() need mParent lock */
9110 AutoMultiWriteLock2 alock (mParent, this);
9111
9112 AssertReturn( mData->mMachineState == MachineState_Saving
9113 && mSnapshotData.mLastState != MachineState_Null
9114 && !mSnapshotData.mProgressId.isEmpty()
9115 && !mSnapshotData.mStateFilePath.isEmpty(),
9116 E_FAIL);
9117
9118 /*
9119 * on success, set the state to Saved;
9120 * on failure, set the state to the state we had when BeginSavingState() was
9121 * called (this is expected by Console::SaveState() and
9122 * Console::saveStateThread())
9123 */
9124 if (aSuccess)
9125 setMachineState (MachineState_Saved);
9126 else
9127 setMachineState (mSnapshotData.mLastState);
9128
9129 return endSavingState (aSuccess);
9130}
9131
9132/**
9133 * @note Locks this object for writing.
9134 */
9135STDMETHODIMP SessionMachine::AdoptSavedState (IN_BSTR aSavedStateFile)
9136{
9137 LogFlowThisFunc(("\n"));
9138
9139 AssertReturn(aSavedStateFile, E_INVALIDARG);
9140
9141 AutoCaller autoCaller(this);
9142 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9143
9144 AutoWriteLock alock(this);
9145
9146 AssertReturn( mData->mMachineState == MachineState_PoweredOff
9147 || mData->mMachineState == MachineState_Teleported
9148 || mData->mMachineState == MachineState_Aborted
9149 , E_FAIL); /** @todo setError. */
9150
9151 Utf8Str stateFilePathFull = aSavedStateFile;
9152 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9153 if (RT_FAILURE(vrc))
9154 return setError(VBOX_E_FILE_ERROR,
9155 tr("Invalid saved state file path '%ls' (%Rrc)"),
9156 aSavedStateFile,
9157 vrc);
9158
9159 mSSData->mStateFilePath = stateFilePathFull;
9160
9161 /* The below setMachineState() will detect the state transition and will
9162 * update the settings file */
9163
9164 return setMachineState (MachineState_Saved);
9165}
9166
9167STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
9168 ComSafeArrayOut(BSTR, aValues),
9169 ComSafeArrayOut(ULONG64, aTimestamps),
9170 ComSafeArrayOut(BSTR, aFlags))
9171{
9172 LogFlowThisFunc(("\n"));
9173
9174#ifdef VBOX_WITH_GUEST_PROPS
9175 using namespace guestProp;
9176
9177 AutoCaller autoCaller(this);
9178 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9179
9180 AutoReadLock alock(this);
9181
9182 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
9183 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
9184 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
9185 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
9186
9187 size_t cEntries = mHWData->mGuestProperties.size();
9188 com::SafeArray<BSTR> names (cEntries);
9189 com::SafeArray<BSTR> values (cEntries);
9190 com::SafeArray<ULONG64> timestamps (cEntries);
9191 com::SafeArray<BSTR> flags (cEntries);
9192 unsigned i = 0;
9193 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
9194 it != mHWData->mGuestProperties.end();
9195 ++it)
9196 {
9197 char szFlags[MAX_FLAGS_LEN + 1];
9198 it->strName.cloneTo(&names[i]);
9199 it->strValue.cloneTo(&values[i]);
9200 timestamps[i] = it->mTimestamp;
9201 /* If it is NULL, keep it NULL. */
9202 if (it->mFlags)
9203 {
9204 writeFlags(it->mFlags, szFlags);
9205 Bstr(szFlags).cloneTo(&flags[i]);
9206 }
9207 else
9208 flags[i] = NULL;
9209 ++i;
9210 }
9211 names.detachTo(ComSafeArrayOutArg(aNames));
9212 values.detachTo(ComSafeArrayOutArg(aValues));
9213 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
9214 flags.detachTo(ComSafeArrayOutArg(aFlags));
9215 mHWData->mPropertyServiceActive = true;
9216 return S_OK;
9217#else
9218 ReturnComNotImplemented();
9219#endif
9220}
9221
9222STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn(IN_BSTR, aNames),
9223 ComSafeArrayIn(IN_BSTR, aValues),
9224 ComSafeArrayIn(ULONG64, aTimestamps),
9225 ComSafeArrayIn(IN_BSTR, aFlags))
9226{
9227 LogFlowThisFunc(("\n"));
9228
9229#ifdef VBOX_WITH_GUEST_PROPS
9230 using namespace guestProp;
9231
9232 AssertReturn(!ComSafeArrayInIsNull(aNames), E_POINTER);
9233 AssertReturn(!ComSafeArrayInIsNull(aValues), E_POINTER);
9234 AssertReturn(!ComSafeArrayInIsNull(aTimestamps), E_POINTER);
9235 AssertReturn(!ComSafeArrayInIsNull(aFlags), E_POINTER);
9236
9237 AutoCaller autoCaller(this);
9238 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9239
9240 AutoWriteLock alock(this);
9241
9242 /*
9243 * Temporarily reset the registered flag, so that our machine state
9244 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in all
9245 * setters will return FALSE for a Machine instance if mRegistered is TRUE).
9246 *
9247 * This is copied from registeredInit(), and may or may not be the right
9248 * way to handle this.
9249 */
9250 Assert(mData->mRegistered);
9251 mData->mRegistered = FALSE;
9252
9253 HRESULT rc = checkStateDependency(MutableStateDep);
9254 AssertLogRelMsgReturn(SUCCEEDED(rc), ("%Rhrc\n", rc), rc);
9255
9256 com::SafeArray<IN_BSTR> names( ComSafeArrayInArg(aNames));
9257 com::SafeArray<IN_BSTR> values( ComSafeArrayInArg(aValues));
9258 com::SafeArray<ULONG64> timestamps(ComSafeArrayInArg(aTimestamps));
9259 com::SafeArray<IN_BSTR> flags( ComSafeArrayInArg(aFlags));
9260
9261 DiscardSettings();
9262 mHWData.backup();
9263
9264 mHWData->mGuestProperties.erase(mHWData->mGuestProperties.begin(),
9265 mHWData->mGuestProperties.end());
9266 for (unsigned i = 0; i < names.size(); ++i)
9267 {
9268 uint32_t fFlags = NILFLAG;
9269 validateFlags(Utf8Str(flags[i]).raw(), &fFlags);
9270 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
9271 mHWData->mGuestProperties.push_back(property);
9272 }
9273
9274 mHWData->mPropertyServiceActive = false;
9275
9276 alock.unlock();
9277 SaveSettings();
9278
9279 /* Restore the mRegistered flag. */
9280 alock.lock();
9281 mData->mRegistered = TRUE;
9282
9283 return S_OK;
9284#else
9285 ReturnComNotImplemented();
9286#endif
9287}
9288
9289STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
9290 IN_BSTR aValue,
9291 ULONG64 aTimestamp,
9292 IN_BSTR aFlags)
9293{
9294 LogFlowThisFunc(("\n"));
9295
9296#ifdef VBOX_WITH_GUEST_PROPS
9297 using namespace guestProp;
9298
9299 CheckComArgNotNull(aName);
9300 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
9301 return E_POINTER; /* aValue can be NULL to indicate deletion */
9302
9303 try
9304 {
9305 /*
9306 * Convert input up front.
9307 */
9308 Utf8Str utf8Name(aName);
9309 uint32_t fFlags = NILFLAG;
9310 if (aFlags)
9311 {
9312 Utf8Str utf8Flags(aFlags);
9313 int vrc = validateFlags(utf8Flags.raw(), &fFlags);
9314 AssertRCReturn(vrc, E_INVALIDARG);
9315 }
9316
9317 /*
9318 * Now grab the object lock, validate the state and do the update.
9319 */
9320 AutoCaller autoCaller(this);
9321 CheckComRCReturnRC(autoCaller.rc());
9322
9323 AutoWriteLock alock(this);
9324
9325 AssertReturn(mHWData->mPropertyServiceActive, VBOX_E_INVALID_OBJECT_STATE);
9326 switch (mData->mMachineState)
9327 {
9328 case MachineState_Paused:
9329 case MachineState_Running:
9330 case MachineState_Teleporting:
9331 case MachineState_TeleportingPausedVM:
9332 case MachineState_LiveSnapshotting:
9333 case MachineState_Saving:
9334 break;
9335
9336 default:
9337 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
9338 VBOX_E_INVALID_VM_STATE);
9339 }
9340
9341 mHWData.backup();
9342
9343 /** @todo r=bird: The careful memory handling doesn't work out here because
9344 * the catch block won't undo any damange we've done. So, if push_back throws
9345 * bad_alloc then you've lost the value.
9346 *
9347 * Another thing. Doing a linear search here isn't extremely efficient, esp.
9348 * since values that changes actually bubbles to the end of the list. Using
9349 * something that has an efficient lookup and can tollerate a bit of updates
9350 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
9351 * combination of RTStrCache (for sharing names and getting uniqueness into
9352 * the bargain) and hash/tree is another. */
9353 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9354 iter != mHWData->mGuestProperties.end();
9355 ++iter)
9356 if (utf8Name == iter->strName)
9357 {
9358 mHWData->mGuestProperties.erase(iter);
9359 break;
9360 }
9361 if (aValue != NULL)
9362 {
9363 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9364 mHWData->mGuestProperties.push_back(property);
9365 }
9366
9367 /*
9368 * Send a callback notification if appropriate
9369 */
9370 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
9371 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
9372 RTSTR_MAX,
9373 utf8Name.raw(),
9374 RTSTR_MAX, NULL)
9375 )
9376 {
9377 alock.leave();
9378
9379 mParent->onGuestPropertyChange(mData->mUuid,
9380 aName,
9381 aValue,
9382 aFlags);
9383 }
9384 }
9385 catch (...)
9386 {
9387 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
9388 }
9389 return S_OK;
9390#else
9391 ReturnComNotImplemented();
9392#endif
9393}
9394
9395// public methods only for internal purposes
9396/////////////////////////////////////////////////////////////////////////////
9397
9398/**
9399 * Called from the client watcher thread to check for expected or unexpected
9400 * death of the client process that has a direct session to this machine.
9401 *
9402 * On Win32 and on OS/2, this method is called only when we've got the
9403 * mutex (i.e. the client has either died or terminated normally) so it always
9404 * returns @c true (the client is terminated, the session machine is
9405 * uninitialized).
9406 *
9407 * On other platforms, the method returns @c true if the client process has
9408 * terminated normally or abnormally and the session machine was uninitialized,
9409 * and @c false if the client process is still alive.
9410 *
9411 * @note Locks this object for writing.
9412 */
9413bool SessionMachine::checkForDeath()
9414{
9415 Uninit::Reason reason;
9416 bool terminated = false;
9417
9418 /* Enclose autoCaller with a block because calling uninit() from under it
9419 * will deadlock. */
9420 {
9421 AutoCaller autoCaller(this);
9422 if (!autoCaller.isOk())
9423 {
9424 /* return true if not ready, to cause the client watcher to exclude
9425 * the corresponding session from watching */
9426 LogFlowThisFunc(("Already uninitialized!\n"));
9427 return true;
9428 }
9429
9430 AutoWriteLock alock(this);
9431
9432 /* Determine the reason of death: if the session state is Closing here,
9433 * everything is fine. Otherwise it means that the client did not call
9434 * OnSessionEnd() before it released the IPC semaphore. This may happen
9435 * either because the client process has abnormally terminated, or
9436 * because it simply forgot to call ISession::Close() before exiting. We
9437 * threat the latter also as an abnormal termination (see
9438 * Session::uninit() for details). */
9439 reason = mData->mSession.mState == SessionState_Closing ?
9440 Uninit::Normal :
9441 Uninit::Abnormal;
9442
9443#if defined(RT_OS_WINDOWS)
9444
9445 AssertMsg (mIPCSem, ("semaphore must be created"));
9446
9447 /* release the IPC mutex */
9448 ::ReleaseMutex (mIPCSem);
9449
9450 terminated = true;
9451
9452#elif defined(RT_OS_OS2)
9453
9454 AssertMsg (mIPCSem, ("semaphore must be created"));
9455
9456 /* release the IPC mutex */
9457 ::DosReleaseMutexSem (mIPCSem);
9458
9459 terminated = true;
9460
9461#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9462
9463 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9464
9465 int val = ::semctl (mIPCSem, 0, GETVAL);
9466 if (val > 0)
9467 {
9468 /* the semaphore is signaled, meaning the session is terminated */
9469 terminated = true;
9470 }
9471
9472#else
9473# error "Port me!"
9474#endif
9475
9476 } /* AutoCaller block */
9477
9478 if (terminated)
9479 uninit (reason);
9480
9481 return terminated;
9482}
9483
9484/**
9485 * @note Locks this object for reading.
9486 */
9487HRESULT SessionMachine::onNetworkAdapterChange (INetworkAdapter *networkAdapter, BOOL changeAdapter)
9488{
9489 LogFlowThisFunc(("\n"));
9490
9491 AutoCaller autoCaller(this);
9492 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9493
9494 ComPtr<IInternalSessionControl> directControl;
9495 {
9496 AutoReadLock alock(this);
9497 directControl = mData->mSession.mDirectControl;
9498 }
9499
9500 /* ignore notifications sent after #OnSessionEnd() is called */
9501 if (!directControl)
9502 return S_OK;
9503
9504 return directControl->OnNetworkAdapterChange (networkAdapter, changeAdapter);
9505}
9506
9507/**
9508 * @note Locks this object for reading.
9509 */
9510HRESULT SessionMachine::onSerialPortChange (ISerialPort *serialPort)
9511{
9512 LogFlowThisFunc(("\n"));
9513
9514 AutoCaller autoCaller(this);
9515 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9516
9517 ComPtr<IInternalSessionControl> directControl;
9518 {
9519 AutoReadLock alock(this);
9520 directControl = mData->mSession.mDirectControl;
9521 }
9522
9523 /* ignore notifications sent after #OnSessionEnd() is called */
9524 if (!directControl)
9525 return S_OK;
9526
9527 return directControl->OnSerialPortChange (serialPort);
9528}
9529
9530/**
9531 * @note Locks this object for reading.
9532 */
9533HRESULT SessionMachine::onParallelPortChange (IParallelPort *parallelPort)
9534{
9535 LogFlowThisFunc(("\n"));
9536
9537 AutoCaller autoCaller(this);
9538 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9539
9540 ComPtr<IInternalSessionControl> directControl;
9541 {
9542 AutoReadLock alock(this);
9543 directControl = mData->mSession.mDirectControl;
9544 }
9545
9546 /* ignore notifications sent after #OnSessionEnd() is called */
9547 if (!directControl)
9548 return S_OK;
9549
9550 return directControl->OnParallelPortChange (parallelPort);
9551}
9552
9553/**
9554 * @note Locks this object for reading.
9555 */
9556HRESULT SessionMachine::onStorageControllerChange ()
9557{
9558 LogFlowThisFunc(("\n"));
9559
9560 AutoCaller autoCaller(this);
9561 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9562
9563 ComPtr<IInternalSessionControl> directControl;
9564 {
9565 AutoReadLock alock(this);
9566 directControl = mData->mSession.mDirectControl;
9567 }
9568
9569 /* ignore notifications sent after #OnSessionEnd() is called */
9570 if (!directControl)
9571 return S_OK;
9572
9573 return directControl->OnStorageControllerChange ();
9574}
9575
9576/**
9577 * @note Locks this object for reading.
9578 */
9579HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
9580{
9581 LogFlowThisFunc(("\n"));
9582
9583 AutoCaller autoCaller(this);
9584 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9585
9586 ComPtr<IInternalSessionControl> directControl;
9587 {
9588 AutoReadLock alock(this);
9589 directControl = mData->mSession.mDirectControl;
9590 }
9591
9592 /* ignore notifications sent after #OnSessionEnd() is called */
9593 if (!directControl)
9594 return S_OK;
9595
9596 return directControl->OnMediumChange(aAttachment, aForce);
9597}
9598
9599/**
9600 * @note Locks this object for reading.
9601 */
9602HRESULT SessionMachine::onVRDPServerChange()
9603{
9604 LogFlowThisFunc(("\n"));
9605
9606 AutoCaller autoCaller(this);
9607 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9608
9609 ComPtr<IInternalSessionControl> directControl;
9610 {
9611 AutoReadLock alock(this);
9612 directControl = mData->mSession.mDirectControl;
9613 }
9614
9615 /* ignore notifications sent after #OnSessionEnd() is called */
9616 if (!directControl)
9617 return S_OK;
9618
9619 return directControl->OnVRDPServerChange();
9620}
9621
9622/**
9623 * @note Locks this object for reading.
9624 */
9625HRESULT SessionMachine::onUSBControllerChange()
9626{
9627 LogFlowThisFunc(("\n"));
9628
9629 AutoCaller autoCaller(this);
9630 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9631
9632 ComPtr<IInternalSessionControl> directControl;
9633 {
9634 AutoReadLock alock(this);
9635 directControl = mData->mSession.mDirectControl;
9636 }
9637
9638 /* ignore notifications sent after #OnSessionEnd() is called */
9639 if (!directControl)
9640 return S_OK;
9641
9642 return directControl->OnUSBControllerChange();
9643}
9644
9645/**
9646 * @note Locks this object for reading.
9647 */
9648HRESULT SessionMachine::onSharedFolderChange()
9649{
9650 LogFlowThisFunc(("\n"));
9651
9652 AutoCaller autoCaller(this);
9653 AssertComRCReturnRC(autoCaller.rc());
9654
9655 ComPtr<IInternalSessionControl> directControl;
9656 {
9657 AutoReadLock alock(this);
9658 directControl = mData->mSession.mDirectControl;
9659 }
9660
9661 /* ignore notifications sent after #OnSessionEnd() is called */
9662 if (!directControl)
9663 return S_OK;
9664
9665 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
9666}
9667
9668/**
9669 * Returns @c true if this machine's USB controller reports it has a matching
9670 * filter for the given USB device and @c false otherwise.
9671 *
9672 * @note Locks this object for reading.
9673 */
9674bool SessionMachine::hasMatchingUSBFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
9675{
9676 AutoCaller autoCaller(this);
9677 /* silently return if not ready -- this method may be called after the
9678 * direct machine session has been called */
9679 if (!autoCaller.isOk())
9680 return false;
9681
9682 AutoReadLock alock(this);
9683
9684#ifdef VBOX_WITH_USB
9685 switch (mData->mMachineState)
9686 {
9687 case MachineState_Starting:
9688 case MachineState_Restoring:
9689 case MachineState_TeleportingIn:
9690 case MachineState_Paused:
9691 case MachineState_Running:
9692 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
9693 * elsewhere... */
9694 return mUSBController->hasMatchingFilter (aDevice, aMaskedIfs);
9695 default: break;
9696 }
9697#else
9698 NOREF(aDevice);
9699 NOREF(aMaskedIfs);
9700#endif
9701 return false;
9702}
9703
9704/**
9705 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9706 */
9707HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
9708 IVirtualBoxErrorInfo *aError,
9709 ULONG aMaskedIfs)
9710{
9711 LogFlowThisFunc(("\n"));
9712
9713 AutoCaller autoCaller(this);
9714
9715 /* This notification may happen after the machine object has been
9716 * uninitialized (the session was closed), so don't assert. */
9717 CheckComRCReturnRC(autoCaller.rc());
9718
9719 ComPtr<IInternalSessionControl> directControl;
9720 {
9721 AutoReadLock alock(this);
9722 directControl = mData->mSession.mDirectControl;
9723 }
9724
9725 /* fail on notifications sent after #OnSessionEnd() is called, it is
9726 * expected by the caller */
9727 if (!directControl)
9728 return E_FAIL;
9729
9730 /* No locks should be held at this point. */
9731 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9732 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9733
9734 return directControl->OnUSBDeviceAttach (aDevice, aError, aMaskedIfs);
9735}
9736
9737/**
9738 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9739 */
9740HRESULT SessionMachine::onUSBDeviceDetach (IN_BSTR aId,
9741 IVirtualBoxErrorInfo *aError)
9742{
9743 LogFlowThisFunc(("\n"));
9744
9745 AutoCaller autoCaller(this);
9746
9747 /* This notification may happen after the machine object has been
9748 * uninitialized (the session was closed), so don't assert. */
9749 CheckComRCReturnRC(autoCaller.rc());
9750
9751 ComPtr<IInternalSessionControl> directControl;
9752 {
9753 AutoReadLock alock(this);
9754 directControl = mData->mSession.mDirectControl;
9755 }
9756
9757 /* fail on notifications sent after #OnSessionEnd() is called, it is
9758 * expected by the caller */
9759 if (!directControl)
9760 return E_FAIL;
9761
9762 /* No locks should be held at this point. */
9763 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9764 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9765
9766 return directControl->OnUSBDeviceDetach (aId, aError);
9767}
9768
9769// protected methods
9770/////////////////////////////////////////////////////////////////////////////
9771
9772/**
9773 * Helper method to finalize saving the state.
9774 *
9775 * @note Must be called from under this object's lock.
9776 *
9777 * @param aSuccess TRUE if the snapshot has been taken successfully
9778 *
9779 * @note Locks mParent + this objects for writing.
9780 */
9781HRESULT SessionMachine::endSavingState (BOOL aSuccess)
9782{
9783 LogFlowThisFuncEnter();
9784
9785 AutoCaller autoCaller(this);
9786 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9787
9788 /* saveSettings() needs mParent lock */
9789 AutoMultiWriteLock2 alock (mParent, this);
9790
9791 HRESULT rc = S_OK;
9792
9793 if (aSuccess)
9794 {
9795 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
9796
9797 /* save all VM settings */
9798 rc = saveSettings();
9799 }
9800 else
9801 {
9802 /* delete the saved state file (it might have been already created) */
9803 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
9804 }
9805
9806 /* remove the completed progress object */
9807 mParent->removeProgress(mSnapshotData.mProgressId);
9808
9809 /* clear out the temporary saved state data */
9810 mSnapshotData.mLastState = MachineState_Null;
9811 mSnapshotData.mProgressId.clear();
9812 mSnapshotData.mStateFilePath.setNull();
9813
9814 LogFlowThisFuncLeave();
9815 return rc;
9816}
9817
9818/**
9819 * Locks the attached media.
9820 *
9821 * All attached hard disks are locked for writing and DVD/floppy are locked for
9822 * reading. Parents of attached hard disks (if any) are locked for reading.
9823 *
9824 * This method also performs accessibility check of all media it locks: if some
9825 * media is inaccessible, the method will return a failure and a bunch of
9826 * extended error info objects per each inaccessible medium.
9827 *
9828 * Note that this method is atomic: if it returns a success, all media are
9829 * locked as described above; on failure no media is locked at all (all
9830 * succeeded individual locks will be undone).
9831 *
9832 * This method is intended to be called when the machine is in Starting or
9833 * Restoring state and asserts otherwise.
9834 *
9835 * The locks made by this method must be undone by calling #unlockMedia() when
9836 * no more needed.
9837 */
9838HRESULT SessionMachine::lockMedia()
9839{
9840 AutoCaller autoCaller(this);
9841 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9842
9843 AutoWriteLock alock(this);
9844
9845 AssertReturn( mData->mMachineState == MachineState_Starting
9846 || mData->mMachineState == MachineState_Restoring
9847 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
9848
9849 typedef std::list <ComPtr<IMedium> > MediaList;
9850
9851 try
9852 {
9853 HRESULT rc = S_OK;
9854
9855 ErrorInfoKeeper eik(true /* aIsNull */);
9856 MultiResult mrc(S_OK);
9857
9858 /* Lock all medium objects attached to the VM.
9859 * Get status for inaccessible media as well. */
9860 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9861 it != mMediaData->mAttachments.end();
9862 ++it)
9863 {
9864 DeviceType_T devType = (*it)->type();
9865 ComObjPtr<Medium> medium = (*it)->medium();
9866
9867 bool first = true;
9868
9869 /** @todo split out the media locking, and put it into
9870 * MediumImpl.cpp, as it needs this functionality too. */
9871 while (!medium.isNull())
9872 {
9873 MediumState_T mediumState = medium->state();
9874
9875 /* accessibility check must be first, otherwise locking
9876 * interferes with getting the medium state. */
9877 if (mediumState == MediumState_Inaccessible)
9878 {
9879 rc = medium->RefreshState(&mediumState);
9880 CheckComRCThrowRC(rc);
9881
9882 if (mediumState == MediumState_Inaccessible)
9883 {
9884 Bstr error;
9885 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
9886 CheckComRCThrowRC(rc);
9887
9888 Bstr loc;
9889 rc = medium->COMGETTER(Location)(loc.asOutParam());
9890 CheckComRCThrowRC(rc);
9891
9892 /* collect multiple errors */
9893 eik.restore();
9894
9895 /* be in sync with MediumBase::setStateError() */
9896 Assert(!error.isEmpty());
9897 mrc = setError(E_FAIL,
9898 tr("Medium '%ls' is not accessible. %ls"),
9899 loc.raw(),
9900 error.raw());
9901
9902 eik.fetch();
9903 }
9904 }
9905
9906 if (first)
9907 {
9908 if (devType != DeviceType_DVD)
9909 {
9910 /* HardDisk and Floppy medium must be locked for writing */
9911 rc = medium->LockWrite(NULL);
9912 CheckComRCThrowRC(rc);
9913 }
9914 else
9915 {
9916 /* DVD medium must be locked for reading */
9917 rc = medium->LockRead(NULL);
9918 CheckComRCThrowRC(rc);
9919 }
9920
9921 mData->mSession.mLockedMedia.push_back(
9922 Data::Session::LockedMedia::value_type(
9923 ComPtr<IMedium>(medium), true));
9924
9925 first = false;
9926 }
9927 else
9928 {
9929 rc = medium->LockRead(NULL);
9930 CheckComRCThrowRC(rc);
9931
9932 mData->mSession.mLockedMedia.push_back(
9933 Data::Session::LockedMedia::value_type(
9934 ComPtr<IMedium>(medium), false));
9935 }
9936
9937
9938 /* no locks or callers here since there should be no way to
9939 * change the hard disk parent at this point (as it is still
9940 * attached to the machine) */
9941 medium = medium->parent();
9942 }
9943 }
9944
9945 eik.restore();
9946 CheckComRCThrowRC((HRESULT)mrc);
9947 }
9948 catch (HRESULT aRC)
9949 {
9950 /* Unlock all locked media on failure */
9951 unlockMedia();
9952 return aRC;
9953 }
9954
9955 return S_OK;
9956}
9957
9958/**
9959 * Undoes the locks made by by #lockMedia().
9960 */
9961void SessionMachine::unlockMedia()
9962{
9963 AutoCaller autoCaller(this);
9964 AssertComRCReturnVoid (autoCaller.rc());
9965
9966 AutoWriteLock alock(this);
9967
9968 /* we may be holding important error info on the current thread;
9969 * preserve it */
9970 ErrorInfoKeeper eik;
9971
9972 HRESULT rc = S_OK;
9973
9974 for (Data::Session::LockedMedia::const_iterator
9975 it = mData->mSession.mLockedMedia.begin();
9976 it != mData->mSession.mLockedMedia.end(); ++it)
9977 {
9978 MediumState_T state;
9979 if (it->second)
9980 rc = it->first->UnlockWrite (&state);
9981 else
9982 rc = it->first->UnlockRead (&state);
9983
9984 /* The second can happen if an object was re-locked in
9985 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
9986 * image was unmounted at runtime. */
9987 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
9988 }
9989
9990 mData->mSession.mLockedMedia.clear();
9991}
9992
9993/**
9994 * Helper to change the machine state (reimplementation).
9995 *
9996 * @note Locks this object for writing.
9997 */
9998HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
9999{
10000 LogFlowThisFuncEnter();
10001 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
10002
10003 AutoCaller autoCaller(this);
10004 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10005
10006 AutoWriteLock alock(this);
10007
10008 MachineState_T oldMachineState = mData->mMachineState;
10009
10010 AssertMsgReturn (oldMachineState != aMachineState,
10011 ("oldMachineState=%d, aMachineState=%d\n",
10012 oldMachineState, aMachineState), E_FAIL);
10013
10014 HRESULT rc = S_OK;
10015
10016 int stsFlags = 0;
10017 bool deleteSavedState = false;
10018
10019 /* detect some state transitions */
10020
10021 if ( ( oldMachineState == MachineState_Saved
10022 && aMachineState == MachineState_Restoring)
10023 || ( ( oldMachineState == MachineState_PoweredOff
10024 || oldMachineState == MachineState_Teleported
10025 || oldMachineState == MachineState_Aborted
10026 )
10027 && ( aMachineState == MachineState_TeleportingIn
10028 || aMachineState == MachineState_Starting
10029 )
10030 )
10031 )
10032 {
10033 /* The EMT thread is about to start */
10034
10035 /* Nothing to do here for now... */
10036
10037 /// @todo NEWMEDIA don't let mDVDDrive and other children
10038 /// change anything when in the Starting/Restoring state
10039 }
10040 else if ( ( oldMachineState == MachineState_Running
10041 || oldMachineState == MachineState_Paused
10042 || oldMachineState == MachineState_Teleporting
10043 || oldMachineState == MachineState_LiveSnapshotting
10044 || oldMachineState == MachineState_Stuck
10045 || oldMachineState == MachineState_Starting
10046 || oldMachineState == MachineState_Stopping
10047 || oldMachineState == MachineState_Saving
10048 || oldMachineState == MachineState_Restoring
10049 || oldMachineState == MachineState_TeleportingPausedVM
10050 || oldMachineState == MachineState_TeleportingIn
10051 )
10052 && ( aMachineState == MachineState_PoweredOff
10053 || aMachineState == MachineState_Saved
10054 || aMachineState == MachineState_Teleported
10055 || aMachineState == MachineState_Aborted
10056 )
10057 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10058 * snapshot */
10059 && ( mSnapshotData.mSnapshot.isNull()
10060 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
10061 )
10062 )
10063 {
10064 /* The EMT thread has just stopped, unlock attached media. Note that as
10065 * opposed to locking that is done from Console, we do unlocking here
10066 * because the VM process may have aborted before having a chance to
10067 * properly unlock all media it locked. */
10068
10069 unlockMedia();
10070 }
10071
10072 if (oldMachineState == MachineState_Restoring)
10073 {
10074 if (aMachineState != MachineState_Saved)
10075 {
10076 /*
10077 * delete the saved state file once the machine has finished
10078 * restoring from it (note that Console sets the state from
10079 * Restoring to Saved if the VM couldn't restore successfully,
10080 * to give the user an ability to fix an error and retry --
10081 * we keep the saved state file in this case)
10082 */
10083 deleteSavedState = true;
10084 }
10085 }
10086 else if ( oldMachineState == MachineState_Saved
10087 && ( aMachineState == MachineState_PoweredOff
10088 || aMachineState == MachineState_Aborted
10089 || aMachineState == MachineState_Teleported
10090 )
10091 )
10092 {
10093 /*
10094 * delete the saved state after Console::DiscardSavedState() is called
10095 * or if the VM process (owning a direct VM session) crashed while the
10096 * VM was Saved
10097 */
10098
10099 /// @todo (dmik)
10100 // Not sure that deleting the saved state file just because of the
10101 // client death before it attempted to restore the VM is a good
10102 // thing. But when it crashes we need to go to the Aborted state
10103 // which cannot have the saved state file associated... The only
10104 // way to fix this is to make the Aborted condition not a VM state
10105 // but a bool flag: i.e., when a crash occurs, set it to true and
10106 // change the state to PoweredOff or Saved depending on the
10107 // saved state presence.
10108
10109 deleteSavedState = true;
10110 mData->mCurrentStateModified = TRUE;
10111 stsFlags |= SaveSTS_CurStateModified;
10112 }
10113
10114 if ( aMachineState == MachineState_Starting
10115 || aMachineState == MachineState_Restoring
10116 || aMachineState == MachineState_TeleportingIn
10117 )
10118 {
10119 /* set the current state modified flag to indicate that the current
10120 * state is no more identical to the state in the
10121 * current snapshot */
10122 if (!mData->mCurrentSnapshot.isNull())
10123 {
10124 mData->mCurrentStateModified = TRUE;
10125 stsFlags |= SaveSTS_CurStateModified;
10126 }
10127 }
10128
10129 if (deleteSavedState)
10130 {
10131 if (mRemoveSavedState)
10132 {
10133 Assert(!mSSData->mStateFilePath.isEmpty());
10134 RTFileDelete(mSSData->mStateFilePath.c_str());
10135 }
10136 mSSData->mStateFilePath.setNull();
10137 stsFlags |= SaveSTS_StateFilePath;
10138 }
10139
10140 /* redirect to the underlying peer machine */
10141 mPeer->setMachineState (aMachineState);
10142
10143 if ( aMachineState == MachineState_PoweredOff
10144 || aMachineState == MachineState_Teleported
10145 || aMachineState == MachineState_Aborted
10146 || aMachineState == MachineState_Saved)
10147 {
10148 /* the machine has stopped execution
10149 * (or the saved state file was adopted) */
10150 stsFlags |= SaveSTS_StateTimeStamp;
10151 }
10152
10153 if ( ( oldMachineState == MachineState_PoweredOff
10154 || oldMachineState == MachineState_Aborted
10155 || oldMachineState == MachineState_Teleported
10156 )
10157 && aMachineState == MachineState_Saved)
10158 {
10159 /* the saved state file was adopted */
10160 Assert(!mSSData->mStateFilePath.isEmpty());
10161 stsFlags |= SaveSTS_StateFilePath;
10162 }
10163
10164 rc = saveStateSettings (stsFlags);
10165
10166 if ( ( oldMachineState != MachineState_PoweredOff
10167 && oldMachineState != MachineState_Aborted
10168 && oldMachineState != MachineState_Teleported
10169 )
10170 && ( aMachineState == MachineState_PoweredOff
10171 || aMachineState == MachineState_Aborted
10172 || aMachineState == MachineState_Teleported
10173 )
10174 )
10175 {
10176 /* we've been shut down for any reason */
10177 /* no special action so far */
10178 }
10179
10180 LogFlowThisFunc(("rc=%08X\n", rc));
10181 LogFlowThisFuncLeave();
10182 return rc;
10183}
10184
10185/**
10186 * Sends the current machine state value to the VM process.
10187 *
10188 * @note Locks this object for reading, then calls a client process.
10189 */
10190HRESULT SessionMachine::updateMachineStateOnClient()
10191{
10192 AutoCaller autoCaller(this);
10193 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10194
10195 ComPtr<IInternalSessionControl> directControl;
10196 {
10197 AutoReadLock alock(this);
10198 AssertReturn(!!mData, E_FAIL);
10199 directControl = mData->mSession.mDirectControl;
10200
10201 /* directControl may be already set to NULL here in #OnSessionEnd()
10202 * called too early by the direct session process while there is still
10203 * some operation (like discarding the snapshot) in progress. The client
10204 * process in this case is waiting inside Session::close() for the
10205 * "end session" process object to complete, while #uninit() called by
10206 * #checkForDeath() on the Watcher thread is waiting for the pending
10207 * operation to complete. For now, we accept this inconsitent behavior
10208 * and simply do nothing here. */
10209
10210 if (mData->mSession.mState == SessionState_Closing)
10211 return S_OK;
10212
10213 AssertReturn(!directControl.isNull(), E_FAIL);
10214 }
10215
10216 return directControl->UpdateMachineState (mData->mMachineState);
10217}
Note: See TracBrowser for help on using the repository browser.

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