VirtualBox

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

Last change on this file since 25414 was 25409, checked in by vboxsync, 14 years ago

IPRT,PDMCritSect,Main: Moved code dealing with lock counting from RTThread to RTLockValidator. Fixed thread termination assertion on windows.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use