VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImpl.cpp@ 70386

Last change on this file since 70386 was 70386, checked in by vboxsync, 7 years ago

Machine::discardSettings: Need to take the machine line lock before locking the machine object for writing, because VirtualBox::i_markRegistryModified() may need to search it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 522.8 KB
Line 
1/* $Id: MachineImpl.cpp 70386 2017-12-29 11:38:27Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine class
232/////////////////////////////////////////////////////////////////////////////
233
234// constructor / destructor
235/////////////////////////////////////////////////////////////////////////////
236
237Machine::Machine() :
238#ifdef VBOX_WITH_RESOURCE_USAGE_API
239 mCollectorGuest(NULL),
240#endif
241 mPeer(NULL),
242 mParent(NULL),
243 mSerialPorts(),
244 mParallelPorts(),
245 uRegistryNeedsSaving(0)
246{}
247
248Machine::~Machine()
249{}
250
251HRESULT Machine::FinalConstruct()
252{
253 LogFlowThisFunc(("\n"));
254 return BaseFinalConstruct();
255}
256
257void Machine::FinalRelease()
258{
259 LogFlowThisFunc(("\n"));
260 uninit();
261 BaseFinalRelease();
262}
263
264/**
265 * Initializes a new machine instance; this init() variant creates a new, empty machine.
266 * This gets called from VirtualBox::CreateMachine().
267 *
268 * @param aParent Associated parent object
269 * @param strConfigFile Local file system path to the VM settings file (can
270 * be relative to the VirtualBox config directory).
271 * @param strName name for the machine
272 * @param llGroups list of groups for the machine
273 * @param aOsType OS Type of this machine or NULL.
274 * @param aId UUID for the new machine.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
277 * scheme (includes the UUID).
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 GuestOSType *aOsType,
286 const Guid &aId,
287 bool fForceOverwrite,
288 bool fDirectoryIncludesUUID)
289{
290 LogFlowThisFuncEnter();
291 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
292
293 /* Enclose the state transition NotReady->InInit->Ready */
294 AutoInitSpan autoInitSpan(this);
295 AssertReturn(autoInitSpan.isOk(), E_FAIL);
296
297 HRESULT rc = initImpl(aParent, strConfigFile);
298 if (FAILED(rc)) return rc;
299
300 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
301 if (FAILED(rc)) return rc;
302
303 if (SUCCEEDED(rc))
304 {
305 // create an empty machine config
306 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
307
308 rc = initDataAndChildObjects();
309 }
310
311 if (SUCCEEDED(rc))
312 {
313 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
314 mData->mAccessible = TRUE;
315
316 unconst(mData->mUuid) = aId;
317
318 mUserData->s.strName = strName;
319
320 mUserData->s.llGroups = llGroups;
321
322 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
323 // the "name sync" flag determines whether the machine directory gets renamed along
324 // with the machine file; say so if the settings file name is the same as the
325 // settings file parent directory (machine directory)
326 mUserData->s.fNameSync = i_isInOwnDir();
327
328 // initialize the default snapshots folder
329 rc = COMSETTER(SnapshotFolder)(NULL);
330 AssertComRC(rc);
331
332 if (aOsType)
333 {
334 /* Store OS type */
335 mUserData->s.strOsType = aOsType->i_id();
336
337 /* Apply BIOS defaults */
338 mBIOSSettings->i_applyDefaults(aOsType);
339
340 /* Let the OS type select 64-bit ness. */
341 mHWData->mLongMode = aOsType->i_is64Bit()
342 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
343
344 /* Let the OS type enable the X2APIC */
345 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
346 }
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Apply parallel port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
358 mParallelPorts[slot]->i_applyDefaults();
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param strConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param aParent Associated parent object.
511 * @param strName Name for the new machine; this overrides what is specified in config and is used
512 * for the settings file as well.
513 * @param config Machine configuration loaded and parsed from XML.
514 *
515 * @return Success indicator. if not S_OK, the machine object is invalid
516 */
517HRESULT Machine::init(VirtualBox *aParent,
518 const Utf8Str &strName,
519 const settings::MachineConfigFile &config)
520{
521 LogFlowThisFuncEnter();
522
523 /* Enclose the state transition NotReady->InInit->Ready */
524 AutoInitSpan autoInitSpan(this);
525 AssertReturn(autoInitSpan.isOk(), E_FAIL);
526
527 Utf8Str strConfigFile;
528 aParent->i_getDefaultMachineFolder(strConfigFile);
529 strConfigFile.append(RTPATH_DELIMITER);
530 strConfigFile.append(strName);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(".vbox");
534
535 HRESULT rc = initImpl(aParent, strConfigFile);
536 if (FAILED(rc)) return rc;
537
538 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
539 if (FAILED(rc)) return rc;
540
541 rc = initDataAndChildObjects();
542
543 if (SUCCEEDED(rc))
544 {
545 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
546 mData->mAccessible = TRUE;
547
548 // create empty machine config for instance data
549 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
550
551 // generate fresh UUID, ignore machine config
552 unconst(mData->mUuid).create();
553
554 rc = i_loadMachineDataFromSettings(config,
555 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
556
557 // override VM name as well, it may be different
558 mUserData->s.strName = strName;
559
560 if (SUCCEEDED(rc))
561 {
562 /* At this point the changing of the current state modification
563 * flag is allowed. */
564 i_allowStateModification();
565
566 /* commit all changes made during the initialization */
567 i_commit();
568 }
569 }
570
571 /* Confirm a successful initialization when it's the case */
572 if (SUCCEEDED(rc))
573 {
574 if (mData->mAccessible)
575 autoInitSpan.setSucceeded();
576 else
577 {
578 /* Ignore all errors from unregistering, they would destroy
579- * the more interesting error information we already have,
580- * pinpointing the issue with the VM config. */
581 ErrorInfoKeeper eik;
582
583 autoInitSpan.setLimited();
584
585 // uninit media from this machine's media registry, or else
586 // reloading the settings will fail
587 mParent->i_unregisterMachineMedia(i_getId());
588 }
589 }
590
591 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
592 "rc=%08X\n",
593 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
594 mData->mRegistered, mData->mAccessible, rc));
595
596 LogFlowThisFuncLeave();
597
598 return rc;
599}
600
601/**
602 * Shared code between the various init() implementations.
603 * @param aParent The VirtualBox object.
604 * @param strConfigFile Settings file.
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it has a
831 * VirtualBox caller for the duration of the
832 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 Log1WarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from i_registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator
1048 it = mUserData->s.llGroups.begin();
1049 it != mUserData->s.llGroups.end();
1050 ++it, ++i)
1051 aGroups[i] = (*it);
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1057{
1058 StringsList llGroups;
1059 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1060 if (FAILED(rc))
1061 return rc;
1062
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 rc = i_checkStateDependency(MutableOrSavedStateDep);
1066 if (FAILED(rc)) return rc;
1067
1068 i_setModified(IsModified_MachineData);
1069 mUserData.backup();
1070 mUserData->s.llGroups = llGroups;
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1076{
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 aOSTypeId = mUserData->s.strOsType;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1085{
1086 /* look up the object by Id to check it is valid */
1087 ComObjPtr<GuestOSType> pGuestOSType;
1088 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1089 pGuestOSType);
1090 if (FAILED(rc)) return rc;
1091
1092 /* when setting, always use the "etalon" value for consistency -- lookup
1093 * by ID is case-insensitive and the input value may have different case */
1094 Utf8Str osTypeId = pGuestOSType->i_id();
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 aParavirtDebug = mHWData->mParavirtDebug;
1223 return S_OK;
1224}
1225
1226HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1227{
1228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1229
1230 HRESULT rc = i_checkStateDependency(MutableStateDep);
1231 if (FAILED(rc)) return rc;
1232
1233 /** @todo Parse/validate options? */
1234 if (aParavirtDebug != mHWData->mParavirtDebug)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtDebug = aParavirtDebug;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249
1250 return S_OK;
1251}
1252
1253HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1254{
1255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 HRESULT rc = i_checkStateDependency(MutableStateDep);
1258 if (FAILED(rc)) return rc;
1259
1260 if (aParavirtProvider != mHWData->mParavirtProvider)
1261 {
1262 i_setModified(IsModified_MachineData);
1263 mHWData.backup();
1264 mHWData->mParavirtProvider = aParavirtProvider;
1265 }
1266
1267 return S_OK;
1268}
1269
1270HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1271{
1272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 *aParavirtProvider = mHWData->mParavirtProvider;
1275 switch (mHWData->mParavirtProvider)
1276 {
1277 case ParavirtProvider_None:
1278 case ParavirtProvider_HyperV:
1279 case ParavirtProvider_KVM:
1280 case ParavirtProvider_Minimal:
1281 break;
1282
1283 /* Resolve dynamic provider types to the effective types. */
1284 default:
1285 {
1286 ComObjPtr<GuestOSType> pGuestOSType;
1287 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1288 pGuestOSType);
1289 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1290
1291 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1292 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1293
1294 switch (mHWData->mParavirtProvider)
1295 {
1296 case ParavirtProvider_Legacy:
1297 {
1298 if (fOsXGuest)
1299 *aParavirtProvider = ParavirtProvider_Minimal;
1300 else
1301 *aParavirtProvider = ParavirtProvider_None;
1302 break;
1303 }
1304
1305 case ParavirtProvider_Default:
1306 {
1307 if (fOsXGuest)
1308 *aParavirtProvider = ParavirtProvider_Minimal;
1309 else if ( mUserData->s.strOsType == "Windows10"
1310 || mUserData->s.strOsType == "Windows10_64"
1311 || mUserData->s.strOsType == "Windows81"
1312 || mUserData->s.strOsType == "Windows81_64"
1313 || mUserData->s.strOsType == "Windows8"
1314 || mUserData->s.strOsType == "Windows8_64"
1315 || mUserData->s.strOsType == "Windows7"
1316 || mUserData->s.strOsType == "Windows7_64"
1317 || mUserData->s.strOsType == "WindowsVista"
1318 || mUserData->s.strOsType == "WindowsVista_64"
1319 || mUserData->s.strOsType == "Windows2012"
1320 || mUserData->s.strOsType == "Windows2012_64"
1321 || mUserData->s.strOsType == "Windows2008"
1322 || mUserData->s.strOsType == "Windows2008_64")
1323 {
1324 *aParavirtProvider = ParavirtProvider_HyperV;
1325 }
1326 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1327 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1328 || mUserData->s.strOsType == "Linux"
1329 || mUserData->s.strOsType == "Linux_64"
1330 || mUserData->s.strOsType == "ArchLinux"
1331 || mUserData->s.strOsType == "ArchLinux_64"
1332 || mUserData->s.strOsType == "Debian"
1333 || mUserData->s.strOsType == "Debian_64"
1334 || mUserData->s.strOsType == "Fedora"
1335 || mUserData->s.strOsType == "Fedora_64"
1336 || mUserData->s.strOsType == "Gentoo"
1337 || mUserData->s.strOsType == "Gentoo_64"
1338 || mUserData->s.strOsType == "Mandriva"
1339 || mUserData->s.strOsType == "Mandriva_64"
1340 || mUserData->s.strOsType == "OpenSUSE"
1341 || mUserData->s.strOsType == "OpenSUSE_64"
1342 || mUserData->s.strOsType == "Oracle"
1343 || mUserData->s.strOsType == "Oracle_64"
1344 || mUserData->s.strOsType == "RedHat"
1345 || mUserData->s.strOsType == "RedHat_64"
1346 || mUserData->s.strOsType == "Turbolinux"
1347 || mUserData->s.strOsType == "Turbolinux_64"
1348 || mUserData->s.strOsType == "Ubuntu"
1349 || mUserData->s.strOsType == "Ubuntu_64"
1350 || mUserData->s.strOsType == "Xandros"
1351 || mUserData->s.strOsType == "Xandros_64")
1352 {
1353 *aParavirtProvider = ParavirtProvider_KVM;
1354 }
1355 else
1356 *aParavirtProvider = ParavirtProvider_None;
1357 break;
1358 }
1359
1360 default: AssertFailedBreak(); /* Shut up MSC. */
1361 }
1362 break;
1363 }
1364 }
1365
1366 Assert( *aParavirtProvider == ParavirtProvider_None
1367 || *aParavirtProvider == ParavirtProvider_Minimal
1368 || *aParavirtProvider == ParavirtProvider_HyperV
1369 || *aParavirtProvider == ParavirtProvider_KVM);
1370 return S_OK;
1371}
1372
1373HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1374{
1375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1376
1377 aHardwareVersion = mHWData->mHWVersion;
1378
1379 return S_OK;
1380}
1381
1382HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1383{
1384 /* check known version */
1385 Utf8Str hwVersion = aHardwareVersion;
1386 if ( hwVersion.compare("1") != 0
1387 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1388 return setError(E_INVALIDARG,
1389 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1390
1391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 HRESULT rc = i_checkStateDependency(MutableStateDep);
1394 if (FAILED(rc)) return rc;
1395
1396 i_setModified(IsModified_MachineData);
1397 mHWData.backup();
1398 mHWData->mHWVersion = aHardwareVersion;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1404{
1405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 if (!mHWData->mHardwareUUID.isZero())
1408 aHardwareUUID = mHWData->mHardwareUUID;
1409 else
1410 aHardwareUUID = mData->mUuid;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1416{
1417 if (!aHardwareUUID.isValid())
1418 return E_INVALIDARG;
1419
1420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 HRESULT rc = i_checkStateDependency(MutableStateDep);
1423 if (FAILED(rc)) return rc;
1424
1425 i_setModified(IsModified_MachineData);
1426 mHWData.backup();
1427 if (aHardwareUUID == mData->mUuid)
1428 mHWData->mHardwareUUID.clear();
1429 else
1430 mHWData->mHardwareUUID = aHardwareUUID;
1431
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 *aMemorySize = mHWData->mMemorySize;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setMemorySize(ULONG aMemorySize)
1445{
1446 /* check RAM limits */
1447 if ( aMemorySize < MM_RAM_MIN_IN_MB
1448 || aMemorySize > MM_RAM_MAX_IN_MB
1449 )
1450 return setError(E_INVALIDARG,
1451 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1452 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 HRESULT rc = i_checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 i_setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mMemorySize = aMemorySize;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1467{
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 *aCPUCount = mHWData->mCPUCount;
1471
1472 return S_OK;
1473}
1474
1475HRESULT Machine::setCPUCount(ULONG aCPUCount)
1476{
1477 /* check CPU limits */
1478 if ( aCPUCount < SchemaDefs::MinCPUCount
1479 || aCPUCount > SchemaDefs::MaxCPUCount
1480 )
1481 return setError(E_INVALIDARG,
1482 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1483 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1484
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1488 if (mHWData->mCPUHotPlugEnabled)
1489 {
1490 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1491 {
1492 if (mHWData->mCPUAttached[idx])
1493 return setError(E_INVALIDARG,
1494 tr("There is still a CPU attached to socket %lu."
1495 "Detach the CPU before removing the socket"),
1496 aCPUCount, idx+1);
1497 }
1498 }
1499
1500 HRESULT rc = i_checkStateDependency(MutableStateDep);
1501 if (FAILED(rc)) return rc;
1502
1503 i_setModified(IsModified_MachineData);
1504 mHWData.backup();
1505 mHWData->mCPUCount = aCPUCount;
1506
1507 return S_OK;
1508}
1509
1510HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1511{
1512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1513
1514 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1515
1516 return S_OK;
1517}
1518
1519HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1520{
1521 HRESULT rc = S_OK;
1522
1523 /* check throttle limits */
1524 if ( aCPUExecutionCap < 1
1525 || aCPUExecutionCap > 100
1526 )
1527 return setError(E_INVALIDARG,
1528 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1529 aCPUExecutionCap, 1, 100);
1530
1531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1532
1533 alock.release();
1534 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1535 alock.acquire();
1536 if (FAILED(rc)) return rc;
1537
1538 i_setModified(IsModified_MachineData);
1539 mHWData.backup();
1540 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1541
1542 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1543 if (Global::IsOnline(mData->mMachineState))
1544 i_saveSettings(NULL);
1545
1546 return S_OK;
1547}
1548
1549HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1550{
1551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1554
1555 return S_OK;
1556}
1557
1558HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1559{
1560 HRESULT rc = S_OK;
1561
1562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1563
1564 rc = i_checkStateDependency(MutableStateDep);
1565 if (FAILED(rc)) return rc;
1566
1567 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1568 {
1569 if (aCPUHotPlugEnabled)
1570 {
1571 i_setModified(IsModified_MachineData);
1572 mHWData.backup();
1573
1574 /* Add the amount of CPUs currently attached */
1575 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1576 mHWData->mCPUAttached[i] = true;
1577 }
1578 else
1579 {
1580 /*
1581 * We can disable hotplug only if the amount of maximum CPUs is equal
1582 * to the amount of attached CPUs
1583 */
1584 unsigned cCpusAttached = 0;
1585 unsigned iHighestId = 0;
1586
1587 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1588 {
1589 if (mHWData->mCPUAttached[i])
1590 {
1591 cCpusAttached++;
1592 iHighestId = i;
1593 }
1594 }
1595
1596 if ( (cCpusAttached != mHWData->mCPUCount)
1597 || (iHighestId >= mHWData->mCPUCount))
1598 return setError(E_INVALIDARG,
1599 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1600
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 }
1604 }
1605
1606 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1607
1608 return rc;
1609}
1610
1611HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1612{
1613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1621{
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1625 if (SUCCEEDED(hrc))
1626 {
1627 i_setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1630 }
1631 return hrc;
1632}
1633
1634HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637 aCPUProfile = mHWData->mCpuProfile;
1638 return S_OK;
1639}
1640
1641HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1642{
1643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1644 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1645 if (SUCCEEDED(hrc))
1646 {
1647 i_setModified(IsModified_MachineData);
1648 mHWData.backup();
1649 /* Empty equals 'host'. */
1650 if (aCPUProfile.isNotEmpty())
1651 mHWData->mCpuProfile = aCPUProfile;
1652 else
1653 mHWData->mCpuProfile = "host";
1654 }
1655 return hrc;
1656}
1657
1658HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1659{
1660#ifdef VBOX_WITH_USB_CARDREADER
1661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
1663 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(aEmulatedUSBCardReaderEnabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1673{
1674#ifdef VBOX_WITH_USB_CARDREADER
1675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1678 if (FAILED(rc)) return rc;
1679
1680 i_setModified(IsModified_MachineData);
1681 mHWData.backup();
1682 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1683
1684 return S_OK;
1685#else
1686 NOREF(aEmulatedUSBCardReaderEnabled);
1687 return E_NOTIMPL;
1688#endif
1689}
1690
1691HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1692{
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 *aHPETEnabled = mHWData->mHPETEnabled;
1696
1697 return S_OK;
1698}
1699
1700HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1701{
1702 HRESULT rc = S_OK;
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 rc = i_checkStateDependency(MutableStateDep);
1707 if (FAILED(rc)) return rc;
1708
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711
1712 mHWData->mHPETEnabled = aHPETEnabled;
1713
1714 return rc;
1715}
1716
1717HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1718{
1719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1726{
1727 HRESULT rc = S_OK;
1728
1729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 i_setModified(IsModified_MachineData);
1732 mHWData.backup();
1733 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1734
1735 alock.release();
1736 rc = i_onVideoCaptureChange();
1737 alock.acquire();
1738 if (FAILED(rc))
1739 {
1740 /*
1741 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1742 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1743 * determine if it should start or stop capturing. Therefore we need to manually
1744 * undo change.
1745 */
1746 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1747 return rc;
1748 }
1749
1750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1751 if (Global::IsOnline(mData->mMachineState))
1752 i_saveSettings(NULL);
1753
1754 return rc;
1755}
1756
1757HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1758{
1759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1760 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1761 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1762 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1763 return S_OK;
1764}
1765
1766HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1767{
1768 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1769 bool fChanged = false;
1770
1771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1774 {
1775 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1776 {
1777 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1778 fChanged = true;
1779 }
1780 }
1781 if (fChanged)
1782 {
1783 alock.release();
1784 HRESULT rc = i_onVideoCaptureChange();
1785 alock.acquire();
1786 if (FAILED(rc)) return rc;
1787 i_setModified(IsModified_MachineData);
1788
1789 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1790 if (Global::IsOnline(mData->mMachineState))
1791 i_saveSettings(NULL);
1792 }
1793
1794 return S_OK;
1795}
1796
1797HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1798{
1799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1800 if (mHWData->mVideoCaptureFile.isEmpty())
1801 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1802 else
1803 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1804 return S_OK;
1805}
1806
1807HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1808{
1809 Utf8Str strFile(aVideoCaptureFile);
1810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1811
1812 if ( Global::IsOnline(mData->mMachineState)
1813 && mHWData->mVideoCaptureEnabled)
1814 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1815
1816 if (!RTPathStartsWithRoot(strFile.c_str()))
1817 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1818
1819 if (!strFile.isEmpty())
1820 {
1821 Utf8Str defaultFile;
1822 i_getDefaultVideoCaptureFile(defaultFile);
1823 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1824 strFile.setNull();
1825 }
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mVideoCaptureFile = strFile;
1830
1831 return S_OK;
1832}
1833
1834HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1835{
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1838 return S_OK;
1839}
1840
1841HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1842{
1843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1844
1845 if ( Global::IsOnline(mData->mMachineState)
1846 && mHWData->mVideoCaptureEnabled)
1847 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1848
1849 i_setModified(IsModified_MachineData);
1850 mHWData.backup();
1851 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1852
1853 return S_OK;
1854}
1855
1856HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1857{
1858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1859 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1860 return S_OK;
1861}
1862
1863HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1864{
1865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 if ( Global::IsOnline(mData->mMachineState)
1868 && mHWData->mVideoCaptureEnabled)
1869 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1870
1871 i_setModified(IsModified_MachineData);
1872 mHWData.backup();
1873 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1874
1875 return S_OK;
1876}
1877
1878HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1879{
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1882 return S_OK;
1883}
1884
1885HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1886{
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 if ( Global::IsOnline(mData->mMachineState)
1890 && mHWData->mVideoCaptureEnabled)
1891 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1892
1893 i_setModified(IsModified_MachineData);
1894 mHWData.backup();
1895 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1896
1897 return S_OK;
1898}
1899
1900HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1901{
1902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1903 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1904 return S_OK;
1905}
1906
1907HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1908{
1909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1910
1911 if ( Global::IsOnline(mData->mMachineState)
1912 && mHWData->mVideoCaptureEnabled)
1913 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1914
1915 i_setModified(IsModified_MachineData);
1916 mHWData.backup();
1917 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1918
1919 return S_OK;
1920}
1921
1922HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1926 return S_OK;
1927}
1928
1929HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1930{
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 if ( Global::IsOnline(mData->mMachineState)
1934 && mHWData->mVideoCaptureEnabled)
1935 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1936
1937 i_setModified(IsModified_MachineData);
1938 mHWData.backup();
1939 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1940
1941 return S_OK;
1942}
1943
1944HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1945{
1946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1947 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1948 return S_OK;
1949}
1950
1951HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1952{
1953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 if ( Global::IsOnline(mData->mMachineState)
1956 && mHWData->mVideoCaptureEnabled)
1957 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1958
1959 i_setModified(IsModified_MachineData);
1960 mHWData.backup();
1961 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1962
1963 return S_OK;
1964}
1965
1966HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1967{
1968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1971 return S_OK;
1972}
1973
1974HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1975{
1976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 if ( Global::IsOnline(mData->mMachineState)
1979 && mHWData->mVideoCaptureEnabled)
1980 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1981
1982 i_setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1985
1986 return S_OK;
1987}
1988
1989HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1994
1995 return S_OK;
1996}
1997
1998HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1999{
2000 switch (aGraphicsControllerType)
2001 {
2002 case GraphicsControllerType_Null:
2003 case GraphicsControllerType_VBoxVGA:
2004#ifdef VBOX_WITH_VMSVGA
2005 case GraphicsControllerType_VMSVGA:
2006#endif
2007 break;
2008 default:
2009 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2010 }
2011
2012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2013
2014 HRESULT rc = i_checkStateDependency(MutableStateDep);
2015 if (FAILED(rc)) return rc;
2016
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2020
2021 return S_OK;
2022}
2023
2024HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2025{
2026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2027
2028 *aVRAMSize = mHWData->mVRAMSize;
2029
2030 return S_OK;
2031}
2032
2033HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2034{
2035 /* check VRAM limits */
2036 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2037 return setError(E_INVALIDARG,
2038 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2039 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2040
2041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 HRESULT rc = i_checkStateDependency(MutableStateDep);
2044 if (FAILED(rc)) return rc;
2045
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mVRAMSize = aVRAMSize;
2049
2050 return S_OK;
2051}
2052
2053/** @todo this method should not be public */
2054HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2055{
2056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2059
2060 return S_OK;
2061}
2062
2063/**
2064 * Set the memory balloon size.
2065 *
2066 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2067 * we have to make sure that we never call IGuest from here.
2068 */
2069HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2070{
2071 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2072#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2073 /* check limits */
2074 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2075 return setError(E_INVALIDARG,
2076 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2077 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2078
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 i_setModified(IsModified_MachineData);
2082 mHWData.backup();
2083 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2084
2085 return S_OK;
2086#else
2087 NOREF(aMemoryBalloonSize);
2088 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2089#endif
2090}
2091
2092HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2093{
2094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2097 return S_OK;
2098}
2099
2100HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2101{
2102#ifdef VBOX_WITH_PAGE_SHARING
2103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2104
2105 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2109 return S_OK;
2110#else
2111 NOREF(aPageFusionEnabled);
2112 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2113#endif
2114}
2115
2116HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2117{
2118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
2120 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2126{
2127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 HRESULT rc = i_checkStateDependency(MutableStateDep);
2130 if (FAILED(rc)) return rc;
2131
2132 /** @todo check validity! */
2133
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2137
2138 return S_OK;
2139}
2140
2141
2142HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2143{
2144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2145
2146 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2147
2148 return S_OK;
2149}
2150
2151HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2152{
2153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 HRESULT rc = i_checkStateDependency(MutableStateDep);
2156 if (FAILED(rc)) return rc;
2157
2158 /** @todo check validity! */
2159 i_setModified(IsModified_MachineData);
2160 mHWData.backup();
2161 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2162
2163 return S_OK;
2164}
2165
2166HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2167{
2168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 *aMonitorCount = mHWData->mMonitorCount;
2171
2172 return S_OK;
2173}
2174
2175HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2176{
2177 /* make sure monitor count is a sensible number */
2178 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2179 return setError(E_INVALIDARG,
2180 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2181 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2182
2183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 HRESULT rc = i_checkStateDependency(MutableStateDep);
2186 if (FAILED(rc)) return rc;
2187
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190 mHWData->mMonitorCount = aMonitorCount;
2191
2192 return S_OK;
2193}
2194
2195HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2196{
2197 /* mBIOSSettings is constant during life time, no need to lock */
2198 aBIOSSettings = mBIOSSettings;
2199
2200 return S_OK;
2201}
2202
2203HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2204{
2205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 switch (aProperty)
2208 {
2209 case CPUPropertyType_PAE:
2210 *aValue = mHWData->mPAEEnabled;
2211 break;
2212
2213 case CPUPropertyType_LongMode:
2214 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2215 *aValue = TRUE;
2216 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2217 *aValue = FALSE;
2218#if HC_ARCH_BITS == 64
2219 else
2220 *aValue = TRUE;
2221#else
2222 else
2223 {
2224 *aValue = FALSE;
2225
2226 ComObjPtr<GuestOSType> pGuestOSType;
2227 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2228 pGuestOSType);
2229 if (SUCCEEDED(hrc2))
2230 {
2231 if (pGuestOSType->i_is64Bit())
2232 {
2233 ComObjPtr<Host> pHost = mParent->i_host();
2234 alock.release();
2235
2236 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2237 if (FAILED(hrc2))
2238 *aValue = FALSE;
2239 }
2240 }
2241 }
2242#endif
2243 break;
2244
2245 case CPUPropertyType_TripleFaultReset:
2246 *aValue = mHWData->mTripleFaultReset;
2247 break;
2248
2249 case CPUPropertyType_APIC:
2250 *aValue = mHWData->mAPIC;
2251 break;
2252
2253 case CPUPropertyType_X2APIC:
2254 *aValue = mHWData->mX2APIC;
2255 break;
2256
2257 default:
2258 return E_INVALIDARG;
2259 }
2260 return S_OK;
2261}
2262
2263HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2264{
2265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 HRESULT rc = i_checkStateDependency(MutableStateDep);
2268 if (FAILED(rc)) return rc;
2269
2270 switch (aProperty)
2271 {
2272 case CPUPropertyType_PAE:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mPAEEnabled = !!aValue;
2276 break;
2277
2278 case CPUPropertyType_LongMode:
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2282 break;
2283
2284 case CPUPropertyType_TripleFaultReset:
2285 i_setModified(IsModified_MachineData);
2286 mHWData.backup();
2287 mHWData->mTripleFaultReset = !!aValue;
2288 break;
2289
2290 case CPUPropertyType_APIC:
2291 if (mHWData->mX2APIC)
2292 aValue = TRUE;
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mAPIC = !!aValue;
2296 break;
2297
2298 case CPUPropertyType_X2APIC:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mX2APIC = !!aValue;
2302 if (aValue)
2303 mHWData->mAPIC = !!aValue;
2304 break;
2305
2306 default:
2307 return E_INVALIDARG;
2308 }
2309 return S_OK;
2310}
2311
2312HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2313 ULONG *aValEcx, ULONG *aValEdx)
2314{
2315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2316 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2317 {
2318 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2319 it != mHWData->mCpuIdLeafList.end();
2320 ++it)
2321 {
2322 if (aOrdinal == 0)
2323 {
2324 const settings::CpuIdLeaf &rLeaf= *it;
2325 *aIdx = rLeaf.idx;
2326 *aSubIdx = rLeaf.idxSub;
2327 *aValEax = rLeaf.uEax;
2328 *aValEbx = rLeaf.uEbx;
2329 *aValEcx = rLeaf.uEcx;
2330 *aValEdx = rLeaf.uEdx;
2331 return S_OK;
2332 }
2333 aOrdinal--;
2334 }
2335 }
2336 return E_INVALIDARG;
2337}
2338
2339HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2340{
2341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 /*
2344 * Search the list.
2345 */
2346 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2347 {
2348 const settings::CpuIdLeaf &rLeaf= *it;
2349 if ( rLeaf.idx == aIdx
2350 && ( aSubIdx == UINT32_MAX
2351 || rLeaf.idxSub == aSubIdx) )
2352 {
2353 *aValEax = rLeaf.uEax;
2354 *aValEbx = rLeaf.uEbx;
2355 *aValEcx = rLeaf.uEcx;
2356 *aValEdx = rLeaf.uEdx;
2357 return S_OK;
2358 }
2359 }
2360
2361 return E_INVALIDARG;
2362}
2363
2364
2365HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2366{
2367 /*
2368 * Validate input before taking locks and checking state.
2369 */
2370 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2371 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2372 if ( aIdx >= UINT32_C(0x20)
2373 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2374 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2375 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2376
2377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2378 HRESULT rc = i_checkStateDependency(MutableStateDep);
2379 if (FAILED(rc)) return rc;
2380
2381 /*
2382 * Impose a maximum number of leaves.
2383 */
2384 if (mHWData->mCpuIdLeafList.size() > 256)
2385 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2386
2387 /*
2388 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2389 */
2390 i_setModified(IsModified_MachineData);
2391 mHWData.backup();
2392
2393 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2394 {
2395 settings::CpuIdLeaf &rLeaf= *it;
2396 if ( rLeaf.idx == aIdx
2397 && ( aSubIdx == UINT32_MAX
2398 || rLeaf.idxSub == aSubIdx) )
2399 it = mHWData->mCpuIdLeafList.erase(it);
2400 else
2401 ++it;
2402 }
2403
2404 settings::CpuIdLeaf NewLeaf;
2405 NewLeaf.idx = aIdx;
2406 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2407 NewLeaf.uEax = aValEax;
2408 NewLeaf.uEbx = aValEbx;
2409 NewLeaf.uEcx = aValEcx;
2410 NewLeaf.uEdx = aValEdx;
2411 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2412 return S_OK;
2413}
2414
2415HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2416{
2417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2418
2419 HRESULT rc = i_checkStateDependency(MutableStateDep);
2420 if (FAILED(rc)) return rc;
2421
2422 /*
2423 * Do the removal.
2424 */
2425 bool fModified = false;
2426 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2427 {
2428 settings::CpuIdLeaf &rLeaf= *it;
2429 if ( rLeaf.idx == aIdx
2430 && ( aSubIdx == UINT32_MAX
2431 || rLeaf.idxSub == aSubIdx) )
2432 {
2433 if (!fModified)
2434 {
2435 fModified = true;
2436 i_setModified(IsModified_MachineData);
2437 mHWData.backup();
2438 }
2439 it = mHWData->mCpuIdLeafList.erase(it);
2440 }
2441 else
2442 ++it;
2443 }
2444
2445 return S_OK;
2446}
2447
2448HRESULT Machine::removeAllCPUIDLeaves()
2449{
2450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2451
2452 HRESULT rc = i_checkStateDependency(MutableStateDep);
2453 if (FAILED(rc)) return rc;
2454
2455 if (mHWData->mCpuIdLeafList.size() > 0)
2456 {
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459
2460 mHWData->mCpuIdLeafList.clear();
2461 }
2462
2463 return S_OK;
2464}
2465HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2466{
2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 switch(aProperty)
2470 {
2471 case HWVirtExPropertyType_Enabled:
2472 *aValue = mHWData->mHWVirtExEnabled;
2473 break;
2474
2475 case HWVirtExPropertyType_VPID:
2476 *aValue = mHWData->mHWVirtExVPIDEnabled;
2477 break;
2478
2479 case HWVirtExPropertyType_NestedPaging:
2480 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2481 break;
2482
2483 case HWVirtExPropertyType_UnrestrictedExecution:
2484 *aValue = mHWData->mHWVirtExUXEnabled;
2485 break;
2486
2487 case HWVirtExPropertyType_LargePages:
2488 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2489#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2490 *aValue = FALSE;
2491#endif
2492 break;
2493
2494 case HWVirtExPropertyType_Force:
2495 *aValue = mHWData->mHWVirtExForceEnabled;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501 return S_OK;
2502}
2503
2504HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2505{
2506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 HRESULT rc = i_checkStateDependency(MutableStateDep);
2509 if (FAILED(rc)) return rc;
2510
2511 switch(aProperty)
2512 {
2513 case HWVirtExPropertyType_Enabled:
2514 i_setModified(IsModified_MachineData);
2515 mHWData.backup();
2516 mHWData->mHWVirtExEnabled = !!aValue;
2517 break;
2518
2519 case HWVirtExPropertyType_VPID:
2520 i_setModified(IsModified_MachineData);
2521 mHWData.backup();
2522 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2523 break;
2524
2525 case HWVirtExPropertyType_NestedPaging:
2526 i_setModified(IsModified_MachineData);
2527 mHWData.backup();
2528 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2529 break;
2530
2531 case HWVirtExPropertyType_UnrestrictedExecution:
2532 i_setModified(IsModified_MachineData);
2533 mHWData.backup();
2534 mHWData->mHWVirtExUXEnabled = !!aValue;
2535 break;
2536
2537 case HWVirtExPropertyType_LargePages:
2538 i_setModified(IsModified_MachineData);
2539 mHWData.backup();
2540 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2541 break;
2542
2543 case HWVirtExPropertyType_Force:
2544 i_setModified(IsModified_MachineData);
2545 mHWData.backup();
2546 mHWData->mHWVirtExForceEnabled = !!aValue;
2547 break;
2548
2549 default:
2550 return E_INVALIDARG;
2551 }
2552
2553 return S_OK;
2554}
2555
2556HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2557{
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2566{
2567 /** @todo (r=dmik):
2568 * 1. Allow to change the name of the snapshot folder containing snapshots
2569 * 2. Rename the folder on disk instead of just changing the property
2570 * value (to be smart and not to leave garbage). Note that it cannot be
2571 * done here because the change may be rolled back. Thus, the right
2572 * place is #saveSettings().
2573 */
2574
2575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 HRESULT rc = i_checkStateDependency(MutableStateDep);
2578 if (FAILED(rc)) return rc;
2579
2580 if (!mData->mCurrentSnapshot.isNull())
2581 return setError(E_FAIL,
2582 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2583
2584 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2585
2586 if (strSnapshotFolder.isEmpty())
2587 strSnapshotFolder = "Snapshots";
2588 int vrc = i_calculateFullPath(strSnapshotFolder,
2589 strSnapshotFolder);
2590 if (RT_FAILURE(vrc))
2591 return setError(E_FAIL,
2592 tr("Invalid snapshot folder '%s' (%Rrc)"),
2593 strSnapshotFolder.c_str(), vrc);
2594
2595 i_setModified(IsModified_MachineData);
2596 mUserData.backup();
2597
2598 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 aMediumAttachments.resize(mMediumAttachments->size());
2608 size_t i = 0;
2609 for (MediumAttachmentList::const_iterator
2610 it = mMediumAttachments->begin();
2611 it != mMediumAttachments->end();
2612 ++it, ++i)
2613 aMediumAttachments[i] = *it;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 Assert(!!mVRDEServer);
2623
2624 aVRDEServer = mVRDEServer;
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2630{
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 aAudioAdapter = mAudioAdapter;
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2639{
2640#ifdef VBOX_WITH_VUSB
2641 clearError();
2642 MultiResult rc(S_OK);
2643
2644# ifdef VBOX_WITH_USB
2645 rc = mParent->i_host()->i_checkUSBProxyService();
2646 if (FAILED(rc)) return rc;
2647# endif
2648
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 aUSBControllers.resize(mUSBControllers->size());
2652 size_t i = 0;
2653 for (USBControllerList::const_iterator
2654 it = mUSBControllers->begin();
2655 it != mUSBControllers->end();
2656 ++it, ++i)
2657 aUSBControllers[i] = *it;
2658
2659 return S_OK;
2660#else
2661 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2662 * extended error info to indicate that USB is simply not available
2663 * (w/o treating it as a failure), for example, as in OSE */
2664 NOREF(aUSBControllers);
2665 ReturnComNotImplemented();
2666#endif /* VBOX_WITH_VUSB */
2667}
2668
2669HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2670{
2671#ifdef VBOX_WITH_VUSB
2672 clearError();
2673 MultiResult rc(S_OK);
2674
2675# ifdef VBOX_WITH_USB
2676 rc = mParent->i_host()->i_checkUSBProxyService();
2677 if (FAILED(rc)) return rc;
2678# endif
2679
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 aUSBDeviceFilters = mUSBDeviceFilters;
2683 return rc;
2684#else
2685 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2686 * extended error info to indicate that USB is simply not available
2687 * (w/o treating it as a failure), for example, as in OSE */
2688 NOREF(aUSBDeviceFilters);
2689 ReturnComNotImplemented();
2690#endif /* VBOX_WITH_VUSB */
2691}
2692
2693HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aSettingsFilePath = mData->m_strConfigFileFull;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2703{
2704 RT_NOREF(aSettingsFilePath);
2705 ReturnComNotImplemented();
2706}
2707
2708HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2713 if (FAILED(rc)) return rc;
2714
2715 if (!mData->pMachineConfigFile->fileExists())
2716 // this is a new machine, and no config file exists yet:
2717 *aSettingsModified = TRUE;
2718 else
2719 *aSettingsModified = (mData->flModifications != 0);
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aSessionState = mData->mSession.mState;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2734{
2735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 aSessionName = mData->mSession.mName;
2738
2739 return S_OK;
2740}
2741
2742HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2743{
2744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 *aSessionPID = mData->mSession.mPID;
2747
2748 return S_OK;
2749}
2750
2751HRESULT Machine::getState(MachineState_T *aState)
2752{
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 *aState = mData->mMachineState;
2756 Assert(mData->mMachineState != MachineState_Null);
2757
2758 return S_OK;
2759}
2760
2761HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 aStateFilePath = mSSData->strStateFilePath;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 i_getLogFolder(aLogFolder);
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 aCurrentSnapshot = mData->mCurrentSnapshot;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2802 ? 0
2803 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 /* Note: for machines with no snapshots, we always return FALSE
2813 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2814 * reasons :) */
2815
2816 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2817 ? FALSE
2818 : mData->mCurrentStateModified;
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 aSharedFolders.resize(mHWData->mSharedFolders.size());
2828 size_t i = 0;
2829 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2830 it = mHWData->mSharedFolders.begin();
2831 it != mHWData->mSharedFolders.end();
2832 ++it, ++i)
2833 aSharedFolders[i] = *it;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aClipboardMode = mHWData->mClipboardMode;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2848{
2849 HRESULT rc = S_OK;
2850
2851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 alock.release();
2854 rc = i_onClipboardModeChange(aClipboardMode);
2855 alock.acquire();
2856 if (FAILED(rc)) return rc;
2857
2858 i_setModified(IsModified_MachineData);
2859 mHWData.backup();
2860 mHWData->mClipboardMode = aClipboardMode;
2861
2862 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2863 if (Global::IsOnline(mData->mMachineState))
2864 i_saveSettings(NULL);
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aDnDMode = mHWData->mDnDMode;
2874
2875 return S_OK;
2876}
2877
2878HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2879{
2880 HRESULT rc = S_OK;
2881
2882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 alock.release();
2885 rc = i_onDnDModeChange(aDnDMode);
2886
2887 alock.acquire();
2888 if (FAILED(rc)) return rc;
2889
2890 i_setModified(IsModified_MachineData);
2891 mHWData.backup();
2892 mHWData->mDnDMode = aDnDMode;
2893
2894 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2895 if (Global::IsOnline(mData->mMachineState))
2896 i_saveSettings(NULL);
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2902{
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 aStorageControllers.resize(mStorageControllers->size());
2906 size_t i = 0;
2907 for (StorageControllerList::const_iterator
2908 it = mStorageControllers->begin();
2909 it != mStorageControllers->end();
2910 ++it, ++i)
2911 aStorageControllers[i] = *it;
2912
2913 return S_OK;
2914}
2915
2916HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2917{
2918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 *aEnabled = mUserData->s.fTeleporterEnabled;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2926{
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 /* Only allow it to be set to true when PoweredOff or Aborted.
2930 (Clearing it is always permitted.) */
2931 if ( aTeleporterEnabled
2932 && mData->mRegistered
2933 && ( !i_isSessionMachine()
2934 || ( mData->mMachineState != MachineState_PoweredOff
2935 && mData->mMachineState != MachineState_Teleported
2936 && mData->mMachineState != MachineState_Aborted
2937 )
2938 )
2939 )
2940 return setError(VBOX_E_INVALID_VM_STATE,
2941 tr("The machine is not powered off (state is %s)"),
2942 Global::stringifyMachineState(mData->mMachineState));
2943
2944 i_setModified(IsModified_MachineData);
2945 mUserData.backup();
2946 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2961{
2962 if (aTeleporterPort >= _64K)
2963 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2964
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2968 if (FAILED(rc)) return rc;
2969
2970 i_setModified(IsModified_MachineData);
2971 mUserData.backup();
2972 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2973
2974 return S_OK;
2975}
2976
2977HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2978{
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2987{
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mUserData.backup();
2995 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3001{
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3004
3005 return S_OK;
3006}
3007
3008HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3009{
3010 /*
3011 * Hash the password first.
3012 */
3013 com::Utf8Str aT = aTeleporterPassword;
3014
3015 if (!aT.isEmpty())
3016 {
3017 if (VBoxIsPasswordHashed(&aT))
3018 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3019 VBoxHashPassword(&aT);
3020 }
3021
3022 /*
3023 * Do the update.
3024 */
3025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3026 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3027 if (SUCCEEDED(hrc))
3028 {
3029 i_setModified(IsModified_MachineData);
3030 mUserData.backup();
3031 mUserData->s.strTeleporterPassword = aT;
3032 }
3033
3034 return hrc;
3035}
3036
3037HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3038{
3039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3042 return S_OK;
3043}
3044
3045HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3046{
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 /** @todo deal with running state change. */
3050 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3051 if (FAILED(rc)) return rc;
3052
3053 i_setModified(IsModified_MachineData);
3054 mUserData.backup();
3055 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3064 return S_OK;
3065}
3066
3067HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3068{
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 /** @todo deal with running state change. */
3072 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3073 if (FAILED(rc)) return rc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3078 return S_OK;
3079}
3080
3081HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3082{
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3086 return S_OK;
3087}
3088
3089HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3090{
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 /** @todo deal with running state change. */
3094 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3095 if (FAILED(rc)) return rc;
3096
3097 i_setModified(IsModified_MachineData);
3098 mUserData.backup();
3099 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3100 return S_OK;
3101}
3102
3103HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3104{
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3113{
3114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 /** @todo deal with running state change. */
3117 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 i_setModified(IsModified_MachineData);
3121 mUserData.backup();
3122 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3123
3124 return S_OK;
3125}
3126
3127HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3128{
3129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3130
3131 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3132 return S_OK;
3133}
3134
3135HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3136{
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /** @todo deal with running state change. */
3140 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 i_setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3146 return S_OK;
3147}
3148
3149HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3150{
3151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3154
3155 return S_OK;
3156}
3157
3158HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3159{
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 /* Only allow it to be set to true when PoweredOff or Aborted.
3163 (Clearing it is always permitted.) */
3164 if ( aRTCUseUTC
3165 && mData->mRegistered
3166 && ( !i_isSessionMachine()
3167 || ( mData->mMachineState != MachineState_PoweredOff
3168 && mData->mMachineState != MachineState_Teleported
3169 && mData->mMachineState != MachineState_Aborted
3170 )
3171 )
3172 )
3173 return setError(VBOX_E_INVALID_VM_STATE,
3174 tr("The machine is not powered off (state is %s)"),
3175 Global::stringifyMachineState(mData->mMachineState));
3176
3177 i_setModified(IsModified_MachineData);
3178 mUserData.backup();
3179 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3180
3181 return S_OK;
3182}
3183
3184HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3185{
3186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3189
3190 return S_OK;
3191}
3192
3193HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3194{
3195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3196
3197 HRESULT rc = i_checkStateDependency(MutableStateDep);
3198 if (FAILED(rc)) return rc;
3199
3200 i_setModified(IsModified_MachineData);
3201 mHWData.backup();
3202 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3203
3204 return S_OK;
3205}
3206
3207HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3208{
3209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 *aIOCacheSize = mHWData->mIOCacheSize;
3212
3213 return S_OK;
3214}
3215
3216HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3217{
3218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3219
3220 HRESULT rc = i_checkStateDependency(MutableStateDep);
3221 if (FAILED(rc)) return rc;
3222
3223 i_setModified(IsModified_MachineData);
3224 mHWData.backup();
3225 mHWData->mIOCacheSize = aIOCacheSize;
3226
3227 return S_OK;
3228}
3229
3230
3231/**
3232 * @note Locks objects!
3233 */
3234HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3235 LockType_T aLockType)
3236{
3237 /* check the session state */
3238 SessionState_T state;
3239 HRESULT rc = aSession->COMGETTER(State)(&state);
3240 if (FAILED(rc)) return rc;
3241
3242 if (state != SessionState_Unlocked)
3243 return setError(VBOX_E_INVALID_OBJECT_STATE,
3244 tr("The given session is busy"));
3245
3246 // get the client's IInternalSessionControl interface
3247 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3248 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3249 E_INVALIDARG);
3250
3251 // session name (only used in some code paths)
3252 Utf8Str strSessionName;
3253
3254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3255
3256 if (!mData->mRegistered)
3257 return setError(E_UNEXPECTED,
3258 tr("The machine '%s' is not registered"),
3259 mUserData->s.strName.c_str());
3260
3261 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3262
3263 SessionState_T oldState = mData->mSession.mState;
3264 /* Hack: in case the session is closing and there is a progress object
3265 * which allows waiting for the session to be closed, take the opportunity
3266 * and do a limited wait (max. 1 second). This helps a lot when the system
3267 * is busy and thus session closing can take a little while. */
3268 if ( mData->mSession.mState == SessionState_Unlocking
3269 && mData->mSession.mProgress)
3270 {
3271 alock.release();
3272 mData->mSession.mProgress->WaitForCompletion(1000);
3273 alock.acquire();
3274 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3275 }
3276
3277 // try again now
3278 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3279 // (i.e. session machine exists)
3280 && (aLockType == LockType_Shared) // caller wants a shared link to the
3281 // existing session that holds the write lock:
3282 )
3283 {
3284 // OK, share the session... we are now dealing with three processes:
3285 // 1) VBoxSVC (where this code runs);
3286 // 2) process C: the caller's client process (who wants a shared session);
3287 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3288
3289 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3290 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3291 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3292 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3293 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3294
3295 /*
3296 * Release the lock before calling the client process. It's safe here
3297 * since the only thing to do after we get the lock again is to add
3298 * the remote control to the list (which doesn't directly influence
3299 * anything).
3300 */
3301 alock.release();
3302
3303 // get the console of the session holding the write lock (this is a remote call)
3304 ComPtr<IConsole> pConsoleW;
3305 if (mData->mSession.mLockType == LockType_VM)
3306 {
3307 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3308 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3309 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3310 if (FAILED(rc))
3311 // the failure may occur w/o any error info (from RPC), so provide one
3312 return setError(VBOX_E_VM_ERROR,
3313 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3314 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3315 }
3316
3317 // share the session machine and W's console with the caller's session
3318 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3319 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3320 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3321
3322 if (FAILED(rc))
3323 // the failure may occur w/o any error info (from RPC), so provide one
3324 return setError(VBOX_E_VM_ERROR,
3325 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3326 alock.acquire();
3327
3328 // need to revalidate the state after acquiring the lock again
3329 if (mData->mSession.mState != SessionState_Locked)
3330 {
3331 pSessionControl->Uninitialize();
3332 return setError(VBOX_E_INVALID_SESSION_STATE,
3333 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3334 mUserData->s.strName.c_str());
3335 }
3336
3337 // add the caller's session to the list
3338 mData->mSession.mRemoteControls.push_back(pSessionControl);
3339 }
3340 else if ( mData->mSession.mState == SessionState_Locked
3341 || mData->mSession.mState == SessionState_Unlocking
3342 )
3343 {
3344 // sharing not permitted, or machine still unlocking:
3345 return setError(VBOX_E_INVALID_OBJECT_STATE,
3346 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3347 mUserData->s.strName.c_str());
3348 }
3349 else
3350 {
3351 // machine is not locked: then write-lock the machine (create the session machine)
3352
3353 // must not be busy
3354 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3355
3356 // get the caller's session PID
3357 RTPROCESS pid = NIL_RTPROCESS;
3358 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3359 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3360 Assert(pid != NIL_RTPROCESS);
3361
3362 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3363
3364 if (fLaunchingVMProcess)
3365 {
3366 if (mData->mSession.mPID == NIL_RTPROCESS)
3367 {
3368 // two or more clients racing for a lock, the one which set the
3369 // session state to Spawning will win, the others will get an
3370 // error as we can't decide here if waiting a little would help
3371 // (only for shared locks this would avoid an error)
3372 return setError(VBOX_E_INVALID_OBJECT_STATE,
3373 tr("The machine '%s' already has a lock request pending"),
3374 mUserData->s.strName.c_str());
3375 }
3376
3377 // this machine is awaiting for a spawning session to be opened:
3378 // then the calling process must be the one that got started by
3379 // LaunchVMProcess()
3380
3381 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3382 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3383
3384#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3385 /* Hardened windows builds spawns three processes when a VM is
3386 launched, the 3rd one is the one that will end up here. */
3387 RTPROCESS ppid;
3388 int rc = RTProcQueryParent(pid, &ppid);
3389 if (RT_SUCCESS(rc))
3390 rc = RTProcQueryParent(ppid, &ppid);
3391 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3392 || rc == VERR_ACCESS_DENIED)
3393 {
3394 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3395 mData->mSession.mPID = pid;
3396 }
3397#endif
3398
3399 if (mData->mSession.mPID != pid)
3400 return setError(E_ACCESSDENIED,
3401 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3402 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3403 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3404 }
3405
3406 // create the mutable SessionMachine from the current machine
3407 ComObjPtr<SessionMachine> sessionMachine;
3408 sessionMachine.createObject();
3409 rc = sessionMachine->init(this);
3410 AssertComRC(rc);
3411
3412 /* NOTE: doing return from this function after this point but
3413 * before the end is forbidden since it may call SessionMachine::uninit()
3414 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3415 * lock while still holding the Machine lock in alock so that a deadlock
3416 * is possible due to the wrong lock order. */
3417
3418 if (SUCCEEDED(rc))
3419 {
3420 /*
3421 * Set the session state to Spawning to protect against subsequent
3422 * attempts to open a session and to unregister the machine after
3423 * we release the lock.
3424 */
3425 SessionState_T origState = mData->mSession.mState;
3426 mData->mSession.mState = SessionState_Spawning;
3427
3428#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3429 /* Get the client token ID to be passed to the client process */
3430 Utf8Str strTokenId;
3431 sessionMachine->i_getTokenId(strTokenId);
3432 Assert(!strTokenId.isEmpty());
3433#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3434 /* Get the client token to be passed to the client process */
3435 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3436 /* The token is now "owned" by pToken, fix refcount */
3437 if (!pToken.isNull())
3438 pToken->Release();
3439#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3440
3441 /*
3442 * Release the lock before calling the client process -- it will call
3443 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3444 * because the state is Spawning, so that LaunchVMProcess() and
3445 * LockMachine() calls will fail. This method, called before we
3446 * acquire the lock again, will fail because of the wrong PID.
3447 *
3448 * Note that mData->mSession.mRemoteControls accessed outside
3449 * the lock may not be modified when state is Spawning, so it's safe.
3450 */
3451 alock.release();
3452
3453 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3454#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3455 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3457 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3458 /* Now the token is owned by the client process. */
3459 pToken.setNull();
3460#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3461 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3462
3463 /* The failure may occur w/o any error info (from RPC), so provide one */
3464 if (FAILED(rc))
3465 setError(VBOX_E_VM_ERROR,
3466 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3467
3468 // get session name, either to remember or to compare against
3469 // the already known session name.
3470 {
3471 Bstr bstrSessionName;
3472 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3473 if (SUCCEEDED(rc2))
3474 strSessionName = bstrSessionName;
3475 }
3476
3477 if ( SUCCEEDED(rc)
3478 && fLaunchingVMProcess
3479 )
3480 {
3481 /* complete the remote session initialization */
3482
3483 /* get the console from the direct session */
3484 ComPtr<IConsole> console;
3485 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3486 ComAssertComRC(rc);
3487
3488 if (SUCCEEDED(rc) && !console)
3489 {
3490 ComAssert(!!console);
3491 rc = E_FAIL;
3492 }
3493
3494 /* assign machine & console to the remote session */
3495 if (SUCCEEDED(rc))
3496 {
3497 /*
3498 * after LaunchVMProcess(), the first and the only
3499 * entry in remoteControls is that remote session
3500 */
3501 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3502 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3503 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3504
3505 /* The failure may occur w/o any error info (from RPC), so provide one */
3506 if (FAILED(rc))
3507 setError(VBOX_E_VM_ERROR,
3508 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3509 }
3510
3511 if (FAILED(rc))
3512 pSessionControl->Uninitialize();
3513 }
3514
3515 /* acquire the lock again */
3516 alock.acquire();
3517
3518 /* Restore the session state */
3519 mData->mSession.mState = origState;
3520 }
3521
3522 // finalize spawning anyway (this is why we don't return on errors above)
3523 if (fLaunchingVMProcess)
3524 {
3525 Assert(mData->mSession.mName == strSessionName);
3526 /* Note that the progress object is finalized later */
3527 /** @todo Consider checking mData->mSession.mProgress for cancellation
3528 * around here. */
3529
3530 /* We don't reset mSession.mPID here because it is necessary for
3531 * SessionMachine::uninit() to reap the child process later. */
3532
3533 if (FAILED(rc))
3534 {
3535 /* Close the remote session, remove the remote control from the list
3536 * and reset session state to Closed (@note keep the code in sync
3537 * with the relevant part in checkForSpawnFailure()). */
3538
3539 Assert(mData->mSession.mRemoteControls.size() == 1);
3540 if (mData->mSession.mRemoteControls.size() == 1)
3541 {
3542 ErrorInfoKeeper eik;
3543 mData->mSession.mRemoteControls.front()->Uninitialize();
3544 }
3545
3546 mData->mSession.mRemoteControls.clear();
3547 mData->mSession.mState = SessionState_Unlocked;
3548 }
3549 }
3550 else
3551 {
3552 /* memorize PID of the directly opened session */
3553 if (SUCCEEDED(rc))
3554 mData->mSession.mPID = pid;
3555 }
3556
3557 if (SUCCEEDED(rc))
3558 {
3559 mData->mSession.mLockType = aLockType;
3560 /* memorize the direct session control and cache IUnknown for it */
3561 mData->mSession.mDirectControl = pSessionControl;
3562 mData->mSession.mState = SessionState_Locked;
3563 if (!fLaunchingVMProcess)
3564 mData->mSession.mName = strSessionName;
3565 /* associate the SessionMachine with this Machine */
3566 mData->mSession.mMachine = sessionMachine;
3567
3568 /* request an IUnknown pointer early from the remote party for later
3569 * identity checks (it will be internally cached within mDirectControl
3570 * at least on XPCOM) */
3571 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3572 NOREF(unk);
3573 }
3574
3575 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3576 * would break the lock order */
3577 alock.release();
3578
3579 /* uninitialize the created session machine on failure */
3580 if (FAILED(rc))
3581 sessionMachine->uninit();
3582 }
3583
3584 if (SUCCEEDED(rc))
3585 {
3586 /*
3587 * tell the client watcher thread to update the set of
3588 * machines that have open sessions
3589 */
3590 mParent->i_updateClientWatcher();
3591
3592 if (oldState != SessionState_Locked)
3593 /* fire an event */
3594 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3595 }
3596
3597 return rc;
3598}
3599
3600/**
3601 * @note Locks objects!
3602 */
3603HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3604 const com::Utf8Str &aName,
3605 const com::Utf8Str &aEnvironment,
3606 ComPtr<IProgress> &aProgress)
3607{
3608 Utf8Str strFrontend(aName);
3609 /* "emergencystop" doesn't need the session, so skip the checks/interface
3610 * retrieval. This code doesn't quite fit in here, but introducing a
3611 * special API method would be even more effort, and would require explicit
3612 * support by every API client. It's better to hide the feature a bit. */
3613 if (strFrontend != "emergencystop")
3614 CheckComArgNotNull(aSession);
3615
3616 HRESULT rc = S_OK;
3617 if (strFrontend.isEmpty())
3618 {
3619 Bstr bstrFrontend;
3620 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3621 if (FAILED(rc))
3622 return rc;
3623 strFrontend = bstrFrontend;
3624 if (strFrontend.isEmpty())
3625 {
3626 ComPtr<ISystemProperties> systemProperties;
3627 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3628 if (FAILED(rc))
3629 return rc;
3630 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3631 if (FAILED(rc))
3632 return rc;
3633 strFrontend = bstrFrontend;
3634 }
3635 /* paranoia - emergencystop is not a valid default */
3636 if (strFrontend == "emergencystop")
3637 strFrontend = Utf8Str::Empty;
3638 }
3639 /* default frontend: Qt GUI */
3640 if (strFrontend.isEmpty())
3641 strFrontend = "GUI/Qt";
3642
3643 if (strFrontend != "emergencystop")
3644 {
3645 /* check the session state */
3646 SessionState_T state;
3647 rc = aSession->COMGETTER(State)(&state);
3648 if (FAILED(rc))
3649 return rc;
3650
3651 if (state != SessionState_Unlocked)
3652 return setError(VBOX_E_INVALID_OBJECT_STATE,
3653 tr("The given session is busy"));
3654
3655 /* get the IInternalSessionControl interface */
3656 ComPtr<IInternalSessionControl> control(aSession);
3657 ComAssertMsgRet(!control.isNull(),
3658 ("No IInternalSessionControl interface"),
3659 E_INVALIDARG);
3660
3661 /* get the teleporter enable state for the progress object init. */
3662 BOOL fTeleporterEnabled;
3663 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3664 if (FAILED(rc))
3665 return rc;
3666
3667 /* create a progress object */
3668 ComObjPtr<ProgressProxy> progress;
3669 progress.createObject();
3670 rc = progress->init(mParent,
3671 static_cast<IMachine*>(this),
3672 Bstr(tr("Starting VM")).raw(),
3673 TRUE /* aCancelable */,
3674 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3675 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3676 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3677 2 /* uFirstOperationWeight */,
3678 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3679
3680 if (SUCCEEDED(rc))
3681 {
3682 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3683 if (SUCCEEDED(rc))
3684 {
3685 aProgress = progress;
3686
3687 /* signal the client watcher thread */
3688 mParent->i_updateClientWatcher();
3689
3690 /* fire an event */
3691 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3692 }
3693 }
3694 }
3695 else
3696 {
3697 /* no progress object - either instant success or failure */
3698 aProgress = NULL;
3699
3700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3701
3702 if (mData->mSession.mState != SessionState_Locked)
3703 return setError(VBOX_E_INVALID_OBJECT_STATE,
3704 tr("The machine '%s' is not locked by a session"),
3705 mUserData->s.strName.c_str());
3706
3707 /* must have a VM process associated - do not kill normal API clients
3708 * with an open session */
3709 if (!Global::IsOnline(mData->mMachineState))
3710 return setError(VBOX_E_INVALID_OBJECT_STATE,
3711 tr("The machine '%s' does not have a VM process"),
3712 mUserData->s.strName.c_str());
3713
3714 /* forcibly terminate the VM process */
3715 if (mData->mSession.mPID != NIL_RTPROCESS)
3716 RTProcTerminate(mData->mSession.mPID);
3717
3718 /* signal the client watcher thread, as most likely the client has
3719 * been terminated */
3720 mParent->i_updateClientWatcher();
3721 }
3722
3723 return rc;
3724}
3725
3726HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3727{
3728 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3729 return setError(E_INVALIDARG,
3730 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3731 aPosition, SchemaDefs::MaxBootPosition);
3732
3733 if (aDevice == DeviceType_USB)
3734 return setError(E_NOTIMPL,
3735 tr("Booting from USB device is currently not supported"));
3736
3737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3738
3739 HRESULT rc = i_checkStateDependency(MutableStateDep);
3740 if (FAILED(rc)) return rc;
3741
3742 i_setModified(IsModified_MachineData);
3743 mHWData.backup();
3744 mHWData->mBootOrder[aPosition - 1] = aDevice;
3745
3746 return S_OK;
3747}
3748
3749HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3750{
3751 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3752 return setError(E_INVALIDARG,
3753 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3754 aPosition, SchemaDefs::MaxBootPosition);
3755
3756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3757
3758 *aDevice = mHWData->mBootOrder[aPosition - 1];
3759
3760 return S_OK;
3761}
3762
3763HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3764 LONG aControllerPort,
3765 LONG aDevice,
3766 DeviceType_T aType,
3767 const ComPtr<IMedium> &aMedium)
3768{
3769 IMedium *aM = aMedium;
3770 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3771 aName.c_str(), aControllerPort, aDevice, aType, aM));
3772
3773 // request the host lock first, since might be calling Host methods for getting host drives;
3774 // next, protect the media tree all the while we're in here, as well as our member variables
3775 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3776 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3777
3778 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3779 if (FAILED(rc)) return rc;
3780
3781 /// @todo NEWMEDIA implicit machine registration
3782 if (!mData->mRegistered)
3783 return setError(VBOX_E_INVALID_OBJECT_STATE,
3784 tr("Cannot attach storage devices to an unregistered machine"));
3785
3786 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3787
3788 /* Check for an existing controller. */
3789 ComObjPtr<StorageController> ctl;
3790 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3791 if (FAILED(rc)) return rc;
3792
3793 StorageControllerType_T ctrlType;
3794 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3795 if (FAILED(rc))
3796 return setError(E_FAIL,
3797 tr("Could not get type of controller '%s'"),
3798 aName.c_str());
3799
3800 bool fSilent = false;
3801 Utf8Str strReconfig;
3802
3803 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3804 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3805 if ( mData->mMachineState == MachineState_Paused
3806 && strReconfig == "1")
3807 fSilent = true;
3808
3809 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3810 bool fHotplug = false;
3811 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3812 fHotplug = true;
3813
3814 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3815 return setError(VBOX_E_INVALID_VM_STATE,
3816 tr("Controller '%s' does not support hotplugging"),
3817 aName.c_str());
3818
3819 // check that the port and device are not out of range
3820 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3821 if (FAILED(rc)) return rc;
3822
3823 /* check if the device slot is already busy */
3824 MediumAttachment *pAttachTemp;
3825 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3826 aName,
3827 aControllerPort,
3828 aDevice)))
3829 {
3830 Medium *pMedium = pAttachTemp->i_getMedium();
3831 if (pMedium)
3832 {
3833 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3834 return setError(VBOX_E_OBJECT_IN_USE,
3835 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3836 pMedium->i_getLocationFull().c_str(),
3837 aControllerPort,
3838 aDevice,
3839 aName.c_str());
3840 }
3841 else
3842 return setError(VBOX_E_OBJECT_IN_USE,
3843 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3844 aControllerPort, aDevice, aName.c_str());
3845 }
3846
3847 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3848 if (aMedium && medium.isNull())
3849 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3850
3851 AutoCaller mediumCaller(medium);
3852 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3853
3854 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3855
3856 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3857 && !medium.isNull()
3858 )
3859 return setError(VBOX_E_OBJECT_IN_USE,
3860 tr("Medium '%s' is already attached to this virtual machine"),
3861 medium->i_getLocationFull().c_str());
3862
3863 if (!medium.isNull())
3864 {
3865 MediumType_T mtype = medium->i_getType();
3866 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3867 // For DVDs it's not written to the config file, so needs no global config
3868 // version bump. For floppies it's a new attribute "type", which is ignored
3869 // by older VirtualBox version, so needs no global config version bump either.
3870 // For hard disks this type is not accepted.
3871 if (mtype == MediumType_MultiAttach)
3872 {
3873 // This type is new with VirtualBox 4.0 and therefore requires settings
3874 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3875 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3876 // two reasons: The medium type is a property of the media registry tree, which
3877 // can reside in the global config file (for pre-4.0 media); we would therefore
3878 // possibly need to bump the global config version. We don't want to do that though
3879 // because that might make downgrading to pre-4.0 impossible.
3880 // As a result, we can only use these two new types if the medium is NOT in the
3881 // global registry:
3882 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3883 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3884 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3885 )
3886 return setError(VBOX_E_INVALID_OBJECT_STATE,
3887 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3888 "to machines that were created with VirtualBox 4.0 or later"),
3889 medium->i_getLocationFull().c_str());
3890 }
3891 }
3892
3893 bool fIndirect = false;
3894 if (!medium.isNull())
3895 fIndirect = medium->i_isReadOnly();
3896 bool associate = true;
3897
3898 do
3899 {
3900 if ( aType == DeviceType_HardDisk
3901 && mMediumAttachments.isBackedUp())
3902 {
3903 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3904
3905 /* check if the medium was attached to the VM before we started
3906 * changing attachments in which case the attachment just needs to
3907 * be restored */
3908 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3909 {
3910 AssertReturn(!fIndirect, E_FAIL);
3911
3912 /* see if it's the same bus/channel/device */
3913 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3914 {
3915 /* the simplest case: restore the whole attachment
3916 * and return, nothing else to do */
3917 mMediumAttachments->push_back(pAttachTemp);
3918
3919 /* Reattach the medium to the VM. */
3920 if (fHotplug || fSilent)
3921 {
3922 mediumLock.release();
3923 treeLock.release();
3924 alock.release();
3925
3926 MediumLockList *pMediumLockList(new MediumLockList());
3927
3928 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3929 medium /* pToLockWrite */,
3930 false /* fMediumLockWriteAll */,
3931 NULL,
3932 *pMediumLockList);
3933 alock.acquire();
3934 if (FAILED(rc))
3935 delete pMediumLockList;
3936 else
3937 {
3938 mData->mSession.mLockedMedia.Unlock();
3939 alock.release();
3940 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3941 mData->mSession.mLockedMedia.Lock();
3942 alock.acquire();
3943 }
3944 alock.release();
3945
3946 if (SUCCEEDED(rc))
3947 {
3948 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3949 /* Remove lock list in case of error. */
3950 if (FAILED(rc))
3951 {
3952 mData->mSession.mLockedMedia.Unlock();
3953 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3954 mData->mSession.mLockedMedia.Lock();
3955 }
3956 }
3957 }
3958
3959 return S_OK;
3960 }
3961
3962 /* bus/channel/device differ; we need a new attachment object,
3963 * but don't try to associate it again */
3964 associate = false;
3965 break;
3966 }
3967 }
3968
3969 /* go further only if the attachment is to be indirect */
3970 if (!fIndirect)
3971 break;
3972
3973 /* perform the so called smart attachment logic for indirect
3974 * attachments. Note that smart attachment is only applicable to base
3975 * hard disks. */
3976
3977 if (medium->i_getParent().isNull())
3978 {
3979 /* first, investigate the backup copy of the current hard disk
3980 * attachments to make it possible to re-attach existing diffs to
3981 * another device slot w/o losing their contents */
3982 if (mMediumAttachments.isBackedUp())
3983 {
3984 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3985
3986 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3987 uint32_t foundLevel = 0;
3988
3989 for (MediumAttachmentList::const_iterator
3990 it = oldAtts.begin();
3991 it != oldAtts.end();
3992 ++it)
3993 {
3994 uint32_t level = 0;
3995 MediumAttachment *pAttach = *it;
3996 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3997 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3998 if (pMedium.isNull())
3999 continue;
4000
4001 if (pMedium->i_getBase(&level) == medium)
4002 {
4003 /* skip the hard disk if its currently attached (we
4004 * cannot attach the same hard disk twice) */
4005 if (i_findAttachment(*mMediumAttachments.data(),
4006 pMedium))
4007 continue;
4008
4009 /* matched device, channel and bus (i.e. attached to the
4010 * same place) will win and immediately stop the search;
4011 * otherwise the attachment that has the youngest
4012 * descendant of medium will be used
4013 */
4014 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4015 {
4016 /* the simplest case: restore the whole attachment
4017 * and return, nothing else to do */
4018 mMediumAttachments->push_back(*it);
4019
4020 /* Reattach the medium to the VM. */
4021 if (fHotplug || fSilent)
4022 {
4023 mediumLock.release();
4024 treeLock.release();
4025 alock.release();
4026
4027 MediumLockList *pMediumLockList(new MediumLockList());
4028
4029 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4030 medium /* pToLockWrite */,
4031 false /* fMediumLockWriteAll */,
4032 NULL,
4033 *pMediumLockList);
4034 alock.acquire();
4035 if (FAILED(rc))
4036 delete pMediumLockList;
4037 else
4038 {
4039 mData->mSession.mLockedMedia.Unlock();
4040 alock.release();
4041 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4042 mData->mSession.mLockedMedia.Lock();
4043 alock.acquire();
4044 }
4045 alock.release();
4046
4047 if (SUCCEEDED(rc))
4048 {
4049 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4050 /* Remove lock list in case of error. */
4051 if (FAILED(rc))
4052 {
4053 mData->mSession.mLockedMedia.Unlock();
4054 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4055 mData->mSession.mLockedMedia.Lock();
4056 }
4057 }
4058 }
4059
4060 return S_OK;
4061 }
4062 else if ( foundIt == oldAtts.end()
4063 || level > foundLevel /* prefer younger */
4064 )
4065 {
4066 foundIt = it;
4067 foundLevel = level;
4068 }
4069 }
4070 }
4071
4072 if (foundIt != oldAtts.end())
4073 {
4074 /* use the previously attached hard disk */
4075 medium = (*foundIt)->i_getMedium();
4076 mediumCaller.attach(medium);
4077 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4078 mediumLock.attach(medium);
4079 /* not implicit, doesn't require association with this VM */
4080 fIndirect = false;
4081 associate = false;
4082 /* go right to the MediumAttachment creation */
4083 break;
4084 }
4085 }
4086
4087 /* must give up the medium lock and medium tree lock as below we
4088 * go over snapshots, which needs a lock with higher lock order. */
4089 mediumLock.release();
4090 treeLock.release();
4091
4092 /* then, search through snapshots for the best diff in the given
4093 * hard disk's chain to base the new diff on */
4094
4095 ComObjPtr<Medium> base;
4096 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4097 while (snap)
4098 {
4099 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4100
4101 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4102
4103 MediumAttachment *pAttachFound = NULL;
4104 uint32_t foundLevel = 0;
4105
4106 for (MediumAttachmentList::const_iterator
4107 it = snapAtts.begin();
4108 it != snapAtts.end();
4109 ++it)
4110 {
4111 MediumAttachment *pAttach = *it;
4112 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4113 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4114 if (pMedium.isNull())
4115 continue;
4116
4117 uint32_t level = 0;
4118 if (pMedium->i_getBase(&level) == medium)
4119 {
4120 /* matched device, channel and bus (i.e. attached to the
4121 * same place) will win and immediately stop the search;
4122 * otherwise the attachment that has the youngest
4123 * descendant of medium will be used
4124 */
4125 if ( pAttach->i_getDevice() == aDevice
4126 && pAttach->i_getPort() == aControllerPort
4127 && pAttach->i_getControllerName() == aName
4128 )
4129 {
4130 pAttachFound = pAttach;
4131 break;
4132 }
4133 else if ( !pAttachFound
4134 || level > foundLevel /* prefer younger */
4135 )
4136 {
4137 pAttachFound = pAttach;
4138 foundLevel = level;
4139 }
4140 }
4141 }
4142
4143 if (pAttachFound)
4144 {
4145 base = pAttachFound->i_getMedium();
4146 break;
4147 }
4148
4149 snap = snap->i_getParent();
4150 }
4151
4152 /* re-lock medium tree and the medium, as we need it below */
4153 treeLock.acquire();
4154 mediumLock.acquire();
4155
4156 /* found a suitable diff, use it as a base */
4157 if (!base.isNull())
4158 {
4159 medium = base;
4160 mediumCaller.attach(medium);
4161 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4162 mediumLock.attach(medium);
4163 }
4164 }
4165
4166 Utf8Str strFullSnapshotFolder;
4167 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4168
4169 ComObjPtr<Medium> diff;
4170 diff.createObject();
4171 // store this diff in the same registry as the parent
4172 Guid uuidRegistryParent;
4173 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4174 {
4175 // parent image has no registry: this can happen if we're attaching a new immutable
4176 // image that has not yet been attached (medium then points to the base and we're
4177 // creating the diff image for the immutable, and the parent is not yet registered);
4178 // put the parent in the machine registry then
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182 i_addMediumToRegistry(medium);
4183 alock.acquire();
4184 treeLock.acquire();
4185 mediumLock.acquire();
4186 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4187 }
4188 rc = diff->init(mParent,
4189 medium->i_getPreferredDiffFormat(),
4190 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4191 uuidRegistryParent,
4192 DeviceType_HardDisk);
4193 if (FAILED(rc)) return rc;
4194
4195 /* Apply the normal locking logic to the entire chain. */
4196 MediumLockList *pMediumLockList(new MediumLockList());
4197 mediumLock.release();
4198 treeLock.release();
4199 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4200 diff /* pToLockWrite */,
4201 false /* fMediumLockWriteAll */,
4202 medium,
4203 *pMediumLockList);
4204 treeLock.acquire();
4205 mediumLock.acquire();
4206 if (SUCCEEDED(rc))
4207 {
4208 mediumLock.release();
4209 treeLock.release();
4210 rc = pMediumLockList->Lock();
4211 treeLock.acquire();
4212 mediumLock.acquire();
4213 if (FAILED(rc))
4214 setError(rc,
4215 tr("Could not lock medium when creating diff '%s'"),
4216 diff->i_getLocationFull().c_str());
4217 else
4218 {
4219 /* will release the lock before the potentially lengthy
4220 * operation, so protect with the special state */
4221 MachineState_T oldState = mData->mMachineState;
4222 i_setMachineState(MachineState_SettingUp);
4223
4224 mediumLock.release();
4225 treeLock.release();
4226 alock.release();
4227
4228 rc = medium->i_createDiffStorage(diff,
4229 medium->i_getPreferredDiffVariant(),
4230 pMediumLockList,
4231 NULL /* aProgress */,
4232 true /* aWait */);
4233
4234 alock.acquire();
4235 treeLock.acquire();
4236 mediumLock.acquire();
4237
4238 i_setMachineState(oldState);
4239 }
4240 }
4241
4242 /* Unlock the media and free the associated memory. */
4243 delete pMediumLockList;
4244
4245 if (FAILED(rc)) return rc;
4246
4247 /* use the created diff for the actual attachment */
4248 medium = diff;
4249 mediumCaller.attach(medium);
4250 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4251 mediumLock.attach(medium);
4252 }
4253 while (0);
4254
4255 ComObjPtr<MediumAttachment> attachment;
4256 attachment.createObject();
4257 rc = attachment->init(this,
4258 medium,
4259 aName,
4260 aControllerPort,
4261 aDevice,
4262 aType,
4263 fIndirect,
4264 false /* fPassthrough */,
4265 false /* fTempEject */,
4266 false /* fNonRotational */,
4267 false /* fDiscard */,
4268 fHotplug /* fHotPluggable */,
4269 Utf8Str::Empty);
4270 if (FAILED(rc)) return rc;
4271
4272 if (associate && !medium.isNull())
4273 {
4274 // as the last step, associate the medium to the VM
4275 rc = medium->i_addBackReference(mData->mUuid);
4276 // here we can fail because of Deleting, or being in process of creating a Diff
4277 if (FAILED(rc)) return rc;
4278
4279 mediumLock.release();
4280 treeLock.release();
4281 alock.release();
4282 i_addMediumToRegistry(medium);
4283 alock.acquire();
4284 treeLock.acquire();
4285 mediumLock.acquire();
4286 }
4287
4288 /* success: finally remember the attachment */
4289 i_setModified(IsModified_Storage);
4290 mMediumAttachments.backup();
4291 mMediumAttachments->push_back(attachment);
4292
4293 mediumLock.release();
4294 treeLock.release();
4295 alock.release();
4296
4297 if (fHotplug || fSilent)
4298 {
4299 if (!medium.isNull())
4300 {
4301 MediumLockList *pMediumLockList(new MediumLockList());
4302
4303 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4304 medium /* pToLockWrite */,
4305 false /* fMediumLockWriteAll */,
4306 NULL,
4307 *pMediumLockList);
4308 alock.acquire();
4309 if (FAILED(rc))
4310 delete pMediumLockList;
4311 else
4312 {
4313 mData->mSession.mLockedMedia.Unlock();
4314 alock.release();
4315 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4316 mData->mSession.mLockedMedia.Lock();
4317 alock.acquire();
4318 }
4319 alock.release();
4320 }
4321
4322 if (SUCCEEDED(rc))
4323 {
4324 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4325 /* Remove lock list in case of error. */
4326 if (FAILED(rc))
4327 {
4328 mData->mSession.mLockedMedia.Unlock();
4329 mData->mSession.mLockedMedia.Remove(attachment);
4330 mData->mSession.mLockedMedia.Lock();
4331 }
4332 }
4333 }
4334
4335 /* Save modified registries, but skip this machine as it's the caller's
4336 * job to save its settings like all other settings changes. */
4337 mParent->i_unmarkRegistryModified(i_getId());
4338 mParent->i_saveModifiedRegistries();
4339
4340 return rc;
4341}
4342
4343HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4344 LONG aDevice)
4345{
4346 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4347 aName.c_str(), aControllerPort, aDevice));
4348
4349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4350
4351 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4352 if (FAILED(rc)) return rc;
4353
4354 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4355
4356 /* Check for an existing controller. */
4357 ComObjPtr<StorageController> ctl;
4358 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4359 if (FAILED(rc)) return rc;
4360
4361 StorageControllerType_T ctrlType;
4362 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4363 if (FAILED(rc))
4364 return setError(E_FAIL,
4365 tr("Could not get type of controller '%s'"),
4366 aName.c_str());
4367
4368 bool fSilent = false;
4369 Utf8Str strReconfig;
4370
4371 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4372 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4373 if ( mData->mMachineState == MachineState_Paused
4374 && strReconfig == "1")
4375 fSilent = true;
4376
4377 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4378 bool fHotplug = false;
4379 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4380 fHotplug = true;
4381
4382 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4383 return setError(VBOX_E_INVALID_VM_STATE,
4384 tr("Controller '%s' does not support hotplugging"),
4385 aName.c_str());
4386
4387 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4388 aName,
4389 aControllerPort,
4390 aDevice);
4391 if (!pAttach)
4392 return setError(VBOX_E_OBJECT_NOT_FOUND,
4393 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4394 aDevice, aControllerPort, aName.c_str());
4395
4396 if (fHotplug && !pAttach->i_getHotPluggable())
4397 return setError(VBOX_E_NOT_SUPPORTED,
4398 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4399 aDevice, aControllerPort, aName.c_str());
4400
4401 /*
4402 * The VM has to detach the device before we delete any implicit diffs.
4403 * If this fails we can roll back without loosing data.
4404 */
4405 if (fHotplug || fSilent)
4406 {
4407 alock.release();
4408 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4409 alock.acquire();
4410 }
4411 if (FAILED(rc)) return rc;
4412
4413 /* If we are here everything went well and we can delete the implicit now. */
4414 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4415
4416 alock.release();
4417
4418 /* Save modified registries, but skip this machine as it's the caller's
4419 * job to save its settings like all other settings changes. */
4420 mParent->i_unmarkRegistryModified(i_getId());
4421 mParent->i_saveModifiedRegistries();
4422
4423 return rc;
4424}
4425
4426HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4427 LONG aDevice, BOOL aPassthrough)
4428{
4429 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4430 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4431
4432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4433
4434 HRESULT rc = i_checkStateDependency(MutableStateDep);
4435 if (FAILED(rc)) return rc;
4436
4437 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4438
4439 if (Global::IsOnlineOrTransient(mData->mMachineState))
4440 return setError(VBOX_E_INVALID_VM_STATE,
4441 tr("Invalid machine state: %s"),
4442 Global::stringifyMachineState(mData->mMachineState));
4443
4444 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4445 aName,
4446 aControllerPort,
4447 aDevice);
4448 if (!pAttach)
4449 return setError(VBOX_E_OBJECT_NOT_FOUND,
4450 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4451 aDevice, aControllerPort, aName.c_str());
4452
4453
4454 i_setModified(IsModified_Storage);
4455 mMediumAttachments.backup();
4456
4457 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4458
4459 if (pAttach->i_getType() != DeviceType_DVD)
4460 return setError(E_INVALIDARG,
4461 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4462 aDevice, aControllerPort, aName.c_str());
4463 pAttach->i_updatePassthrough(!!aPassthrough);
4464
4465 return S_OK;
4466}
4467
4468HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4469 LONG aDevice, BOOL aTemporaryEject)
4470{
4471
4472 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4473 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4474
4475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4476
4477 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4478 if (FAILED(rc)) return rc;
4479
4480 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4481 aName,
4482 aControllerPort,
4483 aDevice);
4484 if (!pAttach)
4485 return setError(VBOX_E_OBJECT_NOT_FOUND,
4486 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4487 aDevice, aControllerPort, aName.c_str());
4488
4489
4490 i_setModified(IsModified_Storage);
4491 mMediumAttachments.backup();
4492
4493 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4494
4495 if (pAttach->i_getType() != DeviceType_DVD)
4496 return setError(E_INVALIDARG,
4497 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4498 aDevice, aControllerPort, aName.c_str());
4499 pAttach->i_updateTempEject(!!aTemporaryEject);
4500
4501 return S_OK;
4502}
4503
4504HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4505 LONG aDevice, BOOL aNonRotational)
4506{
4507
4508 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4509 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4510
4511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4512
4513 HRESULT rc = i_checkStateDependency(MutableStateDep);
4514 if (FAILED(rc)) return rc;
4515
4516 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4517
4518 if (Global::IsOnlineOrTransient(mData->mMachineState))
4519 return setError(VBOX_E_INVALID_VM_STATE,
4520 tr("Invalid machine state: %s"),
4521 Global::stringifyMachineState(mData->mMachineState));
4522
4523 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4524 aName,
4525 aControllerPort,
4526 aDevice);
4527 if (!pAttach)
4528 return setError(VBOX_E_OBJECT_NOT_FOUND,
4529 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4530 aDevice, aControllerPort, aName.c_str());
4531
4532
4533 i_setModified(IsModified_Storage);
4534 mMediumAttachments.backup();
4535
4536 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4537
4538 if (pAttach->i_getType() != DeviceType_HardDisk)
4539 return setError(E_INVALIDARG,
4540 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4541 aDevice, aControllerPort, aName.c_str());
4542 pAttach->i_updateNonRotational(!!aNonRotational);
4543
4544 return S_OK;
4545}
4546
4547HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4548 LONG aDevice, BOOL aDiscard)
4549{
4550
4551 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4552 aName.c_str(), aControllerPort, aDevice, aDiscard));
4553
4554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4555
4556 HRESULT rc = i_checkStateDependency(MutableStateDep);
4557 if (FAILED(rc)) return rc;
4558
4559 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4560
4561 if (Global::IsOnlineOrTransient(mData->mMachineState))
4562 return setError(VBOX_E_INVALID_VM_STATE,
4563 tr("Invalid machine state: %s"),
4564 Global::stringifyMachineState(mData->mMachineState));
4565
4566 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4567 aName,
4568 aControllerPort,
4569 aDevice);
4570 if (!pAttach)
4571 return setError(VBOX_E_OBJECT_NOT_FOUND,
4572 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4573 aDevice, aControllerPort, aName.c_str());
4574
4575
4576 i_setModified(IsModified_Storage);
4577 mMediumAttachments.backup();
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580
4581 if (pAttach->i_getType() != DeviceType_HardDisk)
4582 return setError(E_INVALIDARG,
4583 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4584 aDevice, aControllerPort, aName.c_str());
4585 pAttach->i_updateDiscard(!!aDiscard);
4586
4587 return S_OK;
4588}
4589
4590HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4591 LONG aDevice, BOOL aHotPluggable)
4592{
4593 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4594 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4595
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 HRESULT rc = i_checkStateDependency(MutableStateDep);
4599 if (FAILED(rc)) return rc;
4600
4601 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4602
4603 if (Global::IsOnlineOrTransient(mData->mMachineState))
4604 return setError(VBOX_E_INVALID_VM_STATE,
4605 tr("Invalid machine state: %s"),
4606 Global::stringifyMachineState(mData->mMachineState));
4607
4608 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4609 aName,
4610 aControllerPort,
4611 aDevice);
4612 if (!pAttach)
4613 return setError(VBOX_E_OBJECT_NOT_FOUND,
4614 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4615 aDevice, aControllerPort, aName.c_str());
4616
4617 /* Check for an existing controller. */
4618 ComObjPtr<StorageController> ctl;
4619 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4620 if (FAILED(rc)) return rc;
4621
4622 StorageControllerType_T ctrlType;
4623 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4624 if (FAILED(rc))
4625 return setError(E_FAIL,
4626 tr("Could not get type of controller '%s'"),
4627 aName.c_str());
4628
4629 if (!i_isControllerHotplugCapable(ctrlType))
4630 return setError(VBOX_E_NOT_SUPPORTED,
4631 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4632 aName.c_str());
4633
4634 i_setModified(IsModified_Storage);
4635 mMediumAttachments.backup();
4636
4637 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4638
4639 if (pAttach->i_getType() == DeviceType_Floppy)
4640 return setError(E_INVALIDARG,
4641 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4642 aDevice, aControllerPort, aName.c_str());
4643 pAttach->i_updateHotPluggable(!!aHotPluggable);
4644
4645 return S_OK;
4646}
4647
4648HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4649 LONG aDevice)
4650{
4651 int rc = S_OK;
4652 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4653 aName.c_str(), aControllerPort, aDevice));
4654
4655 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4656
4657 return rc;
4658}
4659
4660HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4661 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4662{
4663 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4664 aName.c_str(), aControllerPort, aDevice));
4665
4666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4667
4668 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4669 if (FAILED(rc)) return rc;
4670
4671 if (Global::IsOnlineOrTransient(mData->mMachineState))
4672 return setError(VBOX_E_INVALID_VM_STATE,
4673 tr("Invalid machine state: %s"),
4674 Global::stringifyMachineState(mData->mMachineState));
4675
4676 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4677 aName,
4678 aControllerPort,
4679 aDevice);
4680 if (!pAttach)
4681 return setError(VBOX_E_OBJECT_NOT_FOUND,
4682 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4683 aDevice, aControllerPort, aName.c_str());
4684
4685
4686 i_setModified(IsModified_Storage);
4687 mMediumAttachments.backup();
4688
4689 IBandwidthGroup *iB = aBandwidthGroup;
4690 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4691 if (aBandwidthGroup && group.isNull())
4692 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4693
4694 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4695
4696 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4697 if (strBandwidthGroupOld.isNotEmpty())
4698 {
4699 /* Get the bandwidth group object and release it - this must not fail. */
4700 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4701 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4702 Assert(SUCCEEDED(rc));
4703
4704 pBandwidthGroupOld->i_release();
4705 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4706 }
4707
4708 if (!group.isNull())
4709 {
4710 group->i_reference();
4711 pAttach->i_updateBandwidthGroup(group->i_getName());
4712 }
4713
4714 return S_OK;
4715}
4716
4717HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4718 LONG aControllerPort,
4719 LONG aDevice,
4720 DeviceType_T aType)
4721{
4722 HRESULT rc = S_OK;
4723
4724 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4725 aName.c_str(), aControllerPort, aDevice, aType));
4726
4727 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4728
4729 return rc;
4730}
4731
4732
4733HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4734 LONG aControllerPort,
4735 LONG aDevice,
4736 BOOL aForce)
4737{
4738 int rc = S_OK;
4739 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4740 aName.c_str(), aControllerPort, aForce));
4741
4742 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4743
4744 return rc;
4745}
4746
4747HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4748 LONG aControllerPort,
4749 LONG aDevice,
4750 const ComPtr<IMedium> &aMedium,
4751 BOOL aForce)
4752{
4753 int rc = S_OK;
4754 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4755 aName.c_str(), aControllerPort, aDevice, aForce));
4756
4757 // request the host lock first, since might be calling Host methods for getting host drives;
4758 // next, protect the media tree all the while we're in here, as well as our member variables
4759 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4760 this->lockHandle(),
4761 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4762
4763 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4764 aName,
4765 aControllerPort,
4766 aDevice);
4767 if (pAttach.isNull())
4768 return setError(VBOX_E_OBJECT_NOT_FOUND,
4769 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4770 aDevice, aControllerPort, aName.c_str());
4771
4772 /* Remember previously mounted medium. The medium before taking the
4773 * backup is not necessarily the same thing. */
4774 ComObjPtr<Medium> oldmedium;
4775 oldmedium = pAttach->i_getMedium();
4776
4777 IMedium *iM = aMedium;
4778 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4779 if (aMedium && pMedium.isNull())
4780 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4781
4782 AutoCaller mediumCaller(pMedium);
4783 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4784
4785 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4786 if (pMedium)
4787 {
4788 DeviceType_T mediumType = pAttach->i_getType();
4789 switch (mediumType)
4790 {
4791 case DeviceType_DVD:
4792 case DeviceType_Floppy:
4793 break;
4794
4795 default:
4796 return setError(VBOX_E_INVALID_OBJECT_STATE,
4797 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4798 aControllerPort,
4799 aDevice,
4800 aName.c_str());
4801 }
4802 }
4803
4804 i_setModified(IsModified_Storage);
4805 mMediumAttachments.backup();
4806
4807 {
4808 // The backup operation makes the pAttach reference point to the
4809 // old settings. Re-get the correct reference.
4810 pAttach = i_findAttachment(*mMediumAttachments.data(),
4811 aName,
4812 aControllerPort,
4813 aDevice);
4814 if (!oldmedium.isNull())
4815 oldmedium->i_removeBackReference(mData->mUuid);
4816 if (!pMedium.isNull())
4817 {
4818 pMedium->i_addBackReference(mData->mUuid);
4819
4820 mediumLock.release();
4821 multiLock.release();
4822 i_addMediumToRegistry(pMedium);
4823 multiLock.acquire();
4824 mediumLock.acquire();
4825 }
4826
4827 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4828 pAttach->i_updateMedium(pMedium);
4829 }
4830
4831 i_setModified(IsModified_Storage);
4832
4833 mediumLock.release();
4834 multiLock.release();
4835 rc = i_onMediumChange(pAttach, aForce);
4836 multiLock.acquire();
4837 mediumLock.acquire();
4838
4839 /* On error roll back this change only. */
4840 if (FAILED(rc))
4841 {
4842 if (!pMedium.isNull())
4843 pMedium->i_removeBackReference(mData->mUuid);
4844 pAttach = i_findAttachment(*mMediumAttachments.data(),
4845 aName,
4846 aControllerPort,
4847 aDevice);
4848 /* If the attachment is gone in the meantime, bail out. */
4849 if (pAttach.isNull())
4850 return rc;
4851 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4852 if (!oldmedium.isNull())
4853 oldmedium->i_addBackReference(mData->mUuid);
4854 pAttach->i_updateMedium(oldmedium);
4855 }
4856
4857 mediumLock.release();
4858 multiLock.release();
4859
4860 /* Save modified registries, but skip this machine as it's the caller's
4861 * job to save its settings like all other settings changes. */
4862 mParent->i_unmarkRegistryModified(i_getId());
4863 mParent->i_saveModifiedRegistries();
4864
4865 return rc;
4866}
4867HRESULT Machine::getMedium(const com::Utf8Str &aName,
4868 LONG aControllerPort,
4869 LONG aDevice,
4870 ComPtr<IMedium> &aMedium)
4871{
4872 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4873 aName.c_str(), aControllerPort, aDevice));
4874
4875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4876
4877 aMedium = NULL;
4878
4879 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4880 aName,
4881 aControllerPort,
4882 aDevice);
4883 if (pAttach.isNull())
4884 return setError(VBOX_E_OBJECT_NOT_FOUND,
4885 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4886 aDevice, aControllerPort, aName.c_str());
4887
4888 aMedium = pAttach->i_getMedium();
4889
4890 return S_OK;
4891}
4892
4893HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4894{
4895
4896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4897
4898 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4899
4900 return S_OK;
4901}
4902
4903HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4904{
4905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4906
4907 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4908
4909 return S_OK;
4910}
4911
4912HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4913{
4914 /* Do not assert if slot is out of range, just return the advertised
4915 status. testdriver/vbox.py triggers this in logVmInfo. */
4916 if (aSlot >= mNetworkAdapters.size())
4917 return setError(E_INVALIDARG,
4918 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4919 aSlot, mNetworkAdapters.size());
4920
4921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4924
4925 return S_OK;
4926}
4927
4928HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4929{
4930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4931
4932 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4933 size_t i = 0;
4934 for (settings::StringsMap::const_iterator
4935 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4936 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4937 ++it, ++i)
4938 aKeys[i] = it->first;
4939
4940 return S_OK;
4941}
4942
4943 /**
4944 * @note Locks this object for reading.
4945 */
4946HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4947 com::Utf8Str &aValue)
4948{
4949 /* start with nothing found */
4950 aValue = "";
4951
4952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4953
4954 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4955 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4956 // found:
4957 aValue = it->second; // source is a Utf8Str
4958
4959 /* return the result to caller (may be empty) */
4960 return S_OK;
4961}
4962
4963 /**
4964 * @note Locks mParent for writing + this object for writing.
4965 */
4966HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4967{
4968 Utf8Str strOldValue; // empty
4969
4970 // locking note: we only hold the read lock briefly to look up the old value,
4971 // then release it and call the onExtraCanChange callbacks. There is a small
4972 // chance of a race insofar as the callback might be called twice if two callers
4973 // change the same key at the same time, but that's a much better solution
4974 // than the deadlock we had here before. The actual changing of the extradata
4975 // is then performed under the write lock and race-free.
4976
4977 // look up the old value first; if nothing has changed then we need not do anything
4978 {
4979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4980
4981 // For snapshots don't even think about allowing changes, extradata
4982 // is global for a machine, so there is nothing snapshot specific.
4983 if (i_isSnapshotMachine())
4984 return setError(VBOX_E_INVALID_VM_STATE,
4985 tr("Cannot set extradata for a snapshot"));
4986
4987 // check if the right IMachine instance is used
4988 if (mData->mRegistered && !i_isSessionMachine())
4989 return setError(VBOX_E_INVALID_VM_STATE,
4990 tr("Cannot set extradata for an immutable machine"));
4991
4992 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4993 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4994 strOldValue = it->second;
4995 }
4996
4997 bool fChanged;
4998 if ((fChanged = (strOldValue != aValue)))
4999 {
5000 // ask for permission from all listeners outside the locks;
5001 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5002 // lock to copy the list of callbacks to invoke
5003 Bstr error;
5004 Bstr bstrValue(aValue);
5005
5006 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5007 {
5008 const char *sep = error.isEmpty() ? "" : ": ";
5009 CBSTR err = error.raw();
5010 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5011 return setError(E_ACCESSDENIED,
5012 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5013 aKey.c_str(),
5014 aValue.c_str(),
5015 sep,
5016 err);
5017 }
5018
5019 // data is changing and change not vetoed: then write it out under the lock
5020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5021
5022 if (aValue.isEmpty())
5023 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5024 else
5025 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5026 // creates a new key if needed
5027
5028 bool fNeedsGlobalSaveSettings = false;
5029 // This saving of settings is tricky: there is no "old state" for the
5030 // extradata items at all (unlike all other settings), so the old/new
5031 // settings comparison would give a wrong result!
5032 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5033
5034 if (fNeedsGlobalSaveSettings)
5035 {
5036 // save the global settings; for that we should hold only the VirtualBox lock
5037 alock.release();
5038 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5039 mParent->i_saveSettings();
5040 }
5041 }
5042
5043 // fire notification outside the lock
5044 if (fChanged)
5045 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5046
5047 return S_OK;
5048}
5049
5050HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5051{
5052 aProgress = NULL;
5053 NOREF(aSettingsFilePath);
5054 ReturnComNotImplemented();
5055}
5056
5057HRESULT Machine::saveSettings()
5058{
5059 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5060
5061 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5062 if (FAILED(rc)) return rc;
5063
5064 /* the settings file path may never be null */
5065 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5066
5067 /* save all VM data excluding snapshots */
5068 bool fNeedsGlobalSaveSettings = false;
5069 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5070 mlock.release();
5071
5072 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5073 {
5074 // save the global settings; for that we should hold only the VirtualBox lock
5075 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5076 rc = mParent->i_saveSettings();
5077 }
5078
5079 return rc;
5080}
5081
5082
5083HRESULT Machine::discardSettings()
5084{
5085 /*
5086 * We need to take the machine list lock here as well as the machine one
5087 * or we'll get into trouble should any media stuff require rolling back.
5088 *
5089 * Details:
5090 *
5091 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5092 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5093 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5094 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5095 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5096 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5097 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5098 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5099 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5100 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5101 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5102 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5103 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5104 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5105 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5106 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5107 * 0:005> k
5108 * # Child-SP RetAddr Call Site
5109 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5110 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5111 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5112 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5113 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5114 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5115 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5116 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5117 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5118 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5119 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5120 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5121 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5122 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5123 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5124 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5125 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5126 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5127 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5128 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5129 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5130 *
5131 */
5132 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5134
5135 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5136 if (FAILED(rc)) return rc;
5137
5138 /*
5139 * during this rollback, the session will be notified if data has
5140 * been actually changed
5141 */
5142 i_rollback(true /* aNotify */);
5143
5144 return S_OK;
5145}
5146
5147/** @note Locks objects! */
5148HRESULT Machine::unregister(AutoCaller &autoCaller,
5149 CleanupMode_T aCleanupMode,
5150 std::vector<ComPtr<IMedium> > &aMedia)
5151{
5152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5153
5154 Guid id(i_getId());
5155
5156 if (mData->mSession.mState != SessionState_Unlocked)
5157 return setError(VBOX_E_INVALID_OBJECT_STATE,
5158 tr("Cannot unregister the machine '%s' while it is locked"),
5159 mUserData->s.strName.c_str());
5160
5161 // wait for state dependents to drop to zero
5162 i_ensureNoStateDependencies();
5163
5164 if (!mData->mAccessible)
5165 {
5166 // inaccessible maschines can only be unregistered; uninitialize ourselves
5167 // here because currently there may be no unregistered that are inaccessible
5168 // (this state combination is not supported). Note releasing the caller and
5169 // leaving the lock before calling uninit()
5170 alock.release();
5171 autoCaller.release();
5172
5173 uninit();
5174
5175 mParent->i_unregisterMachine(this, id);
5176 // calls VirtualBox::i_saveSettings()
5177
5178 return S_OK;
5179 }
5180
5181 HRESULT rc = S_OK;
5182
5183 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5184 // discard saved state
5185 if (mData->mMachineState == MachineState_Saved)
5186 {
5187 // add the saved state file to the list of files the caller should delete
5188 Assert(!mSSData->strStateFilePath.isEmpty());
5189 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5190
5191 mSSData->strStateFilePath.setNull();
5192
5193 // unconditionally set the machine state to powered off, we now
5194 // know no session has locked the machine
5195 mData->mMachineState = MachineState_PoweredOff;
5196 }
5197
5198 size_t cSnapshots = 0;
5199 if (mData->mFirstSnapshot)
5200 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5201 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5202 // fail now before we start detaching media
5203 return setError(VBOX_E_INVALID_OBJECT_STATE,
5204 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5205 mUserData->s.strName.c_str(), cSnapshots);
5206
5207 // This list collects the medium objects from all medium attachments
5208 // which we will detach from the machine and its snapshots, in a specific
5209 // order which allows for closing all media without getting "media in use"
5210 // errors, simply by going through the list from the front to the back:
5211 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5212 // and must be closed before the parent media from the snapshots, or closing the parents
5213 // will fail because they still have children);
5214 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5215 // the root ("first") snapshot of the machine.
5216 MediaList llMedia;
5217
5218 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5219 && mMediumAttachments->size()
5220 )
5221 {
5222 // we have media attachments: detach them all and add the Medium objects to our list
5223 if (aCleanupMode != CleanupMode_UnregisterOnly)
5224 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5225 else
5226 return setError(VBOX_E_INVALID_OBJECT_STATE,
5227 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5228 mUserData->s.strName.c_str(), mMediumAttachments->size());
5229 }
5230
5231 if (cSnapshots)
5232 {
5233 // add the media from the medium attachments of the snapshots to llMedia
5234 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5235 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5236 // into the children first
5237
5238 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5239 MachineState_T oldState = mData->mMachineState;
5240 mData->mMachineState = MachineState_DeletingSnapshot;
5241
5242 // make a copy of the first snapshot so the refcount does not drop to 0
5243 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5244 // because of the AutoCaller voodoo)
5245 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5246
5247 // GO!
5248 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5249
5250 mData->mMachineState = oldState;
5251 }
5252
5253 if (FAILED(rc))
5254 {
5255 i_rollbackMedia();
5256 return rc;
5257 }
5258
5259 // commit all the media changes made above
5260 i_commitMedia();
5261
5262 mData->mRegistered = false;
5263
5264 // machine lock no longer needed
5265 alock.release();
5266
5267 // return media to caller
5268 aMedia.resize(llMedia.size());
5269 size_t i = 0;
5270 for (MediaList::const_iterator
5271 it = llMedia.begin();
5272 it != llMedia.end();
5273 ++it, ++i)
5274 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5275
5276 mParent->i_unregisterMachine(this, id);
5277 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5278
5279 return S_OK;
5280}
5281
5282/**
5283 * Task record for deleting a machine config.
5284 */
5285class Machine::DeleteConfigTask
5286 : public Machine::Task
5287{
5288public:
5289 DeleteConfigTask(Machine *m,
5290 Progress *p,
5291 const Utf8Str &t,
5292 const RTCList<ComPtr<IMedium> > &llMediums,
5293 const StringsList &llFilesToDelete)
5294 : Task(m, p, t),
5295 m_llMediums(llMediums),
5296 m_llFilesToDelete(llFilesToDelete)
5297 {}
5298
5299private:
5300 void handler()
5301 {
5302 try
5303 {
5304 m_pMachine->i_deleteConfigHandler(*this);
5305 }
5306 catch (...)
5307 {
5308 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5309 }
5310 }
5311
5312 RTCList<ComPtr<IMedium> > m_llMediums;
5313 StringsList m_llFilesToDelete;
5314
5315 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5316};
5317
5318/**
5319 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5320 * SessionMachine::taskHandler().
5321 *
5322 * @note Locks this object for writing.
5323 *
5324 * @param task
5325 * @return
5326 */
5327void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5328{
5329 LogFlowThisFuncEnter();
5330
5331 AutoCaller autoCaller(this);
5332 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5333 if (FAILED(autoCaller.rc()))
5334 {
5335 /* we might have been uninitialized because the session was accidentally
5336 * closed by the client, so don't assert */
5337 HRESULT rc = setError(E_FAIL,
5338 tr("The session has been accidentally closed"));
5339 task.m_pProgress->i_notifyComplete(rc);
5340 LogFlowThisFuncLeave();
5341 return;
5342 }
5343
5344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5345
5346 HRESULT rc = S_OK;
5347
5348 try
5349 {
5350 ULONG uLogHistoryCount = 3;
5351 ComPtr<ISystemProperties> systemProperties;
5352 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5353 if (FAILED(rc)) throw rc;
5354
5355 if (!systemProperties.isNull())
5356 {
5357 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5358 if (FAILED(rc)) throw rc;
5359 }
5360
5361 MachineState_T oldState = mData->mMachineState;
5362 i_setMachineState(MachineState_SettingUp);
5363 alock.release();
5364 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5365 {
5366 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5367 {
5368 AutoCaller mac(pMedium);
5369 if (FAILED(mac.rc())) throw mac.rc();
5370 Utf8Str strLocation = pMedium->i_getLocationFull();
5371 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5372 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5373 if (FAILED(rc)) throw rc;
5374 }
5375 if (pMedium->i_isMediumFormatFile())
5376 {
5377 ComPtr<IProgress> pProgress2;
5378 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5379 if (FAILED(rc)) throw rc;
5380 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5381 if (FAILED(rc)) throw rc;
5382 /* Check the result of the asynchronous process. */
5383 LONG iRc;
5384 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5385 if (FAILED(rc)) throw rc;
5386 /* If the thread of the progress object has an error, then
5387 * retrieve the error info from there, or it'll be lost. */
5388 if (FAILED(iRc))
5389 throw setError(ProgressErrorInfo(pProgress2));
5390 }
5391
5392 /* Close the medium, deliberately without checking the return
5393 * code, and without leaving any trace in the error info, as
5394 * a failure here is a very minor issue, which shouldn't happen
5395 * as above we even managed to delete the medium. */
5396 {
5397 ErrorInfoKeeper eik;
5398 pMedium->Close();
5399 }
5400 }
5401 i_setMachineState(oldState);
5402 alock.acquire();
5403
5404 // delete the files pushed on the task list by Machine::Delete()
5405 // (this includes saved states of the machine and snapshots and
5406 // medium storage files from the IMedium list passed in, and the
5407 // machine XML file)
5408 for (StringsList::const_iterator
5409 it = task.m_llFilesToDelete.begin();
5410 it != task.m_llFilesToDelete.end();
5411 ++it)
5412 {
5413 const Utf8Str &strFile = *it;
5414 LogFunc(("Deleting file %s\n", strFile.c_str()));
5415 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5416 if (FAILED(rc)) throw rc;
5417
5418 int vrc = RTFileDelete(strFile.c_str());
5419 if (RT_FAILURE(vrc))
5420 throw setError(VBOX_E_IPRT_ERROR,
5421 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5422 }
5423
5424 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5425 if (FAILED(rc)) throw rc;
5426
5427 /* delete the settings only when the file actually exists */
5428 if (mData->pMachineConfigFile->fileExists())
5429 {
5430 /* Delete any backup or uncommitted XML files. Ignore failures.
5431 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5432 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5433 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5434 RTFileDelete(otherXml.c_str());
5435 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5436 RTFileDelete(otherXml.c_str());
5437
5438 /* delete the Logs folder, nothing important should be left
5439 * there (we don't check for errors because the user might have
5440 * some private files there that we don't want to delete) */
5441 Utf8Str logFolder;
5442 getLogFolder(logFolder);
5443 Assert(logFolder.length());
5444 if (RTDirExists(logFolder.c_str()))
5445 {
5446 /* Delete all VBox.log[.N] files from the Logs folder
5447 * (this must be in sync with the rotation logic in
5448 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5449 * files that may have been created by the GUI. */
5450 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5451 logFolder.c_str(), RTPATH_DELIMITER);
5452 RTFileDelete(log.c_str());
5453 log = Utf8StrFmt("%s%cVBox.png",
5454 logFolder.c_str(), RTPATH_DELIMITER);
5455 RTFileDelete(log.c_str());
5456 for (int i = uLogHistoryCount; i > 0; i--)
5457 {
5458 log = Utf8StrFmt("%s%cVBox.log.%d",
5459 logFolder.c_str(), RTPATH_DELIMITER, i);
5460 RTFileDelete(log.c_str());
5461 log = Utf8StrFmt("%s%cVBox.png.%d",
5462 logFolder.c_str(), RTPATH_DELIMITER, i);
5463 RTFileDelete(log.c_str());
5464 }
5465#if defined(RT_OS_WINDOWS)
5466 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5467 RTFileDelete(log.c_str());
5468 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5469 RTFileDelete(log.c_str());
5470#endif
5471
5472 RTDirRemove(logFolder.c_str());
5473 }
5474
5475 /* delete the Snapshots folder, nothing important should be left
5476 * there (we don't check for errors because the user might have
5477 * some private files there that we don't want to delete) */
5478 Utf8Str strFullSnapshotFolder;
5479 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5480 Assert(!strFullSnapshotFolder.isEmpty());
5481 if (RTDirExists(strFullSnapshotFolder.c_str()))
5482 RTDirRemove(strFullSnapshotFolder.c_str());
5483
5484 // delete the directory that contains the settings file, but only
5485 // if it matches the VM name
5486 Utf8Str settingsDir;
5487 if (i_isInOwnDir(&settingsDir))
5488 RTDirRemove(settingsDir.c_str());
5489 }
5490
5491 alock.release();
5492
5493 mParent->i_saveModifiedRegistries();
5494 }
5495 catch (HRESULT aRC) { rc = aRC; }
5496
5497 task.m_pProgress->i_notifyComplete(rc);
5498
5499 LogFlowThisFuncLeave();
5500}
5501
5502HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5503{
5504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5505
5506 HRESULT rc = i_checkStateDependency(MutableStateDep);
5507 if (FAILED(rc)) return rc;
5508
5509 if (mData->mRegistered)
5510 return setError(VBOX_E_INVALID_VM_STATE,
5511 tr("Cannot delete settings of a registered machine"));
5512
5513 // collect files to delete
5514 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5515 if (mData->pMachineConfigFile->fileExists())
5516 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5517
5518 RTCList<ComPtr<IMedium> > llMediums;
5519 for (size_t i = 0; i < aMedia.size(); ++i)
5520 {
5521 IMedium *pIMedium(aMedia[i]);
5522 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5523 if (pMedium.isNull())
5524 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5525 SafeArray<BSTR> ids;
5526 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5527 if (FAILED(rc)) return rc;
5528 /* At this point the medium should not have any back references
5529 * anymore. If it has it is attached to another VM and *must* not
5530 * deleted. */
5531 if (ids.size() < 1)
5532 llMediums.append(pMedium);
5533 }
5534
5535 ComObjPtr<Progress> pProgress;
5536 pProgress.createObject();
5537 rc = pProgress->init(i_getVirtualBox(),
5538 static_cast<IMachine*>(this) /* aInitiator */,
5539 tr("Deleting files"),
5540 true /* fCancellable */,
5541 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5542 tr("Collecting file inventory"));
5543 if (FAILED(rc))
5544 return rc;
5545
5546 /* create and start the task on a separate thread (note that it will not
5547 * start working until we release alock) */
5548 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5549 rc = pTask->createThread();
5550 if (FAILED(rc))
5551 return rc;
5552
5553 pProgress.queryInterfaceTo(aProgress.asOutParam());
5554
5555 LogFlowFuncLeave();
5556
5557 return S_OK;
5558}
5559
5560HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5561{
5562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5563
5564 ComObjPtr<Snapshot> pSnapshot;
5565 HRESULT rc;
5566
5567 if (aNameOrId.isEmpty())
5568 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5569 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5570 else
5571 {
5572 Guid uuid(aNameOrId);
5573 if (uuid.isValid())
5574 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5575 else
5576 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5577 }
5578 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5579
5580 return rc;
5581}
5582
5583HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5584{
5585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5586
5587 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5588 if (FAILED(rc)) return rc;
5589
5590 ComObjPtr<SharedFolder> sharedFolder;
5591 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5592 if (SUCCEEDED(rc))
5593 return setError(VBOX_E_OBJECT_IN_USE,
5594 tr("Shared folder named '%s' already exists"),
5595 aName.c_str());
5596
5597 sharedFolder.createObject();
5598 rc = sharedFolder->init(i_getMachine(),
5599 aName,
5600 aHostPath,
5601 !!aWritable,
5602 !!aAutomount,
5603 true /* fFailOnError */);
5604 if (FAILED(rc)) return rc;
5605
5606 i_setModified(IsModified_SharedFolders);
5607 mHWData.backup();
5608 mHWData->mSharedFolders.push_back(sharedFolder);
5609
5610 /* inform the direct session if any */
5611 alock.release();
5612 i_onSharedFolderChange();
5613
5614 return S_OK;
5615}
5616
5617HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5618{
5619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5620
5621 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5622 if (FAILED(rc)) return rc;
5623
5624 ComObjPtr<SharedFolder> sharedFolder;
5625 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5626 if (FAILED(rc)) return rc;
5627
5628 i_setModified(IsModified_SharedFolders);
5629 mHWData.backup();
5630 mHWData->mSharedFolders.remove(sharedFolder);
5631
5632 /* inform the direct session if any */
5633 alock.release();
5634 i_onSharedFolderChange();
5635
5636 return S_OK;
5637}
5638
5639HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5640{
5641 /* start with No */
5642 *aCanShow = FALSE;
5643
5644 ComPtr<IInternalSessionControl> directControl;
5645 {
5646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5647
5648 if (mData->mSession.mState != SessionState_Locked)
5649 return setError(VBOX_E_INVALID_VM_STATE,
5650 tr("Machine is not locked for session (session state: %s)"),
5651 Global::stringifySessionState(mData->mSession.mState));
5652
5653 if (mData->mSession.mLockType == LockType_VM)
5654 directControl = mData->mSession.mDirectControl;
5655 }
5656
5657 /* ignore calls made after #OnSessionEnd() is called */
5658 if (!directControl)
5659 return S_OK;
5660
5661 LONG64 dummy;
5662 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5663}
5664
5665HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5666{
5667 ComPtr<IInternalSessionControl> directControl;
5668 {
5669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5670
5671 if (mData->mSession.mState != SessionState_Locked)
5672 return setError(E_FAIL,
5673 tr("Machine is not locked for session (session state: %s)"),
5674 Global::stringifySessionState(mData->mSession.mState));
5675
5676 if (mData->mSession.mLockType == LockType_VM)
5677 directControl = mData->mSession.mDirectControl;
5678 }
5679
5680 /* ignore calls made after #OnSessionEnd() is called */
5681 if (!directControl)
5682 return S_OK;
5683
5684 BOOL dummy;
5685 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5686}
5687
5688#ifdef VBOX_WITH_GUEST_PROPS
5689/**
5690 * Look up a guest property in VBoxSVC's internal structures.
5691 */
5692HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5693 com::Utf8Str &aValue,
5694 LONG64 *aTimestamp,
5695 com::Utf8Str &aFlags) const
5696{
5697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5698
5699 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5700 if (it != mHWData->mGuestProperties.end())
5701 {
5702 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5703 aValue = it->second.strValue;
5704 *aTimestamp = it->second.mTimestamp;
5705 GuestPropWriteFlags(it->second.mFlags, szFlags);
5706 aFlags = Utf8Str(szFlags);
5707 }
5708
5709 return S_OK;
5710}
5711
5712/**
5713 * Query the VM that a guest property belongs to for the property.
5714 * @returns E_ACCESSDENIED if the VM process is not available or not
5715 * currently handling queries and the lookup should then be done in
5716 * VBoxSVC.
5717 */
5718HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5719 com::Utf8Str &aValue,
5720 LONG64 *aTimestamp,
5721 com::Utf8Str &aFlags) const
5722{
5723 HRESULT rc = S_OK;
5724 BSTR bValue = NULL;
5725 BSTR bFlags = NULL;
5726
5727 ComPtr<IInternalSessionControl> directControl;
5728 {
5729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5730 if (mData->mSession.mLockType == LockType_VM)
5731 directControl = mData->mSession.mDirectControl;
5732 }
5733
5734 /* ignore calls made after #OnSessionEnd() is called */
5735 if (!directControl)
5736 rc = E_ACCESSDENIED;
5737 else
5738 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5739 0 /* accessMode */,
5740 &bValue, aTimestamp, &bFlags);
5741
5742 aValue = bValue;
5743 aFlags = bFlags;
5744
5745 return rc;
5746}
5747#endif // VBOX_WITH_GUEST_PROPS
5748
5749HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5750 com::Utf8Str &aValue,
5751 LONG64 *aTimestamp,
5752 com::Utf8Str &aFlags)
5753{
5754#ifndef VBOX_WITH_GUEST_PROPS
5755 ReturnComNotImplemented();
5756#else // VBOX_WITH_GUEST_PROPS
5757
5758 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5759
5760 if (rc == E_ACCESSDENIED)
5761 /* The VM is not running or the service is not (yet) accessible */
5762 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5763 return rc;
5764#endif // VBOX_WITH_GUEST_PROPS
5765}
5766
5767HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5768{
5769 LONG64 dummyTimestamp;
5770 com::Utf8Str dummyFlags;
5771 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5772 return rc;
5773
5774}
5775HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5776{
5777 com::Utf8Str dummyFlags;
5778 com::Utf8Str dummyValue;
5779 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5780 return rc;
5781}
5782
5783#ifdef VBOX_WITH_GUEST_PROPS
5784/**
5785 * Set a guest property in VBoxSVC's internal structures.
5786 */
5787HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5788 const com::Utf8Str &aFlags, bool fDelete)
5789{
5790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5791 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5792 if (FAILED(rc)) return rc;
5793
5794 try
5795 {
5796 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5797 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5798 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5799
5800 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5801 if (it == mHWData->mGuestProperties.end())
5802 {
5803 if (!fDelete)
5804 {
5805 i_setModified(IsModified_MachineData);
5806 mHWData.backupEx();
5807
5808 RTTIMESPEC time;
5809 HWData::GuestProperty prop;
5810 prop.strValue = Bstr(aValue).raw();
5811 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5812 prop.mFlags = fFlags;
5813 mHWData->mGuestProperties[aName] = prop;
5814 }
5815 }
5816 else
5817 {
5818 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5819 {
5820 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5821 }
5822 else
5823 {
5824 i_setModified(IsModified_MachineData);
5825 mHWData.backupEx();
5826
5827 /* The backupEx() operation invalidates our iterator,
5828 * so get a new one. */
5829 it = mHWData->mGuestProperties.find(aName);
5830 Assert(it != mHWData->mGuestProperties.end());
5831
5832 if (!fDelete)
5833 {
5834 RTTIMESPEC time;
5835 it->second.strValue = aValue;
5836 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5837 it->second.mFlags = fFlags;
5838 }
5839 else
5840 mHWData->mGuestProperties.erase(it);
5841 }
5842 }
5843
5844 if (SUCCEEDED(rc))
5845 {
5846 alock.release();
5847
5848 mParent->i_onGuestPropertyChange(mData->mUuid,
5849 Bstr(aName).raw(),
5850 Bstr(aValue).raw(),
5851 Bstr(aFlags).raw());
5852 }
5853 }
5854 catch (std::bad_alloc &)
5855 {
5856 rc = E_OUTOFMEMORY;
5857 }
5858
5859 return rc;
5860}
5861
5862/**
5863 * Set a property on the VM that that property belongs to.
5864 * @returns E_ACCESSDENIED if the VM process is not available or not
5865 * currently handling queries and the setting should then be done in
5866 * VBoxSVC.
5867 */
5868HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5869 const com::Utf8Str &aFlags, bool fDelete)
5870{
5871 HRESULT rc;
5872
5873 try
5874 {
5875 ComPtr<IInternalSessionControl> directControl;
5876 {
5877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5878 if (mData->mSession.mLockType == LockType_VM)
5879 directControl = mData->mSession.mDirectControl;
5880 }
5881
5882 BSTR dummy = NULL; /* will not be changed (setter) */
5883 LONG64 dummy64;
5884 if (!directControl)
5885 rc = E_ACCESSDENIED;
5886 else
5887 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5888 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5889 fDelete? 2: 1 /* accessMode */,
5890 &dummy, &dummy64, &dummy);
5891 }
5892 catch (std::bad_alloc &)
5893 {
5894 rc = E_OUTOFMEMORY;
5895 }
5896
5897 return rc;
5898}
5899#endif // VBOX_WITH_GUEST_PROPS
5900
5901HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5902 const com::Utf8Str &aFlags)
5903{
5904#ifndef VBOX_WITH_GUEST_PROPS
5905 ReturnComNotImplemented();
5906#else // VBOX_WITH_GUEST_PROPS
5907 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5908 if (rc == E_ACCESSDENIED)
5909 /* The VM is not running or the service is not (yet) accessible */
5910 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5911 return rc;
5912#endif // VBOX_WITH_GUEST_PROPS
5913}
5914
5915HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5916{
5917 return setGuestProperty(aProperty, aValue, "");
5918}
5919
5920HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5921{
5922#ifndef VBOX_WITH_GUEST_PROPS
5923 ReturnComNotImplemented();
5924#else // VBOX_WITH_GUEST_PROPS
5925 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5926 if (rc == E_ACCESSDENIED)
5927 /* The VM is not running or the service is not (yet) accessible */
5928 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5929 return rc;
5930#endif // VBOX_WITH_GUEST_PROPS
5931}
5932
5933#ifdef VBOX_WITH_GUEST_PROPS
5934/**
5935 * Enumerate the guest properties in VBoxSVC's internal structures.
5936 */
5937HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5938 std::vector<com::Utf8Str> &aNames,
5939 std::vector<com::Utf8Str> &aValues,
5940 std::vector<LONG64> &aTimestamps,
5941 std::vector<com::Utf8Str> &aFlags)
5942{
5943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5944 Utf8Str strPatterns(aPatterns);
5945
5946 /*
5947 * Look for matching patterns and build up a list.
5948 */
5949 HWData::GuestPropertyMap propMap;
5950 for (HWData::GuestPropertyMap::const_iterator
5951 it = mHWData->mGuestProperties.begin();
5952 it != mHWData->mGuestProperties.end();
5953 ++it)
5954 {
5955 if ( strPatterns.isEmpty()
5956 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5957 RTSTR_MAX,
5958 it->first.c_str(),
5959 RTSTR_MAX,
5960 NULL)
5961 )
5962 propMap.insert(*it);
5963 }
5964
5965 alock.release();
5966
5967 /*
5968 * And build up the arrays for returning the property information.
5969 */
5970 size_t cEntries = propMap.size();
5971
5972 aNames.resize(cEntries);
5973 aValues.resize(cEntries);
5974 aTimestamps.resize(cEntries);
5975 aFlags.resize(cEntries);
5976
5977 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5978 size_t i = 0;
5979 for (HWData::GuestPropertyMap::const_iterator
5980 it = propMap.begin();
5981 it != propMap.end();
5982 ++it, ++i)
5983 {
5984 aNames[i] = it->first;
5985 aValues[i] = it->second.strValue;
5986 aTimestamps[i] = it->second.mTimestamp;
5987 GuestPropWriteFlags(it->second.mFlags, szFlags);
5988 aFlags[i] = Utf8Str(szFlags);
5989 }
5990
5991 return S_OK;
5992}
5993
5994/**
5995 * Enumerate the properties managed by a VM.
5996 * @returns E_ACCESSDENIED if the VM process is not available or not
5997 * currently handling queries and the setting should then be done in
5998 * VBoxSVC.
5999 */
6000HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6001 std::vector<com::Utf8Str> &aNames,
6002 std::vector<com::Utf8Str> &aValues,
6003 std::vector<LONG64> &aTimestamps,
6004 std::vector<com::Utf8Str> &aFlags)
6005{
6006 HRESULT rc;
6007 ComPtr<IInternalSessionControl> directControl;
6008 {
6009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6010 if (mData->mSession.mLockType == LockType_VM)
6011 directControl = mData->mSession.mDirectControl;
6012 }
6013
6014 com::SafeArray<BSTR> bNames;
6015 com::SafeArray<BSTR> bValues;
6016 com::SafeArray<LONG64> bTimestamps;
6017 com::SafeArray<BSTR> bFlags;
6018
6019 if (!directControl)
6020 rc = E_ACCESSDENIED;
6021 else
6022 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6023 ComSafeArrayAsOutParam(bNames),
6024 ComSafeArrayAsOutParam(bValues),
6025 ComSafeArrayAsOutParam(bTimestamps),
6026 ComSafeArrayAsOutParam(bFlags));
6027 size_t i;
6028 aNames.resize(bNames.size());
6029 for (i = 0; i < bNames.size(); ++i)
6030 aNames[i] = Utf8Str(bNames[i]);
6031 aValues.resize(bValues.size());
6032 for (i = 0; i < bValues.size(); ++i)
6033 aValues[i] = Utf8Str(bValues[i]);
6034 aTimestamps.resize(bTimestamps.size());
6035 for (i = 0; i < bTimestamps.size(); ++i)
6036 aTimestamps[i] = bTimestamps[i];
6037 aFlags.resize(bFlags.size());
6038 for (i = 0; i < bFlags.size(); ++i)
6039 aFlags[i] = Utf8Str(bFlags[i]);
6040
6041 return rc;
6042}
6043#endif // VBOX_WITH_GUEST_PROPS
6044HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6045 std::vector<com::Utf8Str> &aNames,
6046 std::vector<com::Utf8Str> &aValues,
6047 std::vector<LONG64> &aTimestamps,
6048 std::vector<com::Utf8Str> &aFlags)
6049{
6050#ifndef VBOX_WITH_GUEST_PROPS
6051 ReturnComNotImplemented();
6052#else // VBOX_WITH_GUEST_PROPS
6053
6054 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6055
6056 if (rc == E_ACCESSDENIED)
6057 /* The VM is not running or the service is not (yet) accessible */
6058 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6059 return rc;
6060#endif // VBOX_WITH_GUEST_PROPS
6061}
6062
6063HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6064 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6065{
6066 MediumAttachmentList atts;
6067
6068 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6069 if (FAILED(rc)) return rc;
6070
6071 aMediumAttachments.resize(atts.size());
6072 size_t i = 0;
6073 for (MediumAttachmentList::const_iterator
6074 it = atts.begin();
6075 it != atts.end();
6076 ++it, ++i)
6077 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6078
6079 return S_OK;
6080}
6081
6082HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6083 LONG aControllerPort,
6084 LONG aDevice,
6085 ComPtr<IMediumAttachment> &aAttachment)
6086{
6087 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6088 aName.c_str(), aControllerPort, aDevice));
6089
6090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6091
6092 aAttachment = NULL;
6093
6094 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6095 aName,
6096 aControllerPort,
6097 aDevice);
6098 if (pAttach.isNull())
6099 return setError(VBOX_E_OBJECT_NOT_FOUND,
6100 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6101 aDevice, aControllerPort, aName.c_str());
6102
6103 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6104
6105 return S_OK;
6106}
6107
6108
6109HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6110 StorageBus_T aConnectionType,
6111 ComPtr<IStorageController> &aController)
6112{
6113 if ( (aConnectionType <= StorageBus_Null)
6114 || (aConnectionType > StorageBus_PCIe))
6115 return setError(E_INVALIDARG,
6116 tr("Invalid connection type: %d"),
6117 aConnectionType);
6118
6119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6120
6121 HRESULT rc = i_checkStateDependency(MutableStateDep);
6122 if (FAILED(rc)) return rc;
6123
6124 /* try to find one with the name first. */
6125 ComObjPtr<StorageController> ctrl;
6126
6127 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6128 if (SUCCEEDED(rc))
6129 return setError(VBOX_E_OBJECT_IN_USE,
6130 tr("Storage controller named '%s' already exists"),
6131 aName.c_str());
6132
6133 ctrl.createObject();
6134
6135 /* get a new instance number for the storage controller */
6136 ULONG ulInstance = 0;
6137 bool fBootable = true;
6138 for (StorageControllerList::const_iterator
6139 it = mStorageControllers->begin();
6140 it != mStorageControllers->end();
6141 ++it)
6142 {
6143 if ((*it)->i_getStorageBus() == aConnectionType)
6144 {
6145 ULONG ulCurInst = (*it)->i_getInstance();
6146
6147 if (ulCurInst >= ulInstance)
6148 ulInstance = ulCurInst + 1;
6149
6150 /* Only one controller of each type can be marked as bootable. */
6151 if ((*it)->i_getBootable())
6152 fBootable = false;
6153 }
6154 }
6155
6156 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6157 if (FAILED(rc)) return rc;
6158
6159 i_setModified(IsModified_Storage);
6160 mStorageControllers.backup();
6161 mStorageControllers->push_back(ctrl);
6162
6163 ctrl.queryInterfaceTo(aController.asOutParam());
6164
6165 /* inform the direct session if any */
6166 alock.release();
6167 i_onStorageControllerChange();
6168
6169 return S_OK;
6170}
6171
6172HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6173 ComPtr<IStorageController> &aStorageController)
6174{
6175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 ComObjPtr<StorageController> ctrl;
6178
6179 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6180 if (SUCCEEDED(rc))
6181 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6182
6183 return rc;
6184}
6185
6186HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6187 ULONG aInstance,
6188 ComPtr<IStorageController> &aStorageController)
6189{
6190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6191
6192 for (StorageControllerList::const_iterator
6193 it = mStorageControllers->begin();
6194 it != mStorageControllers->end();
6195 ++it)
6196 {
6197 if ( (*it)->i_getStorageBus() == aConnectionType
6198 && (*it)->i_getInstance() == aInstance)
6199 {
6200 (*it).queryInterfaceTo(aStorageController.asOutParam());
6201 return S_OK;
6202 }
6203 }
6204
6205 return setError(VBOX_E_OBJECT_NOT_FOUND,
6206 tr("Could not find a storage controller with instance number '%lu'"),
6207 aInstance);
6208}
6209
6210HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6211{
6212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6213
6214 HRESULT rc = i_checkStateDependency(MutableStateDep);
6215 if (FAILED(rc)) return rc;
6216
6217 ComObjPtr<StorageController> ctrl;
6218
6219 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6220 if (SUCCEEDED(rc))
6221 {
6222 /* Ensure that only one controller of each type is marked as bootable. */
6223 if (aBootable == TRUE)
6224 {
6225 for (StorageControllerList::const_iterator
6226 it = mStorageControllers->begin();
6227 it != mStorageControllers->end();
6228 ++it)
6229 {
6230 ComObjPtr<StorageController> aCtrl = (*it);
6231
6232 if ( (aCtrl->i_getName() != aName)
6233 && aCtrl->i_getBootable() == TRUE
6234 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6235 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6236 {
6237 aCtrl->i_setBootable(FALSE);
6238 break;
6239 }
6240 }
6241 }
6242
6243 if (SUCCEEDED(rc))
6244 {
6245 ctrl->i_setBootable(aBootable);
6246 i_setModified(IsModified_Storage);
6247 }
6248 }
6249
6250 if (SUCCEEDED(rc))
6251 {
6252 /* inform the direct session if any */
6253 alock.release();
6254 i_onStorageControllerChange();
6255 }
6256
6257 return rc;
6258}
6259
6260HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6261{
6262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6263
6264 HRESULT rc = i_checkStateDependency(MutableStateDep);
6265 if (FAILED(rc)) return rc;
6266
6267 ComObjPtr<StorageController> ctrl;
6268 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6269 if (FAILED(rc)) return rc;
6270
6271 {
6272 /* find all attached devices to the appropriate storage controller and detach them all */
6273 // make a temporary list because detachDevice invalidates iterators into
6274 // mMediumAttachments
6275 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6276
6277 for (MediumAttachmentList::const_iterator
6278 it = llAttachments2.begin();
6279 it != llAttachments2.end();
6280 ++it)
6281 {
6282 MediumAttachment *pAttachTemp = *it;
6283
6284 AutoCaller localAutoCaller(pAttachTemp);
6285 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6286
6287 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6288
6289 if (pAttachTemp->i_getControllerName() == aName)
6290 {
6291 rc = i_detachDevice(pAttachTemp, alock, NULL);
6292 if (FAILED(rc)) return rc;
6293 }
6294 }
6295 }
6296
6297 /* We can remove it now. */
6298 i_setModified(IsModified_Storage);
6299 mStorageControllers.backup();
6300
6301 ctrl->i_unshare();
6302
6303 mStorageControllers->remove(ctrl);
6304
6305 /* inform the direct session if any */
6306 alock.release();
6307 i_onStorageControllerChange();
6308
6309 return S_OK;
6310}
6311
6312HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6313 ComPtr<IUSBController> &aController)
6314{
6315 if ( (aType <= USBControllerType_Null)
6316 || (aType >= USBControllerType_Last))
6317 return setError(E_INVALIDARG,
6318 tr("Invalid USB controller type: %d"),
6319 aType);
6320
6321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 HRESULT rc = i_checkStateDependency(MutableStateDep);
6324 if (FAILED(rc)) return rc;
6325
6326 /* try to find one with the same type first. */
6327 ComObjPtr<USBController> ctrl;
6328
6329 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6330 if (SUCCEEDED(rc))
6331 return setError(VBOX_E_OBJECT_IN_USE,
6332 tr("USB controller named '%s' already exists"),
6333 aName.c_str());
6334
6335 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6336 ULONG maxInstances;
6337 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6338 if (FAILED(rc))
6339 return rc;
6340
6341 ULONG cInstances = i_getUSBControllerCountByType(aType);
6342 if (cInstances >= maxInstances)
6343 return setError(E_INVALIDARG,
6344 tr("Too many USB controllers of this type"));
6345
6346 ctrl.createObject();
6347
6348 rc = ctrl->init(this, aName, aType);
6349 if (FAILED(rc)) return rc;
6350
6351 i_setModified(IsModified_USB);
6352 mUSBControllers.backup();
6353 mUSBControllers->push_back(ctrl);
6354
6355 ctrl.queryInterfaceTo(aController.asOutParam());
6356
6357 /* inform the direct session if any */
6358 alock.release();
6359 i_onUSBControllerChange();
6360
6361 return S_OK;
6362}
6363
6364HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6365{
6366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6367
6368 ComObjPtr<USBController> ctrl;
6369
6370 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6371 if (SUCCEEDED(rc))
6372 ctrl.queryInterfaceTo(aController.asOutParam());
6373
6374 return rc;
6375}
6376
6377HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6378 ULONG *aControllers)
6379{
6380 if ( (aType <= USBControllerType_Null)
6381 || (aType >= USBControllerType_Last))
6382 return setError(E_INVALIDARG,
6383 tr("Invalid USB controller type: %d"),
6384 aType);
6385
6386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6387
6388 ComObjPtr<USBController> ctrl;
6389
6390 *aControllers = i_getUSBControllerCountByType(aType);
6391
6392 return S_OK;
6393}
6394
6395HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6396{
6397
6398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6399
6400 HRESULT rc = i_checkStateDependency(MutableStateDep);
6401 if (FAILED(rc)) return rc;
6402
6403 ComObjPtr<USBController> ctrl;
6404 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6405 if (FAILED(rc)) return rc;
6406
6407 i_setModified(IsModified_USB);
6408 mUSBControllers.backup();
6409
6410 ctrl->i_unshare();
6411
6412 mUSBControllers->remove(ctrl);
6413
6414 /* inform the direct session if any */
6415 alock.release();
6416 i_onUSBControllerChange();
6417
6418 return S_OK;
6419}
6420
6421HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6422 ULONG *aOriginX,
6423 ULONG *aOriginY,
6424 ULONG *aWidth,
6425 ULONG *aHeight,
6426 BOOL *aEnabled)
6427{
6428 uint32_t u32OriginX= 0;
6429 uint32_t u32OriginY= 0;
6430 uint32_t u32Width = 0;
6431 uint32_t u32Height = 0;
6432 uint16_t u16Flags = 0;
6433
6434 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6435 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6436 if (RT_FAILURE(vrc))
6437 {
6438#ifdef RT_OS_WINDOWS
6439 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6440 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6441 * So just assign fEnable to TRUE again.
6442 * The right fix would be to change GUI API wrappers to make sure that parameters
6443 * are changed only if API succeeds.
6444 */
6445 *aEnabled = TRUE;
6446#endif
6447 return setError(VBOX_E_IPRT_ERROR,
6448 tr("Saved guest size is not available (%Rrc)"),
6449 vrc);
6450 }
6451
6452 *aOriginX = u32OriginX;
6453 *aOriginY = u32OriginY;
6454 *aWidth = u32Width;
6455 *aHeight = u32Height;
6456 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6457
6458 return S_OK;
6459}
6460
6461HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6462 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6463{
6464 if (aScreenId != 0)
6465 return E_NOTIMPL;
6466
6467 if ( aBitmapFormat != BitmapFormat_BGR0
6468 && aBitmapFormat != BitmapFormat_BGRA
6469 && aBitmapFormat != BitmapFormat_RGBA
6470 && aBitmapFormat != BitmapFormat_PNG)
6471 return setError(E_NOTIMPL,
6472 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6473
6474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6475
6476 uint8_t *pu8Data = NULL;
6477 uint32_t cbData = 0;
6478 uint32_t u32Width = 0;
6479 uint32_t u32Height = 0;
6480
6481 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6482
6483 if (RT_FAILURE(vrc))
6484 return setError(VBOX_E_IPRT_ERROR,
6485 tr("Saved thumbnail data is not available (%Rrc)"),
6486 vrc);
6487
6488 HRESULT hr = S_OK;
6489
6490 *aWidth = u32Width;
6491 *aHeight = u32Height;
6492
6493 if (cbData > 0)
6494 {
6495 /* Convert pixels to the format expected by the API caller. */
6496 if (aBitmapFormat == BitmapFormat_BGR0)
6497 {
6498 /* [0] B, [1] G, [2] R, [3] 0. */
6499 aData.resize(cbData);
6500 memcpy(&aData.front(), pu8Data, cbData);
6501 }
6502 else if (aBitmapFormat == BitmapFormat_BGRA)
6503 {
6504 /* [0] B, [1] G, [2] R, [3] A. */
6505 aData.resize(cbData);
6506 for (uint32_t i = 0; i < cbData; i += 4)
6507 {
6508 aData[i] = pu8Data[i];
6509 aData[i + 1] = pu8Data[i + 1];
6510 aData[i + 2] = pu8Data[i + 2];
6511 aData[i + 3] = 0xff;
6512 }
6513 }
6514 else if (aBitmapFormat == BitmapFormat_RGBA)
6515 {
6516 /* [0] R, [1] G, [2] B, [3] A. */
6517 aData.resize(cbData);
6518 for (uint32_t i = 0; i < cbData; i += 4)
6519 {
6520 aData[i] = pu8Data[i + 2];
6521 aData[i + 1] = pu8Data[i + 1];
6522 aData[i + 2] = pu8Data[i];
6523 aData[i + 3] = 0xff;
6524 }
6525 }
6526 else if (aBitmapFormat == BitmapFormat_PNG)
6527 {
6528 uint8_t *pu8PNG = NULL;
6529 uint32_t cbPNG = 0;
6530 uint32_t cxPNG = 0;
6531 uint32_t cyPNG = 0;
6532
6533 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6534
6535 if (RT_SUCCESS(vrc))
6536 {
6537 aData.resize(cbPNG);
6538 if (cbPNG)
6539 memcpy(&aData.front(), pu8PNG, cbPNG);
6540 }
6541 else
6542 hr = setError(VBOX_E_IPRT_ERROR,
6543 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6544 vrc);
6545
6546 RTMemFree(pu8PNG);
6547 }
6548 }
6549
6550 freeSavedDisplayScreenshot(pu8Data);
6551
6552 return hr;
6553}
6554
6555HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6556 ULONG *aWidth,
6557 ULONG *aHeight,
6558 std::vector<BitmapFormat_T> &aBitmapFormats)
6559{
6560 if (aScreenId != 0)
6561 return E_NOTIMPL;
6562
6563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6564
6565 uint8_t *pu8Data = NULL;
6566 uint32_t cbData = 0;
6567 uint32_t u32Width = 0;
6568 uint32_t u32Height = 0;
6569
6570 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6571
6572 if (RT_FAILURE(vrc))
6573 return setError(VBOX_E_IPRT_ERROR,
6574 tr("Saved screenshot data is not available (%Rrc)"),
6575 vrc);
6576
6577 *aWidth = u32Width;
6578 *aHeight = u32Height;
6579 aBitmapFormats.resize(1);
6580 aBitmapFormats[0] = BitmapFormat_PNG;
6581
6582 freeSavedDisplayScreenshot(pu8Data);
6583
6584 return S_OK;
6585}
6586
6587HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6588 BitmapFormat_T aBitmapFormat,
6589 ULONG *aWidth,
6590 ULONG *aHeight,
6591 std::vector<BYTE> &aData)
6592{
6593 if (aScreenId != 0)
6594 return E_NOTIMPL;
6595
6596 if (aBitmapFormat != BitmapFormat_PNG)
6597 return E_NOTIMPL;
6598
6599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 uint8_t *pu8Data = NULL;
6602 uint32_t cbData = 0;
6603 uint32_t u32Width = 0;
6604 uint32_t u32Height = 0;
6605
6606 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6607
6608 if (RT_FAILURE(vrc))
6609 return setError(VBOX_E_IPRT_ERROR,
6610 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6611 vrc);
6612
6613 *aWidth = u32Width;
6614 *aHeight = u32Height;
6615
6616 aData.resize(cbData);
6617 if (cbData)
6618 memcpy(&aData.front(), pu8Data, cbData);
6619
6620 freeSavedDisplayScreenshot(pu8Data);
6621
6622 return S_OK;
6623}
6624
6625HRESULT Machine::hotPlugCPU(ULONG aCpu)
6626{
6627 HRESULT rc = S_OK;
6628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6629
6630 if (!mHWData->mCPUHotPlugEnabled)
6631 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6632
6633 if (aCpu >= mHWData->mCPUCount)
6634 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6635
6636 if (mHWData->mCPUAttached[aCpu])
6637 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6638
6639 alock.release();
6640 rc = i_onCPUChange(aCpu, false);
6641 alock.acquire();
6642 if (FAILED(rc)) return rc;
6643
6644 i_setModified(IsModified_MachineData);
6645 mHWData.backup();
6646 mHWData->mCPUAttached[aCpu] = true;
6647
6648 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6649 if (Global::IsOnline(mData->mMachineState))
6650 i_saveSettings(NULL);
6651
6652 return S_OK;
6653}
6654
6655HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6656{
6657 HRESULT rc = S_OK;
6658
6659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6660
6661 if (!mHWData->mCPUHotPlugEnabled)
6662 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6663
6664 if (aCpu >= SchemaDefs::MaxCPUCount)
6665 return setError(E_INVALIDARG,
6666 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6667 SchemaDefs::MaxCPUCount);
6668
6669 if (!mHWData->mCPUAttached[aCpu])
6670 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6671
6672 /* CPU 0 can't be detached */
6673 if (aCpu == 0)
6674 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6675
6676 alock.release();
6677 rc = i_onCPUChange(aCpu, true);
6678 alock.acquire();
6679 if (FAILED(rc)) return rc;
6680
6681 i_setModified(IsModified_MachineData);
6682 mHWData.backup();
6683 mHWData->mCPUAttached[aCpu] = false;
6684
6685 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6686 if (Global::IsOnline(mData->mMachineState))
6687 i_saveSettings(NULL);
6688
6689 return S_OK;
6690}
6691
6692HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6693{
6694 *aAttached = false;
6695
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 /* If hotplug is enabled the CPU is always enabled. */
6699 if (!mHWData->mCPUHotPlugEnabled)
6700 {
6701 if (aCpu < mHWData->mCPUCount)
6702 *aAttached = true;
6703 }
6704 else
6705 {
6706 if (aCpu < SchemaDefs::MaxCPUCount)
6707 *aAttached = mHWData->mCPUAttached[aCpu];
6708 }
6709
6710 return S_OK;
6711}
6712
6713HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6714{
6715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6716
6717 Utf8Str log = i_getLogFilename(aIdx);
6718 if (!RTFileExists(log.c_str()))
6719 log.setNull();
6720 aFilename = log;
6721
6722 return S_OK;
6723}
6724
6725HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6726{
6727 if (aSize < 0)
6728 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6729
6730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6731
6732 HRESULT rc = S_OK;
6733 Utf8Str log = i_getLogFilename(aIdx);
6734
6735 /* do not unnecessarily hold the lock while doing something which does
6736 * not need the lock and potentially takes a long time. */
6737 alock.release();
6738
6739 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6740 * keeps the SOAP reply size under 1M for the webservice (we're using
6741 * base64 encoded strings for binary data for years now, avoiding the
6742 * expansion of each byte array element to approx. 25 bytes of XML. */
6743 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6744 aData.resize(cbData);
6745
6746 RTFILE LogFile;
6747 int vrc = RTFileOpen(&LogFile, log.c_str(),
6748 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6749 if (RT_SUCCESS(vrc))
6750 {
6751 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6752 if (RT_SUCCESS(vrc))
6753 aData.resize(cbData);
6754 else
6755 rc = setError(VBOX_E_IPRT_ERROR,
6756 tr("Could not read log file '%s' (%Rrc)"),
6757 log.c_str(), vrc);
6758 RTFileClose(LogFile);
6759 }
6760 else
6761 rc = setError(VBOX_E_IPRT_ERROR,
6762 tr("Could not open log file '%s' (%Rrc)"),
6763 log.c_str(), vrc);
6764
6765 if (FAILED(rc))
6766 aData.resize(0);
6767
6768 return rc;
6769}
6770
6771
6772/**
6773 * Currently this method doesn't attach device to the running VM,
6774 * just makes sure it's plugged on next VM start.
6775 */
6776HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6777{
6778 // lock scope
6779 {
6780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6781
6782 HRESULT rc = i_checkStateDependency(MutableStateDep);
6783 if (FAILED(rc)) return rc;
6784
6785 ChipsetType_T aChipset = ChipsetType_PIIX3;
6786 COMGETTER(ChipsetType)(&aChipset);
6787
6788 if (aChipset != ChipsetType_ICH9)
6789 {
6790 return setError(E_INVALIDARG,
6791 tr("Host PCI attachment only supported with ICH9 chipset"));
6792 }
6793
6794 // check if device with this host PCI address already attached
6795 for (HWData::PCIDeviceAssignmentList::const_iterator
6796 it = mHWData->mPCIDeviceAssignments.begin();
6797 it != mHWData->mPCIDeviceAssignments.end();
6798 ++it)
6799 {
6800 LONG iHostAddress = -1;
6801 ComPtr<PCIDeviceAttachment> pAttach;
6802 pAttach = *it;
6803 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6804 if (iHostAddress == aHostAddress)
6805 return setError(E_INVALIDARG,
6806 tr("Device with host PCI address already attached to this VM"));
6807 }
6808
6809 ComObjPtr<PCIDeviceAttachment> pda;
6810 char name[32];
6811
6812 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6813 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6814 pda.createObject();
6815 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6816 i_setModified(IsModified_MachineData);
6817 mHWData.backup();
6818 mHWData->mPCIDeviceAssignments.push_back(pda);
6819 }
6820
6821 return S_OK;
6822}
6823
6824/**
6825 * Currently this method doesn't detach device from the running VM,
6826 * just makes sure it's not plugged on next VM start.
6827 */
6828HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6829{
6830 ComObjPtr<PCIDeviceAttachment> pAttach;
6831 bool fRemoved = false;
6832 HRESULT rc;
6833
6834 // lock scope
6835 {
6836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 rc = i_checkStateDependency(MutableStateDep);
6839 if (FAILED(rc)) return rc;
6840
6841 for (HWData::PCIDeviceAssignmentList::const_iterator
6842 it = mHWData->mPCIDeviceAssignments.begin();
6843 it != mHWData->mPCIDeviceAssignments.end();
6844 ++it)
6845 {
6846 LONG iHostAddress = -1;
6847 pAttach = *it;
6848 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6849 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6850 {
6851 i_setModified(IsModified_MachineData);
6852 mHWData.backup();
6853 mHWData->mPCIDeviceAssignments.remove(pAttach);
6854 fRemoved = true;
6855 break;
6856 }
6857 }
6858 }
6859
6860
6861 /* Fire event outside of the lock */
6862 if (fRemoved)
6863 {
6864 Assert(!pAttach.isNull());
6865 ComPtr<IEventSource> es;
6866 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6867 Assert(SUCCEEDED(rc));
6868 Bstr mid;
6869 rc = this->COMGETTER(Id)(mid.asOutParam());
6870 Assert(SUCCEEDED(rc));
6871 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6872 }
6873
6874 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6875 tr("No host PCI device %08x attached"),
6876 aHostAddress
6877 );
6878}
6879
6880HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6881{
6882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6883
6884 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6885 size_t i = 0;
6886 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6887 it = mHWData->mPCIDeviceAssignments.begin();
6888 it != mHWData->mPCIDeviceAssignments.end();
6889 ++it, ++i)
6890 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6891
6892 return S_OK;
6893}
6894
6895HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6896{
6897 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6898
6899 return S_OK;
6900}
6901
6902HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6903{
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6907
6908 return S_OK;
6909}
6910
6911HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6912{
6913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6914 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6915 if (SUCCEEDED(hrc))
6916 {
6917 hrc = mHWData.backupEx();
6918 if (SUCCEEDED(hrc))
6919 {
6920 i_setModified(IsModified_MachineData);
6921 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6922 }
6923 }
6924 return hrc;
6925}
6926
6927HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6928{
6929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6930 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6931 return S_OK;
6932}
6933
6934HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6935{
6936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6937 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6938 if (SUCCEEDED(hrc))
6939 {
6940 hrc = mHWData.backupEx();
6941 if (SUCCEEDED(hrc))
6942 {
6943 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6944 if (SUCCEEDED(hrc))
6945 i_setModified(IsModified_MachineData);
6946 }
6947 }
6948 return hrc;
6949}
6950
6951HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6952{
6953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6956
6957 return S_OK;
6958}
6959
6960HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6961{
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6964 if (SUCCEEDED(hrc))
6965 {
6966 hrc = mHWData.backupEx();
6967 if (SUCCEEDED(hrc))
6968 {
6969 i_setModified(IsModified_MachineData);
6970 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6971 }
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6981
6982 return S_OK;
6983}
6984
6985HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6990 if ( SUCCEEDED(hrc)
6991 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6992 {
6993 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6994 int vrc;
6995
6996 if (aAutostartEnabled)
6997 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6998 else
6999 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7000
7001 if (RT_SUCCESS(vrc))
7002 {
7003 hrc = mHWData.backupEx();
7004 if (SUCCEEDED(hrc))
7005 {
7006 i_setModified(IsModified_MachineData);
7007 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7008 }
7009 }
7010 else if (vrc == VERR_NOT_SUPPORTED)
7011 hrc = setError(VBOX_E_NOT_SUPPORTED,
7012 tr("The VM autostart feature is not supported on this platform"));
7013 else if (vrc == VERR_PATH_NOT_FOUND)
7014 hrc = setError(E_FAIL,
7015 tr("The path to the autostart database is not set"));
7016 else
7017 hrc = setError(E_UNEXPECTED,
7018 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7019 aAutostartEnabled ? "Adding" : "Removing",
7020 mUserData->s.strName.c_str(), vrc);
7021 }
7022 return hrc;
7023}
7024
7025HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7026{
7027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7028
7029 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7030
7031 return S_OK;
7032}
7033
7034HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7035{
7036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7037 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7038 if (SUCCEEDED(hrc))
7039 {
7040 hrc = mHWData.backupEx();
7041 if (SUCCEEDED(hrc))
7042 {
7043 i_setModified(IsModified_MachineData);
7044 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7045 }
7046 }
7047 return hrc;
7048}
7049
7050HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7051{
7052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7053
7054 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7055
7056 return S_OK;
7057}
7058
7059HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7060{
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7063 if ( SUCCEEDED(hrc)
7064 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7065 {
7066 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7067 int vrc;
7068
7069 if (aAutostopType != AutostopType_Disabled)
7070 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7071 else
7072 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7073
7074 if (RT_SUCCESS(vrc))
7075 {
7076 hrc = mHWData.backupEx();
7077 if (SUCCEEDED(hrc))
7078 {
7079 i_setModified(IsModified_MachineData);
7080 mHWData->mAutostart.enmAutostopType = aAutostopType;
7081 }
7082 }
7083 else if (vrc == VERR_NOT_SUPPORTED)
7084 hrc = setError(VBOX_E_NOT_SUPPORTED,
7085 tr("The VM autostop feature is not supported on this platform"));
7086 else if (vrc == VERR_PATH_NOT_FOUND)
7087 hrc = setError(E_FAIL,
7088 tr("The path to the autostart database is not set"));
7089 else
7090 hrc = setError(E_UNEXPECTED,
7091 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7092 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7093 mUserData->s.strName.c_str(), vrc);
7094 }
7095 return hrc;
7096}
7097
7098HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7099{
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 aDefaultFrontend = mHWData->mDefaultFrontend;
7103
7104 return S_OK;
7105}
7106
7107HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7108{
7109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7110 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7111 if (SUCCEEDED(hrc))
7112 {
7113 hrc = mHWData.backupEx();
7114 if (SUCCEEDED(hrc))
7115 {
7116 i_setModified(IsModified_MachineData);
7117 mHWData->mDefaultFrontend = aDefaultFrontend;
7118 }
7119 }
7120 return hrc;
7121}
7122
7123HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7124{
7125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7126 size_t cbIcon = mUserData->s.ovIcon.size();
7127 aIcon.resize(cbIcon);
7128 if (cbIcon)
7129 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7130 return S_OK;
7131}
7132
7133HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7134{
7135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7136 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7137 if (SUCCEEDED(hrc))
7138 {
7139 i_setModified(IsModified_MachineData);
7140 mUserData.backup();
7141 size_t cbIcon = aIcon.size();
7142 mUserData->s.ovIcon.resize(cbIcon);
7143 if (cbIcon)
7144 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7145 }
7146 return hrc;
7147}
7148
7149HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7150{
7151#ifdef VBOX_WITH_USB
7152 *aUSBProxyAvailable = true;
7153#else
7154 *aUSBProxyAvailable = false;
7155#endif
7156 return S_OK;
7157}
7158
7159HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7160{
7161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7162
7163 aVMProcessPriority = mUserData->s.strVMPriority;
7164
7165 return S_OK;
7166}
7167
7168HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7169{
7170 RT_NOREF(aVMProcessPriority);
7171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7172 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7173 if (SUCCEEDED(hrc))
7174 {
7175 /** @todo r=klaus: currently this is marked as not implemented, as
7176 * the code for setting the priority of the process is not there
7177 * (neither when starting the VM nor at runtime). */
7178 ReturnComNotImplemented();
7179#if 0
7180 hrc = mUserData.backupEx();
7181 if (SUCCEEDED(hrc))
7182 {
7183 i_setModified(IsModified_MachineData);
7184 mUserData->s.strVMPriority = aVMProcessPriority;
7185 }
7186#endif
7187 }
7188 return hrc;
7189}
7190
7191HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7192 ComPtr<IProgress> &aProgress)
7193{
7194 ComObjPtr<Progress> pP;
7195 Progress *ppP = pP;
7196 IProgress *iP = static_cast<IProgress *>(ppP);
7197 IProgress **pProgress = &iP;
7198
7199 IMachine *pTarget = aTarget;
7200
7201 /* Convert the options. */
7202 RTCList<CloneOptions_T> optList;
7203 if (aOptions.size())
7204 for (size_t i = 0; i < aOptions.size(); ++i)
7205 optList.append(aOptions[i]);
7206
7207 if (optList.contains(CloneOptions_Link))
7208 {
7209 if (!i_isSnapshotMachine())
7210 return setError(E_INVALIDARG,
7211 tr("Linked clone can only be created from a snapshot"));
7212 if (aMode != CloneMode_MachineState)
7213 return setError(E_INVALIDARG,
7214 tr("Linked clone can only be created for a single machine state"));
7215 }
7216 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7217
7218 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7219
7220 HRESULT rc = pWorker->start(pProgress);
7221
7222 pP = static_cast<Progress *>(*pProgress);
7223 pP.queryInterfaceTo(aProgress.asOutParam());
7224
7225 return rc;
7226
7227}
7228
7229HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7230{
7231 NOREF(aProgress);
7232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7233
7234 // This check should always fail.
7235 HRESULT rc = i_checkStateDependency(MutableStateDep);
7236 if (FAILED(rc)) return rc;
7237
7238 AssertFailedReturn(E_NOTIMPL);
7239}
7240
7241HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7242{
7243 NOREF(aSavedStateFile);
7244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7245
7246 // This check should always fail.
7247 HRESULT rc = i_checkStateDependency(MutableStateDep);
7248 if (FAILED(rc)) return rc;
7249
7250 AssertFailedReturn(E_NOTIMPL);
7251}
7252
7253HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7254{
7255 NOREF(aFRemoveFile);
7256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7257
7258 // This check should always fail.
7259 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7260 if (FAILED(rc)) return rc;
7261
7262 AssertFailedReturn(E_NOTIMPL);
7263}
7264
7265// public methods for internal purposes
7266/////////////////////////////////////////////////////////////////////////////
7267
7268/**
7269 * Adds the given IsModified_* flag to the dirty flags of the machine.
7270 * This must be called either during i_loadSettings or under the machine write lock.
7271 * @param fl Flag
7272 * @param fAllowStateModification If state modifications are allowed.
7273 */
7274void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7275{
7276 mData->flModifications |= fl;
7277 if (fAllowStateModification && i_isStateModificationAllowed())
7278 mData->mCurrentStateModified = true;
7279}
7280
7281/**
7282 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7283 * care of the write locking.
7284 *
7285 * @param fModification The flag to add.
7286 * @param fAllowStateModification If state modifications are allowed.
7287 */
7288void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7289{
7290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7291 i_setModified(fModification, fAllowStateModification);
7292}
7293
7294/**
7295 * Saves the registry entry of this machine to the given configuration node.
7296 *
7297 * @param data Machine registry data.
7298 *
7299 * @note locks this object for reading.
7300 */
7301HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7302{
7303 AutoLimitedCaller autoCaller(this);
7304 AssertComRCReturnRC(autoCaller.rc());
7305
7306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7307
7308 data.uuid = mData->mUuid;
7309 data.strSettingsFile = mData->m_strConfigFile;
7310
7311 return S_OK;
7312}
7313
7314/**
7315 * Calculates the absolute path of the given path taking the directory of the
7316 * machine settings file as the current directory.
7317 *
7318 * @param strPath Path to calculate the absolute path for.
7319 * @param aResult Where to put the result (used only on success, can be the
7320 * same Utf8Str instance as passed in @a aPath).
7321 * @return IPRT result.
7322 *
7323 * @note Locks this object for reading.
7324 */
7325int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7326{
7327 AutoCaller autoCaller(this);
7328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7329
7330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7331
7332 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7333
7334 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7335
7336 strSettingsDir.stripFilename();
7337 char folder[RTPATH_MAX];
7338 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7339 if (RT_SUCCESS(vrc))
7340 aResult = folder;
7341
7342 return vrc;
7343}
7344
7345/**
7346 * Copies strSource to strTarget, making it relative to the machine folder
7347 * if it is a subdirectory thereof, or simply copying it otherwise.
7348 *
7349 * @param strSource Path to evaluate and copy.
7350 * @param strTarget Buffer to receive target path.
7351 *
7352 * @note Locks this object for reading.
7353 */
7354void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7355 Utf8Str &strTarget)
7356{
7357 AutoCaller autoCaller(this);
7358 AssertComRCReturn(autoCaller.rc(), (void)0);
7359
7360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7361
7362 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7363 // use strTarget as a temporary buffer to hold the machine settings dir
7364 strTarget = mData->m_strConfigFileFull;
7365 strTarget.stripFilename();
7366 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7367 {
7368 // is relative: then append what's left
7369 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7370 // for empty paths (only possible for subdirs) use "." to avoid
7371 // triggering default settings for not present config attributes.
7372 if (strTarget.isEmpty())
7373 strTarget = ".";
7374 }
7375 else
7376 // is not relative: then overwrite
7377 strTarget = strSource;
7378}
7379
7380/**
7381 * Returns the full path to the machine's log folder in the
7382 * \a aLogFolder argument.
7383 */
7384void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7385{
7386 AutoCaller autoCaller(this);
7387 AssertComRCReturnVoid(autoCaller.rc());
7388
7389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7390
7391 char szTmp[RTPATH_MAX];
7392 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7393 if (RT_SUCCESS(vrc))
7394 {
7395 if (szTmp[0] && !mUserData.isNull())
7396 {
7397 char szTmp2[RTPATH_MAX];
7398 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7399 if (RT_SUCCESS(vrc))
7400 aLogFolder = Utf8StrFmt("%s%c%s",
7401 szTmp2,
7402 RTPATH_DELIMITER,
7403 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7404 }
7405 else
7406 vrc = VERR_PATH_IS_RELATIVE;
7407 }
7408
7409 if (RT_FAILURE(vrc))
7410 {
7411 // fallback if VBOX_USER_LOGHOME is not set or invalid
7412 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7413 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7414 aLogFolder.append(RTPATH_DELIMITER);
7415 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7416 }
7417}
7418
7419/**
7420 * Returns the full path to the machine's log file for an given index.
7421 */
7422Utf8Str Machine::i_getLogFilename(ULONG idx)
7423{
7424 Utf8Str logFolder;
7425 getLogFolder(logFolder);
7426 Assert(logFolder.length());
7427
7428 Utf8Str log;
7429 if (idx == 0)
7430 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7431#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7432 else if (idx == 1)
7433 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7434 else
7435 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7436#else
7437 else
7438 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7439#endif
7440 return log;
7441}
7442
7443/**
7444 * Returns the full path to the machine's hardened log file.
7445 */
7446Utf8Str Machine::i_getHardeningLogFilename(void)
7447{
7448 Utf8Str strFilename;
7449 getLogFolder(strFilename);
7450 Assert(strFilename.length());
7451 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7452 return strFilename;
7453}
7454
7455
7456/**
7457 * Composes a unique saved state filename based on the current system time. The filename is
7458 * granular to the second so this will work so long as no more than one snapshot is taken on
7459 * a machine per second.
7460 *
7461 * Before version 4.1, we used this formula for saved state files:
7462 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7463 * which no longer works because saved state files can now be shared between the saved state of the
7464 * "saved" machine and an online snapshot, and the following would cause problems:
7465 * 1) save machine
7466 * 2) create online snapshot from that machine state --> reusing saved state file
7467 * 3) save machine again --> filename would be reused, breaking the online snapshot
7468 *
7469 * So instead we now use a timestamp.
7470 *
7471 * @param strStateFilePath
7472 */
7473
7474void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7475{
7476 AutoCaller autoCaller(this);
7477 AssertComRCReturnVoid(autoCaller.rc());
7478
7479 {
7480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7481 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7482 }
7483
7484 RTTIMESPEC ts;
7485 RTTimeNow(&ts);
7486 RTTIME time;
7487 RTTimeExplode(&time, &ts);
7488
7489 strStateFilePath += RTPATH_DELIMITER;
7490 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7491 time.i32Year, time.u8Month, time.u8MonthDay,
7492 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7493}
7494
7495/**
7496 * Returns the full path to the default video capture file.
7497 */
7498void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7499{
7500 AutoCaller autoCaller(this);
7501 AssertComRCReturnVoid(autoCaller.rc());
7502
7503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7504
7505 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7506 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7507 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7508}
7509
7510/**
7511 * Returns whether at least one USB controller is present for the VM.
7512 */
7513bool Machine::i_isUSBControllerPresent()
7514{
7515 AutoCaller autoCaller(this);
7516 AssertComRCReturn(autoCaller.rc(), false);
7517
7518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7519
7520 return (mUSBControllers->size() > 0);
7521}
7522
7523/**
7524 * @note Locks this object for writing, calls the client process
7525 * (inside the lock).
7526 */
7527HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7528 const Utf8Str &strFrontend,
7529 const Utf8Str &strEnvironment,
7530 ProgressProxy *aProgress)
7531{
7532 LogFlowThisFuncEnter();
7533
7534 AssertReturn(aControl, E_FAIL);
7535 AssertReturn(aProgress, E_FAIL);
7536 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7537
7538 AutoCaller autoCaller(this);
7539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7540
7541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7542
7543 if (!mData->mRegistered)
7544 return setError(E_UNEXPECTED,
7545 tr("The machine '%s' is not registered"),
7546 mUserData->s.strName.c_str());
7547
7548 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7549
7550 /* The process started when launching a VM with separate UI/VM processes is always
7551 * the UI process, i.e. needs special handling as it won't claim the session. */
7552 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7553
7554 if (fSeparate)
7555 {
7556 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7557 return setError(VBOX_E_INVALID_OBJECT_STATE,
7558 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7559 mUserData->s.strName.c_str());
7560 }
7561 else
7562 {
7563 if ( mData->mSession.mState == SessionState_Locked
7564 || mData->mSession.mState == SessionState_Spawning
7565 || mData->mSession.mState == SessionState_Unlocking)
7566 return setError(VBOX_E_INVALID_OBJECT_STATE,
7567 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7568 mUserData->s.strName.c_str());
7569
7570 /* may not be busy */
7571 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7572 }
7573
7574 /* get the path to the executable */
7575 char szPath[RTPATH_MAX];
7576 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7577 size_t cchBufLeft = strlen(szPath);
7578 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7579 szPath[cchBufLeft] = 0;
7580 char *pszNamePart = szPath + cchBufLeft;
7581 cchBufLeft = sizeof(szPath) - cchBufLeft;
7582
7583 int vrc = VINF_SUCCESS;
7584 RTPROCESS pid = NIL_RTPROCESS;
7585
7586 RTENV env = RTENV_DEFAULT;
7587
7588 if (!strEnvironment.isEmpty())
7589 {
7590 char *newEnvStr = NULL;
7591
7592 do
7593 {
7594 /* clone the current environment */
7595 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7596 AssertRCBreakStmt(vrc2, vrc = vrc2);
7597
7598 newEnvStr = RTStrDup(strEnvironment.c_str());
7599 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7600
7601 /* put new variables to the environment
7602 * (ignore empty variable names here since RTEnv API
7603 * intentionally doesn't do that) */
7604 char *var = newEnvStr;
7605 for (char *p = newEnvStr; *p; ++p)
7606 {
7607 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7608 {
7609 *p = '\0';
7610 if (*var)
7611 {
7612 char *val = strchr(var, '=');
7613 if (val)
7614 {
7615 *val++ = '\0';
7616 vrc2 = RTEnvSetEx(env, var, val);
7617 }
7618 else
7619 vrc2 = RTEnvUnsetEx(env, var);
7620 if (RT_FAILURE(vrc2))
7621 break;
7622 }
7623 var = p + 1;
7624 }
7625 }
7626 if (RT_SUCCESS(vrc2) && *var)
7627 vrc2 = RTEnvPutEx(env, var);
7628
7629 AssertRCBreakStmt(vrc2, vrc = vrc2);
7630 }
7631 while (0);
7632
7633 if (newEnvStr != NULL)
7634 RTStrFree(newEnvStr);
7635 }
7636
7637 /* Hardening logging */
7638#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7639 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7640 {
7641 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7642 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7643 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7644 {
7645 Utf8Str strStartupLogDir = strHardeningLogFile;
7646 strStartupLogDir.stripFilename();
7647 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7648 file without stripping the file. */
7649 }
7650 strSupHardeningLogArg.append(strHardeningLogFile);
7651
7652 /* Remove legacy log filename to avoid confusion. */
7653 Utf8Str strOldStartupLogFile;
7654 getLogFolder(strOldStartupLogFile);
7655 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7656 RTFileDelete(strOldStartupLogFile.c_str());
7657 }
7658 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7659#else
7660 const char *pszSupHardeningLogArg = NULL;
7661#endif
7662
7663 Utf8Str strCanonicalName;
7664
7665#ifdef VBOX_WITH_QTGUI
7666 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7667 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7668 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7669 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7670 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7671 {
7672 strCanonicalName = "GUI/Qt";
7673# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7674 /* Modify the base path so that we don't need to use ".." below. */
7675 RTPathStripTrailingSlash(szPath);
7676 RTPathStripFilename(szPath);
7677 cchBufLeft = strlen(szPath);
7678 pszNamePart = szPath + cchBufLeft;
7679 cchBufLeft = sizeof(szPath) - cchBufLeft;
7680
7681# define OSX_APP_NAME "VirtualBoxVM"
7682# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7683
7684 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7685 if ( strAppOverride.contains(".")
7686 || strAppOverride.contains("/")
7687 || strAppOverride.contains("\\")
7688 || strAppOverride.contains(":"))
7689 strAppOverride.setNull();
7690 Utf8Str strAppPath;
7691 if (!strAppOverride.isEmpty())
7692 {
7693 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7694 Utf8Str strFullPath(szPath);
7695 strFullPath.append(strAppPath);
7696 /* there is a race, but people using this deserve the failure */
7697 if (!RTFileExists(strFullPath.c_str()))
7698 strAppOverride.setNull();
7699 }
7700 if (strAppOverride.isEmpty())
7701 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7702 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7703 strcpy(pszNamePart, strAppPath.c_str());
7704# else
7705 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7706 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7707 strcpy(pszNamePart, s_szVirtualBox_exe);
7708# endif
7709
7710 Utf8Str idStr = mData->mUuid.toString();
7711 const char *apszArgs[] =
7712 {
7713 szPath,
7714 "--comment", mUserData->s.strName.c_str(),
7715 "--startvm", idStr.c_str(),
7716 "--no-startvm-errormsgbox",
7717 NULL, /* For "--separate". */
7718 NULL, /* For "--sup-startup-log". */
7719 NULL
7720 };
7721 unsigned iArg = 6;
7722 if (fSeparate)
7723 apszArgs[iArg++] = "--separate";
7724 apszArgs[iArg++] = pszSupHardeningLogArg;
7725
7726 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7727 }
7728#else /* !VBOX_WITH_QTGUI */
7729 if (0)
7730 ;
7731#endif /* VBOX_WITH_QTGUI */
7732
7733 else
7734
7735#ifdef VBOX_WITH_VBOXSDL
7736 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7737 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7738 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7739 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7740 {
7741 strCanonicalName = "GUI/SDL";
7742 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7743 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7744 strcpy(pszNamePart, s_szVBoxSDL_exe);
7745
7746 Utf8Str idStr = mData->mUuid.toString();
7747 const char *apszArgs[] =
7748 {
7749 szPath,
7750 "--comment", mUserData->s.strName.c_str(),
7751 "--startvm", idStr.c_str(),
7752 NULL, /* For "--separate". */
7753 NULL, /* For "--sup-startup-log". */
7754 NULL
7755 };
7756 unsigned iArg = 5;
7757 if (fSeparate)
7758 apszArgs[iArg++] = "--separate";
7759 apszArgs[iArg++] = pszSupHardeningLogArg;
7760
7761 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7762 }
7763#else /* !VBOX_WITH_VBOXSDL */
7764 if (0)
7765 ;
7766#endif /* !VBOX_WITH_VBOXSDL */
7767
7768 else
7769
7770#ifdef VBOX_WITH_HEADLESS
7771 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7772 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7773 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7774 )
7775 {
7776 strCanonicalName = "headless";
7777 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7778 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7779 * and a VM works even if the server has not been installed.
7780 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7781 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7782 * differently in 4.0 and 3.x.
7783 */
7784 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7785 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7786 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7787
7788 Utf8Str idStr = mData->mUuid.toString();
7789 const char *apszArgs[] =
7790 {
7791 szPath,
7792 "--comment", mUserData->s.strName.c_str(),
7793 "--startvm", idStr.c_str(),
7794 "--vrde", "config",
7795 NULL, /* For "--capture". */
7796 NULL, /* For "--sup-startup-log". */
7797 NULL
7798 };
7799 unsigned iArg = 7;
7800 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7801 apszArgs[iArg++] = "--capture";
7802 apszArgs[iArg++] = pszSupHardeningLogArg;
7803
7804# ifdef RT_OS_WINDOWS
7805 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7806# else
7807 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7808# endif
7809 }
7810#else /* !VBOX_WITH_HEADLESS */
7811 if (0)
7812 ;
7813#endif /* !VBOX_WITH_HEADLESS */
7814 else
7815 {
7816 RTEnvDestroy(env);
7817 return setError(E_INVALIDARG,
7818 tr("Invalid frontend name: '%s'"),
7819 strFrontend.c_str());
7820 }
7821
7822 RTEnvDestroy(env);
7823
7824 if (RT_FAILURE(vrc))
7825 return setError(VBOX_E_IPRT_ERROR,
7826 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7827 mUserData->s.strName.c_str(), vrc);
7828
7829 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7830
7831 if (!fSeparate)
7832 {
7833 /*
7834 * Note that we don't release the lock here before calling the client,
7835 * because it doesn't need to call us back if called with a NULL argument.
7836 * Releasing the lock here is dangerous because we didn't prepare the
7837 * launch data yet, but the client we've just started may happen to be
7838 * too fast and call LockMachine() that will fail (because of PID, etc.),
7839 * so that the Machine will never get out of the Spawning session state.
7840 */
7841
7842 /* inform the session that it will be a remote one */
7843 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7844#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7845 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7846#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7847 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7848#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7849 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7850
7851 if (FAILED(rc))
7852 {
7853 /* restore the session state */
7854 mData->mSession.mState = SessionState_Unlocked;
7855 alock.release();
7856 mParent->i_addProcessToReap(pid);
7857 /* The failure may occur w/o any error info (from RPC), so provide one */
7858 return setError(VBOX_E_VM_ERROR,
7859 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7860 }
7861
7862 /* attach launch data to the machine */
7863 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7864 mData->mSession.mRemoteControls.push_back(aControl);
7865 mData->mSession.mProgress = aProgress;
7866 mData->mSession.mPID = pid;
7867 mData->mSession.mState = SessionState_Spawning;
7868 Assert(strCanonicalName.isNotEmpty());
7869 mData->mSession.mName = strCanonicalName;
7870 }
7871 else
7872 {
7873 /* For separate UI process we declare the launch as completed instantly, as the
7874 * actual headless VM start may or may not come. No point in remembering anything
7875 * yet, as what matters for us is when the headless VM gets started. */
7876 aProgress->i_notifyComplete(S_OK);
7877 }
7878
7879 alock.release();
7880 mParent->i_addProcessToReap(pid);
7881
7882 LogFlowThisFuncLeave();
7883 return S_OK;
7884}
7885
7886/**
7887 * Returns @c true if the given session machine instance has an open direct
7888 * session (and optionally also for direct sessions which are closing) and
7889 * returns the session control machine instance if so.
7890 *
7891 * Note that when the method returns @c false, the arguments remain unchanged.
7892 *
7893 * @param aMachine Session machine object.
7894 * @param aControl Direct session control object (optional).
7895 * @param aRequireVM If true then only allow VM sessions.
7896 * @param aAllowClosing If true then additionally a session which is currently
7897 * being closed will also be allowed.
7898 *
7899 * @note locks this object for reading.
7900 */
7901bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7902 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7903 bool aRequireVM /*= false*/,
7904 bool aAllowClosing /*= false*/)
7905{
7906 AutoLimitedCaller autoCaller(this);
7907 AssertComRCReturn(autoCaller.rc(), false);
7908
7909 /* just return false for inaccessible machines */
7910 if (getObjectState().getState() != ObjectState::Ready)
7911 return false;
7912
7913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7914
7915 if ( ( mData->mSession.mState == SessionState_Locked
7916 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7917 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7918 )
7919 {
7920 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7921
7922 aMachine = mData->mSession.mMachine;
7923
7924 if (aControl != NULL)
7925 *aControl = mData->mSession.mDirectControl;
7926
7927 return true;
7928 }
7929
7930 return false;
7931}
7932
7933/**
7934 * Returns @c true if the given machine has an spawning direct session.
7935 *
7936 * @note locks this object for reading.
7937 */
7938bool Machine::i_isSessionSpawning()
7939{
7940 AutoLimitedCaller autoCaller(this);
7941 AssertComRCReturn(autoCaller.rc(), false);
7942
7943 /* just return false for inaccessible machines */
7944 if (getObjectState().getState() != ObjectState::Ready)
7945 return false;
7946
7947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7948
7949 if (mData->mSession.mState == SessionState_Spawning)
7950 return true;
7951
7952 return false;
7953}
7954
7955/**
7956 * Called from the client watcher thread to check for unexpected client process
7957 * death during Session_Spawning state (e.g. before it successfully opened a
7958 * direct session).
7959 *
7960 * On Win32 and on OS/2, this method is called only when we've got the
7961 * direct client's process termination notification, so it always returns @c
7962 * true.
7963 *
7964 * On other platforms, this method returns @c true if the client process is
7965 * terminated and @c false if it's still alive.
7966 *
7967 * @note Locks this object for writing.
7968 */
7969bool Machine::i_checkForSpawnFailure()
7970{
7971 AutoCaller autoCaller(this);
7972 if (!autoCaller.isOk())
7973 {
7974 /* nothing to do */
7975 LogFlowThisFunc(("Already uninitialized!\n"));
7976 return true;
7977 }
7978
7979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7980
7981 if (mData->mSession.mState != SessionState_Spawning)
7982 {
7983 /* nothing to do */
7984 LogFlowThisFunc(("Not spawning any more!\n"));
7985 return true;
7986 }
7987
7988 HRESULT rc = S_OK;
7989
7990 /* PID not yet initialized, skip check. */
7991 if (mData->mSession.mPID == NIL_RTPROCESS)
7992 return false;
7993
7994 RTPROCSTATUS status;
7995 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7996
7997 if (vrc != VERR_PROCESS_RUNNING)
7998 {
7999 Utf8Str strExtraInfo;
8000
8001#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8002 /* If the startup logfile exists and is of non-zero length, tell the
8003 user to look there for more details to encourage them to attach it
8004 when reporting startup issues. */
8005 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8006 uint64_t cbStartupLogFile = 0;
8007 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8008 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8009 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8010#endif
8011
8012 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8013 rc = setError(E_FAIL,
8014 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8015 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8016 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8017 rc = setError(E_FAIL,
8018 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8019 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8020 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8021 rc = setError(E_FAIL,
8022 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8023 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8024 else
8025 rc = setError(E_FAIL,
8026 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8027 i_getName().c_str(), vrc, strExtraInfo.c_str());
8028 }
8029
8030 if (FAILED(rc))
8031 {
8032 /* Close the remote session, remove the remote control from the list
8033 * and reset session state to Closed (@note keep the code in sync with
8034 * the relevant part in LockMachine()). */
8035
8036 Assert(mData->mSession.mRemoteControls.size() == 1);
8037 if (mData->mSession.mRemoteControls.size() == 1)
8038 {
8039 ErrorInfoKeeper eik;
8040 mData->mSession.mRemoteControls.front()->Uninitialize();
8041 }
8042
8043 mData->mSession.mRemoteControls.clear();
8044 mData->mSession.mState = SessionState_Unlocked;
8045
8046 /* finalize the progress after setting the state */
8047 if (!mData->mSession.mProgress.isNull())
8048 {
8049 mData->mSession.mProgress->notifyComplete(rc);
8050 mData->mSession.mProgress.setNull();
8051 }
8052
8053 mData->mSession.mPID = NIL_RTPROCESS;
8054
8055 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8056 return true;
8057 }
8058
8059 return false;
8060}
8061
8062/**
8063 * Checks whether the machine can be registered. If so, commits and saves
8064 * all settings.
8065 *
8066 * @note Must be called from mParent's write lock. Locks this object and
8067 * children for writing.
8068 */
8069HRESULT Machine::i_prepareRegister()
8070{
8071 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8072
8073 AutoLimitedCaller autoCaller(this);
8074 AssertComRCReturnRC(autoCaller.rc());
8075
8076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8077
8078 /* wait for state dependents to drop to zero */
8079 i_ensureNoStateDependencies();
8080
8081 if (!mData->mAccessible)
8082 return setError(VBOX_E_INVALID_OBJECT_STATE,
8083 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8084 mUserData->s.strName.c_str(),
8085 mData->mUuid.toString().c_str());
8086
8087 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8088
8089 if (mData->mRegistered)
8090 return setError(VBOX_E_INVALID_OBJECT_STATE,
8091 tr("The machine '%s' with UUID {%s} is already registered"),
8092 mUserData->s.strName.c_str(),
8093 mData->mUuid.toString().c_str());
8094
8095 HRESULT rc = S_OK;
8096
8097 // Ensure the settings are saved. If we are going to be registered and
8098 // no config file exists yet, create it by calling i_saveSettings() too.
8099 if ( (mData->flModifications)
8100 || (!mData->pMachineConfigFile->fileExists())
8101 )
8102 {
8103 rc = i_saveSettings(NULL);
8104 // no need to check whether VirtualBox.xml needs saving too since
8105 // we can't have a machine XML file rename pending
8106 if (FAILED(rc)) return rc;
8107 }
8108
8109 /* more config checking goes here */
8110
8111 if (SUCCEEDED(rc))
8112 {
8113 /* we may have had implicit modifications we want to fix on success */
8114 i_commit();
8115
8116 mData->mRegistered = true;
8117 }
8118 else
8119 {
8120 /* we may have had implicit modifications we want to cancel on failure*/
8121 i_rollback(false /* aNotify */);
8122 }
8123
8124 return rc;
8125}
8126
8127/**
8128 * Increases the number of objects dependent on the machine state or on the
8129 * registered state. Guarantees that these two states will not change at least
8130 * until #i_releaseStateDependency() is called.
8131 *
8132 * Depending on the @a aDepType value, additional state checks may be made.
8133 * These checks will set extended error info on failure. See
8134 * #i_checkStateDependency() for more info.
8135 *
8136 * If this method returns a failure, the dependency is not added and the caller
8137 * is not allowed to rely on any particular machine state or registration state
8138 * value and may return the failed result code to the upper level.
8139 *
8140 * @param aDepType Dependency type to add.
8141 * @param aState Current machine state (NULL if not interested).
8142 * @param aRegistered Current registered state (NULL if not interested).
8143 *
8144 * @note Locks this object for writing.
8145 */
8146HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8147 MachineState_T *aState /* = NULL */,
8148 BOOL *aRegistered /* = NULL */)
8149{
8150 AutoCaller autoCaller(this);
8151 AssertComRCReturnRC(autoCaller.rc());
8152
8153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8154
8155 HRESULT rc = i_checkStateDependency(aDepType);
8156 if (FAILED(rc)) return rc;
8157
8158 {
8159 if (mData->mMachineStateChangePending != 0)
8160 {
8161 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8162 * drop to zero so don't add more. It may make sense to wait a bit
8163 * and retry before reporting an error (since the pending state
8164 * transition should be really quick) but let's just assert for
8165 * now to see if it ever happens on practice. */
8166
8167 AssertFailed();
8168
8169 return setError(E_ACCESSDENIED,
8170 tr("Machine state change is in progress. Please retry the operation later."));
8171 }
8172
8173 ++mData->mMachineStateDeps;
8174 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8175 }
8176
8177 if (aState)
8178 *aState = mData->mMachineState;
8179 if (aRegistered)
8180 *aRegistered = mData->mRegistered;
8181
8182 return S_OK;
8183}
8184
8185/**
8186 * Decreases the number of objects dependent on the machine state.
8187 * Must always complete the #i_addStateDependency() call after the state
8188 * dependency is no more necessary.
8189 */
8190void Machine::i_releaseStateDependency()
8191{
8192 AutoCaller autoCaller(this);
8193 AssertComRCReturnVoid(autoCaller.rc());
8194
8195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8196
8197 /* releaseStateDependency() w/o addStateDependency()? */
8198 AssertReturnVoid(mData->mMachineStateDeps != 0);
8199 -- mData->mMachineStateDeps;
8200
8201 if (mData->mMachineStateDeps == 0)
8202 {
8203 /* inform i_ensureNoStateDependencies() that there are no more deps */
8204 if (mData->mMachineStateChangePending != 0)
8205 {
8206 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8207 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8208 }
8209 }
8210}
8211
8212Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8213{
8214 /* start with nothing found */
8215 Utf8Str strResult("");
8216
8217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8218
8219 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8220 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8221 // found:
8222 strResult = it->second; // source is a Utf8Str
8223
8224 return strResult;
8225}
8226
8227// protected methods
8228/////////////////////////////////////////////////////////////////////////////
8229
8230/**
8231 * Performs machine state checks based on the @a aDepType value. If a check
8232 * fails, this method will set extended error info, otherwise it will return
8233 * S_OK. It is supposed, that on failure, the caller will immediately return
8234 * the return value of this method to the upper level.
8235 *
8236 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8237 *
8238 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8239 * current state of this machine object allows to change settings of the
8240 * machine (i.e. the machine is not registered, or registered but not running
8241 * and not saved). It is useful to call this method from Machine setters
8242 * before performing any change.
8243 *
8244 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8245 * as for MutableStateDep except that if the machine is saved, S_OK is also
8246 * returned. This is useful in setters which allow changing machine
8247 * properties when it is in the saved state.
8248 *
8249 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8250 * if the current state of this machine object allows to change runtime
8251 * changeable settings of the machine (i.e. the machine is not registered, or
8252 * registered but either running or not running and not saved). It is useful
8253 * to call this method from Machine setters before performing any changes to
8254 * runtime changeable settings.
8255 *
8256 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8257 * the same as for MutableOrRunningStateDep except that if the machine is
8258 * saved, S_OK is also returned. This is useful in setters which allow
8259 * changing runtime and saved state changeable machine properties.
8260 *
8261 * @param aDepType Dependency type to check.
8262 *
8263 * @note Non Machine based classes should use #i_addStateDependency() and
8264 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8265 * template.
8266 *
8267 * @note This method must be called from under this object's read or write
8268 * lock.
8269 */
8270HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8271{
8272 switch (aDepType)
8273 {
8274 case AnyStateDep:
8275 {
8276 break;
8277 }
8278 case MutableStateDep:
8279 {
8280 if ( mData->mRegistered
8281 && ( !i_isSessionMachine()
8282 || ( mData->mMachineState != MachineState_Aborted
8283 && mData->mMachineState != MachineState_Teleported
8284 && mData->mMachineState != MachineState_PoweredOff
8285 )
8286 )
8287 )
8288 return setError(VBOX_E_INVALID_VM_STATE,
8289 tr("The machine is not mutable (state is %s)"),
8290 Global::stringifyMachineState(mData->mMachineState));
8291 break;
8292 }
8293 case MutableOrSavedStateDep:
8294 {
8295 if ( mData->mRegistered
8296 && ( !i_isSessionMachine()
8297 || ( mData->mMachineState != MachineState_Aborted
8298 && mData->mMachineState != MachineState_Teleported
8299 && mData->mMachineState != MachineState_Saved
8300 && mData->mMachineState != MachineState_PoweredOff
8301 )
8302 )
8303 )
8304 return setError(VBOX_E_INVALID_VM_STATE,
8305 tr("The machine is not mutable or saved (state is %s)"),
8306 Global::stringifyMachineState(mData->mMachineState));
8307 break;
8308 }
8309 case MutableOrRunningStateDep:
8310 {
8311 if ( mData->mRegistered
8312 && ( !i_isSessionMachine()
8313 || ( mData->mMachineState != MachineState_Aborted
8314 && mData->mMachineState != MachineState_Teleported
8315 && mData->mMachineState != MachineState_PoweredOff
8316 && !Global::IsOnline(mData->mMachineState)
8317 )
8318 )
8319 )
8320 return setError(VBOX_E_INVALID_VM_STATE,
8321 tr("The machine is not mutable or running (state is %s)"),
8322 Global::stringifyMachineState(mData->mMachineState));
8323 break;
8324 }
8325 case MutableOrSavedOrRunningStateDep:
8326 {
8327 if ( mData->mRegistered
8328 && ( !i_isSessionMachine()
8329 || ( mData->mMachineState != MachineState_Aborted
8330 && mData->mMachineState != MachineState_Teleported
8331 && mData->mMachineState != MachineState_Saved
8332 && mData->mMachineState != MachineState_PoweredOff
8333 && !Global::IsOnline(mData->mMachineState)
8334 )
8335 )
8336 )
8337 return setError(VBOX_E_INVALID_VM_STATE,
8338 tr("The machine is not mutable, saved or running (state is %s)"),
8339 Global::stringifyMachineState(mData->mMachineState));
8340 break;
8341 }
8342 }
8343
8344 return S_OK;
8345}
8346
8347/**
8348 * Helper to initialize all associated child objects and allocate data
8349 * structures.
8350 *
8351 * This method must be called as a part of the object's initialization procedure
8352 * (usually done in the #init() method).
8353 *
8354 * @note Must be called only from #init() or from #i_registeredInit().
8355 */
8356HRESULT Machine::initDataAndChildObjects()
8357{
8358 AutoCaller autoCaller(this);
8359 AssertComRCReturnRC(autoCaller.rc());
8360 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8361 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8362
8363 AssertReturn(!mData->mAccessible, E_FAIL);
8364
8365 /* allocate data structures */
8366 mSSData.allocate();
8367 mUserData.allocate();
8368 mHWData.allocate();
8369 mMediumAttachments.allocate();
8370 mStorageControllers.allocate();
8371 mUSBControllers.allocate();
8372
8373 /* initialize mOSTypeId */
8374 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8375
8376/** @todo r=bird: init() methods never fails, right? Why don't we make them
8377 * return void then! */
8378
8379 /* create associated BIOS settings object */
8380 unconst(mBIOSSettings).createObject();
8381 mBIOSSettings->init(this);
8382
8383 /* create an associated VRDE object (default is disabled) */
8384 unconst(mVRDEServer).createObject();
8385 mVRDEServer->init(this);
8386
8387 /* create associated serial port objects */
8388 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8389 {
8390 unconst(mSerialPorts[slot]).createObject();
8391 mSerialPorts[slot]->init(this, slot);
8392 }
8393
8394 /* create associated parallel port objects */
8395 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8396 {
8397 unconst(mParallelPorts[slot]).createObject();
8398 mParallelPorts[slot]->init(this, slot);
8399 }
8400
8401 /* create the audio adapter object (always present, default is disabled) */
8402 unconst(mAudioAdapter).createObject();
8403 mAudioAdapter->init(this);
8404
8405 /* create the USB device filters object (always present) */
8406 unconst(mUSBDeviceFilters).createObject();
8407 mUSBDeviceFilters->init(this);
8408
8409 /* create associated network adapter objects */
8410 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8411 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8412 {
8413 unconst(mNetworkAdapters[slot]).createObject();
8414 mNetworkAdapters[slot]->init(this, slot);
8415 }
8416
8417 /* create the bandwidth control */
8418 unconst(mBandwidthControl).createObject();
8419 mBandwidthControl->init(this);
8420
8421 return S_OK;
8422}
8423
8424/**
8425 * Helper to uninitialize all associated child objects and to free all data
8426 * structures.
8427 *
8428 * This method must be called as a part of the object's uninitialization
8429 * procedure (usually done in the #uninit() method).
8430 *
8431 * @note Must be called only from #uninit() or from #i_registeredInit().
8432 */
8433void Machine::uninitDataAndChildObjects()
8434{
8435 AutoCaller autoCaller(this);
8436 AssertComRCReturnVoid(autoCaller.rc());
8437 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8438 || getObjectState().getState() == ObjectState::Limited);
8439
8440 /* tell all our other child objects we've been uninitialized */
8441 if (mBandwidthControl)
8442 {
8443 mBandwidthControl->uninit();
8444 unconst(mBandwidthControl).setNull();
8445 }
8446
8447 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8448 {
8449 if (mNetworkAdapters[slot])
8450 {
8451 mNetworkAdapters[slot]->uninit();
8452 unconst(mNetworkAdapters[slot]).setNull();
8453 }
8454 }
8455
8456 if (mUSBDeviceFilters)
8457 {
8458 mUSBDeviceFilters->uninit();
8459 unconst(mUSBDeviceFilters).setNull();
8460 }
8461
8462 if (mAudioAdapter)
8463 {
8464 mAudioAdapter->uninit();
8465 unconst(mAudioAdapter).setNull();
8466 }
8467
8468 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8469 {
8470 if (mParallelPorts[slot])
8471 {
8472 mParallelPorts[slot]->uninit();
8473 unconst(mParallelPorts[slot]).setNull();
8474 }
8475 }
8476
8477 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8478 {
8479 if (mSerialPorts[slot])
8480 {
8481 mSerialPorts[slot]->uninit();
8482 unconst(mSerialPorts[slot]).setNull();
8483 }
8484 }
8485
8486 if (mVRDEServer)
8487 {
8488 mVRDEServer->uninit();
8489 unconst(mVRDEServer).setNull();
8490 }
8491
8492 if (mBIOSSettings)
8493 {
8494 mBIOSSettings->uninit();
8495 unconst(mBIOSSettings).setNull();
8496 }
8497
8498 /* Deassociate media (only when a real Machine or a SnapshotMachine
8499 * instance is uninitialized; SessionMachine instances refer to real
8500 * Machine media). This is necessary for a clean re-initialization of
8501 * the VM after successfully re-checking the accessibility state. Note
8502 * that in case of normal Machine or SnapshotMachine uninitialization (as
8503 * a result of unregistering or deleting the snapshot), outdated media
8504 * attachments will already be uninitialized and deleted, so this
8505 * code will not affect them. */
8506 if ( !mMediumAttachments.isNull()
8507 && !i_isSessionMachine()
8508 )
8509 {
8510 for (MediumAttachmentList::const_iterator
8511 it = mMediumAttachments->begin();
8512 it != mMediumAttachments->end();
8513 ++it)
8514 {
8515 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8516 if (pMedium.isNull())
8517 continue;
8518 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8519 AssertComRC(rc);
8520 }
8521 }
8522
8523 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8524 {
8525 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8526 if (mData->mFirstSnapshot)
8527 {
8528 // snapshots tree is protected by machine write lock; strictly
8529 // this isn't necessary here since we're deleting the entire
8530 // machine, but otherwise we assert in Snapshot::uninit()
8531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8532 mData->mFirstSnapshot->uninit();
8533 mData->mFirstSnapshot.setNull();
8534 }
8535
8536 mData->mCurrentSnapshot.setNull();
8537 }
8538
8539 /* free data structures (the essential mData structure is not freed here
8540 * since it may be still in use) */
8541 mMediumAttachments.free();
8542 mStorageControllers.free();
8543 mUSBControllers.free();
8544 mHWData.free();
8545 mUserData.free();
8546 mSSData.free();
8547}
8548
8549/**
8550 * Returns a pointer to the Machine object for this machine that acts like a
8551 * parent for complex machine data objects such as shared folders, etc.
8552 *
8553 * For primary Machine objects and for SnapshotMachine objects, returns this
8554 * object's pointer itself. For SessionMachine objects, returns the peer
8555 * (primary) machine pointer.
8556 */
8557Machine *Machine::i_getMachine()
8558{
8559 if (i_isSessionMachine())
8560 return (Machine*)mPeer;
8561 return this;
8562}
8563
8564/**
8565 * Makes sure that there are no machine state dependents. If necessary, waits
8566 * for the number of dependents to drop to zero.
8567 *
8568 * Make sure this method is called from under this object's write lock to
8569 * guarantee that no new dependents may be added when this method returns
8570 * control to the caller.
8571 *
8572 * @note Locks this object for writing. The lock will be released while waiting
8573 * (if necessary).
8574 *
8575 * @warning To be used only in methods that change the machine state!
8576 */
8577void Machine::i_ensureNoStateDependencies()
8578{
8579 AssertReturnVoid(isWriteLockOnCurrentThread());
8580
8581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8582
8583 /* Wait for all state dependents if necessary */
8584 if (mData->mMachineStateDeps != 0)
8585 {
8586 /* lazy semaphore creation */
8587 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8588 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8589
8590 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8591 mData->mMachineStateDeps));
8592
8593 ++mData->mMachineStateChangePending;
8594
8595 /* reset the semaphore before waiting, the last dependent will signal
8596 * it */
8597 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8598
8599 alock.release();
8600
8601 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8602
8603 alock.acquire();
8604
8605 -- mData->mMachineStateChangePending;
8606 }
8607}
8608
8609/**
8610 * Changes the machine state and informs callbacks.
8611 *
8612 * This method is not intended to fail so it either returns S_OK or asserts (and
8613 * returns a failure).
8614 *
8615 * @note Locks this object for writing.
8616 */
8617HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8618{
8619 LogFlowThisFuncEnter();
8620 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8621 Assert(aMachineState != MachineState_Null);
8622
8623 AutoCaller autoCaller(this);
8624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8625
8626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8627
8628 /* wait for state dependents to drop to zero */
8629 i_ensureNoStateDependencies();
8630
8631 MachineState_T const enmOldState = mData->mMachineState;
8632 if (enmOldState != aMachineState)
8633 {
8634 mData->mMachineState = aMachineState;
8635 RTTimeNow(&mData->mLastStateChange);
8636
8637#ifdef VBOX_WITH_DTRACE_R3_MAIN
8638 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8639#endif
8640 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8641 }
8642
8643 LogFlowThisFuncLeave();
8644 return S_OK;
8645}
8646
8647/**
8648 * Searches for a shared folder with the given logical name
8649 * in the collection of shared folders.
8650 *
8651 * @param aName logical name of the shared folder
8652 * @param aSharedFolder where to return the found object
8653 * @param aSetError whether to set the error info if the folder is
8654 * not found
8655 * @return
8656 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8657 *
8658 * @note
8659 * must be called from under the object's lock!
8660 */
8661HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8662 ComObjPtr<SharedFolder> &aSharedFolder,
8663 bool aSetError /* = false */)
8664{
8665 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8666 for (HWData::SharedFolderList::const_iterator
8667 it = mHWData->mSharedFolders.begin();
8668 it != mHWData->mSharedFolders.end();
8669 ++it)
8670 {
8671 SharedFolder *pSF = *it;
8672 AutoCaller autoCaller(pSF);
8673 if (pSF->i_getName() == aName)
8674 {
8675 aSharedFolder = pSF;
8676 rc = S_OK;
8677 break;
8678 }
8679 }
8680
8681 if (aSetError && FAILED(rc))
8682 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8683
8684 return rc;
8685}
8686
8687/**
8688 * Initializes all machine instance data from the given settings structures
8689 * from XML. The exception is the machine UUID which needs special handling
8690 * depending on the caller's use case, so the caller needs to set that herself.
8691 *
8692 * This gets called in several contexts during machine initialization:
8693 *
8694 * -- When machine XML exists on disk already and needs to be loaded into memory,
8695 * for example, from #i_registeredInit() to load all registered machines on
8696 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8697 * attached to the machine should be part of some media registry already.
8698 *
8699 * -- During OVF import, when a machine config has been constructed from an
8700 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8701 * ensure that the media listed as attachments in the config (which have
8702 * been imported from the OVF) receive the correct registry ID.
8703 *
8704 * -- During VM cloning.
8705 *
8706 * @param config Machine settings from XML.
8707 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8708 * for each attached medium in the config.
8709 * @return
8710 */
8711HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8712 const Guid *puuidRegistry)
8713{
8714 // copy name, description, OS type, teleporter, UTC etc.
8715 mUserData->s = config.machineUserData;
8716
8717 // look up the object by Id to check it is valid
8718 ComObjPtr<GuestOSType> pGuestOSType;
8719 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8720 pGuestOSType);
8721 if (FAILED(rc)) return rc;
8722 mUserData->s.strOsType = pGuestOSType->i_id();
8723
8724 // stateFile (optional)
8725 if (config.strStateFile.isEmpty())
8726 mSSData->strStateFilePath.setNull();
8727 else
8728 {
8729 Utf8Str stateFilePathFull(config.strStateFile);
8730 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8731 if (RT_FAILURE(vrc))
8732 return setError(E_FAIL,
8733 tr("Invalid saved state file path '%s' (%Rrc)"),
8734 config.strStateFile.c_str(),
8735 vrc);
8736 mSSData->strStateFilePath = stateFilePathFull;
8737 }
8738
8739 // snapshot folder needs special processing so set it again
8740 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8741 if (FAILED(rc)) return rc;
8742
8743 /* Copy the extra data items (config may or may not be the same as
8744 * mData->pMachineConfigFile) if necessary. When loading the XML files
8745 * from disk they are the same, but not for OVF import. */
8746 if (mData->pMachineConfigFile != &config)
8747 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8748
8749 /* currentStateModified (optional, default is true) */
8750 mData->mCurrentStateModified = config.fCurrentStateModified;
8751
8752 mData->mLastStateChange = config.timeLastStateChange;
8753
8754 /*
8755 * note: all mUserData members must be assigned prior this point because
8756 * we need to commit changes in order to let mUserData be shared by all
8757 * snapshot machine instances.
8758 */
8759 mUserData.commitCopy();
8760
8761 // machine registry, if present (must be loaded before snapshots)
8762 if (config.canHaveOwnMediaRegistry())
8763 {
8764 // determine machine folder
8765 Utf8Str strMachineFolder = i_getSettingsFileFull();
8766 strMachineFolder.stripFilename();
8767 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8768 config.mediaRegistry,
8769 strMachineFolder);
8770 if (FAILED(rc)) return rc;
8771 }
8772
8773 /* Snapshot node (optional) */
8774 size_t cRootSnapshots;
8775 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8776 {
8777 // there must be only one root snapshot
8778 Assert(cRootSnapshots == 1);
8779
8780 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8781
8782 rc = i_loadSnapshot(snap,
8783 config.uuidCurrentSnapshot,
8784 NULL); // no parent == first snapshot
8785 if (FAILED(rc)) return rc;
8786 }
8787
8788 // hardware data
8789 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8790 if (FAILED(rc)) return rc;
8791
8792 /*
8793 * NOTE: the assignment below must be the last thing to do,
8794 * otherwise it will be not possible to change the settings
8795 * somewhere in the code above because all setters will be
8796 * blocked by i_checkStateDependency(MutableStateDep).
8797 */
8798
8799 /* set the machine state to Aborted or Saved when appropriate */
8800 if (config.fAborted)
8801 {
8802 mSSData->strStateFilePath.setNull();
8803
8804 /* no need to use i_setMachineState() during init() */
8805 mData->mMachineState = MachineState_Aborted;
8806 }
8807 else if (!mSSData->strStateFilePath.isEmpty())
8808 {
8809 /* no need to use i_setMachineState() during init() */
8810 mData->mMachineState = MachineState_Saved;
8811 }
8812
8813 // after loading settings, we are no longer different from the XML on disk
8814 mData->flModifications = 0;
8815
8816 return S_OK;
8817}
8818
8819/**
8820 * Recursively loads all snapshots starting from the given.
8821 *
8822 * @param data snapshot settings.
8823 * @param aCurSnapshotId Current snapshot ID from the settings file.
8824 * @param aParentSnapshot Parent snapshot.
8825 */
8826HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8827 const Guid &aCurSnapshotId,
8828 Snapshot *aParentSnapshot)
8829{
8830 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8831 AssertReturn(!i_isSessionMachine(), E_FAIL);
8832
8833 HRESULT rc = S_OK;
8834
8835 Utf8Str strStateFile;
8836 if (!data.strStateFile.isEmpty())
8837 {
8838 /* optional */
8839 strStateFile = data.strStateFile;
8840 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8841 if (RT_FAILURE(vrc))
8842 return setError(E_FAIL,
8843 tr("Invalid saved state file path '%s' (%Rrc)"),
8844 strStateFile.c_str(),
8845 vrc);
8846 }
8847
8848 /* create a snapshot machine object */
8849 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8850 pSnapshotMachine.createObject();
8851 rc = pSnapshotMachine->initFromSettings(this,
8852 data.hardware,
8853 &data.debugging,
8854 &data.autostart,
8855 data.uuid.ref(),
8856 strStateFile);
8857 if (FAILED(rc)) return rc;
8858
8859 /* create a snapshot object */
8860 ComObjPtr<Snapshot> pSnapshot;
8861 pSnapshot.createObject();
8862 /* initialize the snapshot */
8863 rc = pSnapshot->init(mParent, // VirtualBox object
8864 data.uuid,
8865 data.strName,
8866 data.strDescription,
8867 data.timestamp,
8868 pSnapshotMachine,
8869 aParentSnapshot);
8870 if (FAILED(rc)) return rc;
8871
8872 /* memorize the first snapshot if necessary */
8873 if (!mData->mFirstSnapshot)
8874 mData->mFirstSnapshot = pSnapshot;
8875
8876 /* memorize the current snapshot when appropriate */
8877 if ( !mData->mCurrentSnapshot
8878 && pSnapshot->i_getId() == aCurSnapshotId
8879 )
8880 mData->mCurrentSnapshot = pSnapshot;
8881
8882 // now create the children
8883 for (settings::SnapshotsList::const_iterator
8884 it = data.llChildSnapshots.begin();
8885 it != data.llChildSnapshots.end();
8886 ++it)
8887 {
8888 const settings::Snapshot &childData = *it;
8889 // recurse
8890 rc = i_loadSnapshot(childData,
8891 aCurSnapshotId,
8892 pSnapshot); // parent = the one we created above
8893 if (FAILED(rc)) return rc;
8894 }
8895
8896 return rc;
8897}
8898
8899/**
8900 * Loads settings into mHWData.
8901 *
8902 * @param puuidRegistry Registry ID.
8903 * @param puuidSnapshot Snapshot ID
8904 * @param data Reference to the hardware settings.
8905 * @param pDbg Pointer to the debugging settings.
8906 * @param pAutostart Pointer to the autostart settings.
8907 */
8908HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8909 const Guid *puuidSnapshot,
8910 const settings::Hardware &data,
8911 const settings::Debugging *pDbg,
8912 const settings::Autostart *pAutostart)
8913{
8914 AssertReturn(!i_isSessionMachine(), E_FAIL);
8915
8916 HRESULT rc = S_OK;
8917
8918 try
8919 {
8920 ComObjPtr<GuestOSType> pGuestOSType;
8921 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8922 pGuestOSType);
8923 if (FAILED(rc))
8924 return rc;
8925
8926 /* The hardware version attribute (optional). */
8927 mHWData->mHWVersion = data.strVersion;
8928 mHWData->mHardwareUUID = data.uuid;
8929
8930 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8931 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8932 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8933 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8934 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8935 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8936 mHWData->mPAEEnabled = data.fPAE;
8937 mHWData->mLongMode = data.enmLongMode;
8938 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8939 mHWData->mAPIC = data.fAPIC;
8940 mHWData->mX2APIC = data.fX2APIC;
8941 mHWData->mCPUCount = data.cCPUs;
8942 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8943 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8944 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8945 mHWData->mCpuProfile = data.strCpuProfile;
8946
8947 // cpu
8948 if (mHWData->mCPUHotPlugEnabled)
8949 {
8950 for (settings::CpuList::const_iterator
8951 it = data.llCpus.begin();
8952 it != data.llCpus.end();
8953 ++it)
8954 {
8955 const settings::Cpu &cpu = *it;
8956
8957 mHWData->mCPUAttached[cpu.ulId] = true;
8958 }
8959 }
8960
8961 // cpuid leafs
8962 for (settings::CpuIdLeafsList::const_iterator
8963 it = data.llCpuIdLeafs.begin();
8964 it != data.llCpuIdLeafs.end();
8965 ++it)
8966 {
8967 const settings::CpuIdLeaf &rLeaf= *it;
8968 if ( rLeaf.idx < UINT32_C(0x20)
8969 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8970 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8971 mHWData->mCpuIdLeafList.push_back(rLeaf);
8972 /* else: just ignore */
8973 }
8974
8975 mHWData->mMemorySize = data.ulMemorySizeMB;
8976 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8977
8978 // boot order
8979 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8980 {
8981 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8982 if (it == data.mapBootOrder.end())
8983 mHWData->mBootOrder[i] = DeviceType_Null;
8984 else
8985 mHWData->mBootOrder[i] = it->second;
8986 }
8987
8988 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8989 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8990 mHWData->mMonitorCount = data.cMonitors;
8991 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8992 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8993 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8994 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8995 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8996 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8997 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8998 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8999 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9000 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9001 if (!data.strVideoCaptureFile.isEmpty())
9002 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9003 else
9004 mHWData->mVideoCaptureFile.setNull();
9005 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9006 mHWData->mFirmwareType = data.firmwareType;
9007 mHWData->mPointingHIDType = data.pointingHIDType;
9008 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9009 mHWData->mChipsetType = data.chipsetType;
9010 mHWData->mParavirtProvider = data.paravirtProvider;
9011 mHWData->mParavirtDebug = data.strParavirtDebug;
9012 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9013 mHWData->mHPETEnabled = data.fHPETEnabled;
9014
9015 /* VRDEServer */
9016 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9017 if (FAILED(rc)) return rc;
9018
9019 /* BIOS */
9020 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9021 if (FAILED(rc)) return rc;
9022
9023 // Bandwidth control (must come before network adapters)
9024 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9025 if (FAILED(rc)) return rc;
9026
9027 /* Shared folders */
9028 for (settings::USBControllerList::const_iterator
9029 it = data.usbSettings.llUSBControllers.begin();
9030 it != data.usbSettings.llUSBControllers.end();
9031 ++it)
9032 {
9033 const settings::USBController &settingsCtrl = *it;
9034 ComObjPtr<USBController> newCtrl;
9035
9036 newCtrl.createObject();
9037 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9038 mUSBControllers->push_back(newCtrl);
9039 }
9040
9041 /* USB device filters */
9042 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9043 if (FAILED(rc)) return rc;
9044
9045 // network adapters (establish array size first and apply defaults, to
9046 // ensure reading the same settings as we saved, since the list skips
9047 // adapters having defaults)
9048 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9049 size_t oldCount = mNetworkAdapters.size();
9050 if (newCount > oldCount)
9051 {
9052 mNetworkAdapters.resize(newCount);
9053 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9054 {
9055 unconst(mNetworkAdapters[slot]).createObject();
9056 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9057 }
9058 }
9059 else if (newCount < oldCount)
9060 mNetworkAdapters.resize(newCount);
9061 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9062 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9063 for (settings::NetworkAdaptersList::const_iterator
9064 it = data.llNetworkAdapters.begin();
9065 it != data.llNetworkAdapters.end();
9066 ++it)
9067 {
9068 const settings::NetworkAdapter &nic = *it;
9069
9070 /* slot uniqueness is guaranteed by XML Schema */
9071 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9072 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9073 if (FAILED(rc)) return rc;
9074 }
9075
9076 // serial ports (establish defaults first, to ensure reading the same
9077 // settings as we saved, since the list skips ports having defaults)
9078 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9079 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9080 for (settings::SerialPortsList::const_iterator
9081 it = data.llSerialPorts.begin();
9082 it != data.llSerialPorts.end();
9083 ++it)
9084 {
9085 const settings::SerialPort &s = *it;
9086
9087 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9088 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9089 if (FAILED(rc)) return rc;
9090 }
9091
9092 // parallel ports (establish defaults first, to ensure reading the same
9093 // settings as we saved, since the list skips ports having defaults)
9094 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9095 mParallelPorts[i]->i_applyDefaults();
9096 for (settings::ParallelPortsList::const_iterator
9097 it = data.llParallelPorts.begin();
9098 it != data.llParallelPorts.end();
9099 ++it)
9100 {
9101 const settings::ParallelPort &p = *it;
9102
9103 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9104 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9105 if (FAILED(rc)) return rc;
9106 }
9107
9108 /* AudioAdapter */
9109 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9110 if (FAILED(rc)) return rc;
9111
9112 /* storage controllers */
9113 rc = i_loadStorageControllers(data.storage,
9114 puuidRegistry,
9115 puuidSnapshot);
9116 if (FAILED(rc)) return rc;
9117
9118 /* Shared folders */
9119 for (settings::SharedFoldersList::const_iterator
9120 it = data.llSharedFolders.begin();
9121 it != data.llSharedFolders.end();
9122 ++it)
9123 {
9124 const settings::SharedFolder &sf = *it;
9125
9126 ComObjPtr<SharedFolder> sharedFolder;
9127 /* Check for double entries. Not allowed! */
9128 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9129 if (SUCCEEDED(rc))
9130 return setError(VBOX_E_OBJECT_IN_USE,
9131 tr("Shared folder named '%s' already exists"),
9132 sf.strName.c_str());
9133
9134 /* Create the new shared folder. Don't break on error. This will be
9135 * reported when the machine starts. */
9136 sharedFolder.createObject();
9137 rc = sharedFolder->init(i_getMachine(),
9138 sf.strName,
9139 sf.strHostPath,
9140 RT_BOOL(sf.fWritable),
9141 RT_BOOL(sf.fAutoMount),
9142 false /* fFailOnError */);
9143 if (FAILED(rc)) return rc;
9144 mHWData->mSharedFolders.push_back(sharedFolder);
9145 }
9146
9147 // Clipboard
9148 mHWData->mClipboardMode = data.clipboardMode;
9149
9150 // drag'n'drop
9151 mHWData->mDnDMode = data.dndMode;
9152
9153 // guest settings
9154 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9155
9156 // IO settings
9157 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9158 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9159
9160 // Host PCI devices
9161 for (settings::HostPCIDeviceAttachmentList::const_iterator
9162 it = data.pciAttachments.begin();
9163 it != data.pciAttachments.end();
9164 ++it)
9165 {
9166 const settings::HostPCIDeviceAttachment &hpda = *it;
9167 ComObjPtr<PCIDeviceAttachment> pda;
9168
9169 pda.createObject();
9170 pda->i_loadSettings(this, hpda);
9171 mHWData->mPCIDeviceAssignments.push_back(pda);
9172 }
9173
9174 /*
9175 * (The following isn't really real hardware, but it lives in HWData
9176 * for reasons of convenience.)
9177 */
9178
9179#ifdef VBOX_WITH_GUEST_PROPS
9180 /* Guest properties (optional) */
9181
9182 /* Only load transient guest properties for configs which have saved
9183 * state, because there shouldn't be any for powered off VMs. The same
9184 * logic applies for snapshots, as offline snapshots shouldn't have
9185 * any such properties. They confuse the code in various places.
9186 * Note: can't rely on the machine state, as it isn't set yet. */
9187 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9188 /* apologies for the hacky unconst() usage, but this needs hacking
9189 * actually inconsistent settings into consistency, otherwise there
9190 * will be some corner cases where the inconsistency survives
9191 * surprisingly long without getting fixed, especially for snapshots
9192 * as there are no config changes. */
9193 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9194 for (settings::GuestPropertiesList::iterator
9195 it = llGuestProperties.begin();
9196 it != llGuestProperties.end();
9197 /*nothing*/)
9198 {
9199 const settings::GuestProperty &prop = *it;
9200 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9201 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9202 if ( fSkipTransientGuestProperties
9203 && ( fFlags & GUEST_PROP_F_TRANSIENT
9204 || fFlags & GUEST_PROP_F_TRANSRESET))
9205 {
9206 it = llGuestProperties.erase(it);
9207 continue;
9208 }
9209 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9210 mHWData->mGuestProperties[prop.strName] = property;
9211 ++it;
9212 }
9213#endif /* VBOX_WITH_GUEST_PROPS defined */
9214
9215 rc = i_loadDebugging(pDbg);
9216 if (FAILED(rc))
9217 return rc;
9218
9219 mHWData->mAutostart = *pAutostart;
9220
9221 /* default frontend */
9222 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9223 }
9224 catch (std::bad_alloc &)
9225 {
9226 return E_OUTOFMEMORY;
9227 }
9228
9229 AssertComRC(rc);
9230 return rc;
9231}
9232
9233/**
9234 * Called from i_loadHardware() to load the debugging settings of the
9235 * machine.
9236 *
9237 * @param pDbg Pointer to the settings.
9238 */
9239HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9240{
9241 mHWData->mDebugging = *pDbg;
9242 /* no more processing currently required, this will probably change. */
9243 return S_OK;
9244}
9245
9246/**
9247 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9248 *
9249 * @param data storage settings.
9250 * @param puuidRegistry media registry ID to set media to or NULL;
9251 * see Machine::i_loadMachineDataFromSettings()
9252 * @param puuidSnapshot snapshot ID
9253 * @return
9254 */
9255HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9256 const Guid *puuidRegistry,
9257 const Guid *puuidSnapshot)
9258{
9259 AssertReturn(!i_isSessionMachine(), E_FAIL);
9260
9261 HRESULT rc = S_OK;
9262
9263 for (settings::StorageControllersList::const_iterator
9264 it = data.llStorageControllers.begin();
9265 it != data.llStorageControllers.end();
9266 ++it)
9267 {
9268 const settings::StorageController &ctlData = *it;
9269
9270 ComObjPtr<StorageController> pCtl;
9271 /* Try to find one with the name first. */
9272 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9273 if (SUCCEEDED(rc))
9274 return setError(VBOX_E_OBJECT_IN_USE,
9275 tr("Storage controller named '%s' already exists"),
9276 ctlData.strName.c_str());
9277
9278 pCtl.createObject();
9279 rc = pCtl->init(this,
9280 ctlData.strName,
9281 ctlData.storageBus,
9282 ctlData.ulInstance,
9283 ctlData.fBootable);
9284 if (FAILED(rc)) return rc;
9285
9286 mStorageControllers->push_back(pCtl);
9287
9288 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9289 if (FAILED(rc)) return rc;
9290
9291 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9292 if (FAILED(rc)) return rc;
9293
9294 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9295 if (FAILED(rc)) return rc;
9296
9297 /* Load the attached devices now. */
9298 rc = i_loadStorageDevices(pCtl,
9299 ctlData,
9300 puuidRegistry,
9301 puuidSnapshot);
9302 if (FAILED(rc)) return rc;
9303 }
9304
9305 return S_OK;
9306}
9307
9308/**
9309 * Called from i_loadStorageControllers for a controller's devices.
9310 *
9311 * @param aStorageController
9312 * @param data
9313 * @param puuidRegistry media registry ID to set media to or NULL; see
9314 * Machine::i_loadMachineDataFromSettings()
9315 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9316 * @return
9317 */
9318HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9319 const settings::StorageController &data,
9320 const Guid *puuidRegistry,
9321 const Guid *puuidSnapshot)
9322{
9323 HRESULT rc = S_OK;
9324
9325 /* paranoia: detect duplicate attachments */
9326 for (settings::AttachedDevicesList::const_iterator
9327 it = data.llAttachedDevices.begin();
9328 it != data.llAttachedDevices.end();
9329 ++it)
9330 {
9331 const settings::AttachedDevice &ad = *it;
9332
9333 for (settings::AttachedDevicesList::const_iterator it2 = it;
9334 it2 != data.llAttachedDevices.end();
9335 ++it2)
9336 {
9337 if (it == it2)
9338 continue;
9339
9340 const settings::AttachedDevice &ad2 = *it2;
9341
9342 if ( ad.lPort == ad2.lPort
9343 && ad.lDevice == ad2.lDevice)
9344 {
9345 return setError(E_FAIL,
9346 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9347 aStorageController->i_getName().c_str(),
9348 ad.lPort,
9349 ad.lDevice,
9350 mUserData->s.strName.c_str());
9351 }
9352 }
9353 }
9354
9355 for (settings::AttachedDevicesList::const_iterator
9356 it = data.llAttachedDevices.begin();
9357 it != data.llAttachedDevices.end();
9358 ++it)
9359 {
9360 const settings::AttachedDevice &dev = *it;
9361 ComObjPtr<Medium> medium;
9362
9363 switch (dev.deviceType)
9364 {
9365 case DeviceType_Floppy:
9366 case DeviceType_DVD:
9367 if (dev.strHostDriveSrc.isNotEmpty())
9368 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9369 false /* fRefresh */, medium);
9370 else
9371 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9372 dev.uuid,
9373 false /* fRefresh */,
9374 false /* aSetError */,
9375 medium);
9376 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9377 // This is not an error. The host drive or UUID might have vanished, so just go
9378 // ahead without this removeable medium attachment
9379 rc = S_OK;
9380 break;
9381
9382 case DeviceType_HardDisk:
9383 {
9384 /* find a hard disk by UUID */
9385 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9386 if (FAILED(rc))
9387 {
9388 if (i_isSnapshotMachine())
9389 {
9390 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9391 // so the user knows that the bad disk is in a snapshot somewhere
9392 com::ErrorInfo info;
9393 return setError(E_FAIL,
9394 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9395 puuidSnapshot->raw(),
9396 info.getText().raw());
9397 }
9398 else
9399 return rc;
9400 }
9401
9402 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9403
9404 if (medium->i_getType() == MediumType_Immutable)
9405 {
9406 if (i_isSnapshotMachine())
9407 return setError(E_FAIL,
9408 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9409 "of the virtual machine '%s' ('%s')"),
9410 medium->i_getLocationFull().c_str(),
9411 dev.uuid.raw(),
9412 puuidSnapshot->raw(),
9413 mUserData->s.strName.c_str(),
9414 mData->m_strConfigFileFull.c_str());
9415
9416 return setError(E_FAIL,
9417 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9418 medium->i_getLocationFull().c_str(),
9419 dev.uuid.raw(),
9420 mUserData->s.strName.c_str(),
9421 mData->m_strConfigFileFull.c_str());
9422 }
9423
9424 if (medium->i_getType() == MediumType_MultiAttach)
9425 {
9426 if (i_isSnapshotMachine())
9427 return setError(E_FAIL,
9428 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9429 "of the virtual machine '%s' ('%s')"),
9430 medium->i_getLocationFull().c_str(),
9431 dev.uuid.raw(),
9432 puuidSnapshot->raw(),
9433 mUserData->s.strName.c_str(),
9434 mData->m_strConfigFileFull.c_str());
9435
9436 return setError(E_FAIL,
9437 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9438 medium->i_getLocationFull().c_str(),
9439 dev.uuid.raw(),
9440 mUserData->s.strName.c_str(),
9441 mData->m_strConfigFileFull.c_str());
9442 }
9443
9444 if ( !i_isSnapshotMachine()
9445 && medium->i_getChildren().size() != 0
9446 )
9447 return setError(E_FAIL,
9448 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9449 "because it has %d differencing child hard disks"),
9450 medium->i_getLocationFull().c_str(),
9451 dev.uuid.raw(),
9452 mUserData->s.strName.c_str(),
9453 mData->m_strConfigFileFull.c_str(),
9454 medium->i_getChildren().size());
9455
9456 if (i_findAttachment(*mMediumAttachments.data(),
9457 medium))
9458 return setError(E_FAIL,
9459 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9460 medium->i_getLocationFull().c_str(),
9461 dev.uuid.raw(),
9462 mUserData->s.strName.c_str(),
9463 mData->m_strConfigFileFull.c_str());
9464
9465 break;
9466 }
9467
9468 default:
9469 return setError(E_FAIL,
9470 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9471 medium->i_getLocationFull().c_str(),
9472 mUserData->s.strName.c_str(),
9473 mData->m_strConfigFileFull.c_str());
9474 }
9475
9476 if (FAILED(rc))
9477 break;
9478
9479 /* Bandwidth groups are loaded at this point. */
9480 ComObjPtr<BandwidthGroup> pBwGroup;
9481
9482 if (!dev.strBwGroup.isEmpty())
9483 {
9484 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9485 if (FAILED(rc))
9486 return setError(E_FAIL,
9487 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9488 medium->i_getLocationFull().c_str(),
9489 dev.strBwGroup.c_str(),
9490 mUserData->s.strName.c_str(),
9491 mData->m_strConfigFileFull.c_str());
9492 pBwGroup->i_reference();
9493 }
9494
9495 const Utf8Str controllerName = aStorageController->i_getName();
9496 ComObjPtr<MediumAttachment> pAttachment;
9497 pAttachment.createObject();
9498 rc = pAttachment->init(this,
9499 medium,
9500 controllerName,
9501 dev.lPort,
9502 dev.lDevice,
9503 dev.deviceType,
9504 false,
9505 dev.fPassThrough,
9506 dev.fTempEject,
9507 dev.fNonRotational,
9508 dev.fDiscard,
9509 dev.fHotPluggable,
9510 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9511 if (FAILED(rc)) break;
9512
9513 /* associate the medium with this machine and snapshot */
9514 if (!medium.isNull())
9515 {
9516 AutoCaller medCaller(medium);
9517 if (FAILED(medCaller.rc())) return medCaller.rc();
9518 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9519
9520 if (i_isSnapshotMachine())
9521 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9522 else
9523 rc = medium->i_addBackReference(mData->mUuid);
9524 /* If the medium->addBackReference fails it sets an appropriate
9525 * error message, so no need to do any guesswork here. */
9526
9527 if (puuidRegistry)
9528 // caller wants registry ID to be set on all attached media (OVF import case)
9529 medium->i_addRegistry(*puuidRegistry);
9530 }
9531
9532 if (FAILED(rc))
9533 break;
9534
9535 /* back up mMediumAttachments to let registeredInit() properly rollback
9536 * on failure (= limited accessibility) */
9537 i_setModified(IsModified_Storage);
9538 mMediumAttachments.backup();
9539 mMediumAttachments->push_back(pAttachment);
9540 }
9541
9542 return rc;
9543}
9544
9545/**
9546 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9547 *
9548 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9549 * @param aSnapshot where to return the found snapshot
9550 * @param aSetError true to set extended error info on failure
9551 */
9552HRESULT Machine::i_findSnapshotById(const Guid &aId,
9553 ComObjPtr<Snapshot> &aSnapshot,
9554 bool aSetError /* = false */)
9555{
9556 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9557
9558 if (!mData->mFirstSnapshot)
9559 {
9560 if (aSetError)
9561 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9562 return E_FAIL;
9563 }
9564
9565 if (aId.isZero())
9566 aSnapshot = mData->mFirstSnapshot;
9567 else
9568 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9569
9570 if (!aSnapshot)
9571 {
9572 if (aSetError)
9573 return setError(E_FAIL,
9574 tr("Could not find a snapshot with UUID {%s}"),
9575 aId.toString().c_str());
9576 return E_FAIL;
9577 }
9578
9579 return S_OK;
9580}
9581
9582/**
9583 * Returns the snapshot with the given name or fails of no such snapshot.
9584 *
9585 * @param strName snapshot name to find
9586 * @param aSnapshot where to return the found snapshot
9587 * @param aSetError true to set extended error info on failure
9588 */
9589HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9590 ComObjPtr<Snapshot> &aSnapshot,
9591 bool aSetError /* = false */)
9592{
9593 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9594
9595 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9596
9597 if (!mData->mFirstSnapshot)
9598 {
9599 if (aSetError)
9600 return setError(VBOX_E_OBJECT_NOT_FOUND,
9601 tr("This machine does not have any snapshots"));
9602 return VBOX_E_OBJECT_NOT_FOUND;
9603 }
9604
9605 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9606
9607 if (!aSnapshot)
9608 {
9609 if (aSetError)
9610 return setError(VBOX_E_OBJECT_NOT_FOUND,
9611 tr("Could not find a snapshot named '%s'"), strName.c_str());
9612 return VBOX_E_OBJECT_NOT_FOUND;
9613 }
9614
9615 return S_OK;
9616}
9617
9618/**
9619 * Returns a storage controller object with the given name.
9620 *
9621 * @param aName storage controller name to find
9622 * @param aStorageController where to return the found storage controller
9623 * @param aSetError true to set extended error info on failure
9624 */
9625HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9626 ComObjPtr<StorageController> &aStorageController,
9627 bool aSetError /* = false */)
9628{
9629 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9630
9631 for (StorageControllerList::const_iterator
9632 it = mStorageControllers->begin();
9633 it != mStorageControllers->end();
9634 ++it)
9635 {
9636 if ((*it)->i_getName() == aName)
9637 {
9638 aStorageController = (*it);
9639 return S_OK;
9640 }
9641 }
9642
9643 if (aSetError)
9644 return setError(VBOX_E_OBJECT_NOT_FOUND,
9645 tr("Could not find a storage controller named '%s'"),
9646 aName.c_str());
9647 return VBOX_E_OBJECT_NOT_FOUND;
9648}
9649
9650/**
9651 * Returns a USB controller object with the given name.
9652 *
9653 * @param aName USB controller name to find
9654 * @param aUSBController where to return the found USB controller
9655 * @param aSetError true to set extended error info on failure
9656 */
9657HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9658 ComObjPtr<USBController> &aUSBController,
9659 bool aSetError /* = false */)
9660{
9661 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9662
9663 for (USBControllerList::const_iterator
9664 it = mUSBControllers->begin();
9665 it != mUSBControllers->end();
9666 ++it)
9667 {
9668 if ((*it)->i_getName() == aName)
9669 {
9670 aUSBController = (*it);
9671 return S_OK;
9672 }
9673 }
9674
9675 if (aSetError)
9676 return setError(VBOX_E_OBJECT_NOT_FOUND,
9677 tr("Could not find a storage controller named '%s'"),
9678 aName.c_str());
9679 return VBOX_E_OBJECT_NOT_FOUND;
9680}
9681
9682/**
9683 * Returns the number of USB controller instance of the given type.
9684 *
9685 * @param enmType USB controller type.
9686 */
9687ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9688{
9689 ULONG cCtrls = 0;
9690
9691 for (USBControllerList::const_iterator
9692 it = mUSBControllers->begin();
9693 it != mUSBControllers->end();
9694 ++it)
9695 {
9696 if ((*it)->i_getControllerType() == enmType)
9697 cCtrls++;
9698 }
9699
9700 return cCtrls;
9701}
9702
9703HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9704 MediumAttachmentList &atts)
9705{
9706 AutoCaller autoCaller(this);
9707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9708
9709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9710
9711 for (MediumAttachmentList::const_iterator
9712 it = mMediumAttachments->begin();
9713 it != mMediumAttachments->end();
9714 ++it)
9715 {
9716 const ComObjPtr<MediumAttachment> &pAtt = *it;
9717 // should never happen, but deal with NULL pointers in the list.
9718 AssertContinue(!pAtt.isNull());
9719
9720 // getControllerName() needs caller+read lock
9721 AutoCaller autoAttCaller(pAtt);
9722 if (FAILED(autoAttCaller.rc()))
9723 {
9724 atts.clear();
9725 return autoAttCaller.rc();
9726 }
9727 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9728
9729 if (pAtt->i_getControllerName() == aName)
9730 atts.push_back(pAtt);
9731 }
9732
9733 return S_OK;
9734}
9735
9736
9737/**
9738 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9739 * file if the machine name was changed and about creating a new settings file
9740 * if this is a new machine.
9741 *
9742 * @note Must be never called directly but only from #saveSettings().
9743 */
9744HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9745{
9746 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9747
9748 HRESULT rc = S_OK;
9749
9750 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9751
9752 /// @todo need to handle primary group change, too
9753
9754 /* attempt to rename the settings file if machine name is changed */
9755 if ( mUserData->s.fNameSync
9756 && mUserData.isBackedUp()
9757 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9758 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9759 )
9760 {
9761 bool dirRenamed = false;
9762 bool fileRenamed = false;
9763
9764 Utf8Str configFile, newConfigFile;
9765 Utf8Str configFilePrev, newConfigFilePrev;
9766 Utf8Str configDir, newConfigDir;
9767
9768 do
9769 {
9770 int vrc = VINF_SUCCESS;
9771
9772 Utf8Str name = mUserData.backedUpData()->s.strName;
9773 Utf8Str newName = mUserData->s.strName;
9774 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9775 if (group == "/")
9776 group.setNull();
9777 Utf8Str newGroup = mUserData->s.llGroups.front();
9778 if (newGroup == "/")
9779 newGroup.setNull();
9780
9781 configFile = mData->m_strConfigFileFull;
9782
9783 /* first, rename the directory if it matches the group and machine name */
9784 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9785 group.c_str(), RTPATH_DELIMITER, name.c_str());
9786 /** @todo hack, make somehow use of ComposeMachineFilename */
9787 if (mUserData->s.fDirectoryIncludesUUID)
9788 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9789 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9790 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9791 /** @todo hack, make somehow use of ComposeMachineFilename */
9792 if (mUserData->s.fDirectoryIncludesUUID)
9793 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9794 configDir = configFile;
9795 configDir.stripFilename();
9796 newConfigDir = configDir;
9797 if ( configDir.length() >= groupPlusName.length()
9798 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9799 groupPlusName.c_str()))
9800 {
9801 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9802 Utf8Str newConfigBaseDir(newConfigDir);
9803 newConfigDir.append(newGroupPlusName);
9804 /* consistency: use \ if appropriate on the platform */
9805 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9806 /* new dir and old dir cannot be equal here because of 'if'
9807 * above and because name != newName */
9808 Assert(configDir != newConfigDir);
9809 if (!fSettingsFileIsNew)
9810 {
9811 /* perform real rename only if the machine is not new */
9812 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9813 if ( vrc == VERR_FILE_NOT_FOUND
9814 || vrc == VERR_PATH_NOT_FOUND)
9815 {
9816 /* create the parent directory, then retry renaming */
9817 Utf8Str parent(newConfigDir);
9818 parent.stripFilename();
9819 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9820 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9821 }
9822 if (RT_FAILURE(vrc))
9823 {
9824 rc = setError(E_FAIL,
9825 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9826 configDir.c_str(),
9827 newConfigDir.c_str(),
9828 vrc);
9829 break;
9830 }
9831 /* delete subdirectories which are no longer needed */
9832 Utf8Str dir(configDir);
9833 dir.stripFilename();
9834 while (dir != newConfigBaseDir && dir != ".")
9835 {
9836 vrc = RTDirRemove(dir.c_str());
9837 if (RT_FAILURE(vrc))
9838 break;
9839 dir.stripFilename();
9840 }
9841 dirRenamed = true;
9842 }
9843 }
9844
9845 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9846 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9847
9848 /* then try to rename the settings file itself */
9849 if (newConfigFile != configFile)
9850 {
9851 /* get the path to old settings file in renamed directory */
9852 configFile = Utf8StrFmt("%s%c%s",
9853 newConfigDir.c_str(),
9854 RTPATH_DELIMITER,
9855 RTPathFilename(configFile.c_str()));
9856 if (!fSettingsFileIsNew)
9857 {
9858 /* perform real rename only if the machine is not new */
9859 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9860 if (RT_FAILURE(vrc))
9861 {
9862 rc = setError(E_FAIL,
9863 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9864 configFile.c_str(),
9865 newConfigFile.c_str(),
9866 vrc);
9867 break;
9868 }
9869 fileRenamed = true;
9870 configFilePrev = configFile;
9871 configFilePrev += "-prev";
9872 newConfigFilePrev = newConfigFile;
9873 newConfigFilePrev += "-prev";
9874 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9875 }
9876 }
9877
9878 // update m_strConfigFileFull amd mConfigFile
9879 mData->m_strConfigFileFull = newConfigFile;
9880 // compute the relative path too
9881 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9882
9883 // store the old and new so that VirtualBox::i_saveSettings() can update
9884 // the media registry
9885 if ( mData->mRegistered
9886 && (configDir != newConfigDir || configFile != newConfigFile))
9887 {
9888 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9889
9890 if (pfNeedsGlobalSaveSettings)
9891 *pfNeedsGlobalSaveSettings = true;
9892 }
9893
9894 // in the saved state file path, replace the old directory with the new directory
9895 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9896 {
9897 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9898 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9899 }
9900
9901 // and do the same thing for the saved state file paths of all the online snapshots
9902 if (mData->mFirstSnapshot)
9903 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9904 newConfigDir.c_str());
9905 }
9906 while (0);
9907
9908 if (FAILED(rc))
9909 {
9910 /* silently try to rename everything back */
9911 if (fileRenamed)
9912 {
9913 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9914 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9915 }
9916 if (dirRenamed)
9917 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9918 }
9919
9920 if (FAILED(rc)) return rc;
9921 }
9922
9923 if (fSettingsFileIsNew)
9924 {
9925 /* create a virgin config file */
9926 int vrc = VINF_SUCCESS;
9927
9928 /* ensure the settings directory exists */
9929 Utf8Str path(mData->m_strConfigFileFull);
9930 path.stripFilename();
9931 if (!RTDirExists(path.c_str()))
9932 {
9933 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9934 if (RT_FAILURE(vrc))
9935 {
9936 return setError(E_FAIL,
9937 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9938 path.c_str(),
9939 vrc);
9940 }
9941 }
9942
9943 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9944 path = Utf8Str(mData->m_strConfigFileFull);
9945 RTFILE f = NIL_RTFILE;
9946 vrc = RTFileOpen(&f, path.c_str(),
9947 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9948 if (RT_FAILURE(vrc))
9949 return setError(E_FAIL,
9950 tr("Could not create the settings file '%s' (%Rrc)"),
9951 path.c_str(),
9952 vrc);
9953 RTFileClose(f);
9954 }
9955
9956 return rc;
9957}
9958
9959/**
9960 * Saves and commits machine data, user data and hardware data.
9961 *
9962 * Note that on failure, the data remains uncommitted.
9963 *
9964 * @a aFlags may combine the following flags:
9965 *
9966 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9967 * Used when saving settings after an operation that makes them 100%
9968 * correspond to the settings from the current snapshot.
9969 * - SaveS_Force: settings will be saved without doing a deep compare of the
9970 * settings structures. This is used when this is called because snapshots
9971 * have changed to avoid the overhead of the deep compare.
9972 *
9973 * @note Must be called from under this object's write lock. Locks children for
9974 * writing.
9975 *
9976 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9977 * initialized to false and that will be set to true by this function if
9978 * the caller must invoke VirtualBox::i_saveSettings() because the global
9979 * settings have changed. This will happen if a machine rename has been
9980 * saved and the global machine and media registries will therefore need
9981 * updating.
9982 * @param aFlags Flags.
9983 */
9984HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9985 int aFlags /*= 0*/)
9986{
9987 LogFlowThisFuncEnter();
9988
9989 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9990
9991 /* make sure child objects are unable to modify the settings while we are
9992 * saving them */
9993 i_ensureNoStateDependencies();
9994
9995 AssertReturn(!i_isSnapshotMachine(),
9996 E_FAIL);
9997
9998 HRESULT rc = S_OK;
9999 bool fNeedsWrite = false;
10000
10001 /* First, prepare to save settings. It will care about renaming the
10002 * settings directory and file if the machine name was changed and about
10003 * creating a new settings file if this is a new machine. */
10004 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10005 if (FAILED(rc)) return rc;
10006
10007 // keep a pointer to the current settings structures
10008 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10009 settings::MachineConfigFile *pNewConfig = NULL;
10010
10011 try
10012 {
10013 // make a fresh one to have everyone write stuff into
10014 pNewConfig = new settings::MachineConfigFile(NULL);
10015 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10016
10017 // now go and copy all the settings data from COM to the settings structures
10018 // (this calls i_saveSettings() on all the COM objects in the machine)
10019 i_copyMachineDataToSettings(*pNewConfig);
10020
10021 if (aFlags & SaveS_ResetCurStateModified)
10022 {
10023 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10024 mData->mCurrentStateModified = FALSE;
10025 fNeedsWrite = true; // always, no need to compare
10026 }
10027 else if (aFlags & SaveS_Force)
10028 {
10029 fNeedsWrite = true; // always, no need to compare
10030 }
10031 else
10032 {
10033 if (!mData->mCurrentStateModified)
10034 {
10035 // do a deep compare of the settings that we just saved with the settings
10036 // previously stored in the config file; this invokes MachineConfigFile::operator==
10037 // which does a deep compare of all the settings, which is expensive but less expensive
10038 // than writing out XML in vain
10039 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10040
10041 // could still be modified if any settings changed
10042 mData->mCurrentStateModified = fAnySettingsChanged;
10043
10044 fNeedsWrite = fAnySettingsChanged;
10045 }
10046 else
10047 fNeedsWrite = true;
10048 }
10049
10050 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10051
10052 if (fNeedsWrite)
10053 // now spit it all out!
10054 pNewConfig->write(mData->m_strConfigFileFull);
10055
10056 mData->pMachineConfigFile = pNewConfig;
10057 delete pOldConfig;
10058 i_commit();
10059
10060 // after saving settings, we are no longer different from the XML on disk
10061 mData->flModifications = 0;
10062 }
10063 catch (HRESULT err)
10064 {
10065 // we assume that error info is set by the thrower
10066 rc = err;
10067
10068 // restore old config
10069 delete pNewConfig;
10070 mData->pMachineConfigFile = pOldConfig;
10071 }
10072 catch (...)
10073 {
10074 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10075 }
10076
10077 if (fNeedsWrite)
10078 {
10079 /* Fire the data change event, even on failure (since we've already
10080 * committed all data). This is done only for SessionMachines because
10081 * mutable Machine instances are always not registered (i.e. private
10082 * to the client process that creates them) and thus don't need to
10083 * inform callbacks. */
10084 if (i_isSessionMachine())
10085 mParent->i_onMachineDataChange(mData->mUuid);
10086 }
10087
10088 LogFlowThisFunc(("rc=%08X\n", rc));
10089 LogFlowThisFuncLeave();
10090 return rc;
10091}
10092
10093/**
10094 * Implementation for saving the machine settings into the given
10095 * settings::MachineConfigFile instance. This copies machine extradata
10096 * from the previous machine config file in the instance data, if any.
10097 *
10098 * This gets called from two locations:
10099 *
10100 * -- Machine::i_saveSettings(), during the regular XML writing;
10101 *
10102 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10103 * exported to OVF and we write the VirtualBox proprietary XML
10104 * into a <vbox:Machine> tag.
10105 *
10106 * This routine fills all the fields in there, including snapshots, *except*
10107 * for the following:
10108 *
10109 * -- fCurrentStateModified. There is some special logic associated with that.
10110 *
10111 * The caller can then call MachineConfigFile::write() or do something else
10112 * with it.
10113 *
10114 * Caller must hold the machine lock!
10115 *
10116 * This throws XML errors and HRESULT, so the caller must have a catch block!
10117 */
10118void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10119{
10120 // deep copy extradata, being extra careful with self assignment (the STL
10121 // map assignment on Mac OS X clang based Xcode isn't checking)
10122 if (&config != mData->pMachineConfigFile)
10123 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10124
10125 config.uuid = mData->mUuid;
10126
10127 // copy name, description, OS type, teleport, UTC etc.
10128 config.machineUserData = mUserData->s;
10129
10130 if ( mData->mMachineState == MachineState_Saved
10131 || mData->mMachineState == MachineState_Restoring
10132 // when doing certain snapshot operations we may or may not have
10133 // a saved state in the current state, so keep everything as is
10134 || ( ( mData->mMachineState == MachineState_Snapshotting
10135 || mData->mMachineState == MachineState_DeletingSnapshot
10136 || mData->mMachineState == MachineState_RestoringSnapshot)
10137 && (!mSSData->strStateFilePath.isEmpty())
10138 )
10139 )
10140 {
10141 Assert(!mSSData->strStateFilePath.isEmpty());
10142 /* try to make the file name relative to the settings file dir */
10143 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10144 }
10145 else
10146 {
10147 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10148 config.strStateFile.setNull();
10149 }
10150
10151 if (mData->mCurrentSnapshot)
10152 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10153 else
10154 config.uuidCurrentSnapshot.clear();
10155
10156 config.timeLastStateChange = mData->mLastStateChange;
10157 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10158 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10159
10160 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10161 if (FAILED(rc)) throw rc;
10162
10163 // save machine's media registry if this is VirtualBox 4.0 or later
10164 if (config.canHaveOwnMediaRegistry())
10165 {
10166 // determine machine folder
10167 Utf8Str strMachineFolder = i_getSettingsFileFull();
10168 strMachineFolder.stripFilename();
10169 mParent->i_saveMediaRegistry(config.mediaRegistry,
10170 i_getId(), // only media with registry ID == machine UUID
10171 strMachineFolder);
10172 // this throws HRESULT
10173 }
10174
10175 // save snapshots
10176 rc = i_saveAllSnapshots(config);
10177 if (FAILED(rc)) throw rc;
10178}
10179
10180/**
10181 * Saves all snapshots of the machine into the given machine config file. Called
10182 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10183 * @param config
10184 * @return
10185 */
10186HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10187{
10188 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10189
10190 HRESULT rc = S_OK;
10191
10192 try
10193 {
10194 config.llFirstSnapshot.clear();
10195
10196 if (mData->mFirstSnapshot)
10197 {
10198 // the settings use a list for "the first snapshot"
10199 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10200
10201 // get reference to the snapshot on the list and work on that
10202 // element straight in the list to avoid excessive copying later
10203 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10204 if (FAILED(rc)) throw rc;
10205 }
10206
10207// if (mType == IsSessionMachine)
10208// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10209
10210 }
10211 catch (HRESULT err)
10212 {
10213 /* we assume that error info is set by the thrower */
10214 rc = err;
10215 }
10216 catch (...)
10217 {
10218 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10219 }
10220
10221 return rc;
10222}
10223
10224/**
10225 * Saves the VM hardware configuration. It is assumed that the
10226 * given node is empty.
10227 *
10228 * @param data Reference to the settings object for the hardware config.
10229 * @param pDbg Pointer to the settings object for the debugging config
10230 * which happens to live in mHWData.
10231 * @param pAutostart Pointer to the settings object for the autostart config
10232 * which happens to live in mHWData.
10233 */
10234HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10235 settings::Autostart *pAutostart)
10236{
10237 HRESULT rc = S_OK;
10238
10239 try
10240 {
10241 /* The hardware version attribute (optional).
10242 Automatically upgrade from 1 to current default hardware version
10243 when there is no saved state. (ugly!) */
10244 if ( mHWData->mHWVersion == "1"
10245 && mSSData->strStateFilePath.isEmpty()
10246 )
10247 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10248
10249 data.strVersion = mHWData->mHWVersion;
10250 data.uuid = mHWData->mHardwareUUID;
10251
10252 // CPU
10253 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10254 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10255 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10256 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10257 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10258 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10259 data.fPAE = !!mHWData->mPAEEnabled;
10260 data.enmLongMode = mHWData->mLongMode;
10261 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10262 data.fAPIC = !!mHWData->mAPIC;
10263 data.fX2APIC = !!mHWData->mX2APIC;
10264 data.cCPUs = mHWData->mCPUCount;
10265 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10266 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10267 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10268 data.strCpuProfile = mHWData->mCpuProfile;
10269
10270 data.llCpus.clear();
10271 if (data.fCpuHotPlug)
10272 {
10273 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10274 {
10275 if (mHWData->mCPUAttached[idx])
10276 {
10277 settings::Cpu cpu;
10278 cpu.ulId = idx;
10279 data.llCpus.push_back(cpu);
10280 }
10281 }
10282 }
10283
10284 /* Standard and Extended CPUID leafs. */
10285 data.llCpuIdLeafs.clear();
10286 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10287
10288 // memory
10289 data.ulMemorySizeMB = mHWData->mMemorySize;
10290 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10291
10292 // firmware
10293 data.firmwareType = mHWData->mFirmwareType;
10294
10295 // HID
10296 data.pointingHIDType = mHWData->mPointingHIDType;
10297 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10298
10299 // chipset
10300 data.chipsetType = mHWData->mChipsetType;
10301
10302 // paravirt
10303 data.paravirtProvider = mHWData->mParavirtProvider;
10304 data.strParavirtDebug = mHWData->mParavirtDebug;
10305
10306 // emulated USB card reader
10307 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10308
10309 // HPET
10310 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10311
10312 // boot order
10313 data.mapBootOrder.clear();
10314 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10315 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10316
10317 // display
10318 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10319 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10320 data.cMonitors = mHWData->mMonitorCount;
10321 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10322 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10323 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10324 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10325 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10326 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10327 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10328 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10329 {
10330 if (mHWData->maVideoCaptureScreens[i])
10331 ASMBitSet(&data.u64VideoCaptureScreens, i);
10332 else
10333 ASMBitClear(&data.u64VideoCaptureScreens, i);
10334 }
10335 /* store relative video capture file if possible */
10336 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10337 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10338
10339 /* VRDEServer settings (optional) */
10340 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10341 if (FAILED(rc)) throw rc;
10342
10343 /* BIOS (required) */
10344 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10345 if (FAILED(rc)) throw rc;
10346
10347 /* USB Controller (required) */
10348 data.usbSettings.llUSBControllers.clear();
10349 for (USBControllerList::const_iterator
10350 it = mUSBControllers->begin();
10351 it != mUSBControllers->end();
10352 ++it)
10353 {
10354 ComObjPtr<USBController> ctrl = *it;
10355 settings::USBController settingsCtrl;
10356
10357 settingsCtrl.strName = ctrl->i_getName();
10358 settingsCtrl.enmType = ctrl->i_getControllerType();
10359
10360 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10361 }
10362
10363 /* USB device filters (required) */
10364 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10365 if (FAILED(rc)) throw rc;
10366
10367 /* Network adapters (required) */
10368 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10369 data.llNetworkAdapters.clear();
10370 /* Write out only the nominal number of network adapters for this
10371 * chipset type. Since Machine::commit() hasn't been called there
10372 * may be extra NIC settings in the vector. */
10373 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10374 {
10375 settings::NetworkAdapter nic;
10376 nic.ulSlot = (uint32_t)slot;
10377 /* paranoia check... must not be NULL, but must not crash either. */
10378 if (mNetworkAdapters[slot])
10379 {
10380 if (mNetworkAdapters[slot]->i_hasDefaults())
10381 continue;
10382
10383 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10384 if (FAILED(rc)) throw rc;
10385
10386 data.llNetworkAdapters.push_back(nic);
10387 }
10388 }
10389
10390 /* Serial ports */
10391 data.llSerialPorts.clear();
10392 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10393 {
10394 if (mSerialPorts[slot]->i_hasDefaults())
10395 continue;
10396
10397 settings::SerialPort s;
10398 s.ulSlot = slot;
10399 rc = mSerialPorts[slot]->i_saveSettings(s);
10400 if (FAILED(rc)) return rc;
10401
10402 data.llSerialPorts.push_back(s);
10403 }
10404
10405 /* Parallel ports */
10406 data.llParallelPorts.clear();
10407 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10408 {
10409 if (mParallelPorts[slot]->i_hasDefaults())
10410 continue;
10411
10412 settings::ParallelPort p;
10413 p.ulSlot = slot;
10414 rc = mParallelPorts[slot]->i_saveSettings(p);
10415 if (FAILED(rc)) return rc;
10416
10417 data.llParallelPorts.push_back(p);
10418 }
10419
10420 /* Audio adapter */
10421 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10422 if (FAILED(rc)) return rc;
10423
10424 rc = i_saveStorageControllers(data.storage);
10425 if (FAILED(rc)) return rc;
10426
10427 /* Shared folders */
10428 data.llSharedFolders.clear();
10429 for (HWData::SharedFolderList::const_iterator
10430 it = mHWData->mSharedFolders.begin();
10431 it != mHWData->mSharedFolders.end();
10432 ++it)
10433 {
10434 SharedFolder *pSF = *it;
10435 AutoCaller sfCaller(pSF);
10436 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10437 settings::SharedFolder sf;
10438 sf.strName = pSF->i_getName();
10439 sf.strHostPath = pSF->i_getHostPath();
10440 sf.fWritable = !!pSF->i_isWritable();
10441 sf.fAutoMount = !!pSF->i_isAutoMounted();
10442
10443 data.llSharedFolders.push_back(sf);
10444 }
10445
10446 // clipboard
10447 data.clipboardMode = mHWData->mClipboardMode;
10448
10449 // drag'n'drop
10450 data.dndMode = mHWData->mDnDMode;
10451
10452 /* Guest */
10453 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10454
10455 // IO settings
10456 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10457 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10458
10459 /* BandwidthControl (required) */
10460 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10461 if (FAILED(rc)) throw rc;
10462
10463 /* Host PCI devices */
10464 data.pciAttachments.clear();
10465 for (HWData::PCIDeviceAssignmentList::const_iterator
10466 it = mHWData->mPCIDeviceAssignments.begin();
10467 it != mHWData->mPCIDeviceAssignments.end();
10468 ++it)
10469 {
10470 ComObjPtr<PCIDeviceAttachment> pda = *it;
10471 settings::HostPCIDeviceAttachment hpda;
10472
10473 rc = pda->i_saveSettings(hpda);
10474 if (FAILED(rc)) throw rc;
10475
10476 data.pciAttachments.push_back(hpda);
10477 }
10478
10479 // guest properties
10480 data.llGuestProperties.clear();
10481#ifdef VBOX_WITH_GUEST_PROPS
10482 for (HWData::GuestPropertyMap::const_iterator
10483 it = mHWData->mGuestProperties.begin();
10484 it != mHWData->mGuestProperties.end();
10485 ++it)
10486 {
10487 HWData::GuestProperty property = it->second;
10488
10489 /* Remove transient guest properties at shutdown unless we
10490 * are saving state. Note that restoring snapshot intentionally
10491 * keeps them, they will be removed if appropriate once the final
10492 * machine state is set (as crashes etc. need to work). */
10493 if ( ( mData->mMachineState == MachineState_PoweredOff
10494 || mData->mMachineState == MachineState_Aborted
10495 || mData->mMachineState == MachineState_Teleported)
10496 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10497 continue;
10498 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10499 prop.strName = it->first;
10500 prop.strValue = property.strValue;
10501 prop.timestamp = property.mTimestamp;
10502 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10503 GuestPropWriteFlags(property.mFlags, szFlags);
10504 prop.strFlags = szFlags;
10505
10506 data.llGuestProperties.push_back(prop);
10507 }
10508
10509 /* I presume this doesn't require a backup(). */
10510 mData->mGuestPropertiesModified = FALSE;
10511#endif /* VBOX_WITH_GUEST_PROPS defined */
10512
10513 *pDbg = mHWData->mDebugging;
10514 *pAutostart = mHWData->mAutostart;
10515
10516 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10517 }
10518 catch (std::bad_alloc &)
10519 {
10520 return E_OUTOFMEMORY;
10521 }
10522
10523 AssertComRC(rc);
10524 return rc;
10525}
10526
10527/**
10528 * Saves the storage controller configuration.
10529 *
10530 * @param data storage settings.
10531 */
10532HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10533{
10534 data.llStorageControllers.clear();
10535
10536 for (StorageControllerList::const_iterator
10537 it = mStorageControllers->begin();
10538 it != mStorageControllers->end();
10539 ++it)
10540 {
10541 HRESULT rc;
10542 ComObjPtr<StorageController> pCtl = *it;
10543
10544 settings::StorageController ctl;
10545 ctl.strName = pCtl->i_getName();
10546 ctl.controllerType = pCtl->i_getControllerType();
10547 ctl.storageBus = pCtl->i_getStorageBus();
10548 ctl.ulInstance = pCtl->i_getInstance();
10549 ctl.fBootable = pCtl->i_getBootable();
10550
10551 /* Save the port count. */
10552 ULONG portCount;
10553 rc = pCtl->COMGETTER(PortCount)(&portCount);
10554 ComAssertComRCRet(rc, rc);
10555 ctl.ulPortCount = portCount;
10556
10557 /* Save fUseHostIOCache */
10558 BOOL fUseHostIOCache;
10559 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10560 ComAssertComRCRet(rc, rc);
10561 ctl.fUseHostIOCache = !!fUseHostIOCache;
10562
10563 /* save the devices now. */
10564 rc = i_saveStorageDevices(pCtl, ctl);
10565 ComAssertComRCRet(rc, rc);
10566
10567 data.llStorageControllers.push_back(ctl);
10568 }
10569
10570 return S_OK;
10571}
10572
10573/**
10574 * Saves the hard disk configuration.
10575 */
10576HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10577 settings::StorageController &data)
10578{
10579 MediumAttachmentList atts;
10580
10581 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10582 if (FAILED(rc)) return rc;
10583
10584 data.llAttachedDevices.clear();
10585 for (MediumAttachmentList::const_iterator
10586 it = atts.begin();
10587 it != atts.end();
10588 ++it)
10589 {
10590 settings::AttachedDevice dev;
10591 IMediumAttachment *iA = *it;
10592 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10593 Medium *pMedium = pAttach->i_getMedium();
10594
10595 dev.deviceType = pAttach->i_getType();
10596 dev.lPort = pAttach->i_getPort();
10597 dev.lDevice = pAttach->i_getDevice();
10598 dev.fPassThrough = pAttach->i_getPassthrough();
10599 dev.fHotPluggable = pAttach->i_getHotPluggable();
10600 if (pMedium)
10601 {
10602 if (pMedium->i_isHostDrive())
10603 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10604 else
10605 dev.uuid = pMedium->i_getId();
10606 dev.fTempEject = pAttach->i_getTempEject();
10607 dev.fNonRotational = pAttach->i_getNonRotational();
10608 dev.fDiscard = pAttach->i_getDiscard();
10609 }
10610
10611 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10612
10613 data.llAttachedDevices.push_back(dev);
10614 }
10615
10616 return S_OK;
10617}
10618
10619/**
10620 * Saves machine state settings as defined by aFlags
10621 * (SaveSTS_* values).
10622 *
10623 * @param aFlags Combination of SaveSTS_* flags.
10624 *
10625 * @note Locks objects for writing.
10626 */
10627HRESULT Machine::i_saveStateSettings(int aFlags)
10628{
10629 if (aFlags == 0)
10630 return S_OK;
10631
10632 AutoCaller autoCaller(this);
10633 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10634
10635 /* This object's write lock is also necessary to serialize file access
10636 * (prevent concurrent reads and writes) */
10637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10638
10639 HRESULT rc = S_OK;
10640
10641 Assert(mData->pMachineConfigFile);
10642
10643 try
10644 {
10645 if (aFlags & SaveSTS_CurStateModified)
10646 mData->pMachineConfigFile->fCurrentStateModified = true;
10647
10648 if (aFlags & SaveSTS_StateFilePath)
10649 {
10650 if (!mSSData->strStateFilePath.isEmpty())
10651 /* try to make the file name relative to the settings file dir */
10652 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10653 else
10654 mData->pMachineConfigFile->strStateFile.setNull();
10655 }
10656
10657 if (aFlags & SaveSTS_StateTimeStamp)
10658 {
10659 Assert( mData->mMachineState != MachineState_Aborted
10660 || mSSData->strStateFilePath.isEmpty());
10661
10662 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10663
10664 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10665/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10666 }
10667
10668 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10669 }
10670 catch (...)
10671 {
10672 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10673 }
10674
10675 return rc;
10676}
10677
10678/**
10679 * Ensures that the given medium is added to a media registry. If this machine
10680 * was created with 4.0 or later, then the machine registry is used. Otherwise
10681 * the global VirtualBox media registry is used.
10682 *
10683 * Caller must NOT hold machine lock, media tree or any medium locks!
10684 *
10685 * @param pMedium
10686 */
10687void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10688{
10689 /* Paranoia checks: do not hold machine or media tree locks. */
10690 AssertReturnVoid(!isWriteLockOnCurrentThread());
10691 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10692
10693 ComObjPtr<Medium> pBase;
10694 {
10695 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10696 pBase = pMedium->i_getBase();
10697 }
10698
10699 /* Paranoia checks: do not hold medium locks. */
10700 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10701 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10702
10703 // decide which medium registry to use now that the medium is attached:
10704 Guid uuid;
10705 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10706 // machine XML is VirtualBox 4.0 or higher:
10707 uuid = i_getId(); // machine UUID
10708 else
10709 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10710
10711 if (pMedium->i_addRegistry(uuid))
10712 mParent->i_markRegistryModified(uuid);
10713
10714 /* For more complex hard disk structures it can happen that the base
10715 * medium isn't yet associated with any medium registry. Do that now. */
10716 if (pMedium != pBase)
10717 {
10718 /* Tree lock needed by Medium::addRegistry when recursing. */
10719 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10720 if (pBase->i_addRegistryRecursive(uuid))
10721 {
10722 treeLock.release();
10723 mParent->i_markRegistryModified(uuid);
10724 }
10725 }
10726}
10727
10728/**
10729 * Creates differencing hard disks for all normal hard disks attached to this
10730 * machine and a new set of attachments to refer to created disks.
10731 *
10732 * Used when taking a snapshot or when deleting the current state. Gets called
10733 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10734 *
10735 * This method assumes that mMediumAttachments contains the original hard disk
10736 * attachments it needs to create diffs for. On success, these attachments will
10737 * be replaced with the created diffs.
10738 *
10739 * Attachments with non-normal hard disks are left as is.
10740 *
10741 * If @a aOnline is @c false then the original hard disks that require implicit
10742 * diffs will be locked for reading. Otherwise it is assumed that they are
10743 * already locked for writing (when the VM was started). Note that in the latter
10744 * case it is responsibility of the caller to lock the newly created diffs for
10745 * writing if this method succeeds.
10746 *
10747 * @param aProgress Progress object to run (must contain at least as
10748 * many operations left as the number of hard disks
10749 * attached).
10750 * @param aWeight Weight of this operation.
10751 * @param aOnline Whether the VM was online prior to this operation.
10752 *
10753 * @note The progress object is not marked as completed, neither on success nor
10754 * on failure. This is a responsibility of the caller.
10755 *
10756 * @note Locks this object and the media tree for writing.
10757 */
10758HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10759 ULONG aWeight,
10760 bool aOnline)
10761{
10762 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10763
10764 AutoCaller autoCaller(this);
10765 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10766
10767 AutoMultiWriteLock2 alock(this->lockHandle(),
10768 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10769
10770 /* must be in a protective state because we release the lock below */
10771 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10772 || mData->mMachineState == MachineState_OnlineSnapshotting
10773 || mData->mMachineState == MachineState_LiveSnapshotting
10774 || mData->mMachineState == MachineState_RestoringSnapshot
10775 || mData->mMachineState == MachineState_DeletingSnapshot
10776 , E_FAIL);
10777
10778 HRESULT rc = S_OK;
10779
10780 // use appropriate locked media map (online or offline)
10781 MediumLockListMap lockedMediaOffline;
10782 MediumLockListMap *lockedMediaMap;
10783 if (aOnline)
10784 lockedMediaMap = &mData->mSession.mLockedMedia;
10785 else
10786 lockedMediaMap = &lockedMediaOffline;
10787
10788 try
10789 {
10790 if (!aOnline)
10791 {
10792 /* lock all attached hard disks early to detect "in use"
10793 * situations before creating actual diffs */
10794 for (MediumAttachmentList::const_iterator
10795 it = mMediumAttachments->begin();
10796 it != mMediumAttachments->end();
10797 ++it)
10798 {
10799 MediumAttachment *pAtt = *it;
10800 if (pAtt->i_getType() == DeviceType_HardDisk)
10801 {
10802 Medium *pMedium = pAtt->i_getMedium();
10803 Assert(pMedium);
10804
10805 MediumLockList *pMediumLockList(new MediumLockList());
10806 alock.release();
10807 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10808 NULL /* pToLockWrite */,
10809 false /* fMediumLockWriteAll */,
10810 NULL,
10811 *pMediumLockList);
10812 alock.acquire();
10813 if (FAILED(rc))
10814 {
10815 delete pMediumLockList;
10816 throw rc;
10817 }
10818 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10819 if (FAILED(rc))
10820 {
10821 throw setError(rc,
10822 tr("Collecting locking information for all attached media failed"));
10823 }
10824 }
10825 }
10826
10827 /* Now lock all media. If this fails, nothing is locked. */
10828 alock.release();
10829 rc = lockedMediaMap->Lock();
10830 alock.acquire();
10831 if (FAILED(rc))
10832 {
10833 throw setError(rc,
10834 tr("Locking of attached media failed"));
10835 }
10836 }
10837
10838 /* remember the current list (note that we don't use backup() since
10839 * mMediumAttachments may be already backed up) */
10840 MediumAttachmentList atts = *mMediumAttachments.data();
10841
10842 /* start from scratch */
10843 mMediumAttachments->clear();
10844
10845 /* go through remembered attachments and create diffs for normal hard
10846 * disks and attach them */
10847 for (MediumAttachmentList::const_iterator
10848 it = atts.begin();
10849 it != atts.end();
10850 ++it)
10851 {
10852 MediumAttachment *pAtt = *it;
10853
10854 DeviceType_T devType = pAtt->i_getType();
10855 Medium *pMedium = pAtt->i_getMedium();
10856
10857 if ( devType != DeviceType_HardDisk
10858 || pMedium == NULL
10859 || pMedium->i_getType() != MediumType_Normal)
10860 {
10861 /* copy the attachment as is */
10862
10863 /** @todo the progress object created in SessionMachine::TakeSnaphot
10864 * only expects operations for hard disks. Later other
10865 * device types need to show up in the progress as well. */
10866 if (devType == DeviceType_HardDisk)
10867 {
10868 if (pMedium == NULL)
10869 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10870 aWeight); // weight
10871 else
10872 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10873 pMedium->i_getBase()->i_getName().c_str()).raw(),
10874 aWeight); // weight
10875 }
10876
10877 mMediumAttachments->push_back(pAtt);
10878 continue;
10879 }
10880
10881 /* need a diff */
10882 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10883 pMedium->i_getBase()->i_getName().c_str()).raw(),
10884 aWeight); // weight
10885
10886 Utf8Str strFullSnapshotFolder;
10887 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10888
10889 ComObjPtr<Medium> diff;
10890 diff.createObject();
10891 // store the diff in the same registry as the parent
10892 // (this cannot fail here because we can't create implicit diffs for
10893 // unregistered images)
10894 Guid uuidRegistryParent;
10895 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10896 Assert(fInRegistry); NOREF(fInRegistry);
10897 rc = diff->init(mParent,
10898 pMedium->i_getPreferredDiffFormat(),
10899 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10900 uuidRegistryParent,
10901 DeviceType_HardDisk);
10902 if (FAILED(rc)) throw rc;
10903
10904 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10905 * the push_back? Looks like we're going to release medium with the
10906 * wrong kind of lock (general issue with if we fail anywhere at all)
10907 * and an orphaned VDI in the snapshots folder. */
10908
10909 /* update the appropriate lock list */
10910 MediumLockList *pMediumLockList;
10911 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10912 AssertComRCThrowRC(rc);
10913 if (aOnline)
10914 {
10915 alock.release();
10916 /* The currently attached medium will be read-only, change
10917 * the lock type to read. */
10918 rc = pMediumLockList->Update(pMedium, false);
10919 alock.acquire();
10920 AssertComRCThrowRC(rc);
10921 }
10922
10923 /* release the locks before the potentially lengthy operation */
10924 alock.release();
10925 rc = pMedium->i_createDiffStorage(diff,
10926 pMedium->i_getPreferredDiffVariant(),
10927 pMediumLockList,
10928 NULL /* aProgress */,
10929 true /* aWait */);
10930 alock.acquire();
10931 if (FAILED(rc)) throw rc;
10932
10933 /* actual lock list update is done in Machine::i_commitMedia */
10934
10935 rc = diff->i_addBackReference(mData->mUuid);
10936 AssertComRCThrowRC(rc);
10937
10938 /* add a new attachment */
10939 ComObjPtr<MediumAttachment> attachment;
10940 attachment.createObject();
10941 rc = attachment->init(this,
10942 diff,
10943 pAtt->i_getControllerName(),
10944 pAtt->i_getPort(),
10945 pAtt->i_getDevice(),
10946 DeviceType_HardDisk,
10947 true /* aImplicit */,
10948 false /* aPassthrough */,
10949 false /* aTempEject */,
10950 pAtt->i_getNonRotational(),
10951 pAtt->i_getDiscard(),
10952 pAtt->i_getHotPluggable(),
10953 pAtt->i_getBandwidthGroup());
10954 if (FAILED(rc)) throw rc;
10955
10956 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10957 AssertComRCThrowRC(rc);
10958 mMediumAttachments->push_back(attachment);
10959 }
10960 }
10961 catch (HRESULT aRC) { rc = aRC; }
10962
10963 /* unlock all hard disks we locked when there is no VM */
10964 if (!aOnline)
10965 {
10966 ErrorInfoKeeper eik;
10967
10968 HRESULT rc1 = lockedMediaMap->Clear();
10969 AssertComRC(rc1);
10970 }
10971
10972 return rc;
10973}
10974
10975/**
10976 * Deletes implicit differencing hard disks created either by
10977 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10978 * mMediumAttachments.
10979 *
10980 * Note that to delete hard disks created by #attachDevice() this method is
10981 * called from #i_rollbackMedia() when the changes are rolled back.
10982 *
10983 * @note Locks this object and the media tree for writing.
10984 */
10985HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10986{
10987 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10988
10989 AutoCaller autoCaller(this);
10990 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10991
10992 AutoMultiWriteLock2 alock(this->lockHandle(),
10993 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10994
10995 /* We absolutely must have backed up state. */
10996 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10997
10998 /* Check if there are any implicitly created diff images. */
10999 bool fImplicitDiffs = false;
11000 for (MediumAttachmentList::const_iterator
11001 it = mMediumAttachments->begin();
11002 it != mMediumAttachments->end();
11003 ++it)
11004 {
11005 const ComObjPtr<MediumAttachment> &pAtt = *it;
11006 if (pAtt->i_isImplicit())
11007 {
11008 fImplicitDiffs = true;
11009 break;
11010 }
11011 }
11012 /* If there is nothing to do, leave early. This saves lots of image locking
11013 * effort. It also avoids a MachineStateChanged event without real reason.
11014 * This is important e.g. when loading a VM config, because there should be
11015 * no events. Otherwise API clients can become thoroughly confused for
11016 * inaccessible VMs (the code for loading VM configs uses this method for
11017 * cleanup if the config makes no sense), as they take such events as an
11018 * indication that the VM is alive, and they would force the VM config to
11019 * be reread, leading to an endless loop. */
11020 if (!fImplicitDiffs)
11021 return S_OK;
11022
11023 HRESULT rc = S_OK;
11024 MachineState_T oldState = mData->mMachineState;
11025
11026 /* will release the lock before the potentially lengthy operation,
11027 * so protect with the special state (unless already protected) */
11028 if ( oldState != MachineState_Snapshotting
11029 && oldState != MachineState_OnlineSnapshotting
11030 && oldState != MachineState_LiveSnapshotting
11031 && oldState != MachineState_RestoringSnapshot
11032 && oldState != MachineState_DeletingSnapshot
11033 && oldState != MachineState_DeletingSnapshotOnline
11034 && oldState != MachineState_DeletingSnapshotPaused
11035 )
11036 i_setMachineState(MachineState_SettingUp);
11037
11038 // use appropriate locked media map (online or offline)
11039 MediumLockListMap lockedMediaOffline;
11040 MediumLockListMap *lockedMediaMap;
11041 if (aOnline)
11042 lockedMediaMap = &mData->mSession.mLockedMedia;
11043 else
11044 lockedMediaMap = &lockedMediaOffline;
11045
11046 try
11047 {
11048 if (!aOnline)
11049 {
11050 /* lock all attached hard disks early to detect "in use"
11051 * situations before deleting actual diffs */
11052 for (MediumAttachmentList::const_iterator
11053 it = mMediumAttachments->begin();
11054 it != mMediumAttachments->end();
11055 ++it)
11056 {
11057 MediumAttachment *pAtt = *it;
11058 if (pAtt->i_getType() == DeviceType_HardDisk)
11059 {
11060 Medium *pMedium = pAtt->i_getMedium();
11061 Assert(pMedium);
11062
11063 MediumLockList *pMediumLockList(new MediumLockList());
11064 alock.release();
11065 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11066 NULL /* pToLockWrite */,
11067 false /* fMediumLockWriteAll */,
11068 NULL,
11069 *pMediumLockList);
11070 alock.acquire();
11071
11072 if (FAILED(rc))
11073 {
11074 delete pMediumLockList;
11075 throw rc;
11076 }
11077
11078 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11079 if (FAILED(rc))
11080 throw rc;
11081 }
11082 }
11083
11084 if (FAILED(rc))
11085 throw rc;
11086 } // end of offline
11087
11088 /* Lock lists are now up to date and include implicitly created media */
11089
11090 /* Go through remembered attachments and delete all implicitly created
11091 * diffs and fix up the attachment information */
11092 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11093 MediumAttachmentList implicitAtts;
11094 for (MediumAttachmentList::const_iterator
11095 it = mMediumAttachments->begin();
11096 it != mMediumAttachments->end();
11097 ++it)
11098 {
11099 ComObjPtr<MediumAttachment> pAtt = *it;
11100 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11101 if (pMedium.isNull())
11102 continue;
11103
11104 // Implicit attachments go on the list for deletion and back references are removed.
11105 if (pAtt->i_isImplicit())
11106 {
11107 /* Deassociate and mark for deletion */
11108 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11109 rc = pMedium->i_removeBackReference(mData->mUuid);
11110 if (FAILED(rc))
11111 throw rc;
11112 implicitAtts.push_back(pAtt);
11113 continue;
11114 }
11115
11116 /* Was this medium attached before? */
11117 if (!i_findAttachment(oldAtts, pMedium))
11118 {
11119 /* no: de-associate */
11120 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11121 rc = pMedium->i_removeBackReference(mData->mUuid);
11122 if (FAILED(rc))
11123 throw rc;
11124 continue;
11125 }
11126 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11127 }
11128
11129 /* If there are implicit attachments to delete, throw away the lock
11130 * map contents (which will unlock all media) since the medium
11131 * attachments will be rolled back. Below we need to completely
11132 * recreate the lock map anyway since it is infinitely complex to
11133 * do this incrementally (would need reconstructing each attachment
11134 * change, which would be extremely hairy). */
11135 if (implicitAtts.size() != 0)
11136 {
11137 ErrorInfoKeeper eik;
11138
11139 HRESULT rc1 = lockedMediaMap->Clear();
11140 AssertComRC(rc1);
11141 }
11142
11143 /* rollback hard disk changes */
11144 mMediumAttachments.rollback();
11145
11146 MultiResult mrc(S_OK);
11147
11148 // Delete unused implicit diffs.
11149 if (implicitAtts.size() != 0)
11150 {
11151 alock.release();
11152
11153 for (MediumAttachmentList::const_iterator
11154 it = implicitAtts.begin();
11155 it != implicitAtts.end();
11156 ++it)
11157 {
11158 // Remove medium associated with this attachment.
11159 ComObjPtr<MediumAttachment> pAtt = *it;
11160 Assert(pAtt);
11161 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11162 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11163 Assert(pMedium);
11164
11165 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11166 // continue on delete failure, just collect error messages
11167 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11168 pMedium->i_getLocationFull().c_str() ));
11169 mrc = rc;
11170 }
11171 // Clear the list of deleted implicit attachments now, while not
11172 // holding the lock, as it will ultimately trigger Medium::uninit()
11173 // calls which assume that the media tree lock isn't held.
11174 implicitAtts.clear();
11175
11176 alock.acquire();
11177
11178 /* if there is a VM recreate media lock map as mentioned above,
11179 * otherwise it is a waste of time and we leave things unlocked */
11180 if (aOnline)
11181 {
11182 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11183 /* must never be NULL, but better safe than sorry */
11184 if (!pMachine.isNull())
11185 {
11186 alock.release();
11187 rc = mData->mSession.mMachine->i_lockMedia();
11188 alock.acquire();
11189 if (FAILED(rc))
11190 throw rc;
11191 }
11192 }
11193 }
11194 }
11195 catch (HRESULT aRC) {rc = aRC;}
11196
11197 if (mData->mMachineState == MachineState_SettingUp)
11198 i_setMachineState(oldState);
11199
11200 /* unlock all hard disks we locked when there is no VM */
11201 if (!aOnline)
11202 {
11203 ErrorInfoKeeper eik;
11204
11205 HRESULT rc1 = lockedMediaMap->Clear();
11206 AssertComRC(rc1);
11207 }
11208
11209 return rc;
11210}
11211
11212
11213/**
11214 * Looks through the given list of media attachments for one with the given parameters
11215 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11216 * can be searched as well if needed.
11217 *
11218 * @param ll
11219 * @param aControllerName
11220 * @param aControllerPort
11221 * @param aDevice
11222 * @return
11223 */
11224MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11225 const Utf8Str &aControllerName,
11226 LONG aControllerPort,
11227 LONG aDevice)
11228{
11229 for (MediumAttachmentList::const_iterator
11230 it = ll.begin();
11231 it != ll.end();
11232 ++it)
11233 {
11234 MediumAttachment *pAttach = *it;
11235 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11236 return pAttach;
11237 }
11238
11239 return NULL;
11240}
11241
11242/**
11243 * Looks through the given list of media attachments for one with the given parameters
11244 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11245 * can be searched as well if needed.
11246 *
11247 * @param ll
11248 * @param pMedium
11249 * @return
11250 */
11251MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11252 ComObjPtr<Medium> pMedium)
11253{
11254 for (MediumAttachmentList::const_iterator
11255 it = ll.begin();
11256 it != ll.end();
11257 ++it)
11258 {
11259 MediumAttachment *pAttach = *it;
11260 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11261 if (pMediumThis == pMedium)
11262 return pAttach;
11263 }
11264
11265 return NULL;
11266}
11267
11268/**
11269 * Looks through the given list of media attachments for one with the given parameters
11270 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11271 * can be searched as well if needed.
11272 *
11273 * @param ll
11274 * @param id
11275 * @return
11276 */
11277MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11278 Guid &id)
11279{
11280 for (MediumAttachmentList::const_iterator
11281 it = ll.begin();
11282 it != ll.end();
11283 ++it)
11284 {
11285 MediumAttachment *pAttach = *it;
11286 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11287 if (pMediumThis->i_getId() == id)
11288 return pAttach;
11289 }
11290
11291 return NULL;
11292}
11293
11294/**
11295 * Main implementation for Machine::DetachDevice. This also gets called
11296 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11297 *
11298 * @param pAttach Medium attachment to detach.
11299 * @param writeLock Machine write lock which the caller must have locked once.
11300 * This may be released temporarily in here.
11301 * @param pSnapshot If NULL, then the detachment is for the current machine.
11302 * Otherwise this is for a SnapshotMachine, and this must be
11303 * its snapshot.
11304 * @return
11305 */
11306HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11307 AutoWriteLock &writeLock,
11308 Snapshot *pSnapshot)
11309{
11310 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11311 DeviceType_T mediumType = pAttach->i_getType();
11312
11313 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11314
11315 if (pAttach->i_isImplicit())
11316 {
11317 /* attempt to implicitly delete the implicitly created diff */
11318
11319 /// @todo move the implicit flag from MediumAttachment to Medium
11320 /// and forbid any hard disk operation when it is implicit. Or maybe
11321 /// a special media state for it to make it even more simple.
11322
11323 Assert(mMediumAttachments.isBackedUp());
11324
11325 /* will release the lock before the potentially lengthy operation, so
11326 * protect with the special state */
11327 MachineState_T oldState = mData->mMachineState;
11328 i_setMachineState(MachineState_SettingUp);
11329
11330 writeLock.release();
11331
11332 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11333 true /*aWait*/);
11334
11335 writeLock.acquire();
11336
11337 i_setMachineState(oldState);
11338
11339 if (FAILED(rc)) return rc;
11340 }
11341
11342 i_setModified(IsModified_Storage);
11343 mMediumAttachments.backup();
11344 mMediumAttachments->remove(pAttach);
11345
11346 if (!oldmedium.isNull())
11347 {
11348 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11349 if (pSnapshot)
11350 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11351 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11352 else if (mediumType != DeviceType_HardDisk)
11353 oldmedium->i_removeBackReference(mData->mUuid);
11354 }
11355
11356 return S_OK;
11357}
11358
11359/**
11360 * Goes thru all media of the given list and
11361 *
11362 * 1) calls i_detachDevice() on each of them for this machine and
11363 * 2) adds all Medium objects found in the process to the given list,
11364 * depending on cleanupMode.
11365 *
11366 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11367 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11368 * media to the list.
11369 *
11370 * This gets called from Machine::Unregister, both for the actual Machine and
11371 * the SnapshotMachine objects that might be found in the snapshots.
11372 *
11373 * Requires caller and locking. The machine lock must be passed in because it
11374 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11375 *
11376 * @param writeLock Machine lock from top-level caller; this gets passed to
11377 * i_detachDevice.
11378 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11379 * object if called for a SnapshotMachine.
11380 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11381 * added to llMedia; if Full, then all media get added;
11382 * otherwise no media get added.
11383 * @param llMedia Caller's list to receive Medium objects which got detached so
11384 * caller can close() them, depending on cleanupMode.
11385 * @return
11386 */
11387HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11388 Snapshot *pSnapshot,
11389 CleanupMode_T cleanupMode,
11390 MediaList &llMedia)
11391{
11392 Assert(isWriteLockOnCurrentThread());
11393
11394 HRESULT rc;
11395
11396 // make a temporary list because i_detachDevice invalidates iterators into
11397 // mMediumAttachments
11398 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11399
11400 for (MediumAttachmentList::iterator
11401 it = llAttachments2.begin();
11402 it != llAttachments2.end();
11403 ++it)
11404 {
11405 ComObjPtr<MediumAttachment> &pAttach = *it;
11406 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11407
11408 if (!pMedium.isNull())
11409 {
11410 AutoCaller mac(pMedium);
11411 if (FAILED(mac.rc())) return mac.rc();
11412 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11413 DeviceType_T devType = pMedium->i_getDeviceType();
11414 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11415 && devType == DeviceType_HardDisk)
11416 || (cleanupMode == CleanupMode_Full)
11417 )
11418 {
11419 llMedia.push_back(pMedium);
11420 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11421 /* Not allowed to keep this lock as below we need the parent
11422 * medium lock, and the lock order is parent to child. */
11423 lock.release();
11424 /*
11425 * Search for medias which are not attached to any machine, but
11426 * in the chain to an attached disk. Mediums are only consided
11427 * if they are:
11428 * - have only one child
11429 * - no references to any machines
11430 * - are of normal medium type
11431 */
11432 while (!pParent.isNull())
11433 {
11434 AutoCaller mac1(pParent);
11435 if (FAILED(mac1.rc())) return mac1.rc();
11436 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11437 if (pParent->i_getChildren().size() == 1)
11438 {
11439 if ( pParent->i_getMachineBackRefCount() == 0
11440 && pParent->i_getType() == MediumType_Normal
11441 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11442 llMedia.push_back(pParent);
11443 }
11444 else
11445 break;
11446 pParent = pParent->i_getParent();
11447 }
11448 }
11449 }
11450
11451 // real machine: then we need to use the proper method
11452 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11453
11454 if (FAILED(rc))
11455 return rc;
11456 }
11457
11458 return S_OK;
11459}
11460
11461/**
11462 * Perform deferred hard disk detachments.
11463 *
11464 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11465 * changed (not backed up).
11466 *
11467 * If @a aOnline is @c true then this method will also unlock the old hard
11468 * disks for which the new implicit diffs were created and will lock these new
11469 * diffs for writing.
11470 *
11471 * @param aOnline Whether the VM was online prior to this operation.
11472 *
11473 * @note Locks this object for writing!
11474 */
11475void Machine::i_commitMedia(bool aOnline /*= false*/)
11476{
11477 AutoCaller autoCaller(this);
11478 AssertComRCReturnVoid(autoCaller.rc());
11479
11480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11481
11482 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11483
11484 HRESULT rc = S_OK;
11485
11486 /* no attach/detach operations -- nothing to do */
11487 if (!mMediumAttachments.isBackedUp())
11488 return;
11489
11490 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11491 bool fMediaNeedsLocking = false;
11492
11493 /* enumerate new attachments */
11494 for (MediumAttachmentList::const_iterator
11495 it = mMediumAttachments->begin();
11496 it != mMediumAttachments->end();
11497 ++it)
11498 {
11499 MediumAttachment *pAttach = *it;
11500
11501 pAttach->i_commit();
11502
11503 Medium *pMedium = pAttach->i_getMedium();
11504 bool fImplicit = pAttach->i_isImplicit();
11505
11506 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11507 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11508 fImplicit));
11509
11510 /** @todo convert all this Machine-based voodoo to MediumAttachment
11511 * based commit logic. */
11512 if (fImplicit)
11513 {
11514 /* convert implicit attachment to normal */
11515 pAttach->i_setImplicit(false);
11516
11517 if ( aOnline
11518 && pMedium
11519 && pAttach->i_getType() == DeviceType_HardDisk
11520 )
11521 {
11522 /* update the appropriate lock list */
11523 MediumLockList *pMediumLockList;
11524 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11525 AssertComRC(rc);
11526 if (pMediumLockList)
11527 {
11528 /* unlock if there's a need to change the locking */
11529 if (!fMediaNeedsLocking)
11530 {
11531 rc = mData->mSession.mLockedMedia.Unlock();
11532 AssertComRC(rc);
11533 fMediaNeedsLocking = true;
11534 }
11535 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11536 AssertComRC(rc);
11537 rc = pMediumLockList->Append(pMedium, true);
11538 AssertComRC(rc);
11539 }
11540 }
11541
11542 continue;
11543 }
11544
11545 if (pMedium)
11546 {
11547 /* was this medium attached before? */
11548 for (MediumAttachmentList::iterator
11549 oldIt = oldAtts.begin();
11550 oldIt != oldAtts.end();
11551 ++oldIt)
11552 {
11553 MediumAttachment *pOldAttach = *oldIt;
11554 if (pOldAttach->i_getMedium() == pMedium)
11555 {
11556 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11557
11558 /* yes: remove from old to avoid de-association */
11559 oldAtts.erase(oldIt);
11560 break;
11561 }
11562 }
11563 }
11564 }
11565
11566 /* enumerate remaining old attachments and de-associate from the
11567 * current machine state */
11568 for (MediumAttachmentList::const_iterator
11569 it = oldAtts.begin();
11570 it != oldAtts.end();
11571 ++it)
11572 {
11573 MediumAttachment *pAttach = *it;
11574 Medium *pMedium = pAttach->i_getMedium();
11575
11576 /* Detach only hard disks, since DVD/floppy media is detached
11577 * instantly in MountMedium. */
11578 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11579 {
11580 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11581
11582 /* now de-associate from the current machine state */
11583 rc = pMedium->i_removeBackReference(mData->mUuid);
11584 AssertComRC(rc);
11585
11586 if (aOnline)
11587 {
11588 /* unlock since medium is not used anymore */
11589 MediumLockList *pMediumLockList;
11590 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11591 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11592 {
11593 /* this happens for online snapshots, there the attachment
11594 * is changing, but only to a diff image created under
11595 * the old one, so there is no separate lock list */
11596 Assert(!pMediumLockList);
11597 }
11598 else
11599 {
11600 AssertComRC(rc);
11601 if (pMediumLockList)
11602 {
11603 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11604 AssertComRC(rc);
11605 }
11606 }
11607 }
11608 }
11609 }
11610
11611 /* take media locks again so that the locking state is consistent */
11612 if (fMediaNeedsLocking)
11613 {
11614 Assert(aOnline);
11615 rc = mData->mSession.mLockedMedia.Lock();
11616 AssertComRC(rc);
11617 }
11618
11619 /* commit the hard disk changes */
11620 mMediumAttachments.commit();
11621
11622 if (i_isSessionMachine())
11623 {
11624 /*
11625 * Update the parent machine to point to the new owner.
11626 * This is necessary because the stored parent will point to the
11627 * session machine otherwise and cause crashes or errors later
11628 * when the session machine gets invalid.
11629 */
11630 /** @todo Change the MediumAttachment class to behave like any other
11631 * class in this regard by creating peer MediumAttachment
11632 * objects for session machines and share the data with the peer
11633 * machine.
11634 */
11635 for (MediumAttachmentList::const_iterator
11636 it = mMediumAttachments->begin();
11637 it != mMediumAttachments->end();
11638 ++it)
11639 (*it)->i_updateParentMachine(mPeer);
11640
11641 /* attach new data to the primary machine and reshare it */
11642 mPeer->mMediumAttachments.attach(mMediumAttachments);
11643 }
11644
11645 return;
11646}
11647
11648/**
11649 * Perform deferred deletion of implicitly created diffs.
11650 *
11651 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11652 * changed (not backed up).
11653 *
11654 * @note Locks this object for writing!
11655 */
11656void Machine::i_rollbackMedia()
11657{
11658 AutoCaller autoCaller(this);
11659 AssertComRCReturnVoid(autoCaller.rc());
11660
11661 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11662 LogFlowThisFunc(("Entering rollbackMedia\n"));
11663
11664 HRESULT rc = S_OK;
11665
11666 /* no attach/detach operations -- nothing to do */
11667 if (!mMediumAttachments.isBackedUp())
11668 return;
11669
11670 /* enumerate new attachments */
11671 for (MediumAttachmentList::const_iterator
11672 it = mMediumAttachments->begin();
11673 it != mMediumAttachments->end();
11674 ++it)
11675 {
11676 MediumAttachment *pAttach = *it;
11677 /* Fix up the backrefs for DVD/floppy media. */
11678 if (pAttach->i_getType() != DeviceType_HardDisk)
11679 {
11680 Medium *pMedium = pAttach->i_getMedium();
11681 if (pMedium)
11682 {
11683 rc = pMedium->i_removeBackReference(mData->mUuid);
11684 AssertComRC(rc);
11685 }
11686 }
11687
11688 (*it)->i_rollback();
11689
11690 pAttach = *it;
11691 /* Fix up the backrefs for DVD/floppy media. */
11692 if (pAttach->i_getType() != DeviceType_HardDisk)
11693 {
11694 Medium *pMedium = pAttach->i_getMedium();
11695 if (pMedium)
11696 {
11697 rc = pMedium->i_addBackReference(mData->mUuid);
11698 AssertComRC(rc);
11699 }
11700 }
11701 }
11702
11703 /** @todo convert all this Machine-based voodoo to MediumAttachment
11704 * based rollback logic. */
11705 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11706
11707 return;
11708}
11709
11710/**
11711 * Returns true if the settings file is located in the directory named exactly
11712 * as the machine; this means, among other things, that the machine directory
11713 * should be auto-renamed.
11714 *
11715 * @param aSettingsDir if not NULL, the full machine settings file directory
11716 * name will be assigned there.
11717 *
11718 * @note Doesn't lock anything.
11719 * @note Not thread safe (must be called from this object's lock).
11720 */
11721bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11722{
11723 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11724 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11725 if (aSettingsDir)
11726 *aSettingsDir = strMachineDirName;
11727 strMachineDirName.stripPath(); // vmname
11728 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11729 strConfigFileOnly.stripPath() // vmname.vbox
11730 .stripSuffix(); // vmname
11731 /** @todo hack, make somehow use of ComposeMachineFilename */
11732 if (mUserData->s.fDirectoryIncludesUUID)
11733 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11734
11735 AssertReturn(!strMachineDirName.isEmpty(), false);
11736 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11737
11738 return strMachineDirName == strConfigFileOnly;
11739}
11740
11741/**
11742 * Discards all changes to machine settings.
11743 *
11744 * @param aNotify Whether to notify the direct session about changes or not.
11745 *
11746 * @note Locks objects for writing!
11747 */
11748void Machine::i_rollback(bool aNotify)
11749{
11750 AutoCaller autoCaller(this);
11751 AssertComRCReturn(autoCaller.rc(), (void)0);
11752
11753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11754
11755 if (!mStorageControllers.isNull())
11756 {
11757 if (mStorageControllers.isBackedUp())
11758 {
11759 /* unitialize all new devices (absent in the backed up list). */
11760 StorageControllerList *backedList = mStorageControllers.backedUpData();
11761 for (StorageControllerList::const_iterator
11762 it = mStorageControllers->begin();
11763 it != mStorageControllers->end();
11764 ++it)
11765 {
11766 if ( std::find(backedList->begin(), backedList->end(), *it)
11767 == backedList->end()
11768 )
11769 {
11770 (*it)->uninit();
11771 }
11772 }
11773
11774 /* restore the list */
11775 mStorageControllers.rollback();
11776 }
11777
11778 /* rollback any changes to devices after restoring the list */
11779 if (mData->flModifications & IsModified_Storage)
11780 {
11781 for (StorageControllerList::const_iterator
11782 it = mStorageControllers->begin();
11783 it != mStorageControllers->end();
11784 ++it)
11785 {
11786 (*it)->i_rollback();
11787 }
11788 }
11789 }
11790
11791 if (!mUSBControllers.isNull())
11792 {
11793 if (mUSBControllers.isBackedUp())
11794 {
11795 /* unitialize all new devices (absent in the backed up list). */
11796 USBControllerList *backedList = mUSBControllers.backedUpData();
11797 for (USBControllerList::const_iterator
11798 it = mUSBControllers->begin();
11799 it != mUSBControllers->end();
11800 ++it)
11801 {
11802 if ( std::find(backedList->begin(), backedList->end(), *it)
11803 == backedList->end()
11804 )
11805 {
11806 (*it)->uninit();
11807 }
11808 }
11809
11810 /* restore the list */
11811 mUSBControllers.rollback();
11812 }
11813
11814 /* rollback any changes to devices after restoring the list */
11815 if (mData->flModifications & IsModified_USB)
11816 {
11817 for (USBControllerList::const_iterator
11818 it = mUSBControllers->begin();
11819 it != mUSBControllers->end();
11820 ++it)
11821 {
11822 (*it)->i_rollback();
11823 }
11824 }
11825 }
11826
11827 mUserData.rollback();
11828
11829 mHWData.rollback();
11830
11831 if (mData->flModifications & IsModified_Storage)
11832 i_rollbackMedia();
11833
11834 if (mBIOSSettings)
11835 mBIOSSettings->i_rollback();
11836
11837 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11838 mVRDEServer->i_rollback();
11839
11840 if (mAudioAdapter)
11841 mAudioAdapter->i_rollback();
11842
11843 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11844 mUSBDeviceFilters->i_rollback();
11845
11846 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11847 mBandwidthControl->i_rollback();
11848
11849 if (!mHWData.isNull())
11850 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11851 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11852 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11853 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11854
11855 if (mData->flModifications & IsModified_NetworkAdapters)
11856 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11857 if ( mNetworkAdapters[slot]
11858 && mNetworkAdapters[slot]->i_isModified())
11859 {
11860 mNetworkAdapters[slot]->i_rollback();
11861 networkAdapters[slot] = mNetworkAdapters[slot];
11862 }
11863
11864 if (mData->flModifications & IsModified_SerialPorts)
11865 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11866 if ( mSerialPorts[slot]
11867 && mSerialPorts[slot]->i_isModified())
11868 {
11869 mSerialPorts[slot]->i_rollback();
11870 serialPorts[slot] = mSerialPorts[slot];
11871 }
11872
11873 if (mData->flModifications & IsModified_ParallelPorts)
11874 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11875 if ( mParallelPorts[slot]
11876 && mParallelPorts[slot]->i_isModified())
11877 {
11878 mParallelPorts[slot]->i_rollback();
11879 parallelPorts[slot] = mParallelPorts[slot];
11880 }
11881
11882 if (aNotify)
11883 {
11884 /* inform the direct session about changes */
11885
11886 ComObjPtr<Machine> that = this;
11887 uint32_t flModifications = mData->flModifications;
11888 alock.release();
11889
11890 if (flModifications & IsModified_SharedFolders)
11891 that->i_onSharedFolderChange();
11892
11893 if (flModifications & IsModified_VRDEServer)
11894 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11895 if (flModifications & IsModified_USB)
11896 that->i_onUSBControllerChange();
11897
11898 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11899 if (networkAdapters[slot])
11900 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11901 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11902 if (serialPorts[slot])
11903 that->i_onSerialPortChange(serialPorts[slot]);
11904 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11905 if (parallelPorts[slot])
11906 that->i_onParallelPortChange(parallelPorts[slot]);
11907
11908 if (flModifications & IsModified_Storage)
11909 that->i_onStorageControllerChange();
11910
11911#if 0
11912 if (flModifications & IsModified_BandwidthControl)
11913 that->onBandwidthControlChange();
11914#endif
11915 }
11916}
11917
11918/**
11919 * Commits all the changes to machine settings.
11920 *
11921 * Note that this operation is supposed to never fail.
11922 *
11923 * @note Locks this object and children for writing.
11924 */
11925void Machine::i_commit()
11926{
11927 AutoCaller autoCaller(this);
11928 AssertComRCReturnVoid(autoCaller.rc());
11929
11930 AutoCaller peerCaller(mPeer);
11931 AssertComRCReturnVoid(peerCaller.rc());
11932
11933 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11934
11935 /*
11936 * use safe commit to ensure Snapshot machines (that share mUserData)
11937 * will still refer to a valid memory location
11938 */
11939 mUserData.commitCopy();
11940
11941 mHWData.commit();
11942
11943 if (mMediumAttachments.isBackedUp())
11944 i_commitMedia(Global::IsOnline(mData->mMachineState));
11945
11946 mBIOSSettings->i_commit();
11947 mVRDEServer->i_commit();
11948 mAudioAdapter->i_commit();
11949 mUSBDeviceFilters->i_commit();
11950 mBandwidthControl->i_commit();
11951
11952 /* Since mNetworkAdapters is a list which might have been changed (resized)
11953 * without using the Backupable<> template we need to handle the copying
11954 * of the list entries manually, including the creation of peers for the
11955 * new objects. */
11956 bool commitNetworkAdapters = false;
11957 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11958 if (mPeer)
11959 {
11960 /* commit everything, even the ones which will go away */
11961 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11962 mNetworkAdapters[slot]->i_commit();
11963 /* copy over the new entries, creating a peer and uninit the original */
11964 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11965 for (size_t slot = 0; slot < newSize; slot++)
11966 {
11967 /* look if this adapter has a peer device */
11968 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11969 if (!peer)
11970 {
11971 /* no peer means the adapter is a newly created one;
11972 * create a peer owning data this data share it with */
11973 peer.createObject();
11974 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11975 }
11976 mPeer->mNetworkAdapters[slot] = peer;
11977 }
11978 /* uninit any no longer needed network adapters */
11979 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11980 mNetworkAdapters[slot]->uninit();
11981 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11982 {
11983 if (mPeer->mNetworkAdapters[slot])
11984 mPeer->mNetworkAdapters[slot]->uninit();
11985 }
11986 /* Keep the original network adapter count until this point, so that
11987 * discarding a chipset type change will not lose settings. */
11988 mNetworkAdapters.resize(newSize);
11989 mPeer->mNetworkAdapters.resize(newSize);
11990 }
11991 else
11992 {
11993 /* we have no peer (our parent is the newly created machine);
11994 * just commit changes to the network adapters */
11995 commitNetworkAdapters = true;
11996 }
11997 if (commitNetworkAdapters)
11998 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11999 mNetworkAdapters[slot]->i_commit();
12000
12001 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12002 mSerialPorts[slot]->i_commit();
12003 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12004 mParallelPorts[slot]->i_commit();
12005
12006 bool commitStorageControllers = false;
12007
12008 if (mStorageControllers.isBackedUp())
12009 {
12010 mStorageControllers.commit();
12011
12012 if (mPeer)
12013 {
12014 /* Commit all changes to new controllers (this will reshare data with
12015 * peers for those who have peers) */
12016 StorageControllerList *newList = new StorageControllerList();
12017 for (StorageControllerList::const_iterator
12018 it = mStorageControllers->begin();
12019 it != mStorageControllers->end();
12020 ++it)
12021 {
12022 (*it)->i_commit();
12023
12024 /* look if this controller has a peer device */
12025 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12026 if (!peer)
12027 {
12028 /* no peer means the device is a newly created one;
12029 * create a peer owning data this device share it with */
12030 peer.createObject();
12031 peer->init(mPeer, *it, true /* aReshare */);
12032 }
12033 else
12034 {
12035 /* remove peer from the old list */
12036 mPeer->mStorageControllers->remove(peer);
12037 }
12038 /* and add it to the new list */
12039 newList->push_back(peer);
12040 }
12041
12042 /* uninit old peer's controllers that are left */
12043 for (StorageControllerList::const_iterator
12044 it = mPeer->mStorageControllers->begin();
12045 it != mPeer->mStorageControllers->end();
12046 ++it)
12047 {
12048 (*it)->uninit();
12049 }
12050
12051 /* attach new list of controllers to our peer */
12052 mPeer->mStorageControllers.attach(newList);
12053 }
12054 else
12055 {
12056 /* we have no peer (our parent is the newly created machine);
12057 * just commit changes to devices */
12058 commitStorageControllers = true;
12059 }
12060 }
12061 else
12062 {
12063 /* the list of controllers itself is not changed,
12064 * just commit changes to controllers themselves */
12065 commitStorageControllers = true;
12066 }
12067
12068 if (commitStorageControllers)
12069 {
12070 for (StorageControllerList::const_iterator
12071 it = mStorageControllers->begin();
12072 it != mStorageControllers->end();
12073 ++it)
12074 {
12075 (*it)->i_commit();
12076 }
12077 }
12078
12079 bool commitUSBControllers = false;
12080
12081 if (mUSBControllers.isBackedUp())
12082 {
12083 mUSBControllers.commit();
12084
12085 if (mPeer)
12086 {
12087 /* Commit all changes to new controllers (this will reshare data with
12088 * peers for those who have peers) */
12089 USBControllerList *newList = new USBControllerList();
12090 for (USBControllerList::const_iterator
12091 it = mUSBControllers->begin();
12092 it != mUSBControllers->end();
12093 ++it)
12094 {
12095 (*it)->i_commit();
12096
12097 /* look if this controller has a peer device */
12098 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12099 if (!peer)
12100 {
12101 /* no peer means the device is a newly created one;
12102 * create a peer owning data this device share it with */
12103 peer.createObject();
12104 peer->init(mPeer, *it, true /* aReshare */);
12105 }
12106 else
12107 {
12108 /* remove peer from the old list */
12109 mPeer->mUSBControllers->remove(peer);
12110 }
12111 /* and add it to the new list */
12112 newList->push_back(peer);
12113 }
12114
12115 /* uninit old peer's controllers that are left */
12116 for (USBControllerList::const_iterator
12117 it = mPeer->mUSBControllers->begin();
12118 it != mPeer->mUSBControllers->end();
12119 ++it)
12120 {
12121 (*it)->uninit();
12122 }
12123
12124 /* attach new list of controllers to our peer */
12125 mPeer->mUSBControllers.attach(newList);
12126 }
12127 else
12128 {
12129 /* we have no peer (our parent is the newly created machine);
12130 * just commit changes to devices */
12131 commitUSBControllers = true;
12132 }
12133 }
12134 else
12135 {
12136 /* the list of controllers itself is not changed,
12137 * just commit changes to controllers themselves */
12138 commitUSBControllers = true;
12139 }
12140
12141 if (commitUSBControllers)
12142 {
12143 for (USBControllerList::const_iterator
12144 it = mUSBControllers->begin();
12145 it != mUSBControllers->end();
12146 ++it)
12147 {
12148 (*it)->i_commit();
12149 }
12150 }
12151
12152 if (i_isSessionMachine())
12153 {
12154 /* attach new data to the primary machine and reshare it */
12155 mPeer->mUserData.attach(mUserData);
12156 mPeer->mHWData.attach(mHWData);
12157 /* mmMediumAttachments is reshared by fixupMedia */
12158 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12159 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12160 }
12161}
12162
12163/**
12164 * Copies all the hardware data from the given machine.
12165 *
12166 * Currently, only called when the VM is being restored from a snapshot. In
12167 * particular, this implies that the VM is not running during this method's
12168 * call.
12169 *
12170 * @note This method must be called from under this object's lock.
12171 *
12172 * @note This method doesn't call #i_commit(), so all data remains backed up and
12173 * unsaved.
12174 */
12175void Machine::i_copyFrom(Machine *aThat)
12176{
12177 AssertReturnVoid(!i_isSnapshotMachine());
12178 AssertReturnVoid(aThat->i_isSnapshotMachine());
12179
12180 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12181
12182 mHWData.assignCopy(aThat->mHWData);
12183
12184 // create copies of all shared folders (mHWData after attaching a copy
12185 // contains just references to original objects)
12186 for (HWData::SharedFolderList::iterator
12187 it = mHWData->mSharedFolders.begin();
12188 it != mHWData->mSharedFolders.end();
12189 ++it)
12190 {
12191 ComObjPtr<SharedFolder> folder;
12192 folder.createObject();
12193 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12194 AssertComRC(rc);
12195 *it = folder;
12196 }
12197
12198 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12199 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12200 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12201 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12202 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12203
12204 /* create private copies of all controllers */
12205 mStorageControllers.backup();
12206 mStorageControllers->clear();
12207 for (StorageControllerList::const_iterator
12208 it = aThat->mStorageControllers->begin();
12209 it != aThat->mStorageControllers->end();
12210 ++it)
12211 {
12212 ComObjPtr<StorageController> ctrl;
12213 ctrl.createObject();
12214 ctrl->initCopy(this, *it);
12215 mStorageControllers->push_back(ctrl);
12216 }
12217
12218 /* create private copies of all USB controllers */
12219 mUSBControllers.backup();
12220 mUSBControllers->clear();
12221 for (USBControllerList::const_iterator
12222 it = aThat->mUSBControllers->begin();
12223 it != aThat->mUSBControllers->end();
12224 ++it)
12225 {
12226 ComObjPtr<USBController> ctrl;
12227 ctrl.createObject();
12228 ctrl->initCopy(this, *it);
12229 mUSBControllers->push_back(ctrl);
12230 }
12231
12232 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12233 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12234 {
12235 if (mNetworkAdapters[slot].isNotNull())
12236 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12237 else
12238 {
12239 unconst(mNetworkAdapters[slot]).createObject();
12240 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12241 }
12242 }
12243 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12244 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12245 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12246 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12247}
12248
12249/**
12250 * Returns whether the given storage controller is hotplug capable.
12251 *
12252 * @returns true if the controller supports hotplugging
12253 * false otherwise.
12254 * @param enmCtrlType The controller type to check for.
12255 */
12256bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12257{
12258 ComPtr<ISystemProperties> systemProperties;
12259 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12260 if (FAILED(rc))
12261 return false;
12262
12263 BOOL aHotplugCapable = FALSE;
12264 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12265
12266 return RT_BOOL(aHotplugCapable);
12267}
12268
12269#ifdef VBOX_WITH_RESOURCE_USAGE_API
12270
12271void Machine::i_getDiskList(MediaList &list)
12272{
12273 for (MediumAttachmentList::const_iterator
12274 it = mMediumAttachments->begin();
12275 it != mMediumAttachments->end();
12276 ++it)
12277 {
12278 MediumAttachment *pAttach = *it;
12279 /* just in case */
12280 AssertContinue(pAttach);
12281
12282 AutoCaller localAutoCallerA(pAttach);
12283 if (FAILED(localAutoCallerA.rc())) continue;
12284
12285 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12286
12287 if (pAttach->i_getType() == DeviceType_HardDisk)
12288 list.push_back(pAttach->i_getMedium());
12289 }
12290}
12291
12292void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12293{
12294 AssertReturnVoid(isWriteLockOnCurrentThread());
12295 AssertPtrReturnVoid(aCollector);
12296
12297 pm::CollectorHAL *hal = aCollector->getHAL();
12298 /* Create sub metrics */
12299 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12300 "Percentage of processor time spent in user mode by the VM process.");
12301 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12302 "Percentage of processor time spent in kernel mode by the VM process.");
12303 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12304 "Size of resident portion of VM process in memory.");
12305 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12306 "Actual size of all VM disks combined.");
12307 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12308 "Network receive rate.");
12309 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12310 "Network transmit rate.");
12311 /* Create and register base metrics */
12312 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12313 cpuLoadUser, cpuLoadKernel);
12314 aCollector->registerBaseMetric(cpuLoad);
12315 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12316 ramUsageUsed);
12317 aCollector->registerBaseMetric(ramUsage);
12318 MediaList disks;
12319 i_getDiskList(disks);
12320 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12321 diskUsageUsed);
12322 aCollector->registerBaseMetric(diskUsage);
12323
12324 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12325 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12326 new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12328 new pm::AggregateMin()));
12329 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12330 new pm::AggregateMax()));
12331 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12332 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12333 new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12335 new pm::AggregateMin()));
12336 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12337 new pm::AggregateMax()));
12338
12339 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12340 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12341 new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12343 new pm::AggregateMin()));
12344 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12345 new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12348 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12349 new pm::AggregateAvg()));
12350 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12351 new pm::AggregateMin()));
12352 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12353 new pm::AggregateMax()));
12354
12355
12356 /* Guest metrics collector */
12357 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12358 aCollector->registerGuest(mCollectorGuest);
12359 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12360
12361 /* Create sub metrics */
12362 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12363 "Percentage of processor time spent in user mode as seen by the guest.");
12364 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12365 "Percentage of processor time spent in kernel mode as seen by the guest.");
12366 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12367 "Percentage of processor time spent idling as seen by the guest.");
12368
12369 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12370 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12371 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12372 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12373 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12374 pm::SubMetric *guestMemCache = new pm::SubMetric(
12375 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12376
12377 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12378 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12379
12380 /* Create and register base metrics */
12381 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12382 machineNetRx, machineNetTx);
12383 aCollector->registerBaseMetric(machineNetRate);
12384
12385 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12386 guestLoadUser, guestLoadKernel, guestLoadIdle);
12387 aCollector->registerBaseMetric(guestCpuLoad);
12388
12389 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12390 guestMemTotal, guestMemFree,
12391 guestMemBalloon, guestMemShared,
12392 guestMemCache, guestPagedTotal);
12393 aCollector->registerBaseMetric(guestCpuMem);
12394
12395 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12396 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12397 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12398 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12399
12400 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12401 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12402 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12403 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12404
12405 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12406 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12407 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12408 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12409
12410 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12411 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12412 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12413 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12414
12415 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12416 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12417 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12418 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12419
12420 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12421 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12422 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12424
12425 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12426 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12427 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12428 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12429
12430 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12431 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12432 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12433 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12434
12435 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12436 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12437 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12438 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12439
12440 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12441 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12442 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12443 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12444
12445 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12446 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12447 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12448 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12449}
12450
12451void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12452{
12453 AssertReturnVoid(isWriteLockOnCurrentThread());
12454
12455 if (aCollector)
12456 {
12457 aCollector->unregisterMetricsFor(aMachine);
12458 aCollector->unregisterBaseMetricsFor(aMachine);
12459 }
12460}
12461
12462#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12463
12464
12465////////////////////////////////////////////////////////////////////////////////
12466
12467DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12468
12469HRESULT SessionMachine::FinalConstruct()
12470{
12471 LogFlowThisFunc(("\n"));
12472
12473 mClientToken = NULL;
12474
12475 return BaseFinalConstruct();
12476}
12477
12478void SessionMachine::FinalRelease()
12479{
12480 LogFlowThisFunc(("\n"));
12481
12482 Assert(!mClientToken);
12483 /* paranoia, should not hang around any more */
12484 if (mClientToken)
12485 {
12486 delete mClientToken;
12487 mClientToken = NULL;
12488 }
12489
12490 uninit(Uninit::Unexpected);
12491
12492 BaseFinalRelease();
12493}
12494
12495/**
12496 * @note Must be called only by Machine::LockMachine() from its own write lock.
12497 */
12498HRESULT SessionMachine::init(Machine *aMachine)
12499{
12500 LogFlowThisFuncEnter();
12501 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12502
12503 AssertReturn(aMachine, E_INVALIDARG);
12504
12505 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12506
12507 /* Enclose the state transition NotReady->InInit->Ready */
12508 AutoInitSpan autoInitSpan(this);
12509 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12510
12511 HRESULT rc = S_OK;
12512
12513 RT_ZERO(mAuthLibCtx);
12514
12515 /* create the machine client token */
12516 try
12517 {
12518 mClientToken = new ClientToken(aMachine, this);
12519 if (!mClientToken->isReady())
12520 {
12521 delete mClientToken;
12522 mClientToken = NULL;
12523 rc = E_FAIL;
12524 }
12525 }
12526 catch (std::bad_alloc &)
12527 {
12528 rc = E_OUTOFMEMORY;
12529 }
12530 if (FAILED(rc))
12531 return rc;
12532
12533 /* memorize the peer Machine */
12534 unconst(mPeer) = aMachine;
12535 /* share the parent pointer */
12536 unconst(mParent) = aMachine->mParent;
12537
12538 /* take the pointers to data to share */
12539 mData.share(aMachine->mData);
12540 mSSData.share(aMachine->mSSData);
12541
12542 mUserData.share(aMachine->mUserData);
12543 mHWData.share(aMachine->mHWData);
12544 mMediumAttachments.share(aMachine->mMediumAttachments);
12545
12546 mStorageControllers.allocate();
12547 for (StorageControllerList::const_iterator
12548 it = aMachine->mStorageControllers->begin();
12549 it != aMachine->mStorageControllers->end();
12550 ++it)
12551 {
12552 ComObjPtr<StorageController> ctl;
12553 ctl.createObject();
12554 ctl->init(this, *it);
12555 mStorageControllers->push_back(ctl);
12556 }
12557
12558 mUSBControllers.allocate();
12559 for (USBControllerList::const_iterator
12560 it = aMachine->mUSBControllers->begin();
12561 it != aMachine->mUSBControllers->end();
12562 ++it)
12563 {
12564 ComObjPtr<USBController> ctl;
12565 ctl.createObject();
12566 ctl->init(this, *it);
12567 mUSBControllers->push_back(ctl);
12568 }
12569
12570 unconst(mBIOSSettings).createObject();
12571 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12572 /* create another VRDEServer object that will be mutable */
12573 unconst(mVRDEServer).createObject();
12574 mVRDEServer->init(this, aMachine->mVRDEServer);
12575 /* create another audio adapter object that will be mutable */
12576 unconst(mAudioAdapter).createObject();
12577 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12578 /* create a list of serial ports that will be mutable */
12579 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12580 {
12581 unconst(mSerialPorts[slot]).createObject();
12582 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12583 }
12584 /* create a list of parallel ports that will be mutable */
12585 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12586 {
12587 unconst(mParallelPorts[slot]).createObject();
12588 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12589 }
12590
12591 /* create another USB device filters object that will be mutable */
12592 unconst(mUSBDeviceFilters).createObject();
12593 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12594
12595 /* create a list of network adapters that will be mutable */
12596 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12597 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12598 {
12599 unconst(mNetworkAdapters[slot]).createObject();
12600 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12601 }
12602
12603 /* create another bandwidth control object that will be mutable */
12604 unconst(mBandwidthControl).createObject();
12605 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12606
12607 /* default is to delete saved state on Saved -> PoweredOff transition */
12608 mRemoveSavedState = true;
12609
12610 /* Confirm a successful initialization when it's the case */
12611 autoInitSpan.setSucceeded();
12612
12613 miNATNetworksStarted = 0;
12614
12615 LogFlowThisFuncLeave();
12616 return rc;
12617}
12618
12619/**
12620 * Uninitializes this session object. If the reason is other than
12621 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12622 * or the client watcher code.
12623 *
12624 * @param aReason uninitialization reason
12625 *
12626 * @note Locks mParent + this object for writing.
12627 */
12628void SessionMachine::uninit(Uninit::Reason aReason)
12629{
12630 LogFlowThisFuncEnter();
12631 LogFlowThisFunc(("reason=%d\n", aReason));
12632
12633 /*
12634 * Strongly reference ourselves to prevent this object deletion after
12635 * mData->mSession.mMachine.setNull() below (which can release the last
12636 * reference and call the destructor). Important: this must be done before
12637 * accessing any members (and before AutoUninitSpan that does it as well).
12638 * This self reference will be released as the very last step on return.
12639 */
12640 ComObjPtr<SessionMachine> selfRef;
12641 if (aReason != Uninit::Unexpected)
12642 selfRef = this;
12643
12644 /* Enclose the state transition Ready->InUninit->NotReady */
12645 AutoUninitSpan autoUninitSpan(this);
12646 if (autoUninitSpan.uninitDone())
12647 {
12648 LogFlowThisFunc(("Already uninitialized\n"));
12649 LogFlowThisFuncLeave();
12650 return;
12651 }
12652
12653 if (autoUninitSpan.initFailed())
12654 {
12655 /* We've been called by init() because it's failed. It's not really
12656 * necessary (nor it's safe) to perform the regular uninit sequence
12657 * below, the following is enough.
12658 */
12659 LogFlowThisFunc(("Initialization failed.\n"));
12660 /* destroy the machine client token */
12661 if (mClientToken)
12662 {
12663 delete mClientToken;
12664 mClientToken = NULL;
12665 }
12666 uninitDataAndChildObjects();
12667 mData.free();
12668 unconst(mParent) = NULL;
12669 unconst(mPeer) = NULL;
12670 LogFlowThisFuncLeave();
12671 return;
12672 }
12673
12674 MachineState_T lastState;
12675 {
12676 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12677 lastState = mData->mMachineState;
12678 }
12679 NOREF(lastState);
12680
12681#ifdef VBOX_WITH_USB
12682 // release all captured USB devices, but do this before requesting the locks below
12683 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12684 {
12685 /* Console::captureUSBDevices() is called in the VM process only after
12686 * setting the machine state to Starting or Restoring.
12687 * Console::detachAllUSBDevices() will be called upon successful
12688 * termination. So, we need to release USB devices only if there was
12689 * an abnormal termination of a running VM.
12690 *
12691 * This is identical to SessionMachine::DetachAllUSBDevices except
12692 * for the aAbnormal argument. */
12693 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12694 AssertComRC(rc);
12695 NOREF(rc);
12696
12697 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12698 if (service)
12699 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12700 }
12701#endif /* VBOX_WITH_USB */
12702
12703 // we need to lock this object in uninit() because the lock is shared
12704 // with mPeer (as well as data we modify below). mParent lock is needed
12705 // by several calls to it.
12706 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12707
12708#ifdef VBOX_WITH_RESOURCE_USAGE_API
12709 /*
12710 * It is safe to call Machine::i_unregisterMetrics() here because
12711 * PerformanceCollector::samplerCallback no longer accesses guest methods
12712 * holding the lock.
12713 */
12714 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12715 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12716 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12717 if (mCollectorGuest)
12718 {
12719 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12720 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12721 mCollectorGuest = NULL;
12722 }
12723#endif
12724
12725 if (aReason == Uninit::Abnormal)
12726 {
12727 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12728
12729 /* reset the state to Aborted */
12730 if (mData->mMachineState != MachineState_Aborted)
12731 i_setMachineState(MachineState_Aborted);
12732 }
12733
12734 // any machine settings modified?
12735 if (mData->flModifications)
12736 {
12737 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12738 i_rollback(false /* aNotify */);
12739 }
12740
12741 mData->mSession.mPID = NIL_RTPROCESS;
12742
12743 if (aReason == Uninit::Unexpected)
12744 {
12745 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12746 * client watcher thread to update the set of machines that have open
12747 * sessions. */
12748 mParent->i_updateClientWatcher();
12749 }
12750
12751 /* uninitialize all remote controls */
12752 if (mData->mSession.mRemoteControls.size())
12753 {
12754 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12755 mData->mSession.mRemoteControls.size()));
12756
12757 /* Always restart a the beginning, since the iterator is invalidated
12758 * by using erase(). */
12759 for (Data::Session::RemoteControlList::iterator
12760 it = mData->mSession.mRemoteControls.begin();
12761 it != mData->mSession.mRemoteControls.end();
12762 it = mData->mSession.mRemoteControls.begin())
12763 {
12764 ComPtr<IInternalSessionControl> pControl = *it;
12765 mData->mSession.mRemoteControls.erase(it);
12766 multilock.release();
12767 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12768 HRESULT rc = pControl->Uninitialize();
12769 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12770 if (FAILED(rc))
12771 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12772 multilock.acquire();
12773 }
12774 mData->mSession.mRemoteControls.clear();
12775 }
12776
12777 /* Remove all references to the NAT network service. The service will stop
12778 * if all references (also from other VMs) are removed. */
12779 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12780 {
12781 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12782 {
12783 BOOL enabled;
12784 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12785 if ( FAILED(hrc)
12786 || !enabled)
12787 continue;
12788
12789 NetworkAttachmentType_T type;
12790 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12791 if ( SUCCEEDED(hrc)
12792 && type == NetworkAttachmentType_NATNetwork)
12793 {
12794 Bstr name;
12795 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12796 if (SUCCEEDED(hrc))
12797 {
12798 multilock.release();
12799 Utf8Str strName(name);
12800 LogRel(("VM '%s' stops using NAT network '%s'\n",
12801 mUserData->s.strName.c_str(), strName.c_str()));
12802 mParent->i_natNetworkRefDec(strName);
12803 multilock.acquire();
12804 }
12805 }
12806 }
12807 }
12808
12809 /*
12810 * An expected uninitialization can come only from #i_checkForDeath().
12811 * Otherwise it means that something's gone really wrong (for example,
12812 * the Session implementation has released the VirtualBox reference
12813 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12814 * etc). However, it's also possible, that the client releases the IPC
12815 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12816 * but the VirtualBox release event comes first to the server process.
12817 * This case is practically possible, so we should not assert on an
12818 * unexpected uninit, just log a warning.
12819 */
12820
12821 if (aReason == Uninit::Unexpected)
12822 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12823
12824 if (aReason != Uninit::Normal)
12825 {
12826 mData->mSession.mDirectControl.setNull();
12827 }
12828 else
12829 {
12830 /* this must be null here (see #OnSessionEnd()) */
12831 Assert(mData->mSession.mDirectControl.isNull());
12832 Assert(mData->mSession.mState == SessionState_Unlocking);
12833 Assert(!mData->mSession.mProgress.isNull());
12834 }
12835 if (mData->mSession.mProgress)
12836 {
12837 if (aReason == Uninit::Normal)
12838 mData->mSession.mProgress->i_notifyComplete(S_OK);
12839 else
12840 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12841 COM_IIDOF(ISession),
12842 getComponentName(),
12843 tr("The VM session was aborted"));
12844 mData->mSession.mProgress.setNull();
12845 }
12846
12847 if (mConsoleTaskData.mProgress)
12848 {
12849 Assert(aReason == Uninit::Abnormal);
12850 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12851 COM_IIDOF(ISession),
12852 getComponentName(),
12853 tr("The VM session was aborted"));
12854 mConsoleTaskData.mProgress.setNull();
12855 }
12856
12857 /* remove the association between the peer machine and this session machine */
12858 Assert( (SessionMachine*)mData->mSession.mMachine == this
12859 || aReason == Uninit::Unexpected);
12860
12861 /* reset the rest of session data */
12862 mData->mSession.mLockType = LockType_Null;
12863 mData->mSession.mMachine.setNull();
12864 mData->mSession.mState = SessionState_Unlocked;
12865 mData->mSession.mName.setNull();
12866
12867 /* destroy the machine client token before leaving the exclusive lock */
12868 if (mClientToken)
12869 {
12870 delete mClientToken;
12871 mClientToken = NULL;
12872 }
12873
12874 /* fire an event */
12875 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12876
12877 uninitDataAndChildObjects();
12878
12879 /* free the essential data structure last */
12880 mData.free();
12881
12882 /* release the exclusive lock before setting the below two to NULL */
12883 multilock.release();
12884
12885 unconst(mParent) = NULL;
12886 unconst(mPeer) = NULL;
12887
12888 AuthLibUnload(&mAuthLibCtx);
12889
12890 LogFlowThisFuncLeave();
12891}
12892
12893// util::Lockable interface
12894////////////////////////////////////////////////////////////////////////////////
12895
12896/**
12897 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12898 * with the primary Machine instance (mPeer).
12899 */
12900RWLockHandle *SessionMachine::lockHandle() const
12901{
12902 AssertReturn(mPeer != NULL, NULL);
12903 return mPeer->lockHandle();
12904}
12905
12906// IInternalMachineControl methods
12907////////////////////////////////////////////////////////////////////////////////
12908
12909/**
12910 * Passes collected guest statistics to performance collector object
12911 */
12912HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12913 ULONG aCpuKernel, ULONG aCpuIdle,
12914 ULONG aMemTotal, ULONG aMemFree,
12915 ULONG aMemBalloon, ULONG aMemShared,
12916 ULONG aMemCache, ULONG aPageTotal,
12917 ULONG aAllocVMM, ULONG aFreeVMM,
12918 ULONG aBalloonedVMM, ULONG aSharedVMM,
12919 ULONG aVmNetRx, ULONG aVmNetTx)
12920{
12921#ifdef VBOX_WITH_RESOURCE_USAGE_API
12922 if (mCollectorGuest)
12923 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12924 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12925 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12926 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12927
12928 return S_OK;
12929#else
12930 NOREF(aValidStats);
12931 NOREF(aCpuUser);
12932 NOREF(aCpuKernel);
12933 NOREF(aCpuIdle);
12934 NOREF(aMemTotal);
12935 NOREF(aMemFree);
12936 NOREF(aMemBalloon);
12937 NOREF(aMemShared);
12938 NOREF(aMemCache);
12939 NOREF(aPageTotal);
12940 NOREF(aAllocVMM);
12941 NOREF(aFreeVMM);
12942 NOREF(aBalloonedVMM);
12943 NOREF(aSharedVMM);
12944 NOREF(aVmNetRx);
12945 NOREF(aVmNetTx);
12946 return E_NOTIMPL;
12947#endif
12948}
12949
12950////////////////////////////////////////////////////////////////////////////////
12951//
12952// SessionMachine task records
12953//
12954////////////////////////////////////////////////////////////////////////////////
12955
12956/**
12957 * Task record for saving the machine state.
12958 */
12959class SessionMachine::SaveStateTask
12960 : public Machine::Task
12961{
12962public:
12963 SaveStateTask(SessionMachine *m,
12964 Progress *p,
12965 const Utf8Str &t,
12966 Reason_T enmReason,
12967 const Utf8Str &strStateFilePath)
12968 : Task(m, p, t),
12969 m_enmReason(enmReason),
12970 m_strStateFilePath(strStateFilePath)
12971 {}
12972
12973private:
12974 void handler()
12975 {
12976 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12977 }
12978
12979 Reason_T m_enmReason;
12980 Utf8Str m_strStateFilePath;
12981
12982 friend class SessionMachine;
12983};
12984
12985/**
12986 * Task thread implementation for SessionMachine::SaveState(), called from
12987 * SessionMachine::taskHandler().
12988 *
12989 * @note Locks this object for writing.
12990 *
12991 * @param task
12992 * @return
12993 */
12994void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12995{
12996 LogFlowThisFuncEnter();
12997
12998 AutoCaller autoCaller(this);
12999 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13000 if (FAILED(autoCaller.rc()))
13001 {
13002 /* we might have been uninitialized because the session was accidentally
13003 * closed by the client, so don't assert */
13004 HRESULT rc = setError(E_FAIL,
13005 tr("The session has been accidentally closed"));
13006 task.m_pProgress->i_notifyComplete(rc);
13007 LogFlowThisFuncLeave();
13008 return;
13009 }
13010
13011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13012
13013 HRESULT rc = S_OK;
13014
13015 try
13016 {
13017 ComPtr<IInternalSessionControl> directControl;
13018 if (mData->mSession.mLockType == LockType_VM)
13019 directControl = mData->mSession.mDirectControl;
13020 if (directControl.isNull())
13021 throw setError(VBOX_E_INVALID_VM_STATE,
13022 tr("Trying to save state without a running VM"));
13023 alock.release();
13024 BOOL fSuspendedBySave;
13025 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13026 Assert(!fSuspendedBySave);
13027 alock.acquire();
13028
13029 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13030 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13031 throw E_FAIL);
13032
13033 if (SUCCEEDED(rc))
13034 {
13035 mSSData->strStateFilePath = task.m_strStateFilePath;
13036
13037 /* save all VM settings */
13038 rc = i_saveSettings(NULL);
13039 // no need to check whether VirtualBox.xml needs saving also since
13040 // we can't have a name change pending at this point
13041 }
13042 else
13043 {
13044 // On failure, set the state to the state we had at the beginning.
13045 i_setMachineState(task.m_machineStateBackup);
13046 i_updateMachineStateOnClient();
13047
13048 // Delete the saved state file (might have been already created).
13049 // No need to check whether this is shared with a snapshot here
13050 // because we certainly created a fresh saved state file here.
13051 RTFileDelete(task.m_strStateFilePath.c_str());
13052 }
13053 }
13054 catch (HRESULT aRC) { rc = aRC; }
13055
13056 task.m_pProgress->i_notifyComplete(rc);
13057
13058 LogFlowThisFuncLeave();
13059}
13060
13061/**
13062 * @note Locks this object for writing.
13063 */
13064HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13065{
13066 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13067}
13068
13069HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13070{
13071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13072
13073 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13074 if (FAILED(rc)) return rc;
13075
13076 if ( mData->mMachineState != MachineState_Running
13077 && mData->mMachineState != MachineState_Paused
13078 )
13079 return setError(VBOX_E_INVALID_VM_STATE,
13080 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13081 Global::stringifyMachineState(mData->mMachineState));
13082
13083 ComObjPtr<Progress> pProgress;
13084 pProgress.createObject();
13085 rc = pProgress->init(i_getVirtualBox(),
13086 static_cast<IMachine *>(this) /* aInitiator */,
13087 tr("Saving the execution state of the virtual machine"),
13088 FALSE /* aCancelable */);
13089 if (FAILED(rc))
13090 return rc;
13091
13092 Utf8Str strStateFilePath;
13093 i_composeSavedStateFilename(strStateFilePath);
13094
13095 /* create and start the task on a separate thread (note that it will not
13096 * start working until we release alock) */
13097 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13098 rc = pTask->createThread();
13099 if (FAILED(rc))
13100 return rc;
13101
13102 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13103 i_setMachineState(MachineState_Saving);
13104 i_updateMachineStateOnClient();
13105
13106 pProgress.queryInterfaceTo(aProgress.asOutParam());
13107
13108 return S_OK;
13109}
13110
13111/**
13112 * @note Locks this object for writing.
13113 */
13114HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13115{
13116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13117
13118 HRESULT rc = i_checkStateDependency(MutableStateDep);
13119 if (FAILED(rc)) return rc;
13120
13121 if ( mData->mMachineState != MachineState_PoweredOff
13122 && mData->mMachineState != MachineState_Teleported
13123 && mData->mMachineState != MachineState_Aborted
13124 )
13125 return setError(VBOX_E_INVALID_VM_STATE,
13126 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13127 Global::stringifyMachineState(mData->mMachineState));
13128
13129 com::Utf8Str stateFilePathFull;
13130 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13131 if (RT_FAILURE(vrc))
13132 return setError(VBOX_E_FILE_ERROR,
13133 tr("Invalid saved state file path '%s' (%Rrc)"),
13134 aSavedStateFile.c_str(),
13135 vrc);
13136
13137 mSSData->strStateFilePath = stateFilePathFull;
13138
13139 /* The below i_setMachineState() will detect the state transition and will
13140 * update the settings file */
13141
13142 return i_setMachineState(MachineState_Saved);
13143}
13144
13145/**
13146 * @note Locks this object for writing.
13147 */
13148HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13149{
13150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13151
13152 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13153 if (FAILED(rc)) return rc;
13154
13155 if (mData->mMachineState != MachineState_Saved)
13156 return setError(VBOX_E_INVALID_VM_STATE,
13157 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13158 Global::stringifyMachineState(mData->mMachineState));
13159
13160 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13161
13162 /*
13163 * Saved -> PoweredOff transition will be detected in the SessionMachine
13164 * and properly handled.
13165 */
13166 rc = i_setMachineState(MachineState_PoweredOff);
13167 return rc;
13168}
13169
13170
13171/**
13172 * @note Locks the same as #i_setMachineState() does.
13173 */
13174HRESULT SessionMachine::updateState(MachineState_T aState)
13175{
13176 return i_setMachineState(aState);
13177}
13178
13179/**
13180 * @note Locks this object for writing.
13181 */
13182HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13183{
13184 IProgress *pProgress(aProgress);
13185
13186 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13187
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 if (mData->mSession.mState != SessionState_Locked)
13191 return VBOX_E_INVALID_OBJECT_STATE;
13192
13193 if (!mData->mSession.mProgress.isNull())
13194 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13195
13196 /* If we didn't reference the NAT network service yet, add a reference to
13197 * force a start */
13198 if (miNATNetworksStarted < 1)
13199 {
13200 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13201 {
13202 BOOL enabled;
13203 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13204 if ( FAILED(hrc)
13205 || !enabled)
13206 continue;
13207
13208 NetworkAttachmentType_T type;
13209 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13210 if ( SUCCEEDED(hrc)
13211 && type == NetworkAttachmentType_NATNetwork)
13212 {
13213 Bstr name;
13214 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13215 if (SUCCEEDED(hrc))
13216 {
13217 Utf8Str strName(name);
13218 LogRel(("VM '%s' starts using NAT network '%s'\n",
13219 mUserData->s.strName.c_str(), strName.c_str()));
13220 mPeer->lockHandle()->unlockWrite();
13221 mParent->i_natNetworkRefInc(strName);
13222#ifdef RT_LOCK_STRICT
13223 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13224#else
13225 mPeer->lockHandle()->lockWrite();
13226#endif
13227 }
13228 }
13229 }
13230 miNATNetworksStarted++;
13231 }
13232
13233 LogFlowThisFunc(("returns S_OK.\n"));
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks this object for writing.
13239 */
13240HRESULT SessionMachine::endPowerUp(LONG aResult)
13241{
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 if (mData->mSession.mState != SessionState_Locked)
13245 return VBOX_E_INVALID_OBJECT_STATE;
13246
13247 /* Finalize the LaunchVMProcess progress object. */
13248 if (mData->mSession.mProgress)
13249 {
13250 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13251 mData->mSession.mProgress.setNull();
13252 }
13253
13254 if (SUCCEEDED((HRESULT)aResult))
13255 {
13256#ifdef VBOX_WITH_RESOURCE_USAGE_API
13257 /* The VM has been powered up successfully, so it makes sense
13258 * now to offer the performance metrics for a running machine
13259 * object. Doing it earlier wouldn't be safe. */
13260 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13261 mData->mSession.mPID);
13262#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13263 }
13264
13265 return S_OK;
13266}
13267
13268/**
13269 * @note Locks this object for writing.
13270 */
13271HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13272{
13273 LogFlowThisFuncEnter();
13274
13275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13276
13277 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13278 E_FAIL);
13279
13280 /* create a progress object to track operation completion */
13281 ComObjPtr<Progress> pProgress;
13282 pProgress.createObject();
13283 pProgress->init(i_getVirtualBox(),
13284 static_cast<IMachine *>(this) /* aInitiator */,
13285 tr("Stopping the virtual machine"),
13286 FALSE /* aCancelable */);
13287
13288 /* fill in the console task data */
13289 mConsoleTaskData.mLastState = mData->mMachineState;
13290 mConsoleTaskData.mProgress = pProgress;
13291
13292 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13293 i_setMachineState(MachineState_Stopping);
13294
13295 pProgress.queryInterfaceTo(aProgress.asOutParam());
13296
13297 return S_OK;
13298}
13299
13300/**
13301 * @note Locks this object for writing.
13302 */
13303HRESULT SessionMachine::endPoweringDown(LONG aResult,
13304 const com::Utf8Str &aErrMsg)
13305{
13306 LogFlowThisFuncEnter();
13307
13308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13309
13310 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13311 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13312 && mConsoleTaskData.mLastState != MachineState_Null,
13313 E_FAIL);
13314
13315 /*
13316 * On failure, set the state to the state we had when BeginPoweringDown()
13317 * was called (this is expected by Console::PowerDown() and the associated
13318 * task). On success the VM process already changed the state to
13319 * MachineState_PoweredOff, so no need to do anything.
13320 */
13321 if (FAILED(aResult))
13322 i_setMachineState(mConsoleTaskData.mLastState);
13323
13324 /* notify the progress object about operation completion */
13325 Assert(mConsoleTaskData.mProgress);
13326 if (SUCCEEDED(aResult))
13327 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13328 else
13329 {
13330 if (aErrMsg.length())
13331 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13332 COM_IIDOF(ISession),
13333 getComponentName(),
13334 aErrMsg.c_str());
13335 else
13336 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13337 }
13338
13339 /* clear out the temporary saved state data */
13340 mConsoleTaskData.mLastState = MachineState_Null;
13341 mConsoleTaskData.mProgress.setNull();
13342
13343 LogFlowThisFuncLeave();
13344 return S_OK;
13345}
13346
13347
13348/**
13349 * Goes through the USB filters of the given machine to see if the given
13350 * device matches any filter or not.
13351 *
13352 * @note Locks the same as USBController::hasMatchingFilter() does.
13353 */
13354HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13355 BOOL *aMatched,
13356 ULONG *aMaskedInterfaces)
13357{
13358 LogFlowThisFunc(("\n"));
13359
13360#ifdef VBOX_WITH_USB
13361 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13362#else
13363 NOREF(aDevice);
13364 NOREF(aMaskedInterfaces);
13365 *aMatched = FALSE;
13366#endif
13367
13368 return S_OK;
13369}
13370
13371/**
13372 * @note Locks the same as Host::captureUSBDevice() does.
13373 */
13374HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378#ifdef VBOX_WITH_USB
13379 /* if captureDeviceForVM() fails, it must have set extended error info */
13380 clearError();
13381 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13382 if (FAILED(rc)) return rc;
13383
13384 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13385 AssertReturn(service, E_FAIL);
13386 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13387#else
13388 NOREF(aId);
13389 return E_NOTIMPL;
13390#endif
13391}
13392
13393/**
13394 * @note Locks the same as Host::detachUSBDevice() does.
13395 */
13396HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13397 BOOL aDone)
13398{
13399 LogFlowThisFunc(("\n"));
13400
13401#ifdef VBOX_WITH_USB
13402 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13403 AssertReturn(service, E_FAIL);
13404 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13405#else
13406 NOREF(aId);
13407 NOREF(aDone);
13408 return E_NOTIMPL;
13409#endif
13410}
13411
13412/**
13413 * Inserts all machine filters to the USB proxy service and then calls
13414 * Host::autoCaptureUSBDevices().
13415 *
13416 * Called by Console from the VM process upon VM startup.
13417 *
13418 * @note Locks what called methods lock.
13419 */
13420HRESULT SessionMachine::autoCaptureUSBDevices()
13421{
13422 LogFlowThisFunc(("\n"));
13423
13424#ifdef VBOX_WITH_USB
13425 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13426 AssertComRC(rc);
13427 NOREF(rc);
13428
13429 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13430 AssertReturn(service, E_FAIL);
13431 return service->autoCaptureDevicesForVM(this);
13432#else
13433 return S_OK;
13434#endif
13435}
13436
13437/**
13438 * Removes all machine filters from the USB proxy service and then calls
13439 * Host::detachAllUSBDevices().
13440 *
13441 * Called by Console from the VM process upon normal VM termination or by
13442 * SessionMachine::uninit() upon abnormal VM termination (from under the
13443 * Machine/SessionMachine lock).
13444 *
13445 * @note Locks what called methods lock.
13446 */
13447HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13448{
13449 LogFlowThisFunc(("\n"));
13450
13451#ifdef VBOX_WITH_USB
13452 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13453 AssertComRC(rc);
13454 NOREF(rc);
13455
13456 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13457 AssertReturn(service, E_FAIL);
13458 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13459#else
13460 NOREF(aDone);
13461 return S_OK;
13462#endif
13463}
13464
13465/**
13466 * @note Locks this object for writing.
13467 */
13468HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13469 ComPtr<IProgress> &aProgress)
13470{
13471 LogFlowThisFuncEnter();
13472
13473 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13474 /*
13475 * We don't assert below because it might happen that a non-direct session
13476 * informs us it is closed right after we've been uninitialized -- it's ok.
13477 */
13478
13479 /* get IInternalSessionControl interface */
13480 ComPtr<IInternalSessionControl> control(aSession);
13481
13482 ComAssertRet(!control.isNull(), E_INVALIDARG);
13483
13484 /* Creating a Progress object requires the VirtualBox lock, and
13485 * thus locking it here is required by the lock order rules. */
13486 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13487
13488 if (control == mData->mSession.mDirectControl)
13489 {
13490 /* The direct session is being normally closed by the client process
13491 * ----------------------------------------------------------------- */
13492
13493 /* go to the closing state (essential for all open*Session() calls and
13494 * for #i_checkForDeath()) */
13495 Assert(mData->mSession.mState == SessionState_Locked);
13496 mData->mSession.mState = SessionState_Unlocking;
13497
13498 /* set direct control to NULL to release the remote instance */
13499 mData->mSession.mDirectControl.setNull();
13500 LogFlowThisFunc(("Direct control is set to NULL\n"));
13501
13502 if (mData->mSession.mProgress)
13503 {
13504 /* finalize the progress, someone might wait if a frontend
13505 * closes the session before powering on the VM. */
13506 mData->mSession.mProgress->notifyComplete(E_FAIL,
13507 COM_IIDOF(ISession),
13508 getComponentName(),
13509 tr("The VM session was closed before any attempt to power it on"));
13510 mData->mSession.mProgress.setNull();
13511 }
13512
13513 /* Create the progress object the client will use to wait until
13514 * #i_checkForDeath() is called to uninitialize this session object after
13515 * it releases the IPC semaphore.
13516 * Note! Because we're "reusing" mProgress here, this must be a proxy
13517 * object just like for LaunchVMProcess. */
13518 Assert(mData->mSession.mProgress.isNull());
13519 ComObjPtr<ProgressProxy> progress;
13520 progress.createObject();
13521 ComPtr<IUnknown> pPeer(mPeer);
13522 progress->init(mParent, pPeer,
13523 Bstr(tr("Closing session")).raw(),
13524 FALSE /* aCancelable */);
13525 progress.queryInterfaceTo(aProgress.asOutParam());
13526 mData->mSession.mProgress = progress;
13527 }
13528 else
13529 {
13530 /* the remote session is being normally closed */
13531 bool found = false;
13532 for (Data::Session::RemoteControlList::iterator
13533 it = mData->mSession.mRemoteControls.begin();
13534 it != mData->mSession.mRemoteControls.end();
13535 ++it)
13536 {
13537 if (control == *it)
13538 {
13539 found = true;
13540 // This MUST be erase(it), not remove(*it) as the latter
13541 // triggers a very nasty use after free due to the place where
13542 // the value "lives".
13543 mData->mSession.mRemoteControls.erase(it);
13544 break;
13545 }
13546 }
13547 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13548 E_INVALIDARG);
13549 }
13550
13551 /* signal the client watcher thread, because the client is going away */
13552 mParent->i_updateClientWatcher();
13553
13554 LogFlowThisFuncLeave();
13555 return S_OK;
13556}
13557
13558HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13559 std::vector<com::Utf8Str> &aValues,
13560 std::vector<LONG64> &aTimestamps,
13561 std::vector<com::Utf8Str> &aFlags)
13562{
13563 LogFlowThisFunc(("\n"));
13564
13565#ifdef VBOX_WITH_GUEST_PROPS
13566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13567
13568 size_t cEntries = mHWData->mGuestProperties.size();
13569 aNames.resize(cEntries);
13570 aValues.resize(cEntries);
13571 aTimestamps.resize(cEntries);
13572 aFlags.resize(cEntries);
13573
13574 size_t i = 0;
13575 for (HWData::GuestPropertyMap::const_iterator
13576 it = mHWData->mGuestProperties.begin();
13577 it != mHWData->mGuestProperties.end();
13578 ++it, ++i)
13579 {
13580 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13581 aNames[i] = it->first;
13582 aValues[i] = it->second.strValue;
13583 aTimestamps[i] = it->second.mTimestamp;
13584
13585 /* If it is NULL, keep it NULL. */
13586 if (it->second.mFlags)
13587 {
13588 GuestPropWriteFlags(it->second.mFlags, szFlags);
13589 aFlags[i] = szFlags;
13590 }
13591 else
13592 aFlags[i] = "";
13593 }
13594 return S_OK;
13595#else
13596 ReturnComNotImplemented();
13597#endif
13598}
13599
13600HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13601 const com::Utf8Str &aValue,
13602 LONG64 aTimestamp,
13603 const com::Utf8Str &aFlags)
13604{
13605 LogFlowThisFunc(("\n"));
13606
13607#ifdef VBOX_WITH_GUEST_PROPS
13608 try
13609 {
13610 /*
13611 * Convert input up front.
13612 */
13613 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13614 if (aFlags.length())
13615 {
13616 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13617 AssertRCReturn(vrc, E_INVALIDARG);
13618 }
13619
13620 /*
13621 * Now grab the object lock, validate the state and do the update.
13622 */
13623
13624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13625
13626 if (!Global::IsOnline(mData->mMachineState))
13627 {
13628 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13629 VBOX_E_INVALID_VM_STATE);
13630 }
13631
13632 i_setModified(IsModified_MachineData);
13633 mHWData.backup();
13634
13635 bool fDelete = !aValue.length();
13636 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13637 if (it != mHWData->mGuestProperties.end())
13638 {
13639 if (!fDelete)
13640 {
13641 it->second.strValue = aValue;
13642 it->second.mTimestamp = aTimestamp;
13643 it->second.mFlags = fFlags;
13644 }
13645 else
13646 mHWData->mGuestProperties.erase(it);
13647
13648 mData->mGuestPropertiesModified = TRUE;
13649 }
13650 else if (!fDelete)
13651 {
13652 HWData::GuestProperty prop;
13653 prop.strValue = aValue;
13654 prop.mTimestamp = aTimestamp;
13655 prop.mFlags = fFlags;
13656
13657 mHWData->mGuestProperties[aName] = prop;
13658 mData->mGuestPropertiesModified = TRUE;
13659 }
13660
13661 alock.release();
13662
13663 mParent->i_onGuestPropertyChange(mData->mUuid,
13664 Bstr(aName).raw(),
13665 Bstr(aValue).raw(),
13666 Bstr(aFlags).raw());
13667 }
13668 catch (...)
13669 {
13670 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13671 }
13672 return S_OK;
13673#else
13674 ReturnComNotImplemented();
13675#endif
13676}
13677
13678
13679HRESULT SessionMachine::lockMedia()
13680{
13681 AutoMultiWriteLock2 alock(this->lockHandle(),
13682 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13683
13684 AssertReturn( mData->mMachineState == MachineState_Starting
13685 || mData->mMachineState == MachineState_Restoring
13686 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13687
13688 clearError();
13689 alock.release();
13690 return i_lockMedia();
13691}
13692
13693HRESULT SessionMachine::unlockMedia()
13694{
13695 HRESULT hrc = i_unlockMedia();
13696 return hrc;
13697}
13698
13699HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13700 ComPtr<IMediumAttachment> &aNewAttachment)
13701{
13702 // request the host lock first, since might be calling Host methods for getting host drives;
13703 // next, protect the media tree all the while we're in here, as well as our member variables
13704 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13705 this->lockHandle(),
13706 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13707
13708 IMediumAttachment *iAttach = aAttachment;
13709 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13710
13711 Utf8Str ctrlName;
13712 LONG lPort;
13713 LONG lDevice;
13714 bool fTempEject;
13715 {
13716 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13717
13718 /* Need to query the details first, as the IMediumAttachment reference
13719 * might be to the original settings, which we are going to change. */
13720 ctrlName = pAttach->i_getControllerName();
13721 lPort = pAttach->i_getPort();
13722 lDevice = pAttach->i_getDevice();
13723 fTempEject = pAttach->i_getTempEject();
13724 }
13725
13726 if (!fTempEject)
13727 {
13728 /* Remember previously mounted medium. The medium before taking the
13729 * backup is not necessarily the same thing. */
13730 ComObjPtr<Medium> oldmedium;
13731 oldmedium = pAttach->i_getMedium();
13732
13733 i_setModified(IsModified_Storage);
13734 mMediumAttachments.backup();
13735
13736 // The backup operation makes the pAttach reference point to the
13737 // old settings. Re-get the correct reference.
13738 pAttach = i_findAttachment(*mMediumAttachments.data(),
13739 ctrlName,
13740 lPort,
13741 lDevice);
13742
13743 {
13744 AutoCaller autoAttachCaller(this);
13745 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13746
13747 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13748 if (!oldmedium.isNull())
13749 oldmedium->i_removeBackReference(mData->mUuid);
13750
13751 pAttach->i_updateMedium(NULL);
13752 pAttach->i_updateEjected();
13753 }
13754
13755 i_setModified(IsModified_Storage);
13756 }
13757 else
13758 {
13759 {
13760 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13761 pAttach->i_updateEjected();
13762 }
13763 }
13764
13765 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13766
13767 return S_OK;
13768}
13769
13770HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13771 com::Utf8Str &aResult)
13772{
13773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13774
13775 HRESULT hr = S_OK;
13776
13777 if (!mAuthLibCtx.hAuthLibrary)
13778 {
13779 /* Load the external authentication library. */
13780 Bstr authLibrary;
13781 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13782
13783 Utf8Str filename = authLibrary;
13784
13785 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13786 if (RT_FAILURE(rc))
13787 {
13788 hr = setError(E_FAIL,
13789 tr("Could not load the external authentication library '%s' (%Rrc)"),
13790 filename.c_str(), rc);
13791 }
13792 }
13793
13794 /* The auth library might need the machine lock. */
13795 alock.release();
13796
13797 if (FAILED(hr))
13798 return hr;
13799
13800 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13801 {
13802 enum VRDEAuthParams
13803 {
13804 parmUuid = 1,
13805 parmGuestJudgement,
13806 parmUser,
13807 parmPassword,
13808 parmDomain,
13809 parmClientId
13810 };
13811
13812 AuthResult result = AuthResultAccessDenied;
13813
13814 Guid uuid(aAuthParams[parmUuid]);
13815 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13816 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13817
13818 result = AuthLibAuthenticate(&mAuthLibCtx,
13819 uuid.raw(), guestJudgement,
13820 aAuthParams[parmUser].c_str(),
13821 aAuthParams[parmPassword].c_str(),
13822 aAuthParams[parmDomain].c_str(),
13823 u32ClientId);
13824
13825 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13826 size_t cbPassword = aAuthParams[parmPassword].length();
13827 if (cbPassword)
13828 {
13829 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13830 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13831 }
13832
13833 if (result == AuthResultAccessGranted)
13834 aResult = "granted";
13835 else
13836 aResult = "denied";
13837
13838 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13839 aAuthParams[parmUser].c_str(), aResult.c_str()));
13840 }
13841 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13842 {
13843 enum VRDEAuthDisconnectParams
13844 {
13845 parmUuid = 1,
13846 parmClientId
13847 };
13848
13849 Guid uuid(aAuthParams[parmUuid]);
13850 uint32_t u32ClientId = 0;
13851 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13852 }
13853 else
13854 {
13855 hr = E_INVALIDARG;
13856 }
13857
13858 return hr;
13859}
13860
13861// public methods only for internal purposes
13862/////////////////////////////////////////////////////////////////////////////
13863
13864#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13865/**
13866 * Called from the client watcher thread to check for expected or unexpected
13867 * death of the client process that has a direct session to this machine.
13868 *
13869 * On Win32 and on OS/2, this method is called only when we've got the
13870 * mutex (i.e. the client has either died or terminated normally) so it always
13871 * returns @c true (the client is terminated, the session machine is
13872 * uninitialized).
13873 *
13874 * On other platforms, the method returns @c true if the client process has
13875 * terminated normally or abnormally and the session machine was uninitialized,
13876 * and @c false if the client process is still alive.
13877 *
13878 * @note Locks this object for writing.
13879 */
13880bool SessionMachine::i_checkForDeath()
13881{
13882 Uninit::Reason reason;
13883 bool terminated = false;
13884
13885 /* Enclose autoCaller with a block because calling uninit() from under it
13886 * will deadlock. */
13887 {
13888 AutoCaller autoCaller(this);
13889 if (!autoCaller.isOk())
13890 {
13891 /* return true if not ready, to cause the client watcher to exclude
13892 * the corresponding session from watching */
13893 LogFlowThisFunc(("Already uninitialized!\n"));
13894 return true;
13895 }
13896
13897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13898
13899 /* Determine the reason of death: if the session state is Closing here,
13900 * everything is fine. Otherwise it means that the client did not call
13901 * OnSessionEnd() before it released the IPC semaphore. This may happen
13902 * either because the client process has abnormally terminated, or
13903 * because it simply forgot to call ISession::Close() before exiting. We
13904 * threat the latter also as an abnormal termination (see
13905 * Session::uninit() for details). */
13906 reason = mData->mSession.mState == SessionState_Unlocking ?
13907 Uninit::Normal :
13908 Uninit::Abnormal;
13909
13910 if (mClientToken)
13911 terminated = mClientToken->release();
13912 } /* AutoCaller block */
13913
13914 if (terminated)
13915 uninit(reason);
13916
13917 return terminated;
13918}
13919
13920void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13921{
13922 LogFlowThisFunc(("\n"));
13923
13924 strTokenId.setNull();
13925
13926 AutoCaller autoCaller(this);
13927 AssertComRCReturnVoid(autoCaller.rc());
13928
13929 Assert(mClientToken);
13930 if (mClientToken)
13931 mClientToken->getId(strTokenId);
13932}
13933#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13934IToken *SessionMachine::i_getToken()
13935{
13936 LogFlowThisFunc(("\n"));
13937
13938 AutoCaller autoCaller(this);
13939 AssertComRCReturn(autoCaller.rc(), NULL);
13940
13941 Assert(mClientToken);
13942 if (mClientToken)
13943 return mClientToken->getToken();
13944 else
13945 return NULL;
13946}
13947#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13948
13949Machine::ClientToken *SessionMachine::i_getClientToken()
13950{
13951 LogFlowThisFunc(("\n"));
13952
13953 AutoCaller autoCaller(this);
13954 AssertComRCReturn(autoCaller.rc(), NULL);
13955
13956 return mClientToken;
13957}
13958
13959
13960/**
13961 * @note Locks this object for reading.
13962 */
13963HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13964{
13965 LogFlowThisFunc(("\n"));
13966
13967 AutoCaller autoCaller(this);
13968 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13969
13970 ComPtr<IInternalSessionControl> directControl;
13971 {
13972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13973 if (mData->mSession.mLockType == LockType_VM)
13974 directControl = mData->mSession.mDirectControl;
13975 }
13976
13977 /* ignore notifications sent after #OnSessionEnd() is called */
13978 if (!directControl)
13979 return S_OK;
13980
13981 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13982}
13983
13984/**
13985 * @note Locks this object for reading.
13986 */
13987HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13988 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13989 IN_BSTR aGuestIp, LONG aGuestPort)
13990{
13991 LogFlowThisFunc(("\n"));
13992
13993 AutoCaller autoCaller(this);
13994 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13995
13996 ComPtr<IInternalSessionControl> directControl;
13997 {
13998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13999 if (mData->mSession.mLockType == LockType_VM)
14000 directControl = mData->mSession.mDirectControl;
14001 }
14002
14003 /* ignore notifications sent after #OnSessionEnd() is called */
14004 if (!directControl)
14005 return S_OK;
14006 /*
14007 * instead acting like callback we ask IVirtualBox deliver corresponding event
14008 */
14009
14010 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14011 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14012 return S_OK;
14013}
14014
14015/**
14016 * @note Locks this object for reading.
14017 */
14018HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14019{
14020 LogFlowThisFunc(("\n"));
14021
14022 AutoCaller autoCaller(this);
14023 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14024
14025 ComPtr<IInternalSessionControl> directControl;
14026 {
14027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14028 if (mData->mSession.mLockType == LockType_VM)
14029 directControl = mData->mSession.mDirectControl;
14030 }
14031
14032 /* ignore notifications sent after #OnSessionEnd() is called */
14033 if (!directControl)
14034 return S_OK;
14035
14036 return directControl->OnAudioAdapterChange(audioAdapter);
14037}
14038
14039/**
14040 * @note Locks this object for reading.
14041 */
14042HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14043{
14044 LogFlowThisFunc(("\n"));
14045
14046 AutoCaller autoCaller(this);
14047 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14048
14049 ComPtr<IInternalSessionControl> directControl;
14050 {
14051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14052 if (mData->mSession.mLockType == LockType_VM)
14053 directControl = mData->mSession.mDirectControl;
14054 }
14055
14056 /* ignore notifications sent after #OnSessionEnd() is called */
14057 if (!directControl)
14058 return S_OK;
14059
14060 return directControl->OnSerialPortChange(serialPort);
14061}
14062
14063/**
14064 * @note Locks this object for reading.
14065 */
14066HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14067{
14068 LogFlowThisFunc(("\n"));
14069
14070 AutoCaller autoCaller(this);
14071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14072
14073 ComPtr<IInternalSessionControl> directControl;
14074 {
14075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14076 if (mData->mSession.mLockType == LockType_VM)
14077 directControl = mData->mSession.mDirectControl;
14078 }
14079
14080 /* ignore notifications sent after #OnSessionEnd() is called */
14081 if (!directControl)
14082 return S_OK;
14083
14084 return directControl->OnParallelPortChange(parallelPort);
14085}
14086
14087/**
14088 * @note Locks this object for reading.
14089 */
14090HRESULT SessionMachine::i_onStorageControllerChange()
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14096
14097 ComPtr<IInternalSessionControl> directControl;
14098 {
14099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14100 if (mData->mSession.mLockType == LockType_VM)
14101 directControl = mData->mSession.mDirectControl;
14102 }
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnStorageControllerChange();
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnMediumChange(aAttachment, aForce);
14133}
14134
14135/**
14136 * @note Locks this object for reading.
14137 */
14138HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnCPUChange(aCPU, aRemove);
14157}
14158
14159HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163 AutoCaller autoCaller(this);
14164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14165
14166 ComPtr<IInternalSessionControl> directControl;
14167 {
14168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14169 if (mData->mSession.mLockType == LockType_VM)
14170 directControl = mData->mSession.mDirectControl;
14171 }
14172
14173 /* ignore notifications sent after #OnSessionEnd() is called */
14174 if (!directControl)
14175 return S_OK;
14176
14177 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14178}
14179
14180/**
14181 * @note Locks this object for reading.
14182 */
14183HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14184{
14185 LogFlowThisFunc(("\n"));
14186
14187 AutoCaller autoCaller(this);
14188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14189
14190 ComPtr<IInternalSessionControl> directControl;
14191 {
14192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14193 if (mData->mSession.mLockType == LockType_VM)
14194 directControl = mData->mSession.mDirectControl;
14195 }
14196
14197 /* ignore notifications sent after #OnSessionEnd() is called */
14198 if (!directControl)
14199 return S_OK;
14200
14201 return directControl->OnVRDEServerChange(aRestart);
14202}
14203
14204/**
14205 * @note Locks this object for reading.
14206 */
14207HRESULT SessionMachine::i_onVideoCaptureChange()
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14213
14214 ComPtr<IInternalSessionControl> directControl;
14215 {
14216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14217 if (mData->mSession.mLockType == LockType_VM)
14218 directControl = mData->mSession.mDirectControl;
14219 }
14220
14221 /* ignore notifications sent after #OnSessionEnd() is called */
14222 if (!directControl)
14223 return S_OK;
14224
14225 return directControl->OnVideoCaptureChange();
14226}
14227
14228/**
14229 * @note Locks this object for reading.
14230 */
14231HRESULT SessionMachine::i_onUSBControllerChange()
14232{
14233 LogFlowThisFunc(("\n"));
14234
14235 AutoCaller autoCaller(this);
14236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14237
14238 ComPtr<IInternalSessionControl> directControl;
14239 {
14240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14241 if (mData->mSession.mLockType == LockType_VM)
14242 directControl = mData->mSession.mDirectControl;
14243 }
14244
14245 /* ignore notifications sent after #OnSessionEnd() is called */
14246 if (!directControl)
14247 return S_OK;
14248
14249 return directControl->OnUSBControllerChange();
14250}
14251
14252/**
14253 * @note Locks this object for reading.
14254 */
14255HRESULT SessionMachine::i_onSharedFolderChange()
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturnRC(autoCaller.rc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14265 if (mData->mSession.mLockType == LockType_VM)
14266 directControl = mData->mSession.mDirectControl;
14267 }
14268
14269 /* ignore notifications sent after #OnSessionEnd() is called */
14270 if (!directControl)
14271 return S_OK;
14272
14273 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14274}
14275
14276/**
14277 * @note Locks this object for reading.
14278 */
14279HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14280{
14281 LogFlowThisFunc(("\n"));
14282
14283 AutoCaller autoCaller(this);
14284 AssertComRCReturnRC(autoCaller.rc());
14285
14286 ComPtr<IInternalSessionControl> directControl;
14287 {
14288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14289 if (mData->mSession.mLockType == LockType_VM)
14290 directControl = mData->mSession.mDirectControl;
14291 }
14292
14293 /* ignore notifications sent after #OnSessionEnd() is called */
14294 if (!directControl)
14295 return S_OK;
14296
14297 return directControl->OnClipboardModeChange(aClipboardMode);
14298}
14299
14300/**
14301 * @note Locks this object for reading.
14302 */
14303HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14304{
14305 LogFlowThisFunc(("\n"));
14306
14307 AutoCaller autoCaller(this);
14308 AssertComRCReturnRC(autoCaller.rc());
14309
14310 ComPtr<IInternalSessionControl> directControl;
14311 {
14312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14313 if (mData->mSession.mLockType == LockType_VM)
14314 directControl = mData->mSession.mDirectControl;
14315 }
14316
14317 /* ignore notifications sent after #OnSessionEnd() is called */
14318 if (!directControl)
14319 return S_OK;
14320
14321 return directControl->OnDnDModeChange(aDnDMode);
14322}
14323
14324/**
14325 * @note Locks this object for reading.
14326 */
14327HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14328{
14329 LogFlowThisFunc(("\n"));
14330
14331 AutoCaller autoCaller(this);
14332 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14333
14334 ComPtr<IInternalSessionControl> directControl;
14335 {
14336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14337 if (mData->mSession.mLockType == LockType_VM)
14338 directControl = mData->mSession.mDirectControl;
14339 }
14340
14341 /* ignore notifications sent after #OnSessionEnd() is called */
14342 if (!directControl)
14343 return S_OK;
14344
14345 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14346}
14347
14348/**
14349 * @note Locks this object for reading.
14350 */
14351HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14352{
14353 LogFlowThisFunc(("\n"));
14354
14355 AutoCaller autoCaller(this);
14356 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14357
14358 ComPtr<IInternalSessionControl> directControl;
14359 {
14360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14361 if (mData->mSession.mLockType == LockType_VM)
14362 directControl = mData->mSession.mDirectControl;
14363 }
14364
14365 /* ignore notifications sent after #OnSessionEnd() is called */
14366 if (!directControl)
14367 return S_OK;
14368
14369 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14370}
14371
14372/**
14373 * Returns @c true if this machine's USB controller reports it has a matching
14374 * filter for the given USB device and @c false otherwise.
14375 *
14376 * @note locks this object for reading.
14377 */
14378bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14379{
14380 AutoCaller autoCaller(this);
14381 /* silently return if not ready -- this method may be called after the
14382 * direct machine session has been called */
14383 if (!autoCaller.isOk())
14384 return false;
14385
14386#ifdef VBOX_WITH_USB
14387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14388
14389 switch (mData->mMachineState)
14390 {
14391 case MachineState_Starting:
14392 case MachineState_Restoring:
14393 case MachineState_TeleportingIn:
14394 case MachineState_Paused:
14395 case MachineState_Running:
14396 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14397 * elsewhere... */
14398 alock.release();
14399 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14400 default: break;
14401 }
14402#else
14403 NOREF(aDevice);
14404 NOREF(aMaskedIfs);
14405#endif
14406 return false;
14407}
14408
14409/**
14410 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14411 */
14412HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14413 IVirtualBoxErrorInfo *aError,
14414 ULONG aMaskedIfs,
14415 const com::Utf8Str &aCaptureFilename)
14416{
14417 LogFlowThisFunc(("\n"));
14418
14419 AutoCaller autoCaller(this);
14420
14421 /* This notification may happen after the machine object has been
14422 * uninitialized (the session was closed), so don't assert. */
14423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14424
14425 ComPtr<IInternalSessionControl> directControl;
14426 {
14427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14428 if (mData->mSession.mLockType == LockType_VM)
14429 directControl = mData->mSession.mDirectControl;
14430 }
14431
14432 /* fail on notifications sent after #OnSessionEnd() is called, it is
14433 * expected by the caller */
14434 if (!directControl)
14435 return E_FAIL;
14436
14437 /* No locks should be held at this point. */
14438 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14439 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14440
14441 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14442}
14443
14444/**
14445 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14446 */
14447HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14448 IVirtualBoxErrorInfo *aError)
14449{
14450 LogFlowThisFunc(("\n"));
14451
14452 AutoCaller autoCaller(this);
14453
14454 /* This notification may happen after the machine object has been
14455 * uninitialized (the session was closed), so don't assert. */
14456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14457
14458 ComPtr<IInternalSessionControl> directControl;
14459 {
14460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14461 if (mData->mSession.mLockType == LockType_VM)
14462 directControl = mData->mSession.mDirectControl;
14463 }
14464
14465 /* fail on notifications sent after #OnSessionEnd() is called, it is
14466 * expected by the caller */
14467 if (!directControl)
14468 return E_FAIL;
14469
14470 /* No locks should be held at this point. */
14471 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14472 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14473
14474 return directControl->OnUSBDeviceDetach(aId, aError);
14475}
14476
14477// protected methods
14478/////////////////////////////////////////////////////////////////////////////
14479
14480/**
14481 * Deletes the given file if it is no longer in use by either the current machine state
14482 * (if the machine is "saved") or any of the machine's snapshots.
14483 *
14484 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14485 * but is different for each SnapshotMachine. When calling this, the order of calling this
14486 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14487 * is therefore critical. I know, it's all rather messy.
14488 *
14489 * @param strStateFile
14490 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14491 * the test for whether the saved state file is in use.
14492 */
14493void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14494 Snapshot *pSnapshotToIgnore)
14495{
14496 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14497 if ( (strStateFile.isNotEmpty())
14498 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14499 )
14500 // ... and it must also not be shared with other snapshots
14501 if ( !mData->mFirstSnapshot
14502 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14503 // this checks the SnapshotMachine's state file paths
14504 )
14505 RTFileDelete(strStateFile.c_str());
14506}
14507
14508/**
14509 * Locks the attached media.
14510 *
14511 * All attached hard disks are locked for writing and DVD/floppy are locked for
14512 * reading. Parents of attached hard disks (if any) are locked for reading.
14513 *
14514 * This method also performs accessibility check of all media it locks: if some
14515 * media is inaccessible, the method will return a failure and a bunch of
14516 * extended error info objects per each inaccessible medium.
14517 *
14518 * Note that this method is atomic: if it returns a success, all media are
14519 * locked as described above; on failure no media is locked at all (all
14520 * succeeded individual locks will be undone).
14521 *
14522 * The caller is responsible for doing the necessary state sanity checks.
14523 *
14524 * The locks made by this method must be undone by calling #unlockMedia() when
14525 * no more needed.
14526 */
14527HRESULT SessionMachine::i_lockMedia()
14528{
14529 AutoCaller autoCaller(this);
14530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14531
14532 AutoMultiWriteLock2 alock(this->lockHandle(),
14533 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14534
14535 /* bail out if trying to lock things with already set up locking */
14536 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14537
14538 MultiResult mrc(S_OK);
14539
14540 /* Collect locking information for all medium objects attached to the VM. */
14541 for (MediumAttachmentList::const_iterator
14542 it = mMediumAttachments->begin();
14543 it != mMediumAttachments->end();
14544 ++it)
14545 {
14546 MediumAttachment *pAtt = *it;
14547 DeviceType_T devType = pAtt->i_getType();
14548 Medium *pMedium = pAtt->i_getMedium();
14549
14550 MediumLockList *pMediumLockList(new MediumLockList());
14551 // There can be attachments without a medium (floppy/dvd), and thus
14552 // it's impossible to create a medium lock list. It still makes sense
14553 // to have the empty medium lock list in the map in case a medium is
14554 // attached later.
14555 if (pMedium != NULL)
14556 {
14557 MediumType_T mediumType = pMedium->i_getType();
14558 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14559 || mediumType == MediumType_Shareable;
14560 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14561
14562 alock.release();
14563 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14564 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14565 false /* fMediumLockWriteAll */,
14566 NULL,
14567 *pMediumLockList);
14568 alock.acquire();
14569 if (FAILED(mrc))
14570 {
14571 delete pMediumLockList;
14572 mData->mSession.mLockedMedia.Clear();
14573 break;
14574 }
14575 }
14576
14577 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14578 if (FAILED(rc))
14579 {
14580 mData->mSession.mLockedMedia.Clear();
14581 mrc = setError(rc,
14582 tr("Collecting locking information for all attached media failed"));
14583 break;
14584 }
14585 }
14586
14587 if (SUCCEEDED(mrc))
14588 {
14589 /* Now lock all media. If this fails, nothing is locked. */
14590 alock.release();
14591 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14592 alock.acquire();
14593 if (FAILED(rc))
14594 {
14595 mrc = setError(rc,
14596 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14597 }
14598 }
14599
14600 return mrc;
14601}
14602
14603/**
14604 * Undoes the locks made by by #lockMedia().
14605 */
14606HRESULT SessionMachine::i_unlockMedia()
14607{
14608 AutoCaller autoCaller(this);
14609 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14610
14611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14612
14613 /* we may be holding important error info on the current thread;
14614 * preserve it */
14615 ErrorInfoKeeper eik;
14616
14617 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14618 AssertComRC(rc);
14619 return rc;
14620}
14621
14622/**
14623 * Helper to change the machine state (reimplementation).
14624 *
14625 * @note Locks this object for writing.
14626 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14627 * it can cause crashes in random places due to unexpectedly committing
14628 * the current settings. The caller is responsible for that. The call
14629 * to saveStateSettings is fine, because this method does not commit.
14630 */
14631HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14632{
14633 LogFlowThisFuncEnter();
14634 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14635
14636 AutoCaller autoCaller(this);
14637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14638
14639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14640
14641 MachineState_T oldMachineState = mData->mMachineState;
14642
14643 AssertMsgReturn(oldMachineState != aMachineState,
14644 ("oldMachineState=%s, aMachineState=%s\n",
14645 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14646 E_FAIL);
14647
14648 HRESULT rc = S_OK;
14649
14650 int stsFlags = 0;
14651 bool deleteSavedState = false;
14652
14653 /* detect some state transitions */
14654
14655 if ( ( oldMachineState == MachineState_Saved
14656 && aMachineState == MachineState_Restoring)
14657 || ( ( oldMachineState == MachineState_PoweredOff
14658 || oldMachineState == MachineState_Teleported
14659 || oldMachineState == MachineState_Aborted
14660 )
14661 && ( aMachineState == MachineState_TeleportingIn
14662 || aMachineState == MachineState_Starting
14663 )
14664 )
14665 )
14666 {
14667 /* The EMT thread is about to start */
14668
14669 /* Nothing to do here for now... */
14670
14671 /// @todo NEWMEDIA don't let mDVDDrive and other children
14672 /// change anything when in the Starting/Restoring state
14673 }
14674 else if ( ( oldMachineState == MachineState_Running
14675 || oldMachineState == MachineState_Paused
14676 || oldMachineState == MachineState_Teleporting
14677 || oldMachineState == MachineState_OnlineSnapshotting
14678 || oldMachineState == MachineState_LiveSnapshotting
14679 || oldMachineState == MachineState_Stuck
14680 || oldMachineState == MachineState_Starting
14681 || oldMachineState == MachineState_Stopping
14682 || oldMachineState == MachineState_Saving
14683 || oldMachineState == MachineState_Restoring
14684 || oldMachineState == MachineState_TeleportingPausedVM
14685 || oldMachineState == MachineState_TeleportingIn
14686 )
14687 && ( aMachineState == MachineState_PoweredOff
14688 || aMachineState == MachineState_Saved
14689 || aMachineState == MachineState_Teleported
14690 || aMachineState == MachineState_Aborted
14691 )
14692 )
14693 {
14694 /* The EMT thread has just stopped, unlock attached media. Note that as
14695 * opposed to locking that is done from Console, we do unlocking here
14696 * because the VM process may have aborted before having a chance to
14697 * properly unlock all media it locked. */
14698
14699 unlockMedia();
14700 }
14701
14702 if (oldMachineState == MachineState_Restoring)
14703 {
14704 if (aMachineState != MachineState_Saved)
14705 {
14706 /*
14707 * delete the saved state file once the machine has finished
14708 * restoring from it (note that Console sets the state from
14709 * Restoring to Saved if the VM couldn't restore successfully,
14710 * to give the user an ability to fix an error and retry --
14711 * we keep the saved state file in this case)
14712 */
14713 deleteSavedState = true;
14714 }
14715 }
14716 else if ( oldMachineState == MachineState_Saved
14717 && ( aMachineState == MachineState_PoweredOff
14718 || aMachineState == MachineState_Aborted
14719 || aMachineState == MachineState_Teleported
14720 )
14721 )
14722 {
14723 /*
14724 * delete the saved state after SessionMachine::ForgetSavedState() is called
14725 * or if the VM process (owning a direct VM session) crashed while the
14726 * VM was Saved
14727 */
14728
14729 /// @todo (dmik)
14730 // Not sure that deleting the saved state file just because of the
14731 // client death before it attempted to restore the VM is a good
14732 // thing. But when it crashes we need to go to the Aborted state
14733 // which cannot have the saved state file associated... The only
14734 // way to fix this is to make the Aborted condition not a VM state
14735 // but a bool flag: i.e., when a crash occurs, set it to true and
14736 // change the state to PoweredOff or Saved depending on the
14737 // saved state presence.
14738
14739 deleteSavedState = true;
14740 mData->mCurrentStateModified = TRUE;
14741 stsFlags |= SaveSTS_CurStateModified;
14742 }
14743
14744 if ( aMachineState == MachineState_Starting
14745 || aMachineState == MachineState_Restoring
14746 || aMachineState == MachineState_TeleportingIn
14747 )
14748 {
14749 /* set the current state modified flag to indicate that the current
14750 * state is no more identical to the state in the
14751 * current snapshot */
14752 if (!mData->mCurrentSnapshot.isNull())
14753 {
14754 mData->mCurrentStateModified = TRUE;
14755 stsFlags |= SaveSTS_CurStateModified;
14756 }
14757 }
14758
14759 if (deleteSavedState)
14760 {
14761 if (mRemoveSavedState)
14762 {
14763 Assert(!mSSData->strStateFilePath.isEmpty());
14764
14765 // it is safe to delete the saved state file if ...
14766 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14767 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14768 // ... none of the snapshots share the saved state file
14769 )
14770 RTFileDelete(mSSData->strStateFilePath.c_str());
14771 }
14772
14773 mSSData->strStateFilePath.setNull();
14774 stsFlags |= SaveSTS_StateFilePath;
14775 }
14776
14777 /* redirect to the underlying peer machine */
14778 mPeer->i_setMachineState(aMachineState);
14779
14780 if ( oldMachineState != MachineState_RestoringSnapshot
14781 && ( aMachineState == MachineState_PoweredOff
14782 || aMachineState == MachineState_Teleported
14783 || aMachineState == MachineState_Aborted
14784 || aMachineState == MachineState_Saved))
14785 {
14786 /* the machine has stopped execution
14787 * (or the saved state file was adopted) */
14788 stsFlags |= SaveSTS_StateTimeStamp;
14789 }
14790
14791 if ( ( oldMachineState == MachineState_PoweredOff
14792 || oldMachineState == MachineState_Aborted
14793 || oldMachineState == MachineState_Teleported
14794 )
14795 && aMachineState == MachineState_Saved)
14796 {
14797 /* the saved state file was adopted */
14798 Assert(!mSSData->strStateFilePath.isEmpty());
14799 stsFlags |= SaveSTS_StateFilePath;
14800 }
14801
14802#ifdef VBOX_WITH_GUEST_PROPS
14803 if ( aMachineState == MachineState_PoweredOff
14804 || aMachineState == MachineState_Aborted
14805 || aMachineState == MachineState_Teleported)
14806 {
14807 /* Make sure any transient guest properties get removed from the
14808 * property store on shutdown. */
14809 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14810
14811 /* remove it from the settings representation */
14812 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14813 for (settings::GuestPropertiesList::iterator
14814 it = llGuestProperties.begin();
14815 it != llGuestProperties.end();
14816 /*nothing*/)
14817 {
14818 const settings::GuestProperty &prop = *it;
14819 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14820 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14821 {
14822 it = llGuestProperties.erase(it);
14823 fNeedsSaving = true;
14824 }
14825 else
14826 {
14827 ++it;
14828 }
14829 }
14830
14831 /* Additionally remove it from the HWData representation. Required to
14832 * keep everything in sync, as this is what the API keeps using. */
14833 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14834 for (HWData::GuestPropertyMap::iterator
14835 it = llHWGuestProperties.begin();
14836 it != llHWGuestProperties.end();
14837 /*nothing*/)
14838 {
14839 uint32_t fFlags = it->second.mFlags;
14840 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14841 {
14842 /* iterator where we need to continue after the erase call
14843 * (C++03 is a fact still, and it doesn't return the iterator
14844 * which would allow continuing) */
14845 HWData::GuestPropertyMap::iterator it2 = it;
14846 ++it2;
14847 llHWGuestProperties.erase(it);
14848 it = it2;
14849 fNeedsSaving = true;
14850 }
14851 else
14852 {
14853 ++it;
14854 }
14855 }
14856
14857 if (fNeedsSaving)
14858 {
14859 mData->mCurrentStateModified = TRUE;
14860 stsFlags |= SaveSTS_CurStateModified;
14861 }
14862 }
14863#endif /* VBOX_WITH_GUEST_PROPS */
14864
14865 rc = i_saveStateSettings(stsFlags);
14866
14867 if ( ( oldMachineState != MachineState_PoweredOff
14868 && oldMachineState != MachineState_Aborted
14869 && oldMachineState != MachineState_Teleported
14870 )
14871 && ( aMachineState == MachineState_PoweredOff
14872 || aMachineState == MachineState_Aborted
14873 || aMachineState == MachineState_Teleported
14874 )
14875 )
14876 {
14877 /* we've been shut down for any reason */
14878 /* no special action so far */
14879 }
14880
14881 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14882 LogFlowThisFuncLeave();
14883 return rc;
14884}
14885
14886/**
14887 * Sends the current machine state value to the VM process.
14888 *
14889 * @note Locks this object for reading, then calls a client process.
14890 */
14891HRESULT SessionMachine::i_updateMachineStateOnClient()
14892{
14893 AutoCaller autoCaller(this);
14894 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14895
14896 ComPtr<IInternalSessionControl> directControl;
14897 {
14898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14899 AssertReturn(!!mData, E_FAIL);
14900 if (mData->mSession.mLockType == LockType_VM)
14901 directControl = mData->mSession.mDirectControl;
14902
14903 /* directControl may be already set to NULL here in #OnSessionEnd()
14904 * called too early by the direct session process while there is still
14905 * some operation (like deleting the snapshot) in progress. The client
14906 * process in this case is waiting inside Session::close() for the
14907 * "end session" process object to complete, while #uninit() called by
14908 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14909 * operation to complete. For now, we accept this inconsistent behavior
14910 * and simply do nothing here. */
14911
14912 if (mData->mSession.mState == SessionState_Unlocking)
14913 return S_OK;
14914 }
14915
14916 /* ignore notifications sent after #OnSessionEnd() is called */
14917 if (!directControl)
14918 return S_OK;
14919
14920 return directControl->UpdateMachineState(mData->mMachineState);
14921}
14922
14923
14924/*static*/
14925HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14926{
14927 va_list args;
14928 va_start(args, pcszMsg);
14929 HRESULT rc = setErrorInternal(aResultCode,
14930 getStaticClassIID(),
14931 getStaticComponentName(),
14932 Utf8Str(pcszMsg, args),
14933 false /* aWarning */,
14934 true /* aLogIt */);
14935 va_end(args);
14936 return rc;
14937}
14938
14939
14940HRESULT Machine::updateState(MachineState_T aState)
14941{
14942 NOREF(aState);
14943 ReturnComNotImplemented();
14944}
14945
14946HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14947{
14948 NOREF(aProgress);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::endPowerUp(LONG aResult)
14953{
14954 NOREF(aResult);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14959{
14960 NOREF(aProgress);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::endPoweringDown(LONG aResult,
14965 const com::Utf8Str &aErrMsg)
14966{
14967 NOREF(aResult);
14968 NOREF(aErrMsg);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14973 BOOL *aMatched,
14974 ULONG *aMaskedInterfaces)
14975{
14976 NOREF(aDevice);
14977 NOREF(aMatched);
14978 NOREF(aMaskedInterfaces);
14979 ReturnComNotImplemented();
14980
14981}
14982
14983HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14984{
14985 NOREF(aId); NOREF(aCaptureFilename);
14986 ReturnComNotImplemented();
14987}
14988
14989HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14990 BOOL aDone)
14991{
14992 NOREF(aId);
14993 NOREF(aDone);
14994 ReturnComNotImplemented();
14995}
14996
14997HRESULT Machine::autoCaptureUSBDevices()
14998{
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15003{
15004 NOREF(aDone);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15009 ComPtr<IProgress> &aProgress)
15010{
15011 NOREF(aSession);
15012 NOREF(aProgress);
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::finishOnlineMergeMedium()
15017{
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15022 std::vector<com::Utf8Str> &aValues,
15023 std::vector<LONG64> &aTimestamps,
15024 std::vector<com::Utf8Str> &aFlags)
15025{
15026 NOREF(aNames);
15027 NOREF(aValues);
15028 NOREF(aTimestamps);
15029 NOREF(aFlags);
15030 ReturnComNotImplemented();
15031}
15032
15033HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15034 const com::Utf8Str &aValue,
15035 LONG64 aTimestamp,
15036 const com::Utf8Str &aFlags)
15037{
15038 NOREF(aName);
15039 NOREF(aValue);
15040 NOREF(aTimestamp);
15041 NOREF(aFlags);
15042 ReturnComNotImplemented();
15043}
15044
15045HRESULT Machine::lockMedia()
15046{
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::unlockMedia()
15051{
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15056 ComPtr<IMediumAttachment> &aNewAttachment)
15057{
15058 NOREF(aAttachment);
15059 NOREF(aNewAttachment);
15060 ReturnComNotImplemented();
15061}
15062
15063HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15064 ULONG aCpuUser,
15065 ULONG aCpuKernel,
15066 ULONG aCpuIdle,
15067 ULONG aMemTotal,
15068 ULONG aMemFree,
15069 ULONG aMemBalloon,
15070 ULONG aMemShared,
15071 ULONG aMemCache,
15072 ULONG aPagedTotal,
15073 ULONG aMemAllocTotal,
15074 ULONG aMemFreeTotal,
15075 ULONG aMemBalloonTotal,
15076 ULONG aMemSharedTotal,
15077 ULONG aVmNetRx,
15078 ULONG aVmNetTx)
15079{
15080 NOREF(aValidStats);
15081 NOREF(aCpuUser);
15082 NOREF(aCpuKernel);
15083 NOREF(aCpuIdle);
15084 NOREF(aMemTotal);
15085 NOREF(aMemFree);
15086 NOREF(aMemBalloon);
15087 NOREF(aMemShared);
15088 NOREF(aMemCache);
15089 NOREF(aPagedTotal);
15090 NOREF(aMemAllocTotal);
15091 NOREF(aMemFreeTotal);
15092 NOREF(aMemBalloonTotal);
15093 NOREF(aMemSharedTotal);
15094 NOREF(aVmNetRx);
15095 NOREF(aVmNetTx);
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15100 com::Utf8Str &aResult)
15101{
15102 NOREF(aAuthParams);
15103 NOREF(aResult);
15104 ReturnComNotImplemented();
15105}
15106
15107HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15108{
15109 NOREF(aFlags);
15110 ReturnComNotImplemented();
15111}
15112
15113/* This isn't handled entirely by the wrapper generator yet. */
15114#ifdef VBOX_WITH_XPCOM
15115NS_DECL_CLASSINFO(SessionMachine)
15116NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15117
15118NS_DECL_CLASSINFO(SnapshotMachine)
15119NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15120#endif
Note: See TracBrowser for help on using the repository browser.

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