VirtualBox

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

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

Init

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

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