VirtualBox

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

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

introduced VBoxManage modifyvm --rtcuseutc

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