VirtualBox

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

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

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

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

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