VirtualBox

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

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

Main: bugref:9049: Support for split GUI functionality, to start VirtualBoxVM instead of VirtualBox when launchVMProcess called.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 526.7 KB
Line 
1/* $Id: MachineImpl.cpp 72323 2018-05-24 15:57:38Z 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#include "MachineImplMoveVM.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mAPIC = true;
197 mX2APIC = false;
198 mIBPBOnVMExit = false;
199 mIBPBOnVMEntry = false;
200 mSpecCtrl = false;
201 mSpecCtrlByHost = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mDnDMode = DnDMode_Disabled;
217
218 mFirmwareType = FirmwareType_BIOS;
219 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
220 mPointingHIDType = PointingHIDType_PS2Mouse;
221 mChipsetType = ChipsetType_PIIX3;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
283 * scheme (includes the UUID).
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 mUserData->s.llGroups = llGroups;
327
328 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
329 // the "name sync" flag determines whether the machine directory gets renamed along
330 // with the machine file; say so if the settings file name is the same as the
331 // settings file parent directory (machine directory)
332 mUserData->s.fNameSync = i_isInOwnDir();
333
334 // initialize the default snapshots folder
335 rc = COMSETTER(SnapshotFolder)(NULL);
336 AssertComRC(rc);
337
338 if (aOsType)
339 {
340 /* Store OS type */
341 mUserData->s.strOsType = aOsType->i_id();
342
343 /* Apply BIOS defaults */
344 mBIOSSettings->i_applyDefaults(aOsType);
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352 }
353
354 /* Apply network adapters defaults */
355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
356 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
357
358 /* Apply serial port defaults */
359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
360 mSerialPorts[slot]->i_applyDefaults(aOsType);
361
362 /* Apply parallel port defaults */
363 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
364 mParallelPorts[slot]->i_applyDefaults();
365
366 /* At this point the changing of the current state modification
367 * flag is allowed. */
368 i_allowStateModification();
369
370 /* commit all changes made during the initialization */
371 i_commit();
372 }
373
374 /* Confirm a successful initialization when it's the case */
375 if (SUCCEEDED(rc))
376 {
377 if (mData->mAccessible)
378 autoInitSpan.setSucceeded();
379 else
380 autoInitSpan.setLimited();
381 }
382
383 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
384 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
385 mData->mRegistered,
386 mData->mAccessible,
387 rc));
388
389 LogFlowThisFuncLeave();
390
391 return rc;
392}
393
394/**
395 * Initializes a new instance with data from machine XML (formerly Init_Registered).
396 * Gets called in two modes:
397 *
398 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
399 * UUID is specified and we mark the machine as "registered";
400 *
401 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
402 * and the machine remains unregistered until RegisterMachine() is called.
403 *
404 * @param aParent Associated parent object
405 * @param strConfigFile Local file system path to the VM settings file (can
406 * be relative to the VirtualBox config directory).
407 * @param aId UUID of the machine or NULL (see above).
408 *
409 * @return Success indicator. if not S_OK, the machine object is invalid
410 */
411HRESULT Machine::initFromSettings(VirtualBox *aParent,
412 const Utf8Str &strConfigFile,
413 const Guid *aId)
414{
415 LogFlowThisFuncEnter();
416 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
417
418 /* Enclose the state transition NotReady->InInit->Ready */
419 AutoInitSpan autoInitSpan(this);
420 AssertReturn(autoInitSpan.isOk(), E_FAIL);
421
422 HRESULT rc = initImpl(aParent, strConfigFile);
423 if (FAILED(rc)) return rc;
424
425 if (aId)
426 {
427 // loading a registered VM:
428 unconst(mData->mUuid) = *aId;
429 mData->mRegistered = TRUE;
430 // now load the settings from XML:
431 rc = i_registeredInit();
432 // this calls initDataAndChildObjects() and loadSettings()
433 }
434 else
435 {
436 // opening an unregistered VM (VirtualBox::OpenMachine()):
437 rc = initDataAndChildObjects();
438
439 if (SUCCEEDED(rc))
440 {
441 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
442 mData->mAccessible = TRUE;
443
444 try
445 {
446 // load and parse machine XML; this will throw on XML or logic errors
447 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
448
449 // reject VM UUID duplicates, they can happen if someone
450 // tries to register an already known VM config again
451 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
452 true /* fPermitInaccessible */,
453 false /* aDoSetError */,
454 NULL) != VBOX_E_OBJECT_NOT_FOUND)
455 {
456 throw setError(E_FAIL,
457 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
458 mData->m_strConfigFile.c_str());
459 }
460
461 // use UUID from machine config
462 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
463
464 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
465 NULL /* puuidRegistry */);
466 if (FAILED(rc)) throw rc;
467
468 /* At this point the changing of the current state modification
469 * flag is allowed. */
470 i_allowStateModification();
471
472 i_commit();
473 }
474 catch (HRESULT err)
475 {
476 /* we assume that error info is set by the thrower */
477 rc = err;
478 }
479 catch (...)
480 {
481 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
482 }
483 }
484 }
485
486 /* Confirm a successful initialization when it's the case */
487 if (SUCCEEDED(rc))
488 {
489 if (mData->mAccessible)
490 autoInitSpan.setSucceeded();
491 else
492 {
493 autoInitSpan.setLimited();
494
495 // uninit media from this machine's media registry, or else
496 // reloading the settings will fail
497 mParent->i_unregisterMachineMedia(i_getId());
498 }
499 }
500
501 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
502 "rc=%08X\n",
503 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
504 mData->mRegistered, mData->mAccessible, rc));
505
506 LogFlowThisFuncLeave();
507
508 return rc;
509}
510
511/**
512 * Initializes a new instance from a machine config that is already in memory
513 * (import OVF case). Since we are importing, the UUID in the machine
514 * config is ignored and we always generate a fresh one.
515 *
516 * @param aParent Associated parent object.
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->i_getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = i_loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 i_allowStateModification();
571
572 /* commit all changes made during the initialization */
573 i_commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 /* Ignore all errors from unregistering, they would destroy
585- * the more interesting error information we already have,
586- * pinpointing the issue with the VM config. */
587 ErrorInfoKeeper eik;
588
589 autoInitSpan.setLimited();
590
591 // uninit media from this machine's media registry, or else
592 // reloading the settings will fail
593 mParent->i_unregisterMachineMedia(i_getId());
594 }
595 }
596
597 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
598 "rc=%08X\n",
599 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
600 mData->mRegistered, mData->mAccessible, rc));
601
602 LogFlowThisFuncLeave();
603
604 return rc;
605}
606
607/**
608 * Shared code between the various init() implementations.
609 * @param aParent The VirtualBox object.
610 * @param strConfigFile Settings file.
611 * @return
612 */
613HRESULT Machine::initImpl(VirtualBox *aParent,
614 const Utf8Str &strConfigFile)
615{
616 LogFlowThisFuncEnter();
617
618 AssertReturn(aParent, E_INVALIDARG);
619 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
620
621 HRESULT rc = S_OK;
622
623 /* share the parent weakly */
624 unconst(mParent) = aParent;
625
626 /* allocate the essential machine data structure (the rest will be
627 * allocated later by initDataAndChildObjects() */
628 mData.allocate();
629
630 /* memorize the config file name (as provided) */
631 mData->m_strConfigFile = strConfigFile;
632
633 /* get the full file name */
634 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
635 if (RT_FAILURE(vrc1))
636 return setError(VBOX_E_FILE_ERROR,
637 tr("Invalid machine settings file name '%s' (%Rrc)"),
638 strConfigFile.c_str(),
639 vrc1);
640
641 LogFlowThisFuncLeave();
642
643 return rc;
644}
645
646/**
647 * Tries to create a machine settings file in the path stored in the machine
648 * instance data. Used when a new machine is created to fail gracefully if
649 * the settings file could not be written (e.g. because machine dir is read-only).
650 * @return
651 */
652HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
653{
654 HRESULT rc = S_OK;
655
656 // when we create a new machine, we must be able to create the settings file
657 RTFILE f = NIL_RTFILE;
658 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
659 if ( RT_SUCCESS(vrc)
660 || vrc == VERR_SHARING_VIOLATION
661 )
662 {
663 if (RT_SUCCESS(vrc))
664 RTFileClose(f);
665 if (!fForceOverwrite)
666 rc = setError(VBOX_E_FILE_ERROR,
667 tr("Machine settings file '%s' already exists"),
668 mData->m_strConfigFileFull.c_str());
669 else
670 {
671 /* try to delete the config file, as otherwise the creation
672 * of a new settings file will fail. */
673 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
674 if (RT_FAILURE(vrc2))
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Could not delete the existing settings file '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(), vrc2);
678 }
679 }
680 else if ( vrc != VERR_FILE_NOT_FOUND
681 && vrc != VERR_PATH_NOT_FOUND
682 )
683 rc = setError(VBOX_E_FILE_ERROR,
684 tr("Invalid machine settings file name '%s' (%Rrc)"),
685 mData->m_strConfigFileFull.c_str(),
686 vrc);
687 return rc;
688}
689
690/**
691 * Initializes the registered machine by loading the settings file.
692 * This method is separated from #init() in order to make it possible to
693 * retry the operation after VirtualBox startup instead of refusing to
694 * startup the whole VirtualBox server in case if the settings file of some
695 * registered VM is invalid or inaccessible.
696 *
697 * @note Must be always called from this object's write lock
698 * (unless called from #init() that doesn't need any locking).
699 * @note Locks the mUSBController method for writing.
700 * @note Subclasses must not call this method.
701 */
702HRESULT Machine::i_registeredInit()
703{
704 AssertReturn(!i_isSessionMachine(), E_FAIL);
705 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
706 AssertReturn(mData->mUuid.isValid(), E_FAIL);
707 AssertReturn(!mData->mAccessible, E_FAIL);
708
709 HRESULT rc = initDataAndChildObjects();
710
711 if (SUCCEEDED(rc))
712 {
713 /* Temporarily reset the registered flag in order to let setters
714 * potentially called from loadSettings() succeed (isMutable() used in
715 * all setters will return FALSE for a Machine instance if mRegistered
716 * is TRUE). */
717 mData->mRegistered = FALSE;
718
719 try
720 {
721 // load and parse machine XML; this will throw on XML or logic errors
722 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
723
724 if (mData->mUuid != mData->pMachineConfigFile->uuid)
725 throw setError(E_FAIL,
726 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
727 mData->pMachineConfigFile->uuid.raw(),
728 mData->m_strConfigFileFull.c_str(),
729 mData->mUuid.toString().c_str(),
730 mParent->i_settingsFilePath().c_str());
731
732 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
733 NULL /* const Guid *puuidRegistry */);
734 if (FAILED(rc)) throw rc;
735 }
736 catch (HRESULT err)
737 {
738 /* we assume that error info is set by the thrower */
739 rc = err;
740 }
741 catch (...)
742 {
743 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
744 }
745
746 /* Restore the registered flag (even on failure) */
747 mData->mRegistered = TRUE;
748 }
749
750 if (SUCCEEDED(rc))
751 {
752 /* Set mAccessible to TRUE only if we successfully locked and loaded
753 * the settings file */
754 mData->mAccessible = TRUE;
755
756 /* commit all changes made during loading the settings file */
757 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
758 /// @todo r=klaus for some reason the settings loading logic backs up
759 // the settings, and therefore a commit is needed. Should probably be changed.
760 }
761 else
762 {
763 /* If the machine is registered, then, instead of returning a
764 * failure, we mark it as inaccessible and set the result to
765 * success to give it a try later */
766
767 /* fetch the current error info */
768 mData->mAccessError = com::ErrorInfo();
769 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
770
771 /* rollback all changes */
772 i_rollback(false /* aNotify */);
773
774 // uninit media from this machine's media registry, or else
775 // reloading the settings will fail
776 mParent->i_unregisterMachineMedia(i_getId());
777
778 /* uninitialize the common part to make sure all data is reset to
779 * default (null) values */
780 uninitDataAndChildObjects();
781
782 rc = S_OK;
783 }
784
785 return rc;
786}
787
788/**
789 * Uninitializes the instance.
790 * Called either from FinalRelease() or by the parent when it gets destroyed.
791 *
792 * @note The caller of this method must make sure that this object
793 * a) doesn't have active callers on the current thread and b) is not locked
794 * by the current thread; otherwise uninit() will hang either a) due to
795 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
796 * a dead-lock caused by this thread waiting for all callers on the other
797 * threads are done but preventing them from doing so by holding a lock.
798 */
799void Machine::uninit()
800{
801 LogFlowThisFuncEnter();
802
803 Assert(!isWriteLockOnCurrentThread());
804
805 Assert(!uRegistryNeedsSaving);
806 if (uRegistryNeedsSaving)
807 {
808 AutoCaller autoCaller(this);
809 if (SUCCEEDED(autoCaller.rc()))
810 {
811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
812 i_saveSettings(NULL, Machine::SaveS_Force);
813 }
814 }
815
816 /* Enclose the state transition Ready->InUninit->NotReady */
817 AutoUninitSpan autoUninitSpan(this);
818 if (autoUninitSpan.uninitDone())
819 return;
820
821 Assert(!i_isSnapshotMachine());
822 Assert(!i_isSessionMachine());
823 Assert(!!mData);
824
825 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
826 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
827
828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
829
830 if (!mData->mSession.mMachine.isNull())
831 {
832 /* Theoretically, this can only happen if the VirtualBox server has been
833 * terminated while there were clients running that owned open direct
834 * sessions. Since in this case we are definitely called by
835 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
836 * won't happen on the client watcher thread (because it has a
837 * VirtualBox caller for the duration of the
838 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
839 * cannot happen until the VirtualBox caller is released). This is
840 * important, because SessionMachine::uninit() cannot correctly operate
841 * after we return from this method (it expects the Machine instance is
842 * still valid). We'll call it ourselves below.
843 */
844 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
845 (SessionMachine*)mData->mSession.mMachine));
846
847 if (Global::IsOnlineOrTransient(mData->mMachineState))
848 {
849 Log1WarningThisFunc(("Setting state to Aborted!\n"));
850 /* set machine state using SessionMachine reimplementation */
851 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
852 }
853
854 /*
855 * Uninitialize SessionMachine using public uninit() to indicate
856 * an unexpected uninitialization.
857 */
858 mData->mSession.mMachine->uninit();
859 /* SessionMachine::uninit() must set mSession.mMachine to null */
860 Assert(mData->mSession.mMachine.isNull());
861 }
862
863 // uninit media from this machine's media registry, if they're still there
864 Guid uuidMachine(i_getId());
865
866 /* the lock is no more necessary (SessionMachine is uninitialized) */
867 alock.release();
868
869 /* XXX This will fail with
870 * "cannot be closed because it is still attached to 1 virtual machines"
871 * because at this point we did not call uninitDataAndChildObjects() yet
872 * and therefore also removeBackReference() for all these mediums was not called! */
873
874 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
875 mParent->i_unregisterMachineMedia(uuidMachine);
876
877 // has machine been modified?
878 if (mData->flModifications)
879 {
880 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
881 i_rollback(false /* aNotify */);
882 }
883
884 if (mData->mAccessible)
885 uninitDataAndChildObjects();
886
887 /* free the essential data structure last */
888 mData.free();
889
890 LogFlowThisFuncLeave();
891}
892
893// Wrapped IMachine properties
894/////////////////////////////////////////////////////////////////////////////
895HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
896{
897 /* mParent is constant during life time, no need to lock */
898 ComObjPtr<VirtualBox> pVirtualBox(mParent);
899 aParent = pVirtualBox;
900
901 return S_OK;
902}
903
904
905HRESULT Machine::getAccessible(BOOL *aAccessible)
906{
907 /* In some cases (medium registry related), it is necessary to be able to
908 * go through the list of all machines. Happens when an inaccessible VM
909 * has a sensible medium registry. */
910 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
912
913 HRESULT rc = S_OK;
914
915 if (!mData->mAccessible)
916 {
917 /* try to initialize the VM once more if not accessible */
918
919 AutoReinitSpan autoReinitSpan(this);
920 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
921
922#ifdef DEBUG
923 LogFlowThisFunc(("Dumping media backreferences\n"));
924 mParent->i_dumpAllBackRefs();
925#endif
926
927 if (mData->pMachineConfigFile)
928 {
929 // reset the XML file to force loadSettings() (called from i_registeredInit())
930 // to parse it again; the file might have changed
931 delete mData->pMachineConfigFile;
932 mData->pMachineConfigFile = NULL;
933 }
934
935 rc = i_registeredInit();
936
937 if (SUCCEEDED(rc) && mData->mAccessible)
938 {
939 autoReinitSpan.setSucceeded();
940
941 /* make sure interesting parties will notice the accessibility
942 * state change */
943 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
944 mParent->i_onMachineDataChange(mData->mUuid);
945 }
946 }
947
948 if (SUCCEEDED(rc))
949 *aAccessible = mData->mAccessible;
950
951 LogFlowThisFuncLeave();
952
953 return rc;
954}
955
956HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
957{
958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
959
960 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
961 {
962 /* return shortly */
963 aAccessError = NULL;
964 return S_OK;
965 }
966
967 HRESULT rc = S_OK;
968
969 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
970 rc = errorInfo.createObject();
971 if (SUCCEEDED(rc))
972 {
973 errorInfo->init(mData->mAccessError.getResultCode(),
974 mData->mAccessError.getInterfaceID().ref(),
975 Utf8Str(mData->mAccessError.getComponent()).c_str(),
976 Utf8Str(mData->mAccessError.getText()));
977 aAccessError = errorInfo;
978 }
979
980 return rc;
981}
982
983HRESULT Machine::getName(com::Utf8Str &aName)
984{
985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
986
987 aName = mUserData->s.strName;
988
989 return S_OK;
990}
991
992HRESULT Machine::setName(const com::Utf8Str &aName)
993{
994 // prohibit setting a UUID only as the machine name, or else it can
995 // never be found by findMachine()
996 Guid test(aName);
997
998 if (test.isValid())
999 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1000
1001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1002
1003 HRESULT rc = i_checkStateDependency(MutableStateDep);
1004 if (FAILED(rc)) return rc;
1005
1006 i_setModified(IsModified_MachineData);
1007 mUserData.backup();
1008 mUserData->s.strName = aName;
1009
1010 return S_OK;
1011}
1012
1013HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1014{
1015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1016
1017 aDescription = mUserData->s.strDescription;
1018
1019 return S_OK;
1020}
1021
1022HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1023{
1024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 // this can be done in principle in any state as it doesn't affect the VM
1027 // significantly, but play safe by not messing around while complex
1028 // activities are going on
1029 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1030 if (FAILED(rc)) return rc;
1031
1032 i_setModified(IsModified_MachineData);
1033 mUserData.backup();
1034 mUserData->s.strDescription = aDescription;
1035
1036 return S_OK;
1037}
1038
1039HRESULT Machine::getId(com::Guid &aId)
1040{
1041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1042
1043 aId = mData->mUuid;
1044
1045 return S_OK;
1046}
1047
1048HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1049{
1050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1051 aGroups.resize(mUserData->s.llGroups.size());
1052 size_t i = 0;
1053 for (StringsList::const_iterator
1054 it = mUserData->s.llGroups.begin();
1055 it != mUserData->s.llGroups.end();
1056 ++it, ++i)
1057 aGroups[i] = (*it);
1058
1059 return S_OK;
1060}
1061
1062HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1063{
1064 StringsList llGroups;
1065 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1066 if (FAILED(rc))
1067 return rc;
1068
1069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1070
1071 rc = i_checkStateDependency(MutableOrSavedStateDep);
1072 if (FAILED(rc)) return rc;
1073
1074 i_setModified(IsModified_MachineData);
1075 mUserData.backup();
1076 mUserData->s.llGroups = llGroups;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1082{
1083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1084
1085 aOSTypeId = mUserData->s.strOsType;
1086
1087 return S_OK;
1088}
1089
1090HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1091{
1092 /* look up the object by Id to check it is valid */
1093 ComObjPtr<GuestOSType> pGuestOSType;
1094 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1095 pGuestOSType);
1096 if (FAILED(rc)) return rc;
1097
1098 /* when setting, always use the "etalon" value for consistency -- lookup
1099 * by ID is case-insensitive and the input value may have different case */
1100 Utf8Str osTypeId = pGuestOSType->i_id();
1101
1102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 rc = i_checkStateDependency(MutableStateDep);
1105 if (FAILED(rc)) return rc;
1106
1107 i_setModified(IsModified_MachineData);
1108 mUserData.backup();
1109 mUserData->s.strOsType = osTypeId;
1110
1111 return S_OK;
1112}
1113
1114HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1115{
1116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 *aFirmwareType = mHWData->mFirmwareType;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1124{
1125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 HRESULT rc = i_checkStateDependency(MutableStateDep);
1128 if (FAILED(rc)) return rc;
1129
1130 i_setModified(IsModified_MachineData);
1131 mHWData.backup();
1132 mHWData->mFirmwareType = aFirmwareType;
1133
1134 return S_OK;
1135}
1136
1137HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1138{
1139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1140
1141 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1142
1143 return S_OK;
1144}
1145
1146HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1147{
1148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1149
1150 HRESULT rc = i_checkStateDependency(MutableStateDep);
1151 if (FAILED(rc)) return rc;
1152
1153 i_setModified(IsModified_MachineData);
1154 mHWData.backup();
1155 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1156
1157 return S_OK;
1158}
1159
1160HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1161{
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aPointingHIDType = mHWData->mPointingHIDType;
1165
1166 return S_OK;
1167}
1168
1169HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1170{
1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 HRESULT rc = i_checkStateDependency(MutableStateDep);
1174 if (FAILED(rc)) return rc;
1175
1176 i_setModified(IsModified_MachineData);
1177 mHWData.backup();
1178 mHWData->mPointingHIDType = aPointingHIDType;
1179
1180 return S_OK;
1181}
1182
1183HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1184{
1185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 *aChipsetType = mHWData->mChipsetType;
1188
1189 return S_OK;
1190}
1191
1192HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1193{
1194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1195
1196 HRESULT rc = i_checkStateDependency(MutableStateDep);
1197 if (FAILED(rc)) return rc;
1198
1199 if (aChipsetType != mHWData->mChipsetType)
1200 {
1201 i_setModified(IsModified_MachineData);
1202 mHWData.backup();
1203 mHWData->mChipsetType = aChipsetType;
1204
1205 // Resize network adapter array, to be finalized on commit/rollback.
1206 // We must not throw away entries yet, otherwise settings are lost
1207 // without a way to roll back.
1208 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1209 size_t oldCount = mNetworkAdapters.size();
1210 if (newCount > oldCount)
1211 {
1212 mNetworkAdapters.resize(newCount);
1213 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1214 {
1215 unconst(mNetworkAdapters[slot]).createObject();
1216 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1217 }
1218 }
1219 }
1220
1221 return S_OK;
1222}
1223
1224HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1225{
1226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
1228 aParavirtDebug = mHWData->mParavirtDebug;
1229 return S_OK;
1230}
1231
1232HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1233{
1234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 HRESULT rc = i_checkStateDependency(MutableStateDep);
1237 if (FAILED(rc)) return rc;
1238
1239 /** @todo Parse/validate options? */
1240 if (aParavirtDebug != mHWData->mParavirtDebug)
1241 {
1242 i_setModified(IsModified_MachineData);
1243 mHWData.backup();
1244 mHWData->mParavirtDebug = aParavirtDebug;
1245 }
1246
1247 return S_OK;
1248}
1249
1250HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1251{
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aParavirtProvider = mHWData->mParavirtProvider;
1255
1256 return S_OK;
1257}
1258
1259HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1260{
1261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 HRESULT rc = i_checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 if (aParavirtProvider != mHWData->mParavirtProvider)
1267 {
1268 i_setModified(IsModified_MachineData);
1269 mHWData.backup();
1270 mHWData->mParavirtProvider = aParavirtProvider;
1271 }
1272
1273 return S_OK;
1274}
1275
1276HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1277{
1278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 *aParavirtProvider = mHWData->mParavirtProvider;
1281 switch (mHWData->mParavirtProvider)
1282 {
1283 case ParavirtProvider_None:
1284 case ParavirtProvider_HyperV:
1285 case ParavirtProvider_KVM:
1286 case ParavirtProvider_Minimal:
1287 break;
1288
1289 /* Resolve dynamic provider types to the effective types. */
1290 default:
1291 {
1292 ComObjPtr<GuestOSType> pGuestOSType;
1293 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1294 pGuestOSType);
1295 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1296
1297 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1298 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1299
1300 switch (mHWData->mParavirtProvider)
1301 {
1302 case ParavirtProvider_Legacy:
1303 {
1304 if (fOsXGuest)
1305 *aParavirtProvider = ParavirtProvider_Minimal;
1306 else
1307 *aParavirtProvider = ParavirtProvider_None;
1308 break;
1309 }
1310
1311 case ParavirtProvider_Default:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else if ( mUserData->s.strOsType == "Windows10"
1316 || mUserData->s.strOsType == "Windows10_64"
1317 || mUserData->s.strOsType == "Windows81"
1318 || mUserData->s.strOsType == "Windows81_64"
1319 || mUserData->s.strOsType == "Windows8"
1320 || mUserData->s.strOsType == "Windows8_64"
1321 || mUserData->s.strOsType == "Windows7"
1322 || mUserData->s.strOsType == "Windows7_64"
1323 || mUserData->s.strOsType == "WindowsVista"
1324 || mUserData->s.strOsType == "WindowsVista_64"
1325 || mUserData->s.strOsType == "Windows2012"
1326 || mUserData->s.strOsType == "Windows2012_64"
1327 || mUserData->s.strOsType == "Windows2008"
1328 || mUserData->s.strOsType == "Windows2008_64")
1329 {
1330 *aParavirtProvider = ParavirtProvider_HyperV;
1331 }
1332 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1333 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1334 || mUserData->s.strOsType == "Linux"
1335 || mUserData->s.strOsType == "Linux_64"
1336 || mUserData->s.strOsType == "ArchLinux"
1337 || mUserData->s.strOsType == "ArchLinux_64"
1338 || mUserData->s.strOsType == "Debian"
1339 || mUserData->s.strOsType == "Debian_64"
1340 || mUserData->s.strOsType == "Fedora"
1341 || mUserData->s.strOsType == "Fedora_64"
1342 || mUserData->s.strOsType == "Gentoo"
1343 || mUserData->s.strOsType == "Gentoo_64"
1344 || mUserData->s.strOsType == "Mandriva"
1345 || mUserData->s.strOsType == "Mandriva_64"
1346 || mUserData->s.strOsType == "OpenSUSE"
1347 || mUserData->s.strOsType == "OpenSUSE_64"
1348 || mUserData->s.strOsType == "Oracle"
1349 || mUserData->s.strOsType == "Oracle_64"
1350 || mUserData->s.strOsType == "RedHat"
1351 || mUserData->s.strOsType == "RedHat_64"
1352 || mUserData->s.strOsType == "Turbolinux"
1353 || mUserData->s.strOsType == "Turbolinux_64"
1354 || mUserData->s.strOsType == "Ubuntu"
1355 || mUserData->s.strOsType == "Ubuntu_64"
1356 || mUserData->s.strOsType == "Xandros"
1357 || mUserData->s.strOsType == "Xandros_64")
1358 {
1359 *aParavirtProvider = ParavirtProvider_KVM;
1360 }
1361 else
1362 *aParavirtProvider = ParavirtProvider_None;
1363 break;
1364 }
1365
1366 default: AssertFailedBreak(); /* Shut up MSC. */
1367 }
1368 break;
1369 }
1370 }
1371
1372 Assert( *aParavirtProvider == ParavirtProvider_None
1373 || *aParavirtProvider == ParavirtProvider_Minimal
1374 || *aParavirtProvider == ParavirtProvider_HyperV
1375 || *aParavirtProvider == ParavirtProvider_KVM);
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 aHardwareVersion = mHWData->mHWVersion;
1384
1385 return S_OK;
1386}
1387
1388HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1389{
1390 /* check known version */
1391 Utf8Str hwVersion = aHardwareVersion;
1392 if ( hwVersion.compare("1") != 0
1393 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1394 return setError(E_INVALIDARG,
1395 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1396
1397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 HRESULT rc = i_checkStateDependency(MutableStateDep);
1400 if (FAILED(rc)) return rc;
1401
1402 i_setModified(IsModified_MachineData);
1403 mHWData.backup();
1404 mHWData->mHWVersion = aHardwareVersion;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1410{
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 if (!mHWData->mHardwareUUID.isZero())
1414 aHardwareUUID = mHWData->mHardwareUUID;
1415 else
1416 aHardwareUUID = mData->mUuid;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1422{
1423 if (!aHardwareUUID.isValid())
1424 return E_INVALIDARG;
1425
1426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 HRESULT rc = i_checkStateDependency(MutableStateDep);
1429 if (FAILED(rc)) return rc;
1430
1431 i_setModified(IsModified_MachineData);
1432 mHWData.backup();
1433 if (aHardwareUUID == mData->mUuid)
1434 mHWData->mHardwareUUID.clear();
1435 else
1436 mHWData->mHardwareUUID = aHardwareUUID;
1437
1438 return S_OK;
1439}
1440
1441HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1442{
1443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 *aMemorySize = mHWData->mMemorySize;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::setMemorySize(ULONG aMemorySize)
1451{
1452 /* check RAM limits */
1453 if ( aMemorySize < MM_RAM_MIN_IN_MB
1454 || aMemorySize > MM_RAM_MAX_IN_MB
1455 )
1456 return setError(E_INVALIDARG,
1457 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1458 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 HRESULT rc = i_checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mMemorySize = aMemorySize;
1468
1469 return S_OK;
1470}
1471
1472HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1473{
1474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 *aCPUCount = mHWData->mCPUCount;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::setCPUCount(ULONG aCPUCount)
1482{
1483 /* check CPU limits */
1484 if ( aCPUCount < SchemaDefs::MinCPUCount
1485 || aCPUCount > SchemaDefs::MaxCPUCount
1486 )
1487 return setError(E_INVALIDARG,
1488 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1489 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1490
1491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1494 if (mHWData->mCPUHotPlugEnabled)
1495 {
1496 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1497 {
1498 if (mHWData->mCPUAttached[idx])
1499 return setError(E_INVALIDARG,
1500 tr("There is still a CPU attached to socket %lu."
1501 "Detach the CPU before removing the socket"),
1502 aCPUCount, idx+1);
1503 }
1504 }
1505
1506 HRESULT rc = i_checkStateDependency(MutableStateDep);
1507 if (FAILED(rc)) return rc;
1508
1509 i_setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCPUCount = aCPUCount;
1512
1513 return S_OK;
1514}
1515
1516HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1517{
1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1519
1520 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1526{
1527 HRESULT rc = S_OK;
1528
1529 /* check throttle limits */
1530 if ( aCPUExecutionCap < 1
1531 || aCPUExecutionCap > 100
1532 )
1533 return setError(E_INVALIDARG,
1534 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1535 aCPUExecutionCap, 1, 100);
1536
1537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 alock.release();
1540 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1541 alock.acquire();
1542 if (FAILED(rc)) return rc;
1543
1544 i_setModified(IsModified_MachineData);
1545 mHWData.backup();
1546 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1547
1548 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1549 if (Global::IsOnline(mData->mMachineState))
1550 i_saveSettings(NULL);
1551
1552 return S_OK;
1553}
1554
1555HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1556{
1557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1565{
1566 HRESULT rc = S_OK;
1567
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1574 {
1575 if (aCPUHotPlugEnabled)
1576 {
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579
1580 /* Add the amount of CPUs currently attached */
1581 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1582 mHWData->mCPUAttached[i] = true;
1583 }
1584 else
1585 {
1586 /*
1587 * We can disable hotplug only if the amount of maximum CPUs is equal
1588 * to the amount of attached CPUs
1589 */
1590 unsigned cCpusAttached = 0;
1591 unsigned iHighestId = 0;
1592
1593 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1594 {
1595 if (mHWData->mCPUAttached[i])
1596 {
1597 cCpusAttached++;
1598 iHighestId = i;
1599 }
1600 }
1601
1602 if ( (cCpusAttached != mHWData->mCPUCount)
1603 || (iHighestId >= mHWData->mCPUCount))
1604 return setError(E_INVALIDARG,
1605 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609 }
1610 }
1611
1612 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1613
1614 return rc;
1615}
1616
1617HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1618{
1619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1620
1621 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1622
1623 return S_OK;
1624}
1625
1626HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1627{
1628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1631 if (SUCCEEDED(hrc))
1632 {
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1636 }
1637 return hrc;
1638}
1639
1640HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1641{
1642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1643 aCPUProfile = mHWData->mCpuProfile;
1644 return S_OK;
1645}
1646
1647HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1648{
1649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1650 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1651 if (SUCCEEDED(hrc))
1652 {
1653 i_setModified(IsModified_MachineData);
1654 mHWData.backup();
1655 /* Empty equals 'host'. */
1656 if (aCPUProfile.isNotEmpty())
1657 mHWData->mCpuProfile = aCPUProfile;
1658 else
1659 mHWData->mCpuProfile = "host";
1660 }
1661 return hrc;
1662}
1663
1664HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1665{
1666#ifdef VBOX_WITH_USB_CARDREADER
1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1670
1671 return S_OK;
1672#else
1673 NOREF(aEmulatedUSBCardReaderEnabled);
1674 return E_NOTIMPL;
1675#endif
1676}
1677
1678HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1679{
1680#ifdef VBOX_WITH_USB_CARDREADER
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1684 if (FAILED(rc)) return rc;
1685
1686 i_setModified(IsModified_MachineData);
1687 mHWData.backup();
1688 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1689
1690 return S_OK;
1691#else
1692 NOREF(aEmulatedUSBCardReaderEnabled);
1693 return E_NOTIMPL;
1694#endif
1695}
1696
1697HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 *aHPETEnabled = mHWData->mHPETEnabled;
1702
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1707{
1708 HRESULT rc = S_OK;
1709
1710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 rc = i_checkStateDependency(MutableStateDep);
1713 if (FAILED(rc)) return rc;
1714
1715 i_setModified(IsModified_MachineData);
1716 mHWData.backup();
1717
1718 mHWData->mHPETEnabled = aHPETEnabled;
1719
1720 return rc;
1721}
1722
1723HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1724{
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1728 return S_OK;
1729}
1730
1731HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1732{
1733 HRESULT rc = S_OK;
1734
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 i_setModified(IsModified_MachineData);
1738 mHWData.backup();
1739 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1740
1741 alock.release();
1742 rc = i_onVideoCaptureChange();
1743 alock.acquire();
1744 if (FAILED(rc))
1745 {
1746 /*
1747 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1748 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1749 * determine if it should start or stop capturing. Therefore we need to manually
1750 * undo change.
1751 */
1752 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1753 return rc;
1754 }
1755
1756 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1757 if (Global::IsOnline(mData->mMachineState))
1758 i_saveSettings(NULL);
1759
1760 return rc;
1761}
1762
1763HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1767 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1768 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1769 return S_OK;
1770}
1771
1772HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1773{
1774 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1775 bool fChanged = false;
1776
1777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1778
1779 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1780 {
1781 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1782 {
1783 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1784 fChanged = true;
1785 }
1786 }
1787 if (fChanged)
1788 {
1789 alock.release();
1790 HRESULT rc = i_onVideoCaptureChange();
1791 alock.acquire();
1792 if (FAILED(rc)) return rc;
1793 i_setModified(IsModified_MachineData);
1794
1795 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1796 if (Global::IsOnline(mData->mMachineState))
1797 i_saveSettings(NULL);
1798 }
1799
1800 return S_OK;
1801}
1802
1803HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1804{
1805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806 if (mHWData->mVideoCaptureFile.isEmpty())
1807 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1808 else
1809 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1810 return S_OK;
1811}
1812
1813HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1814{
1815 Utf8Str strFile(aVideoCaptureFile);
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 if (!RTPathStartsWithRoot(strFile.c_str()))
1823 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1824
1825 if (!strFile.isEmpty())
1826 {
1827 Utf8Str defaultFile;
1828 i_getDefaultVideoCaptureFile(defaultFile);
1829 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1830 strFile.setNull();
1831 }
1832
1833 i_setModified(IsModified_MachineData);
1834 mHWData.backup();
1835 mHWData->mVideoCaptureFile = strFile;
1836
1837 return S_OK;
1838}
1839
1840HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1841{
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1844 return S_OK;
1845}
1846
1847HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1848{
1849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 if ( Global::IsOnline(mData->mMachineState)
1852 && mHWData->mVideoCaptureEnabled)
1853 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1854
1855 i_setModified(IsModified_MachineData);
1856 mHWData.backup();
1857 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1858
1859 return S_OK;
1860}
1861
1862HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 if ( Global::IsOnline(mData->mMachineState)
1874 && mHWData->mVideoCaptureEnabled)
1875 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1876
1877 i_setModified(IsModified_MachineData);
1878 mHWData.backup();
1879 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1888 return S_OK;
1889}
1890
1891HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1892{
1893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 if ( Global::IsOnline(mData->mMachineState)
1896 && mHWData->mVideoCaptureEnabled)
1897 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1898
1899 i_setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1902
1903 return S_OK;
1904}
1905
1906HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1907{
1908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1909 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1910 return S_OK;
1911}
1912
1913HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1914{
1915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 if ( Global::IsOnline(mData->mMachineState)
1918 && mHWData->mVideoCaptureEnabled)
1919 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1920
1921 i_setModified(IsModified_MachineData);
1922 mHWData.backup();
1923 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1924
1925 return S_OK;
1926}
1927
1928HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1929{
1930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1931 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1932 return S_OK;
1933}
1934
1935HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1936{
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 if ( Global::IsOnline(mData->mMachineState)
1940 && mHWData->mVideoCaptureEnabled)
1941 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1942
1943 i_setModified(IsModified_MachineData);
1944 mHWData.backup();
1945 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1946
1947 return S_OK;
1948}
1949
1950HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1954 return S_OK;
1955}
1956
1957HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1958{
1959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 if ( Global::IsOnline(mData->mMachineState)
1962 && mHWData->mVideoCaptureEnabled)
1963 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1964
1965 i_setModified(IsModified_MachineData);
1966 mHWData.backup();
1967 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1968
1969 return S_OK;
1970}
1971
1972HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1973{
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1977 return S_OK;
1978}
1979
1980HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1981{
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 if ( Global::IsOnline(mData->mMachineState)
1985 && mHWData->mVideoCaptureEnabled)
1986 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1987
1988 i_setModified(IsModified_MachineData);
1989 mHWData.backup();
1990 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1991
1992 return S_OK;
1993}
1994
1995HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1996{
1997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2000
2001 return S_OK;
2002}
2003
2004HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2005{
2006 switch (aGraphicsControllerType)
2007 {
2008 case GraphicsControllerType_Null:
2009 case GraphicsControllerType_VBoxVGA:
2010#ifdef VBOX_WITH_VMSVGA
2011 case GraphicsControllerType_VMSVGA:
2012#endif
2013 break;
2014 default:
2015 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2016 }
2017
2018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 HRESULT rc = i_checkStateDependency(MutableStateDep);
2021 if (FAILED(rc)) return rc;
2022
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2026
2027 return S_OK;
2028}
2029
2030HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2031{
2032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2033
2034 *aVRAMSize = mHWData->mVRAMSize;
2035
2036 return S_OK;
2037}
2038
2039HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2040{
2041 /* check VRAM limits */
2042 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2043 return setError(E_INVALIDARG,
2044 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2045 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2046
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 HRESULT rc = i_checkStateDependency(MutableStateDep);
2050 if (FAILED(rc)) return rc;
2051
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mVRAMSize = aVRAMSize;
2055
2056 return S_OK;
2057}
2058
2059/** @todo this method should not be public */
2060HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2061{
2062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2065
2066 return S_OK;
2067}
2068
2069/**
2070 * Set the memory balloon size.
2071 *
2072 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2073 * we have to make sure that we never call IGuest from here.
2074 */
2075HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2076{
2077 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2078#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2079 /* check limits */
2080 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2081 return setError(E_INVALIDARG,
2082 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2083 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2084
2085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2086
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2090
2091 return S_OK;
2092#else
2093 NOREF(aMemoryBalloonSize);
2094 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2095#endif
2096}
2097
2098HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2099{
2100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2103 return S_OK;
2104}
2105
2106HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2107{
2108#ifdef VBOX_WITH_PAGE_SHARING
2109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2110
2111 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2112 i_setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2115 return S_OK;
2116#else
2117 NOREF(aPageFusionEnabled);
2118 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2119#endif
2120}
2121
2122HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2123{
2124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2125
2126 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2127
2128 return S_OK;
2129}
2130
2131HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2132{
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = i_checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /** @todo check validity! */
2139
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2143
2144 return S_OK;
2145}
2146
2147
2148HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2149{
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2153
2154 return S_OK;
2155}
2156
2157HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2158{
2159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 HRESULT rc = i_checkStateDependency(MutableStateDep);
2162 if (FAILED(rc)) return rc;
2163
2164 /** @todo check validity! */
2165 i_setModified(IsModified_MachineData);
2166 mHWData.backup();
2167 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2168
2169 return S_OK;
2170}
2171
2172HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2173{
2174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2175
2176 *aMonitorCount = mHWData->mMonitorCount;
2177
2178 return S_OK;
2179}
2180
2181HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2182{
2183 /* make sure monitor count is a sensible number */
2184 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2185 return setError(E_INVALIDARG,
2186 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2187 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2188
2189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 HRESULT rc = i_checkStateDependency(MutableStateDep);
2192 if (FAILED(rc)) return rc;
2193
2194 i_setModified(IsModified_MachineData);
2195 mHWData.backup();
2196 mHWData->mMonitorCount = aMonitorCount;
2197
2198 return S_OK;
2199}
2200
2201HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2202{
2203 /* mBIOSSettings is constant during life time, no need to lock */
2204 aBIOSSettings = mBIOSSettings;
2205
2206 return S_OK;
2207}
2208
2209HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2210{
2211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2212
2213 switch (aProperty)
2214 {
2215 case CPUPropertyType_PAE:
2216 *aValue = mHWData->mPAEEnabled;
2217 break;
2218
2219 case CPUPropertyType_LongMode:
2220 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2221 *aValue = TRUE;
2222 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2223 *aValue = FALSE;
2224#if HC_ARCH_BITS == 64
2225 else
2226 *aValue = TRUE;
2227#else
2228 else
2229 {
2230 *aValue = FALSE;
2231
2232 ComObjPtr<GuestOSType> pGuestOSType;
2233 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2234 pGuestOSType);
2235 if (SUCCEEDED(hrc2))
2236 {
2237 if (pGuestOSType->i_is64Bit())
2238 {
2239 ComObjPtr<Host> pHost = mParent->i_host();
2240 alock.release();
2241
2242 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2243 if (FAILED(hrc2))
2244 *aValue = FALSE;
2245 }
2246 }
2247 }
2248#endif
2249 break;
2250
2251 case CPUPropertyType_TripleFaultReset:
2252 *aValue = mHWData->mTripleFaultReset;
2253 break;
2254
2255 case CPUPropertyType_APIC:
2256 *aValue = mHWData->mAPIC;
2257 break;
2258
2259 case CPUPropertyType_X2APIC:
2260 *aValue = mHWData->mX2APIC;
2261 break;
2262
2263 case CPUPropertyType_IBPBOnVMExit:
2264 *aValue = mHWData->mIBPBOnVMExit;
2265 break;
2266
2267 case CPUPropertyType_IBPBOnVMEntry:
2268 *aValue = mHWData->mIBPBOnVMEntry;
2269 break;
2270
2271 case CPUPropertyType_SpecCtrl:
2272 *aValue = mHWData->mSpecCtrl;
2273 break;
2274
2275 case CPUPropertyType_SpecCtrlByHost:
2276 *aValue = mHWData->mSpecCtrlByHost;
2277 break;
2278
2279 case CPUPropertyType_HWVirt:
2280 *aValue = mHWData->mNestedHWVirt;
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2290{
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 HRESULT rc = i_checkStateDependency(MutableStateDep);
2294 if (FAILED(rc)) return rc;
2295
2296 switch (aProperty)
2297 {
2298 case CPUPropertyType_PAE:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mPAEEnabled = !!aValue;
2302 break;
2303
2304 case CPUPropertyType_LongMode:
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2308 break;
2309
2310 case CPUPropertyType_TripleFaultReset:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mTripleFaultReset = !!aValue;
2314 break;
2315
2316 case CPUPropertyType_APIC:
2317 if (mHWData->mX2APIC)
2318 aValue = TRUE;
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mAPIC = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_X2APIC:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mX2APIC = !!aValue;
2328 if (aValue)
2329 mHWData->mAPIC = !!aValue;
2330 break;
2331
2332 case CPUPropertyType_IBPBOnVMExit:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mIBPBOnVMExit = !!aValue;
2336 break;
2337
2338 case CPUPropertyType_IBPBOnVMEntry:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mIBPBOnVMEntry = !!aValue;
2342 break;
2343
2344 case CPUPropertyType_SpecCtrl:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mSpecCtrl = !!aValue;
2348 break;
2349
2350 case CPUPropertyType_SpecCtrlByHost:
2351 i_setModified(IsModified_MachineData);
2352 mHWData.backup();
2353 mHWData->mSpecCtrlByHost = !!aValue;
2354 break;
2355
2356 case CPUPropertyType_HWVirt:
2357 i_setModified(IsModified_MachineData);
2358 mHWData.backup();
2359 mHWData->mNestedHWVirt = !!aValue;
2360 break;
2361
2362 default:
2363 return E_INVALIDARG;
2364 }
2365 return S_OK;
2366}
2367
2368HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2369 ULONG *aValEcx, ULONG *aValEdx)
2370{
2371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2372 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2373 {
2374 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2375 it != mHWData->mCpuIdLeafList.end();
2376 ++it)
2377 {
2378 if (aOrdinal == 0)
2379 {
2380 const settings::CpuIdLeaf &rLeaf= *it;
2381 *aIdx = rLeaf.idx;
2382 *aSubIdx = rLeaf.idxSub;
2383 *aValEax = rLeaf.uEax;
2384 *aValEbx = rLeaf.uEbx;
2385 *aValEcx = rLeaf.uEcx;
2386 *aValEdx = rLeaf.uEdx;
2387 return S_OK;
2388 }
2389 aOrdinal--;
2390 }
2391 }
2392 return E_INVALIDARG;
2393}
2394
2395HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2396{
2397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 /*
2400 * Search the list.
2401 */
2402 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2403 {
2404 const settings::CpuIdLeaf &rLeaf= *it;
2405 if ( rLeaf.idx == aIdx
2406 && ( aSubIdx == UINT32_MAX
2407 || rLeaf.idxSub == aSubIdx) )
2408 {
2409 *aValEax = rLeaf.uEax;
2410 *aValEbx = rLeaf.uEbx;
2411 *aValEcx = rLeaf.uEcx;
2412 *aValEdx = rLeaf.uEdx;
2413 return S_OK;
2414 }
2415 }
2416
2417 return E_INVALIDARG;
2418}
2419
2420
2421HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2422{
2423 /*
2424 * Validate input before taking locks and checking state.
2425 */
2426 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2427 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2428 if ( aIdx >= UINT32_C(0x20)
2429 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2430 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2431 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2432
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434 HRESULT rc = i_checkStateDependency(MutableStateDep);
2435 if (FAILED(rc)) return rc;
2436
2437 /*
2438 * Impose a maximum number of leaves.
2439 */
2440 if (mHWData->mCpuIdLeafList.size() > 256)
2441 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2442
2443 /*
2444 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2445 */
2446 i_setModified(IsModified_MachineData);
2447 mHWData.backup();
2448
2449 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2450 {
2451 settings::CpuIdLeaf &rLeaf= *it;
2452 if ( rLeaf.idx == aIdx
2453 && ( aSubIdx == UINT32_MAX
2454 || rLeaf.idxSub == aSubIdx) )
2455 it = mHWData->mCpuIdLeafList.erase(it);
2456 else
2457 ++it;
2458 }
2459
2460 settings::CpuIdLeaf NewLeaf;
2461 NewLeaf.idx = aIdx;
2462 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2463 NewLeaf.uEax = aValEax;
2464 NewLeaf.uEbx = aValEbx;
2465 NewLeaf.uEcx = aValEcx;
2466 NewLeaf.uEdx = aValEdx;
2467 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2468 return S_OK;
2469}
2470
2471HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2472{
2473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2474
2475 HRESULT rc = i_checkStateDependency(MutableStateDep);
2476 if (FAILED(rc)) return rc;
2477
2478 /*
2479 * Do the removal.
2480 */
2481 bool fModified = false;
2482 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2483 {
2484 settings::CpuIdLeaf &rLeaf= *it;
2485 if ( rLeaf.idx == aIdx
2486 && ( aSubIdx == UINT32_MAX
2487 || rLeaf.idxSub == aSubIdx) )
2488 {
2489 if (!fModified)
2490 {
2491 fModified = true;
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494 }
2495 it = mHWData->mCpuIdLeafList.erase(it);
2496 }
2497 else
2498 ++it;
2499 }
2500
2501 return S_OK;
2502}
2503
2504HRESULT Machine::removeAllCPUIDLeaves()
2505{
2506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 HRESULT rc = i_checkStateDependency(MutableStateDep);
2509 if (FAILED(rc)) return rc;
2510
2511 if (mHWData->mCpuIdLeafList.size() > 0)
2512 {
2513 i_setModified(IsModified_MachineData);
2514 mHWData.backup();
2515
2516 mHWData->mCpuIdLeafList.clear();
2517 }
2518
2519 return S_OK;
2520}
2521HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 switch(aProperty)
2526 {
2527 case HWVirtExPropertyType_Enabled:
2528 *aValue = mHWData->mHWVirtExEnabled;
2529 break;
2530
2531 case HWVirtExPropertyType_VPID:
2532 *aValue = mHWData->mHWVirtExVPIDEnabled;
2533 break;
2534
2535 case HWVirtExPropertyType_NestedPaging:
2536 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2537 break;
2538
2539 case HWVirtExPropertyType_UnrestrictedExecution:
2540 *aValue = mHWData->mHWVirtExUXEnabled;
2541 break;
2542
2543 case HWVirtExPropertyType_LargePages:
2544 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2545#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2546 *aValue = FALSE;
2547#endif
2548 break;
2549
2550 case HWVirtExPropertyType_Force:
2551 *aValue = mHWData->mHWVirtExForceEnabled;
2552 break;
2553
2554 default:
2555 return E_INVALIDARG;
2556 }
2557 return S_OK;
2558}
2559
2560HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2561{
2562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 HRESULT rc = i_checkStateDependency(MutableStateDep);
2565 if (FAILED(rc)) return rc;
2566
2567 switch(aProperty)
2568 {
2569 case HWVirtExPropertyType_Enabled:
2570 i_setModified(IsModified_MachineData);
2571 mHWData.backup();
2572 mHWData->mHWVirtExEnabled = !!aValue;
2573 break;
2574
2575 case HWVirtExPropertyType_VPID:
2576 i_setModified(IsModified_MachineData);
2577 mHWData.backup();
2578 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2579 break;
2580
2581 case HWVirtExPropertyType_NestedPaging:
2582 i_setModified(IsModified_MachineData);
2583 mHWData.backup();
2584 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2585 break;
2586
2587 case HWVirtExPropertyType_UnrestrictedExecution:
2588 i_setModified(IsModified_MachineData);
2589 mHWData.backup();
2590 mHWData->mHWVirtExUXEnabled = !!aValue;
2591 break;
2592
2593 case HWVirtExPropertyType_LargePages:
2594 i_setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2597 break;
2598
2599 case HWVirtExPropertyType_Force:
2600 i_setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mHWVirtExForceEnabled = !!aValue;
2603 break;
2604
2605 default:
2606 return E_INVALIDARG;
2607 }
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2613{
2614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2617
2618 return S_OK;
2619}
2620
2621HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2622{
2623 /** @todo (r=dmik):
2624 * 1. Allow to change the name of the snapshot folder containing snapshots
2625 * 2. Rename the folder on disk instead of just changing the property
2626 * value (to be smart and not to leave garbage). Note that it cannot be
2627 * done here because the change may be rolled back. Thus, the right
2628 * place is #saveSettings().
2629 */
2630
2631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 HRESULT rc = i_checkStateDependency(MutableStateDep);
2634 if (FAILED(rc)) return rc;
2635
2636 if (!mData->mCurrentSnapshot.isNull())
2637 return setError(E_FAIL,
2638 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2639
2640 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2641
2642 if (strSnapshotFolder.isEmpty())
2643 strSnapshotFolder = "Snapshots";
2644 int vrc = i_calculateFullPath(strSnapshotFolder,
2645 strSnapshotFolder);
2646 if (RT_FAILURE(vrc))
2647 return setError(E_FAIL,
2648 tr("Invalid snapshot folder '%s' (%Rrc)"),
2649 strSnapshotFolder.c_str(), vrc);
2650
2651 i_setModified(IsModified_MachineData);
2652 mUserData.backup();
2653
2654 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 aMediumAttachments.resize(mMediumAttachments->size());
2664 size_t i = 0;
2665 for (MediumAttachmentList::const_iterator
2666 it = mMediumAttachments->begin();
2667 it != mMediumAttachments->end();
2668 ++it, ++i)
2669 aMediumAttachments[i] = *it;
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 Assert(!!mVRDEServer);
2679
2680 aVRDEServer = mVRDEServer;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2686{
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 aAudioAdapter = mAudioAdapter;
2690
2691 return S_OK;
2692}
2693
2694HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2695{
2696#ifdef VBOX_WITH_VUSB
2697 clearError();
2698 MultiResult rc(S_OK);
2699
2700# ifdef VBOX_WITH_USB
2701 rc = mParent->i_host()->i_checkUSBProxyService();
2702 if (FAILED(rc)) return rc;
2703# endif
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 aUSBControllers.resize(mUSBControllers->size());
2708 size_t i = 0;
2709 for (USBControllerList::const_iterator
2710 it = mUSBControllers->begin();
2711 it != mUSBControllers->end();
2712 ++it, ++i)
2713 aUSBControllers[i] = *it;
2714
2715 return S_OK;
2716#else
2717 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2718 * extended error info to indicate that USB is simply not available
2719 * (w/o treating it as a failure), for example, as in OSE */
2720 NOREF(aUSBControllers);
2721 ReturnComNotImplemented();
2722#endif /* VBOX_WITH_VUSB */
2723}
2724
2725HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2726{
2727#ifdef VBOX_WITH_VUSB
2728 clearError();
2729 MultiResult rc(S_OK);
2730
2731# ifdef VBOX_WITH_USB
2732 rc = mParent->i_host()->i_checkUSBProxyService();
2733 if (FAILED(rc)) return rc;
2734# endif
2735
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 aUSBDeviceFilters = mUSBDeviceFilters;
2739 return rc;
2740#else
2741 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2742 * extended error info to indicate that USB is simply not available
2743 * (w/o treating it as a failure), for example, as in OSE */
2744 NOREF(aUSBDeviceFilters);
2745 ReturnComNotImplemented();
2746#endif /* VBOX_WITH_VUSB */
2747}
2748
2749HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2750{
2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 aSettingsFilePath = mData->m_strConfigFileFull;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2759{
2760 RT_NOREF(aSettingsFilePath);
2761 ReturnComNotImplemented();
2762}
2763
2764HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2765{
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2769 if (FAILED(rc)) return rc;
2770
2771 if (!mData->pMachineConfigFile->fileExists())
2772 // this is a new machine, and no config file exists yet:
2773 *aSettingsModified = TRUE;
2774 else
2775 *aSettingsModified = (mData->flModifications != 0);
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2781{
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 *aSessionState = mData->mSession.mState;
2785
2786 return S_OK;
2787}
2788
2789HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2790{
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 aSessionName = mData->mSession.mName;
2794
2795 return S_OK;
2796}
2797
2798HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2799{
2800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 *aSessionPID = mData->mSession.mPID;
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getState(MachineState_T *aState)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 *aState = mData->mMachineState;
2812 Assert(mData->mMachineState != MachineState_Null);
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2827{
2828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 aStateFilePath = mSSData->strStateFilePath;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2836{
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 i_getLogFolder(aLogFolder);
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 aCurrentSnapshot = mData->mCurrentSnapshot;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2854{
2855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2858 ? 0
2859 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2860
2861 return S_OK;
2862}
2863
2864HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 /* Note: for machines with no snapshots, we always return FALSE
2869 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2870 * reasons :) */
2871
2872 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2873 ? FALSE
2874 : mData->mCurrentStateModified;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 aSharedFolders.resize(mHWData->mSharedFolders.size());
2884 size_t i = 0;
2885 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2886 it = mHWData->mSharedFolders.begin();
2887 it != mHWData->mSharedFolders.end();
2888 ++it, ++i)
2889 aSharedFolders[i] = *it;
2890
2891 return S_OK;
2892}
2893
2894HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2895{
2896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2897
2898 *aClipboardMode = mHWData->mClipboardMode;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2904{
2905 HRESULT rc = S_OK;
2906
2907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 alock.release();
2910 rc = i_onClipboardModeChange(aClipboardMode);
2911 alock.acquire();
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mHWData.backup();
2916 mHWData->mClipboardMode = aClipboardMode;
2917
2918 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2919 if (Global::IsOnline(mData->mMachineState))
2920 i_saveSettings(NULL);
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aDnDMode = mHWData->mDnDMode;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2935{
2936 HRESULT rc = S_OK;
2937
2938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 alock.release();
2941 rc = i_onDnDModeChange(aDnDMode);
2942
2943 alock.acquire();
2944 if (FAILED(rc)) return rc;
2945
2946 i_setModified(IsModified_MachineData);
2947 mHWData.backup();
2948 mHWData->mDnDMode = aDnDMode;
2949
2950 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2951 if (Global::IsOnline(mData->mMachineState))
2952 i_saveSettings(NULL);
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2958{
2959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 aStorageControllers.resize(mStorageControllers->size());
2962 size_t i = 0;
2963 for (StorageControllerList::const_iterator
2964 it = mStorageControllers->begin();
2965 it != mStorageControllers->end();
2966 ++it, ++i)
2967 aStorageControllers[i] = *it;
2968
2969 return S_OK;
2970}
2971
2972HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2973{
2974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 *aEnabled = mUserData->s.fTeleporterEnabled;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2982{
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 /* Only allow it to be set to true when PoweredOff or Aborted.
2986 (Clearing it is always permitted.) */
2987 if ( aTeleporterEnabled
2988 && mData->mRegistered
2989 && ( !i_isSessionMachine()
2990 || ( mData->mMachineState != MachineState_PoweredOff
2991 && mData->mMachineState != MachineState_Teleported
2992 && mData->mMachineState != MachineState_Aborted
2993 )
2994 )
2995 )
2996 return setError(VBOX_E_INVALID_VM_STATE,
2997 tr("The machine is not powered off (state is %s)"),
2998 Global::stringifyMachineState(mData->mMachineState));
2999
3000 i_setModified(IsModified_MachineData);
3001 mUserData.backup();
3002 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3008{
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3012
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3017{
3018 if (aTeleporterPort >= _64K)
3019 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3020
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3024 if (FAILED(rc)) return rc;
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3029
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3043{
3044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3047 if (FAILED(rc)) return rc;
3048
3049 i_setModified(IsModified_MachineData);
3050 mUserData.backup();
3051 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3052
3053 return S_OK;
3054}
3055
3056HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3057{
3058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3059 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3060
3061 return S_OK;
3062}
3063
3064HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3065{
3066 /*
3067 * Hash the password first.
3068 */
3069 com::Utf8Str aT = aTeleporterPassword;
3070
3071 if (!aT.isEmpty())
3072 {
3073 if (VBoxIsPasswordHashed(&aT))
3074 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3075 VBoxHashPassword(&aT);
3076 }
3077
3078 /*
3079 * Do the update.
3080 */
3081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3082 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3083 if (SUCCEEDED(hrc))
3084 {
3085 i_setModified(IsModified_MachineData);
3086 mUserData.backup();
3087 mUserData->s.strTeleporterPassword = aT;
3088 }
3089
3090 return hrc;
3091}
3092
3093HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3094{
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096
3097 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3098 return S_OK;
3099}
3100
3101HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3102{
3103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 /** @todo deal with running state change. */
3106 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3107 if (FAILED(rc)) return rc;
3108
3109 i_setModified(IsModified_MachineData);
3110 mUserData.backup();
3111 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3112 return S_OK;
3113}
3114
3115HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3116{
3117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3118
3119 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3124{
3125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 /** @todo deal with running state change. */
3128 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3129 if (FAILED(rc)) return rc;
3130
3131 i_setModified(IsModified_MachineData);
3132 mUserData.backup();
3133 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3134 return S_OK;
3135}
3136
3137HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3138{
3139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3142 return S_OK;
3143}
3144
3145HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3146{
3147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3148
3149 /** @todo deal with running state change. */
3150 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3151 if (FAILED(rc)) return rc;
3152
3153 i_setModified(IsModified_MachineData);
3154 mUserData.backup();
3155 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3156 return S_OK;
3157}
3158
3159HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3160{
3161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3162
3163 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3164
3165 return S_OK;
3166}
3167
3168HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3169{
3170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 /** @todo deal with running state change. */
3173 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3174 if (FAILED(rc)) return rc;
3175
3176 i_setModified(IsModified_MachineData);
3177 mUserData.backup();
3178 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3179
3180 return S_OK;
3181}
3182
3183HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3184{
3185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3186
3187 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3188 return S_OK;
3189}
3190
3191HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3192{
3193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3194
3195 /** @todo deal with running state change. */
3196 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3197 if (FAILED(rc)) return rc;
3198
3199 i_setModified(IsModified_MachineData);
3200 mUserData.backup();
3201 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3202 return S_OK;
3203}
3204
3205HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3206{
3207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3208
3209 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3210
3211 return S_OK;
3212}
3213
3214HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3215{
3216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3217
3218 /* Only allow it to be set to true when PoweredOff or Aborted.
3219 (Clearing it is always permitted.) */
3220 if ( aRTCUseUTC
3221 && mData->mRegistered
3222 && ( !i_isSessionMachine()
3223 || ( mData->mMachineState != MachineState_PoweredOff
3224 && mData->mMachineState != MachineState_Teleported
3225 && mData->mMachineState != MachineState_Aborted
3226 )
3227 )
3228 )
3229 return setError(VBOX_E_INVALID_VM_STATE,
3230 tr("The machine is not powered off (state is %s)"),
3231 Global::stringifyMachineState(mData->mMachineState));
3232
3233 i_setModified(IsModified_MachineData);
3234 mUserData.backup();
3235 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3236
3237 return S_OK;
3238}
3239
3240HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3241{
3242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3245
3246 return S_OK;
3247}
3248
3249HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3250{
3251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3252
3253 HRESULT rc = i_checkStateDependency(MutableStateDep);
3254 if (FAILED(rc)) return rc;
3255
3256 i_setModified(IsModified_MachineData);
3257 mHWData.backup();
3258 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3259
3260 return S_OK;
3261}
3262
3263HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3264{
3265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3266
3267 *aIOCacheSize = mHWData->mIOCacheSize;
3268
3269 return S_OK;
3270}
3271
3272HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3273{
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 HRESULT rc = i_checkStateDependency(MutableStateDep);
3277 if (FAILED(rc)) return rc;
3278
3279 i_setModified(IsModified_MachineData);
3280 mHWData.backup();
3281 mHWData->mIOCacheSize = aIOCacheSize;
3282
3283 return S_OK;
3284}
3285
3286
3287/**
3288 * @note Locks objects!
3289 */
3290HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3291 LockType_T aLockType)
3292{
3293 /* check the session state */
3294 SessionState_T state;
3295 HRESULT rc = aSession->COMGETTER(State)(&state);
3296 if (FAILED(rc)) return rc;
3297
3298 if (state != SessionState_Unlocked)
3299 return setError(VBOX_E_INVALID_OBJECT_STATE,
3300 tr("The given session is busy"));
3301
3302 // get the client's IInternalSessionControl interface
3303 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3304 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3305 E_INVALIDARG);
3306
3307 // session name (only used in some code paths)
3308 Utf8Str strSessionName;
3309
3310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3311
3312 if (!mData->mRegistered)
3313 return setError(E_UNEXPECTED,
3314 tr("The machine '%s' is not registered"),
3315 mUserData->s.strName.c_str());
3316
3317 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3318
3319 SessionState_T oldState = mData->mSession.mState;
3320 /* Hack: in case the session is closing and there is a progress object
3321 * which allows waiting for the session to be closed, take the opportunity
3322 * and do a limited wait (max. 1 second). This helps a lot when the system
3323 * is busy and thus session closing can take a little while. */
3324 if ( mData->mSession.mState == SessionState_Unlocking
3325 && mData->mSession.mProgress)
3326 {
3327 alock.release();
3328 mData->mSession.mProgress->WaitForCompletion(1000);
3329 alock.acquire();
3330 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3331 }
3332
3333 // try again now
3334 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3335 // (i.e. session machine exists)
3336 && (aLockType == LockType_Shared) // caller wants a shared link to the
3337 // existing session that holds the write lock:
3338 )
3339 {
3340 // OK, share the session... we are now dealing with three processes:
3341 // 1) VBoxSVC (where this code runs);
3342 // 2) process C: the caller's client process (who wants a shared session);
3343 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3344
3345 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3346 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3347 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3348 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3349 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3350
3351 /*
3352 * Release the lock before calling the client process. It's safe here
3353 * since the only thing to do after we get the lock again is to add
3354 * the remote control to the list (which doesn't directly influence
3355 * anything).
3356 */
3357 alock.release();
3358
3359 // get the console of the session holding the write lock (this is a remote call)
3360 ComPtr<IConsole> pConsoleW;
3361 if (mData->mSession.mLockType == LockType_VM)
3362 {
3363 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3364 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3365 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3366 if (FAILED(rc))
3367 // the failure may occur w/o any error info (from RPC), so provide one
3368 return setError(VBOX_E_VM_ERROR,
3369 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3370 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3371 }
3372
3373 // share the session machine and W's console with the caller's session
3374 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3375 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3376 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3377
3378 if (FAILED(rc))
3379 // the failure may occur w/o any error info (from RPC), so provide one
3380 return setError(VBOX_E_VM_ERROR,
3381 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3382 alock.acquire();
3383
3384 // need to revalidate the state after acquiring the lock again
3385 if (mData->mSession.mState != SessionState_Locked)
3386 {
3387 pSessionControl->Uninitialize();
3388 return setError(VBOX_E_INVALID_SESSION_STATE,
3389 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3390 mUserData->s.strName.c_str());
3391 }
3392
3393 // add the caller's session to the list
3394 mData->mSession.mRemoteControls.push_back(pSessionControl);
3395 }
3396 else if ( mData->mSession.mState == SessionState_Locked
3397 || mData->mSession.mState == SessionState_Unlocking
3398 )
3399 {
3400 // sharing not permitted, or machine still unlocking:
3401 return setError(VBOX_E_INVALID_OBJECT_STATE,
3402 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3403 mUserData->s.strName.c_str());
3404 }
3405 else
3406 {
3407 // machine is not locked: then write-lock the machine (create the session machine)
3408
3409 // must not be busy
3410 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3411
3412 // get the caller's session PID
3413 RTPROCESS pid = NIL_RTPROCESS;
3414 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3415 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3416 Assert(pid != NIL_RTPROCESS);
3417
3418 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3419
3420 if (fLaunchingVMProcess)
3421 {
3422 if (mData->mSession.mPID == NIL_RTPROCESS)
3423 {
3424 // two or more clients racing for a lock, the one which set the
3425 // session state to Spawning will win, the others will get an
3426 // error as we can't decide here if waiting a little would help
3427 // (only for shared locks this would avoid an error)
3428 return setError(VBOX_E_INVALID_OBJECT_STATE,
3429 tr("The machine '%s' already has a lock request pending"),
3430 mUserData->s.strName.c_str());
3431 }
3432
3433 // this machine is awaiting for a spawning session to be opened:
3434 // then the calling process must be the one that got started by
3435 // LaunchVMProcess()
3436
3437 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3438 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3439
3440#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3441 /* Hardened windows builds spawns three processes when a VM is
3442 launched, the 3rd one is the one that will end up here. */
3443 RTPROCESS ppid;
3444 int rc = RTProcQueryParent(pid, &ppid);
3445 if (RT_SUCCESS(rc))
3446 rc = RTProcQueryParent(ppid, &ppid);
3447 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3448 || rc == VERR_ACCESS_DENIED)
3449 {
3450 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3451 mData->mSession.mPID = pid;
3452 }
3453#endif
3454
3455 if (mData->mSession.mPID != pid)
3456 return setError(E_ACCESSDENIED,
3457 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3458 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3459 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3460 }
3461
3462 // create the mutable SessionMachine from the current machine
3463 ComObjPtr<SessionMachine> sessionMachine;
3464 sessionMachine.createObject();
3465 rc = sessionMachine->init(this);
3466 AssertComRC(rc);
3467
3468 /* NOTE: doing return from this function after this point but
3469 * before the end is forbidden since it may call SessionMachine::uninit()
3470 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3471 * lock while still holding the Machine lock in alock so that a deadlock
3472 * is possible due to the wrong lock order. */
3473
3474 if (SUCCEEDED(rc))
3475 {
3476 /*
3477 * Set the session state to Spawning to protect against subsequent
3478 * attempts to open a session and to unregister the machine after
3479 * we release the lock.
3480 */
3481 SessionState_T origState = mData->mSession.mState;
3482 mData->mSession.mState = SessionState_Spawning;
3483
3484#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3485 /* Get the client token ID to be passed to the client process */
3486 Utf8Str strTokenId;
3487 sessionMachine->i_getTokenId(strTokenId);
3488 Assert(!strTokenId.isEmpty());
3489#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3490 /* Get the client token to be passed to the client process */
3491 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3492 /* The token is now "owned" by pToken, fix refcount */
3493 if (!pToken.isNull())
3494 pToken->Release();
3495#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3496
3497 /*
3498 * Release the lock before calling the client process -- it will call
3499 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3500 * because the state is Spawning, so that LaunchVMProcess() and
3501 * LockMachine() calls will fail. This method, called before we
3502 * acquire the lock again, will fail because of the wrong PID.
3503 *
3504 * Note that mData->mSession.mRemoteControls accessed outside
3505 * the lock may not be modified when state is Spawning, so it's safe.
3506 */
3507 alock.release();
3508
3509 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3510#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3511 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3512#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3513 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3514 /* Now the token is owned by the client process. */
3515 pToken.setNull();
3516#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3517 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3518
3519 /* The failure may occur w/o any error info (from RPC), so provide one */
3520 if (FAILED(rc))
3521 setError(VBOX_E_VM_ERROR,
3522 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3523
3524 // get session name, either to remember or to compare against
3525 // the already known session name.
3526 {
3527 Bstr bstrSessionName;
3528 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3529 if (SUCCEEDED(rc2))
3530 strSessionName = bstrSessionName;
3531 }
3532
3533 if ( SUCCEEDED(rc)
3534 && fLaunchingVMProcess
3535 )
3536 {
3537 /* complete the remote session initialization */
3538
3539 /* get the console from the direct session */
3540 ComPtr<IConsole> console;
3541 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3542 ComAssertComRC(rc);
3543
3544 if (SUCCEEDED(rc) && !console)
3545 {
3546 ComAssert(!!console);
3547 rc = E_FAIL;
3548 }
3549
3550 /* assign machine & console to the remote session */
3551 if (SUCCEEDED(rc))
3552 {
3553 /*
3554 * after LaunchVMProcess(), the first and the only
3555 * entry in remoteControls is that remote session
3556 */
3557 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3558 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3559 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3560
3561 /* The failure may occur w/o any error info (from RPC), so provide one */
3562 if (FAILED(rc))
3563 setError(VBOX_E_VM_ERROR,
3564 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3565 }
3566
3567 if (FAILED(rc))
3568 pSessionControl->Uninitialize();
3569 }
3570
3571 /* acquire the lock again */
3572 alock.acquire();
3573
3574 /* Restore the session state */
3575 mData->mSession.mState = origState;
3576 }
3577
3578 // finalize spawning anyway (this is why we don't return on errors above)
3579 if (fLaunchingVMProcess)
3580 {
3581 Assert(mData->mSession.mName == strSessionName);
3582 /* Note that the progress object is finalized later */
3583 /** @todo Consider checking mData->mSession.mProgress for cancellation
3584 * around here. */
3585
3586 /* We don't reset mSession.mPID here because it is necessary for
3587 * SessionMachine::uninit() to reap the child process later. */
3588
3589 if (FAILED(rc))
3590 {
3591 /* Close the remote session, remove the remote control from the list
3592 * and reset session state to Closed (@note keep the code in sync
3593 * with the relevant part in checkForSpawnFailure()). */
3594
3595 Assert(mData->mSession.mRemoteControls.size() == 1);
3596 if (mData->mSession.mRemoteControls.size() == 1)
3597 {
3598 ErrorInfoKeeper eik;
3599 mData->mSession.mRemoteControls.front()->Uninitialize();
3600 }
3601
3602 mData->mSession.mRemoteControls.clear();
3603 mData->mSession.mState = SessionState_Unlocked;
3604 }
3605 }
3606 else
3607 {
3608 /* memorize PID of the directly opened session */
3609 if (SUCCEEDED(rc))
3610 mData->mSession.mPID = pid;
3611 }
3612
3613 if (SUCCEEDED(rc))
3614 {
3615 mData->mSession.mLockType = aLockType;
3616 /* memorize the direct session control and cache IUnknown for it */
3617 mData->mSession.mDirectControl = pSessionControl;
3618 mData->mSession.mState = SessionState_Locked;
3619 if (!fLaunchingVMProcess)
3620 mData->mSession.mName = strSessionName;
3621 /* associate the SessionMachine with this Machine */
3622 mData->mSession.mMachine = sessionMachine;
3623
3624 /* request an IUnknown pointer early from the remote party for later
3625 * identity checks (it will be internally cached within mDirectControl
3626 * at least on XPCOM) */
3627 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3628 NOREF(unk);
3629 }
3630
3631 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3632 * would break the lock order */
3633 alock.release();
3634
3635 /* uninitialize the created session machine on failure */
3636 if (FAILED(rc))
3637 sessionMachine->uninit();
3638 }
3639
3640 if (SUCCEEDED(rc))
3641 {
3642 /*
3643 * tell the client watcher thread to update the set of
3644 * machines that have open sessions
3645 */
3646 mParent->i_updateClientWatcher();
3647
3648 if (oldState != SessionState_Locked)
3649 /* fire an event */
3650 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3651 }
3652
3653 return rc;
3654}
3655
3656/**
3657 * @note Locks objects!
3658 */
3659HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3660 const com::Utf8Str &aName,
3661 const com::Utf8Str &aEnvironment,
3662 ComPtr<IProgress> &aProgress)
3663{
3664 Utf8Str strFrontend(aName);
3665 /* "emergencystop" doesn't need the session, so skip the checks/interface
3666 * retrieval. This code doesn't quite fit in here, but introducing a
3667 * special API method would be even more effort, and would require explicit
3668 * support by every API client. It's better to hide the feature a bit. */
3669 if (strFrontend != "emergencystop")
3670 CheckComArgNotNull(aSession);
3671
3672 HRESULT rc = S_OK;
3673 if (strFrontend.isEmpty())
3674 {
3675 Bstr bstrFrontend;
3676 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3677 if (FAILED(rc))
3678 return rc;
3679 strFrontend = bstrFrontend;
3680 if (strFrontend.isEmpty())
3681 {
3682 ComPtr<ISystemProperties> systemProperties;
3683 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3684 if (FAILED(rc))
3685 return rc;
3686 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3687 if (FAILED(rc))
3688 return rc;
3689 strFrontend = bstrFrontend;
3690 }
3691 /* paranoia - emergencystop is not a valid default */
3692 if (strFrontend == "emergencystop")
3693 strFrontend = Utf8Str::Empty;
3694 }
3695 /* default frontend: Qt GUI */
3696 if (strFrontend.isEmpty())
3697 strFrontend = "GUI/Qt";
3698
3699 if (strFrontend != "emergencystop")
3700 {
3701 /* check the session state */
3702 SessionState_T state;
3703 rc = aSession->COMGETTER(State)(&state);
3704 if (FAILED(rc))
3705 return rc;
3706
3707 if (state != SessionState_Unlocked)
3708 return setError(VBOX_E_INVALID_OBJECT_STATE,
3709 tr("The given session is busy"));
3710
3711 /* get the IInternalSessionControl interface */
3712 ComPtr<IInternalSessionControl> control(aSession);
3713 ComAssertMsgRet(!control.isNull(),
3714 ("No IInternalSessionControl interface"),
3715 E_INVALIDARG);
3716
3717 /* get the teleporter enable state for the progress object init. */
3718 BOOL fTeleporterEnabled;
3719 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3720 if (FAILED(rc))
3721 return rc;
3722
3723 /* create a progress object */
3724 ComObjPtr<ProgressProxy> progress;
3725 progress.createObject();
3726 rc = progress->init(mParent,
3727 static_cast<IMachine*>(this),
3728 Bstr(tr("Starting VM")).raw(),
3729 TRUE /* aCancelable */,
3730 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3731 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3732 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3733 2 /* uFirstOperationWeight */,
3734 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3735
3736 if (SUCCEEDED(rc))
3737 {
3738 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3739 if (SUCCEEDED(rc))
3740 {
3741 aProgress = progress;
3742
3743 /* signal the client watcher thread */
3744 mParent->i_updateClientWatcher();
3745
3746 /* fire an event */
3747 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3748 }
3749 }
3750 }
3751 else
3752 {
3753 /* no progress object - either instant success or failure */
3754 aProgress = NULL;
3755
3756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3757
3758 if (mData->mSession.mState != SessionState_Locked)
3759 return setError(VBOX_E_INVALID_OBJECT_STATE,
3760 tr("The machine '%s' is not locked by a session"),
3761 mUserData->s.strName.c_str());
3762
3763 /* must have a VM process associated - do not kill normal API clients
3764 * with an open session */
3765 if (!Global::IsOnline(mData->mMachineState))
3766 return setError(VBOX_E_INVALID_OBJECT_STATE,
3767 tr("The machine '%s' does not have a VM process"),
3768 mUserData->s.strName.c_str());
3769
3770 /* forcibly terminate the VM process */
3771 if (mData->mSession.mPID != NIL_RTPROCESS)
3772 RTProcTerminate(mData->mSession.mPID);
3773
3774 /* signal the client watcher thread, as most likely the client has
3775 * been terminated */
3776 mParent->i_updateClientWatcher();
3777 }
3778
3779 return rc;
3780}
3781
3782HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3783{
3784 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3785 return setError(E_INVALIDARG,
3786 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3787 aPosition, SchemaDefs::MaxBootPosition);
3788
3789 if (aDevice == DeviceType_USB)
3790 return setError(E_NOTIMPL,
3791 tr("Booting from USB device is currently not supported"));
3792
3793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3794
3795 HRESULT rc = i_checkStateDependency(MutableStateDep);
3796 if (FAILED(rc)) return rc;
3797
3798 i_setModified(IsModified_MachineData);
3799 mHWData.backup();
3800 mHWData->mBootOrder[aPosition - 1] = aDevice;
3801
3802 return S_OK;
3803}
3804
3805HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3806{
3807 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3808 return setError(E_INVALIDARG,
3809 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3810 aPosition, SchemaDefs::MaxBootPosition);
3811
3812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3813
3814 *aDevice = mHWData->mBootOrder[aPosition - 1];
3815
3816 return S_OK;
3817}
3818
3819HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3820 LONG aControllerPort,
3821 LONG aDevice,
3822 DeviceType_T aType,
3823 const ComPtr<IMedium> &aMedium)
3824{
3825 IMedium *aM = aMedium;
3826 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3827 aName.c_str(), aControllerPort, aDevice, aType, aM));
3828
3829 // request the host lock first, since might be calling Host methods for getting host drives;
3830 // next, protect the media tree all the while we're in here, as well as our member variables
3831 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3832 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3833
3834 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3835 if (FAILED(rc)) return rc;
3836
3837 /// @todo NEWMEDIA implicit machine registration
3838 if (!mData->mRegistered)
3839 return setError(VBOX_E_INVALID_OBJECT_STATE,
3840 tr("Cannot attach storage devices to an unregistered machine"));
3841
3842 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3843
3844 /* Check for an existing controller. */
3845 ComObjPtr<StorageController> ctl;
3846 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3847 if (FAILED(rc)) return rc;
3848
3849 StorageControllerType_T ctrlType;
3850 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3851 if (FAILED(rc))
3852 return setError(E_FAIL,
3853 tr("Could not get type of controller '%s'"),
3854 aName.c_str());
3855
3856 bool fSilent = false;
3857 Utf8Str strReconfig;
3858
3859 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3860 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3861 if ( mData->mMachineState == MachineState_Paused
3862 && strReconfig == "1")
3863 fSilent = true;
3864
3865 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3866 bool fHotplug = false;
3867 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3868 fHotplug = true;
3869
3870 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3871 return setError(VBOX_E_INVALID_VM_STATE,
3872 tr("Controller '%s' does not support hotplugging"),
3873 aName.c_str());
3874
3875 // check that the port and device are not out of range
3876 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3877 if (FAILED(rc)) return rc;
3878
3879 /* check if the device slot is already busy */
3880 MediumAttachment *pAttachTemp;
3881 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3882 aName,
3883 aControllerPort,
3884 aDevice)))
3885 {
3886 Medium *pMedium = pAttachTemp->i_getMedium();
3887 if (pMedium)
3888 {
3889 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3890 return setError(VBOX_E_OBJECT_IN_USE,
3891 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3892 pMedium->i_getLocationFull().c_str(),
3893 aControllerPort,
3894 aDevice,
3895 aName.c_str());
3896 }
3897 else
3898 return setError(VBOX_E_OBJECT_IN_USE,
3899 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3900 aControllerPort, aDevice, aName.c_str());
3901 }
3902
3903 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3904 if (aMedium && medium.isNull())
3905 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3906
3907 AutoCaller mediumCaller(medium);
3908 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3909
3910 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3911
3912 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3913 && !medium.isNull()
3914 )
3915 return setError(VBOX_E_OBJECT_IN_USE,
3916 tr("Medium '%s' is already attached to this virtual machine"),
3917 medium->i_getLocationFull().c_str());
3918
3919 if (!medium.isNull())
3920 {
3921 MediumType_T mtype = medium->i_getType();
3922 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3923 // For DVDs it's not written to the config file, so needs no global config
3924 // version bump. For floppies it's a new attribute "type", which is ignored
3925 // by older VirtualBox version, so needs no global config version bump either.
3926 // For hard disks this type is not accepted.
3927 if (mtype == MediumType_MultiAttach)
3928 {
3929 // This type is new with VirtualBox 4.0 and therefore requires settings
3930 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3931 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3932 // two reasons: The medium type is a property of the media registry tree, which
3933 // can reside in the global config file (for pre-4.0 media); we would therefore
3934 // possibly need to bump the global config version. We don't want to do that though
3935 // because that might make downgrading to pre-4.0 impossible.
3936 // As a result, we can only use these two new types if the medium is NOT in the
3937 // global registry:
3938 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3939 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3940 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3941 )
3942 return setError(VBOX_E_INVALID_OBJECT_STATE,
3943 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3944 "to machines that were created with VirtualBox 4.0 or later"),
3945 medium->i_getLocationFull().c_str());
3946 }
3947 }
3948
3949 bool fIndirect = false;
3950 if (!medium.isNull())
3951 fIndirect = medium->i_isReadOnly();
3952 bool associate = true;
3953
3954 do
3955 {
3956 if ( aType == DeviceType_HardDisk
3957 && mMediumAttachments.isBackedUp())
3958 {
3959 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3960
3961 /* check if the medium was attached to the VM before we started
3962 * changing attachments in which case the attachment just needs to
3963 * be restored */
3964 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3965 {
3966 AssertReturn(!fIndirect, E_FAIL);
3967
3968 /* see if it's the same bus/channel/device */
3969 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3970 {
3971 /* the simplest case: restore the whole attachment
3972 * and return, nothing else to do */
3973 mMediumAttachments->push_back(pAttachTemp);
3974
3975 /* Reattach the medium to the VM. */
3976 if (fHotplug || fSilent)
3977 {
3978 mediumLock.release();
3979 treeLock.release();
3980 alock.release();
3981
3982 MediumLockList *pMediumLockList(new MediumLockList());
3983
3984 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3985 medium /* pToLockWrite */,
3986 false /* fMediumLockWriteAll */,
3987 NULL,
3988 *pMediumLockList);
3989 alock.acquire();
3990 if (FAILED(rc))
3991 delete pMediumLockList;
3992 else
3993 {
3994 mData->mSession.mLockedMedia.Unlock();
3995 alock.release();
3996 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3997 mData->mSession.mLockedMedia.Lock();
3998 alock.acquire();
3999 }
4000 alock.release();
4001
4002 if (SUCCEEDED(rc))
4003 {
4004 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4005 /* Remove lock list in case of error. */
4006 if (FAILED(rc))
4007 {
4008 mData->mSession.mLockedMedia.Unlock();
4009 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4010 mData->mSession.mLockedMedia.Lock();
4011 }
4012 }
4013 }
4014
4015 return S_OK;
4016 }
4017
4018 /* bus/channel/device differ; we need a new attachment object,
4019 * but don't try to associate it again */
4020 associate = false;
4021 break;
4022 }
4023 }
4024
4025 /* go further only if the attachment is to be indirect */
4026 if (!fIndirect)
4027 break;
4028
4029 /* perform the so called smart attachment logic for indirect
4030 * attachments. Note that smart attachment is only applicable to base
4031 * hard disks. */
4032
4033 if (medium->i_getParent().isNull())
4034 {
4035 /* first, investigate the backup copy of the current hard disk
4036 * attachments to make it possible to re-attach existing diffs to
4037 * another device slot w/o losing their contents */
4038 if (mMediumAttachments.isBackedUp())
4039 {
4040 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4041
4042 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4043 uint32_t foundLevel = 0;
4044
4045 for (MediumAttachmentList::const_iterator
4046 it = oldAtts.begin();
4047 it != oldAtts.end();
4048 ++it)
4049 {
4050 uint32_t level = 0;
4051 MediumAttachment *pAttach = *it;
4052 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4053 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4054 if (pMedium.isNull())
4055 continue;
4056
4057 if (pMedium->i_getBase(&level) == medium)
4058 {
4059 /* skip the hard disk if its currently attached (we
4060 * cannot attach the same hard disk twice) */
4061 if (i_findAttachment(*mMediumAttachments.data(),
4062 pMedium))
4063 continue;
4064
4065 /* matched device, channel and bus (i.e. attached to the
4066 * same place) will win and immediately stop the search;
4067 * otherwise the attachment that has the youngest
4068 * descendant of medium will be used
4069 */
4070 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4071 {
4072 /* the simplest case: restore the whole attachment
4073 * and return, nothing else to do */
4074 mMediumAttachments->push_back(*it);
4075
4076 /* Reattach the medium to the VM. */
4077 if (fHotplug || fSilent)
4078 {
4079 mediumLock.release();
4080 treeLock.release();
4081 alock.release();
4082
4083 MediumLockList *pMediumLockList(new MediumLockList());
4084
4085 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4086 medium /* pToLockWrite */,
4087 false /* fMediumLockWriteAll */,
4088 NULL,
4089 *pMediumLockList);
4090 alock.acquire();
4091 if (FAILED(rc))
4092 delete pMediumLockList;
4093 else
4094 {
4095 mData->mSession.mLockedMedia.Unlock();
4096 alock.release();
4097 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4098 mData->mSession.mLockedMedia.Lock();
4099 alock.acquire();
4100 }
4101 alock.release();
4102
4103 if (SUCCEEDED(rc))
4104 {
4105 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4106 /* Remove lock list in case of error. */
4107 if (FAILED(rc))
4108 {
4109 mData->mSession.mLockedMedia.Unlock();
4110 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4111 mData->mSession.mLockedMedia.Lock();
4112 }
4113 }
4114 }
4115
4116 return S_OK;
4117 }
4118 else if ( foundIt == oldAtts.end()
4119 || level > foundLevel /* prefer younger */
4120 )
4121 {
4122 foundIt = it;
4123 foundLevel = level;
4124 }
4125 }
4126 }
4127
4128 if (foundIt != oldAtts.end())
4129 {
4130 /* use the previously attached hard disk */
4131 medium = (*foundIt)->i_getMedium();
4132 mediumCaller.attach(medium);
4133 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4134 mediumLock.attach(medium);
4135 /* not implicit, doesn't require association with this VM */
4136 fIndirect = false;
4137 associate = false;
4138 /* go right to the MediumAttachment creation */
4139 break;
4140 }
4141 }
4142
4143 /* must give up the medium lock and medium tree lock as below we
4144 * go over snapshots, which needs a lock with higher lock order. */
4145 mediumLock.release();
4146 treeLock.release();
4147
4148 /* then, search through snapshots for the best diff in the given
4149 * hard disk's chain to base the new diff on */
4150
4151 ComObjPtr<Medium> base;
4152 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4153 while (snap)
4154 {
4155 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4156
4157 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4158
4159 MediumAttachment *pAttachFound = NULL;
4160 uint32_t foundLevel = 0;
4161
4162 for (MediumAttachmentList::const_iterator
4163 it = snapAtts.begin();
4164 it != snapAtts.end();
4165 ++it)
4166 {
4167 MediumAttachment *pAttach = *it;
4168 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4169 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4170 if (pMedium.isNull())
4171 continue;
4172
4173 uint32_t level = 0;
4174 if (pMedium->i_getBase(&level) == medium)
4175 {
4176 /* matched device, channel and bus (i.e. attached to the
4177 * same place) will win and immediately stop the search;
4178 * otherwise the attachment that has the youngest
4179 * descendant of medium will be used
4180 */
4181 if ( pAttach->i_getDevice() == aDevice
4182 && pAttach->i_getPort() == aControllerPort
4183 && pAttach->i_getControllerName() == aName
4184 )
4185 {
4186 pAttachFound = pAttach;
4187 break;
4188 }
4189 else if ( !pAttachFound
4190 || level > foundLevel /* prefer younger */
4191 )
4192 {
4193 pAttachFound = pAttach;
4194 foundLevel = level;
4195 }
4196 }
4197 }
4198
4199 if (pAttachFound)
4200 {
4201 base = pAttachFound->i_getMedium();
4202 break;
4203 }
4204
4205 snap = snap->i_getParent();
4206 }
4207
4208 /* re-lock medium tree and the medium, as we need it below */
4209 treeLock.acquire();
4210 mediumLock.acquire();
4211
4212 /* found a suitable diff, use it as a base */
4213 if (!base.isNull())
4214 {
4215 medium = base;
4216 mediumCaller.attach(medium);
4217 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4218 mediumLock.attach(medium);
4219 }
4220 }
4221
4222 Utf8Str strFullSnapshotFolder;
4223 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4224
4225 ComObjPtr<Medium> diff;
4226 diff.createObject();
4227 // store this diff in the same registry as the parent
4228 Guid uuidRegistryParent;
4229 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4230 {
4231 // parent image has no registry: this can happen if we're attaching a new immutable
4232 // image that has not yet been attached (medium then points to the base and we're
4233 // creating the diff image for the immutable, and the parent is not yet registered);
4234 // put the parent in the machine registry then
4235 mediumLock.release();
4236 treeLock.release();
4237 alock.release();
4238 i_addMediumToRegistry(medium);
4239 alock.acquire();
4240 treeLock.acquire();
4241 mediumLock.acquire();
4242 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4243 }
4244 rc = diff->init(mParent,
4245 medium->i_getPreferredDiffFormat(),
4246 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4247 uuidRegistryParent,
4248 DeviceType_HardDisk);
4249 if (FAILED(rc)) return rc;
4250
4251 /* Apply the normal locking logic to the entire chain. */
4252 MediumLockList *pMediumLockList(new MediumLockList());
4253 mediumLock.release();
4254 treeLock.release();
4255 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4256 diff /* pToLockWrite */,
4257 false /* fMediumLockWriteAll */,
4258 medium,
4259 *pMediumLockList);
4260 treeLock.acquire();
4261 mediumLock.acquire();
4262 if (SUCCEEDED(rc))
4263 {
4264 mediumLock.release();
4265 treeLock.release();
4266 rc = pMediumLockList->Lock();
4267 treeLock.acquire();
4268 mediumLock.acquire();
4269 if (FAILED(rc))
4270 setError(rc,
4271 tr("Could not lock medium when creating diff '%s'"),
4272 diff->i_getLocationFull().c_str());
4273 else
4274 {
4275 /* will release the lock before the potentially lengthy
4276 * operation, so protect with the special state */
4277 MachineState_T oldState = mData->mMachineState;
4278 i_setMachineState(MachineState_SettingUp);
4279
4280 mediumLock.release();
4281 treeLock.release();
4282 alock.release();
4283
4284 rc = medium->i_createDiffStorage(diff,
4285 medium->i_getPreferredDiffVariant(),
4286 pMediumLockList,
4287 NULL /* aProgress */,
4288 true /* aWait */);
4289
4290 alock.acquire();
4291 treeLock.acquire();
4292 mediumLock.acquire();
4293
4294 i_setMachineState(oldState);
4295 }
4296 }
4297
4298 /* Unlock the media and free the associated memory. */
4299 delete pMediumLockList;
4300
4301 if (FAILED(rc)) return rc;
4302
4303 /* use the created diff for the actual attachment */
4304 medium = diff;
4305 mediumCaller.attach(medium);
4306 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4307 mediumLock.attach(medium);
4308 }
4309 while (0);
4310
4311 ComObjPtr<MediumAttachment> attachment;
4312 attachment.createObject();
4313 rc = attachment->init(this,
4314 medium,
4315 aName,
4316 aControllerPort,
4317 aDevice,
4318 aType,
4319 fIndirect,
4320 false /* fPassthrough */,
4321 false /* fTempEject */,
4322 false /* fNonRotational */,
4323 false /* fDiscard */,
4324 fHotplug /* fHotPluggable */,
4325 Utf8Str::Empty);
4326 if (FAILED(rc)) return rc;
4327
4328 if (associate && !medium.isNull())
4329 {
4330 // as the last step, associate the medium to the VM
4331 rc = medium->i_addBackReference(mData->mUuid);
4332 // here we can fail because of Deleting, or being in process of creating a Diff
4333 if (FAILED(rc)) return rc;
4334
4335 mediumLock.release();
4336 treeLock.release();
4337 alock.release();
4338 i_addMediumToRegistry(medium);
4339 alock.acquire();
4340 treeLock.acquire();
4341 mediumLock.acquire();
4342 }
4343
4344 /* success: finally remember the attachment */
4345 i_setModified(IsModified_Storage);
4346 mMediumAttachments.backup();
4347 mMediumAttachments->push_back(attachment);
4348
4349 mediumLock.release();
4350 treeLock.release();
4351 alock.release();
4352
4353 if (fHotplug || fSilent)
4354 {
4355 if (!medium.isNull())
4356 {
4357 MediumLockList *pMediumLockList(new MediumLockList());
4358
4359 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4360 medium /* pToLockWrite */,
4361 false /* fMediumLockWriteAll */,
4362 NULL,
4363 *pMediumLockList);
4364 alock.acquire();
4365 if (FAILED(rc))
4366 delete pMediumLockList;
4367 else
4368 {
4369 mData->mSession.mLockedMedia.Unlock();
4370 alock.release();
4371 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4372 mData->mSession.mLockedMedia.Lock();
4373 alock.acquire();
4374 }
4375 alock.release();
4376 }
4377
4378 if (SUCCEEDED(rc))
4379 {
4380 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4381 /* Remove lock list in case of error. */
4382 if (FAILED(rc))
4383 {
4384 mData->mSession.mLockedMedia.Unlock();
4385 mData->mSession.mLockedMedia.Remove(attachment);
4386 mData->mSession.mLockedMedia.Lock();
4387 }
4388 }
4389 }
4390
4391 /* Save modified registries, but skip this machine as it's the caller's
4392 * job to save its settings like all other settings changes. */
4393 mParent->i_unmarkRegistryModified(i_getId());
4394 mParent->i_saveModifiedRegistries();
4395
4396 return rc;
4397}
4398
4399HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4400 LONG aDevice)
4401{
4402 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4403 aName.c_str(), aControllerPort, aDevice));
4404
4405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4406
4407 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4408 if (FAILED(rc)) return rc;
4409
4410 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4411
4412 /* Check for an existing controller. */
4413 ComObjPtr<StorageController> ctl;
4414 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4415 if (FAILED(rc)) return rc;
4416
4417 StorageControllerType_T ctrlType;
4418 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4419 if (FAILED(rc))
4420 return setError(E_FAIL,
4421 tr("Could not get type of controller '%s'"),
4422 aName.c_str());
4423
4424 bool fSilent = false;
4425 Utf8Str strReconfig;
4426
4427 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4428 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4429 if ( mData->mMachineState == MachineState_Paused
4430 && strReconfig == "1")
4431 fSilent = true;
4432
4433 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4434 bool fHotplug = false;
4435 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4436 fHotplug = true;
4437
4438 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4439 return setError(VBOX_E_INVALID_VM_STATE,
4440 tr("Controller '%s' does not support hotplugging"),
4441 aName.c_str());
4442
4443 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4444 aName,
4445 aControllerPort,
4446 aDevice);
4447 if (!pAttach)
4448 return setError(VBOX_E_OBJECT_NOT_FOUND,
4449 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4450 aDevice, aControllerPort, aName.c_str());
4451
4452 if (fHotplug && !pAttach->i_getHotPluggable())
4453 return setError(VBOX_E_NOT_SUPPORTED,
4454 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457 /*
4458 * The VM has to detach the device before we delete any implicit diffs.
4459 * If this fails we can roll back without loosing data.
4460 */
4461 if (fHotplug || fSilent)
4462 {
4463 alock.release();
4464 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4465 alock.acquire();
4466 }
4467 if (FAILED(rc)) return rc;
4468
4469 /* If we are here everything went well and we can delete the implicit now. */
4470 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4471
4472 alock.release();
4473
4474 /* Save modified registries, but skip this machine as it's the caller's
4475 * job to save its settings like all other settings changes. */
4476 mParent->i_unmarkRegistryModified(i_getId());
4477 mParent->i_saveModifiedRegistries();
4478
4479 return rc;
4480}
4481
4482HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4483 LONG aDevice, BOOL aPassthrough)
4484{
4485 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4486 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4487
4488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4489
4490 HRESULT rc = i_checkStateDependency(MutableStateDep);
4491 if (FAILED(rc)) return rc;
4492
4493 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4494
4495 if (Global::IsOnlineOrTransient(mData->mMachineState))
4496 return setError(VBOX_E_INVALID_VM_STATE,
4497 tr("Invalid machine state: %s"),
4498 Global::stringifyMachineState(mData->mMachineState));
4499
4500 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4501 aName,
4502 aControllerPort,
4503 aDevice);
4504 if (!pAttach)
4505 return setError(VBOX_E_OBJECT_NOT_FOUND,
4506 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4507 aDevice, aControllerPort, aName.c_str());
4508
4509
4510 i_setModified(IsModified_Storage);
4511 mMediumAttachments.backup();
4512
4513 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4514
4515 if (pAttach->i_getType() != DeviceType_DVD)
4516 return setError(E_INVALIDARG,
4517 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4518 aDevice, aControllerPort, aName.c_str());
4519 pAttach->i_updatePassthrough(!!aPassthrough);
4520
4521 return S_OK;
4522}
4523
4524HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4525 LONG aDevice, BOOL aTemporaryEject)
4526{
4527
4528 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4529 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4530
4531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4532
4533 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4534 if (FAILED(rc)) return rc;
4535
4536 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4537 aName,
4538 aControllerPort,
4539 aDevice);
4540 if (!pAttach)
4541 return setError(VBOX_E_OBJECT_NOT_FOUND,
4542 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4543 aDevice, aControllerPort, aName.c_str());
4544
4545
4546 i_setModified(IsModified_Storage);
4547 mMediumAttachments.backup();
4548
4549 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4550
4551 if (pAttach->i_getType() != DeviceType_DVD)
4552 return setError(E_INVALIDARG,
4553 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4554 aDevice, aControllerPort, aName.c_str());
4555 pAttach->i_updateTempEject(!!aTemporaryEject);
4556
4557 return S_OK;
4558}
4559
4560HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4561 LONG aDevice, BOOL aNonRotational)
4562{
4563
4564 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4565 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4566
4567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4568
4569 HRESULT rc = i_checkStateDependency(MutableStateDep);
4570 if (FAILED(rc)) return rc;
4571
4572 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4573
4574 if (Global::IsOnlineOrTransient(mData->mMachineState))
4575 return setError(VBOX_E_INVALID_VM_STATE,
4576 tr("Invalid machine state: %s"),
4577 Global::stringifyMachineState(mData->mMachineState));
4578
4579 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4580 aName,
4581 aControllerPort,
4582 aDevice);
4583 if (!pAttach)
4584 return setError(VBOX_E_OBJECT_NOT_FOUND,
4585 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4586 aDevice, aControllerPort, aName.c_str());
4587
4588
4589 i_setModified(IsModified_Storage);
4590 mMediumAttachments.backup();
4591
4592 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4593
4594 if (pAttach->i_getType() != DeviceType_HardDisk)
4595 return setError(E_INVALIDARG,
4596 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"),
4597 aDevice, aControllerPort, aName.c_str());
4598 pAttach->i_updateNonRotational(!!aNonRotational);
4599
4600 return S_OK;
4601}
4602
4603HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4604 LONG aDevice, BOOL aDiscard)
4605{
4606
4607 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4608 aName.c_str(), aControllerPort, aDevice, aDiscard));
4609
4610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 HRESULT rc = i_checkStateDependency(MutableStateDep);
4613 if (FAILED(rc)) return rc;
4614
4615 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4616
4617 if (Global::IsOnlineOrTransient(mData->mMachineState))
4618 return setError(VBOX_E_INVALID_VM_STATE,
4619 tr("Invalid machine state: %s"),
4620 Global::stringifyMachineState(mData->mMachineState));
4621
4622 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4623 aName,
4624 aControllerPort,
4625 aDevice);
4626 if (!pAttach)
4627 return setError(VBOX_E_OBJECT_NOT_FOUND,
4628 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4629 aDevice, aControllerPort, aName.c_str());
4630
4631
4632 i_setModified(IsModified_Storage);
4633 mMediumAttachments.backup();
4634
4635 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4636
4637 if (pAttach->i_getType() != DeviceType_HardDisk)
4638 return setError(E_INVALIDARG,
4639 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"),
4640 aDevice, aControllerPort, aName.c_str());
4641 pAttach->i_updateDiscard(!!aDiscard);
4642
4643 return S_OK;
4644}
4645
4646HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4647 LONG aDevice, BOOL aHotPluggable)
4648{
4649 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4650 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4651
4652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4653
4654 HRESULT rc = i_checkStateDependency(MutableStateDep);
4655 if (FAILED(rc)) return rc;
4656
4657 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4658
4659 if (Global::IsOnlineOrTransient(mData->mMachineState))
4660 return setError(VBOX_E_INVALID_VM_STATE,
4661 tr("Invalid machine state: %s"),
4662 Global::stringifyMachineState(mData->mMachineState));
4663
4664 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4665 aName,
4666 aControllerPort,
4667 aDevice);
4668 if (!pAttach)
4669 return setError(VBOX_E_OBJECT_NOT_FOUND,
4670 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4671 aDevice, aControllerPort, aName.c_str());
4672
4673 /* Check for an existing controller. */
4674 ComObjPtr<StorageController> ctl;
4675 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4676 if (FAILED(rc)) return rc;
4677
4678 StorageControllerType_T ctrlType;
4679 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4680 if (FAILED(rc))
4681 return setError(E_FAIL,
4682 tr("Could not get type of controller '%s'"),
4683 aName.c_str());
4684
4685 if (!i_isControllerHotplugCapable(ctrlType))
4686 return setError(VBOX_E_NOT_SUPPORTED,
4687 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4688 aName.c_str());
4689
4690 i_setModified(IsModified_Storage);
4691 mMediumAttachments.backup();
4692
4693 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4694
4695 if (pAttach->i_getType() == DeviceType_Floppy)
4696 return setError(E_INVALIDARG,
4697 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"),
4698 aDevice, aControllerPort, aName.c_str());
4699 pAttach->i_updateHotPluggable(!!aHotPluggable);
4700
4701 return S_OK;
4702}
4703
4704HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4705 LONG aDevice)
4706{
4707 int rc = S_OK;
4708 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4709 aName.c_str(), aControllerPort, aDevice));
4710
4711 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4712
4713 return rc;
4714}
4715
4716HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4717 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4718{
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4720 aName.c_str(), aControllerPort, aDevice));
4721
4722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4725 if (FAILED(rc)) return rc;
4726
4727 if (Global::IsOnlineOrTransient(mData->mMachineState))
4728 return setError(VBOX_E_INVALID_VM_STATE,
4729 tr("Invalid machine state: %s"),
4730 Global::stringifyMachineState(mData->mMachineState));
4731
4732 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4733 aName,
4734 aControllerPort,
4735 aDevice);
4736 if (!pAttach)
4737 return setError(VBOX_E_OBJECT_NOT_FOUND,
4738 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4739 aDevice, aControllerPort, aName.c_str());
4740
4741
4742 i_setModified(IsModified_Storage);
4743 mMediumAttachments.backup();
4744
4745 IBandwidthGroup *iB = aBandwidthGroup;
4746 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4747 if (aBandwidthGroup && group.isNull())
4748 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4749
4750 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4751
4752 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4753 if (strBandwidthGroupOld.isNotEmpty())
4754 {
4755 /* Get the bandwidth group object and release it - this must not fail. */
4756 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4757 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4758 Assert(SUCCEEDED(rc));
4759
4760 pBandwidthGroupOld->i_release();
4761 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4762 }
4763
4764 if (!group.isNull())
4765 {
4766 group->i_reference();
4767 pAttach->i_updateBandwidthGroup(group->i_getName());
4768 }
4769
4770 return S_OK;
4771}
4772
4773HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4774 LONG aControllerPort,
4775 LONG aDevice,
4776 DeviceType_T aType)
4777{
4778 HRESULT rc = S_OK;
4779
4780 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4781 aName.c_str(), aControllerPort, aDevice, aType));
4782
4783 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4784
4785 return rc;
4786}
4787
4788
4789HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4790 LONG aControllerPort,
4791 LONG aDevice,
4792 BOOL aForce)
4793{
4794 int rc = S_OK;
4795 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4796 aName.c_str(), aControllerPort, aForce));
4797
4798 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4799
4800 return rc;
4801}
4802
4803HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4804 LONG aControllerPort,
4805 LONG aDevice,
4806 const ComPtr<IMedium> &aMedium,
4807 BOOL aForce)
4808{
4809 int rc = S_OK;
4810 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4811 aName.c_str(), aControllerPort, aDevice, aForce));
4812
4813 // request the host lock first, since might be calling Host methods for getting host drives;
4814 // next, protect the media tree all the while we're in here, as well as our member variables
4815 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4816 this->lockHandle(),
4817 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4818
4819 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4820 aName,
4821 aControllerPort,
4822 aDevice);
4823 if (pAttach.isNull())
4824 return setError(VBOX_E_OBJECT_NOT_FOUND,
4825 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4826 aDevice, aControllerPort, aName.c_str());
4827
4828 /* Remember previously mounted medium. The medium before taking the
4829 * backup is not necessarily the same thing. */
4830 ComObjPtr<Medium> oldmedium;
4831 oldmedium = pAttach->i_getMedium();
4832
4833 IMedium *iM = aMedium;
4834 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4835 if (aMedium && pMedium.isNull())
4836 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4837
4838 AutoCaller mediumCaller(pMedium);
4839 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4840
4841 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4842 if (pMedium)
4843 {
4844 DeviceType_T mediumType = pAttach->i_getType();
4845 switch (mediumType)
4846 {
4847 case DeviceType_DVD:
4848 case DeviceType_Floppy:
4849 break;
4850
4851 default:
4852 return setError(VBOX_E_INVALID_OBJECT_STATE,
4853 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4854 aControllerPort,
4855 aDevice,
4856 aName.c_str());
4857 }
4858 }
4859
4860 i_setModified(IsModified_Storage);
4861 mMediumAttachments.backup();
4862
4863 {
4864 // The backup operation makes the pAttach reference point to the
4865 // old settings. Re-get the correct reference.
4866 pAttach = i_findAttachment(*mMediumAttachments.data(),
4867 aName,
4868 aControllerPort,
4869 aDevice);
4870 if (!oldmedium.isNull())
4871 oldmedium->i_removeBackReference(mData->mUuid);
4872 if (!pMedium.isNull())
4873 {
4874 pMedium->i_addBackReference(mData->mUuid);
4875
4876 mediumLock.release();
4877 multiLock.release();
4878 i_addMediumToRegistry(pMedium);
4879 multiLock.acquire();
4880 mediumLock.acquire();
4881 }
4882
4883 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4884 pAttach->i_updateMedium(pMedium);
4885 }
4886
4887 i_setModified(IsModified_Storage);
4888
4889 mediumLock.release();
4890 multiLock.release();
4891 rc = i_onMediumChange(pAttach, aForce);
4892 multiLock.acquire();
4893 mediumLock.acquire();
4894
4895 /* On error roll back this change only. */
4896 if (FAILED(rc))
4897 {
4898 if (!pMedium.isNull())
4899 pMedium->i_removeBackReference(mData->mUuid);
4900 pAttach = i_findAttachment(*mMediumAttachments.data(),
4901 aName,
4902 aControllerPort,
4903 aDevice);
4904 /* If the attachment is gone in the meantime, bail out. */
4905 if (pAttach.isNull())
4906 return rc;
4907 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4908 if (!oldmedium.isNull())
4909 oldmedium->i_addBackReference(mData->mUuid);
4910 pAttach->i_updateMedium(oldmedium);
4911 }
4912
4913 mediumLock.release();
4914 multiLock.release();
4915
4916 /* Save modified registries, but skip this machine as it's the caller's
4917 * job to save its settings like all other settings changes. */
4918 mParent->i_unmarkRegistryModified(i_getId());
4919 mParent->i_saveModifiedRegistries();
4920
4921 return rc;
4922}
4923HRESULT Machine::getMedium(const com::Utf8Str &aName,
4924 LONG aControllerPort,
4925 LONG aDevice,
4926 ComPtr<IMedium> &aMedium)
4927{
4928 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4929 aName.c_str(), aControllerPort, aDevice));
4930
4931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 aMedium = NULL;
4934
4935 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4936 aName,
4937 aControllerPort,
4938 aDevice);
4939 if (pAttach.isNull())
4940 return setError(VBOX_E_OBJECT_NOT_FOUND,
4941 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4942 aDevice, aControllerPort, aName.c_str());
4943
4944 aMedium = pAttach->i_getMedium();
4945
4946 return S_OK;
4947}
4948
4949HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4950{
4951
4952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4953
4954 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4955
4956 return S_OK;
4957}
4958
4959HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4960{
4961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4962
4963 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4964
4965 return S_OK;
4966}
4967
4968HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4969{
4970 /* Do not assert if slot is out of range, just return the advertised
4971 status. testdriver/vbox.py triggers this in logVmInfo. */
4972 if (aSlot >= mNetworkAdapters.size())
4973 return setError(E_INVALIDARG,
4974 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4975 aSlot, mNetworkAdapters.size());
4976
4977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4978
4979 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4980
4981 return S_OK;
4982}
4983
4984HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4985{
4986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4987
4988 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4989 size_t i = 0;
4990 for (settings::StringsMap::const_iterator
4991 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4992 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4993 ++it, ++i)
4994 aKeys[i] = it->first;
4995
4996 return S_OK;
4997}
4998
4999 /**
5000 * @note Locks this object for reading.
5001 */
5002HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5003 com::Utf8Str &aValue)
5004{
5005 /* start with nothing found */
5006 aValue = "";
5007
5008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5009
5010 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5011 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5012 // found:
5013 aValue = it->second; // source is a Utf8Str
5014
5015 /* return the result to caller (may be empty) */
5016 return S_OK;
5017}
5018
5019 /**
5020 * @note Locks mParent for writing + this object for writing.
5021 */
5022HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5023{
5024 Utf8Str strOldValue; // empty
5025
5026 // locking note: we only hold the read lock briefly to look up the old value,
5027 // then release it and call the onExtraCanChange callbacks. There is a small
5028 // chance of a race insofar as the callback might be called twice if two callers
5029 // change the same key at the same time, but that's a much better solution
5030 // than the deadlock we had here before. The actual changing of the extradata
5031 // is then performed under the write lock and race-free.
5032
5033 // look up the old value first; if nothing has changed then we need not do anything
5034 {
5035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5036
5037 // For snapshots don't even think about allowing changes, extradata
5038 // is global for a machine, so there is nothing snapshot specific.
5039 if (i_isSnapshotMachine())
5040 return setError(VBOX_E_INVALID_VM_STATE,
5041 tr("Cannot set extradata for a snapshot"));
5042
5043 // check if the right IMachine instance is used
5044 if (mData->mRegistered && !i_isSessionMachine())
5045 return setError(VBOX_E_INVALID_VM_STATE,
5046 tr("Cannot set extradata for an immutable machine"));
5047
5048 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5049 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5050 strOldValue = it->second;
5051 }
5052
5053 bool fChanged;
5054 if ((fChanged = (strOldValue != aValue)))
5055 {
5056 // ask for permission from all listeners outside the locks;
5057 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5058 // lock to copy the list of callbacks to invoke
5059 Bstr error;
5060 Bstr bstrValue(aValue);
5061
5062 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5063 {
5064 const char *sep = error.isEmpty() ? "" : ": ";
5065 CBSTR err = error.raw();
5066 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5067 return setError(E_ACCESSDENIED,
5068 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5069 aKey.c_str(),
5070 aValue.c_str(),
5071 sep,
5072 err);
5073 }
5074
5075 // data is changing and change not vetoed: then write it out under the lock
5076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5077
5078 if (aValue.isEmpty())
5079 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5080 else
5081 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5082 // creates a new key if needed
5083
5084 bool fNeedsGlobalSaveSettings = false;
5085 // This saving of settings is tricky: there is no "old state" for the
5086 // extradata items at all (unlike all other settings), so the old/new
5087 // settings comparison would give a wrong result!
5088 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5089
5090 if (fNeedsGlobalSaveSettings)
5091 {
5092 // save the global settings; for that we should hold only the VirtualBox lock
5093 alock.release();
5094 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5095 mParent->i_saveSettings();
5096 }
5097 }
5098
5099 // fire notification outside the lock
5100 if (fChanged)
5101 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5102
5103 return S_OK;
5104}
5105
5106HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5107{
5108 aProgress = NULL;
5109 NOREF(aSettingsFilePath);
5110 ReturnComNotImplemented();
5111}
5112
5113HRESULT Machine::saveSettings()
5114{
5115 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5116
5117 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5118 if (FAILED(rc)) return rc;
5119
5120 /* the settings file path may never be null */
5121 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5122
5123 /* save all VM data excluding snapshots */
5124 bool fNeedsGlobalSaveSettings = false;
5125 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5126 mlock.release();
5127
5128 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5129 {
5130 // save the global settings; for that we should hold only the VirtualBox lock
5131 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5132 rc = mParent->i_saveSettings();
5133 }
5134
5135 return rc;
5136}
5137
5138
5139HRESULT Machine::discardSettings()
5140{
5141 /*
5142 * We need to take the machine list lock here as well as the machine one
5143 * or we'll get into trouble should any media stuff require rolling back.
5144 *
5145 * Details:
5146 *
5147 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5148 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5149 * 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]
5150 * 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
5151 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5152 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5153 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5154 * 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
5155 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5156 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5157 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5158 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5159 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5160 * 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]
5161 * 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] (*)
5162 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5163 * 0:005> k
5164 * # Child-SP RetAddr Call Site
5165 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5166 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5167 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5168 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5169 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5170 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5171 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5172 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5173 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5174 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5175 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5176 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5177 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5178 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5179 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5180 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5181 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5182 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5183 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5184 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5185 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5186 *
5187 */
5188 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5190
5191 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5192 if (FAILED(rc)) return rc;
5193
5194 /*
5195 * during this rollback, the session will be notified if data has
5196 * been actually changed
5197 */
5198 i_rollback(true /* aNotify */);
5199
5200 return S_OK;
5201}
5202
5203/** @note Locks objects! */
5204HRESULT Machine::unregister(AutoCaller &autoCaller,
5205 CleanupMode_T aCleanupMode,
5206 std::vector<ComPtr<IMedium> > &aMedia)
5207{
5208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5209
5210 Guid id(i_getId());
5211
5212 if (mData->mSession.mState != SessionState_Unlocked)
5213 return setError(VBOX_E_INVALID_OBJECT_STATE,
5214 tr("Cannot unregister the machine '%s' while it is locked"),
5215 mUserData->s.strName.c_str());
5216
5217 // wait for state dependents to drop to zero
5218 i_ensureNoStateDependencies();
5219
5220 if (!mData->mAccessible)
5221 {
5222 // inaccessible maschines can only be unregistered; uninitialize ourselves
5223 // here because currently there may be no unregistered that are inaccessible
5224 // (this state combination is not supported). Note releasing the caller and
5225 // leaving the lock before calling uninit()
5226 alock.release();
5227 autoCaller.release();
5228
5229 uninit();
5230
5231 mParent->i_unregisterMachine(this, id);
5232 // calls VirtualBox::i_saveSettings()
5233
5234 return S_OK;
5235 }
5236
5237 HRESULT rc = S_OK;
5238
5239 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5240 // discard saved state
5241 if (mData->mMachineState == MachineState_Saved)
5242 {
5243 // add the saved state file to the list of files the caller should delete
5244 Assert(!mSSData->strStateFilePath.isEmpty());
5245 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5246
5247 mSSData->strStateFilePath.setNull();
5248
5249 // unconditionally set the machine state to powered off, we now
5250 // know no session has locked the machine
5251 mData->mMachineState = MachineState_PoweredOff;
5252 }
5253
5254 size_t cSnapshots = 0;
5255 if (mData->mFirstSnapshot)
5256 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5257 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5258 // fail now before we start detaching media
5259 return setError(VBOX_E_INVALID_OBJECT_STATE,
5260 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5261 mUserData->s.strName.c_str(), cSnapshots);
5262
5263 // This list collects the medium objects from all medium attachments
5264 // which we will detach from the machine and its snapshots, in a specific
5265 // order which allows for closing all media without getting "media in use"
5266 // errors, simply by going through the list from the front to the back:
5267 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5268 // and must be closed before the parent media from the snapshots, or closing the parents
5269 // will fail because they still have children);
5270 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5271 // the root ("first") snapshot of the machine.
5272 MediaList llMedia;
5273
5274 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5275 && mMediumAttachments->size()
5276 )
5277 {
5278 // we have media attachments: detach them all and add the Medium objects to our list
5279 if (aCleanupMode != CleanupMode_UnregisterOnly)
5280 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5281 else
5282 return setError(VBOX_E_INVALID_OBJECT_STATE,
5283 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5284 mUserData->s.strName.c_str(), mMediumAttachments->size());
5285 }
5286
5287 if (cSnapshots)
5288 {
5289 // add the media from the medium attachments of the snapshots to llMedia
5290 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5291 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5292 // into the children first
5293
5294 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5295 MachineState_T oldState = mData->mMachineState;
5296 mData->mMachineState = MachineState_DeletingSnapshot;
5297
5298 // make a copy of the first snapshot so the refcount does not drop to 0
5299 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5300 // because of the AutoCaller voodoo)
5301 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5302
5303 // GO!
5304 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5305
5306 mData->mMachineState = oldState;
5307 }
5308
5309 if (FAILED(rc))
5310 {
5311 i_rollbackMedia();
5312 return rc;
5313 }
5314
5315 // commit all the media changes made above
5316 i_commitMedia();
5317
5318 mData->mRegistered = false;
5319
5320 // machine lock no longer needed
5321 alock.release();
5322
5323 // return media to caller
5324 aMedia.resize(llMedia.size());
5325 size_t i = 0;
5326 for (MediaList::const_iterator
5327 it = llMedia.begin();
5328 it != llMedia.end();
5329 ++it, ++i)
5330 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5331
5332 mParent->i_unregisterMachine(this, id);
5333 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5334
5335 return S_OK;
5336}
5337
5338/**
5339 * Task record for deleting a machine config.
5340 */
5341class Machine::DeleteConfigTask
5342 : public Machine::Task
5343{
5344public:
5345 DeleteConfigTask(Machine *m,
5346 Progress *p,
5347 const Utf8Str &t,
5348 const RTCList<ComPtr<IMedium> > &llMediums,
5349 const StringsList &llFilesToDelete)
5350 : Task(m, p, t),
5351 m_llMediums(llMediums),
5352 m_llFilesToDelete(llFilesToDelete)
5353 {}
5354
5355private:
5356 void handler()
5357 {
5358 try
5359 {
5360 m_pMachine->i_deleteConfigHandler(*this);
5361 }
5362 catch (...)
5363 {
5364 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5365 }
5366 }
5367
5368 RTCList<ComPtr<IMedium> > m_llMediums;
5369 StringsList m_llFilesToDelete;
5370
5371 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5372};
5373
5374/**
5375 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5376 * SessionMachine::taskHandler().
5377 *
5378 * @note Locks this object for writing.
5379 *
5380 * @param task
5381 * @return
5382 */
5383void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5384{
5385 LogFlowThisFuncEnter();
5386
5387 AutoCaller autoCaller(this);
5388 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5389 if (FAILED(autoCaller.rc()))
5390 {
5391 /* we might have been uninitialized because the session was accidentally
5392 * closed by the client, so don't assert */
5393 HRESULT rc = setError(E_FAIL,
5394 tr("The session has been accidentally closed"));
5395 task.m_pProgress->i_notifyComplete(rc);
5396 LogFlowThisFuncLeave();
5397 return;
5398 }
5399
5400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 HRESULT rc = S_OK;
5403
5404 try
5405 {
5406 ULONG uLogHistoryCount = 3;
5407 ComPtr<ISystemProperties> systemProperties;
5408 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5409 if (FAILED(rc)) throw rc;
5410
5411 if (!systemProperties.isNull())
5412 {
5413 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5414 if (FAILED(rc)) throw rc;
5415 }
5416
5417 MachineState_T oldState = mData->mMachineState;
5418 i_setMachineState(MachineState_SettingUp);
5419 alock.release();
5420 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5421 {
5422 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5423 {
5424 AutoCaller mac(pMedium);
5425 if (FAILED(mac.rc())) throw mac.rc();
5426 Utf8Str strLocation = pMedium->i_getLocationFull();
5427 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5428 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5429 if (FAILED(rc)) throw rc;
5430 }
5431 if (pMedium->i_isMediumFormatFile())
5432 {
5433 ComPtr<IProgress> pProgress2;
5434 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5435 if (FAILED(rc)) throw rc;
5436 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5437 if (FAILED(rc)) throw rc;
5438 /* Check the result of the asynchronous process. */
5439 LONG iRc;
5440 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5441 if (FAILED(rc)) throw rc;
5442 /* If the thread of the progress object has an error, then
5443 * retrieve the error info from there, or it'll be lost. */
5444 if (FAILED(iRc))
5445 throw setError(ProgressErrorInfo(pProgress2));
5446 }
5447
5448 /* Close the medium, deliberately without checking the return
5449 * code, and without leaving any trace in the error info, as
5450 * a failure here is a very minor issue, which shouldn't happen
5451 * as above we even managed to delete the medium. */
5452 {
5453 ErrorInfoKeeper eik;
5454 pMedium->Close();
5455 }
5456 }
5457 i_setMachineState(oldState);
5458 alock.acquire();
5459
5460 // delete the files pushed on the task list by Machine::Delete()
5461 // (this includes saved states of the machine and snapshots and
5462 // medium storage files from the IMedium list passed in, and the
5463 // machine XML file)
5464 for (StringsList::const_iterator
5465 it = task.m_llFilesToDelete.begin();
5466 it != task.m_llFilesToDelete.end();
5467 ++it)
5468 {
5469 const Utf8Str &strFile = *it;
5470 LogFunc(("Deleting file %s\n", strFile.c_str()));
5471 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5472 if (FAILED(rc)) throw rc;
5473
5474 int vrc = RTFileDelete(strFile.c_str());
5475 if (RT_FAILURE(vrc))
5476 throw setError(VBOX_E_IPRT_ERROR,
5477 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5478 }
5479
5480 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5481 if (FAILED(rc)) throw rc;
5482
5483 /* delete the settings only when the file actually exists */
5484 if (mData->pMachineConfigFile->fileExists())
5485 {
5486 /* Delete any backup or uncommitted XML files. Ignore failures.
5487 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5488 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5489 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5490 RTFileDelete(otherXml.c_str());
5491 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5492 RTFileDelete(otherXml.c_str());
5493
5494 /* delete the Logs folder, nothing important should be left
5495 * there (we don't check for errors because the user might have
5496 * some private files there that we don't want to delete) */
5497 Utf8Str logFolder;
5498 getLogFolder(logFolder);
5499 Assert(logFolder.length());
5500 if (RTDirExists(logFolder.c_str()))
5501 {
5502 /* Delete all VBox.log[.N] files from the Logs folder
5503 * (this must be in sync with the rotation logic in
5504 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5505 * files that may have been created by the GUI. */
5506 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5507 logFolder.c_str(), RTPATH_DELIMITER);
5508 RTFileDelete(log.c_str());
5509 log = Utf8StrFmt("%s%cVBox.png",
5510 logFolder.c_str(), RTPATH_DELIMITER);
5511 RTFileDelete(log.c_str());
5512 for (int i = uLogHistoryCount; i > 0; i--)
5513 {
5514 log = Utf8StrFmt("%s%cVBox.log.%d",
5515 logFolder.c_str(), RTPATH_DELIMITER, i);
5516 RTFileDelete(log.c_str());
5517 log = Utf8StrFmt("%s%cVBox.png.%d",
5518 logFolder.c_str(), RTPATH_DELIMITER, i);
5519 RTFileDelete(log.c_str());
5520 }
5521#if defined(RT_OS_WINDOWS)
5522 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5523 RTFileDelete(log.c_str());
5524 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5525 RTFileDelete(log.c_str());
5526#endif
5527
5528 RTDirRemove(logFolder.c_str());
5529 }
5530
5531 /* delete the Snapshots folder, nothing important should be left
5532 * there (we don't check for errors because the user might have
5533 * some private files there that we don't want to delete) */
5534 Utf8Str strFullSnapshotFolder;
5535 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5536 Assert(!strFullSnapshotFolder.isEmpty());
5537 if (RTDirExists(strFullSnapshotFolder.c_str()))
5538 RTDirRemove(strFullSnapshotFolder.c_str());
5539
5540 // delete the directory that contains the settings file, but only
5541 // if it matches the VM name
5542 Utf8Str settingsDir;
5543 if (i_isInOwnDir(&settingsDir))
5544 RTDirRemove(settingsDir.c_str());
5545 }
5546
5547 alock.release();
5548
5549 mParent->i_saveModifiedRegistries();
5550 }
5551 catch (HRESULT aRC) { rc = aRC; }
5552
5553 task.m_pProgress->i_notifyComplete(rc);
5554
5555 LogFlowThisFuncLeave();
5556}
5557
5558HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5559{
5560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5561
5562 HRESULT rc = i_checkStateDependency(MutableStateDep);
5563 if (FAILED(rc)) return rc;
5564
5565 if (mData->mRegistered)
5566 return setError(VBOX_E_INVALID_VM_STATE,
5567 tr("Cannot delete settings of a registered machine"));
5568
5569 // collect files to delete
5570 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5571 if (mData->pMachineConfigFile->fileExists())
5572 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5573
5574 RTCList<ComPtr<IMedium> > llMediums;
5575 for (size_t i = 0; i < aMedia.size(); ++i)
5576 {
5577 IMedium *pIMedium(aMedia[i]);
5578 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5579 if (pMedium.isNull())
5580 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5581 SafeArray<BSTR> ids;
5582 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5583 if (FAILED(rc)) return rc;
5584 /* At this point the medium should not have any back references
5585 * anymore. If it has it is attached to another VM and *must* not
5586 * deleted. */
5587 if (ids.size() < 1)
5588 llMediums.append(pMedium);
5589 }
5590
5591 ComObjPtr<Progress> pProgress;
5592 pProgress.createObject();
5593 rc = pProgress->init(i_getVirtualBox(),
5594 static_cast<IMachine*>(this) /* aInitiator */,
5595 tr("Deleting files"),
5596 true /* fCancellable */,
5597 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5598 tr("Collecting file inventory"));
5599 if (FAILED(rc))
5600 return rc;
5601
5602 /* create and start the task on a separate thread (note that it will not
5603 * start working until we release alock) */
5604 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5605 rc = pTask->createThread();
5606 if (FAILED(rc))
5607 return rc;
5608
5609 pProgress.queryInterfaceTo(aProgress.asOutParam());
5610
5611 LogFlowFuncLeave();
5612
5613 return S_OK;
5614}
5615
5616HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5617{
5618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5619
5620 ComObjPtr<Snapshot> pSnapshot;
5621 HRESULT rc;
5622
5623 if (aNameOrId.isEmpty())
5624 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5625 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5626 else
5627 {
5628 Guid uuid(aNameOrId);
5629 if (uuid.isValid())
5630 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5631 else
5632 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5633 }
5634 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5635
5636 return rc;
5637}
5638
5639HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5640{
5641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5642
5643 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5644 if (FAILED(rc)) return rc;
5645
5646 ComObjPtr<SharedFolder> sharedFolder;
5647 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5648 if (SUCCEEDED(rc))
5649 return setError(VBOX_E_OBJECT_IN_USE,
5650 tr("Shared folder named '%s' already exists"),
5651 aName.c_str());
5652
5653 sharedFolder.createObject();
5654 rc = sharedFolder->init(i_getMachine(),
5655 aName,
5656 aHostPath,
5657 !!aWritable,
5658 !!aAutomount,
5659 true /* fFailOnError */);
5660 if (FAILED(rc)) return rc;
5661
5662 i_setModified(IsModified_SharedFolders);
5663 mHWData.backup();
5664 mHWData->mSharedFolders.push_back(sharedFolder);
5665
5666 /* inform the direct session if any */
5667 alock.release();
5668 i_onSharedFolderChange();
5669
5670 return S_OK;
5671}
5672
5673HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5674{
5675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5676
5677 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5678 if (FAILED(rc)) return rc;
5679
5680 ComObjPtr<SharedFolder> sharedFolder;
5681 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5682 if (FAILED(rc)) return rc;
5683
5684 i_setModified(IsModified_SharedFolders);
5685 mHWData.backup();
5686 mHWData->mSharedFolders.remove(sharedFolder);
5687
5688 /* inform the direct session if any */
5689 alock.release();
5690 i_onSharedFolderChange();
5691
5692 return S_OK;
5693}
5694
5695HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5696{
5697 /* start with No */
5698 *aCanShow = FALSE;
5699
5700 ComPtr<IInternalSessionControl> directControl;
5701 {
5702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5703
5704 if (mData->mSession.mState != SessionState_Locked)
5705 return setError(VBOX_E_INVALID_VM_STATE,
5706 tr("Machine is not locked for session (session state: %s)"),
5707 Global::stringifySessionState(mData->mSession.mState));
5708
5709 if (mData->mSession.mLockType == LockType_VM)
5710 directControl = mData->mSession.mDirectControl;
5711 }
5712
5713 /* ignore calls made after #OnSessionEnd() is called */
5714 if (!directControl)
5715 return S_OK;
5716
5717 LONG64 dummy;
5718 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5719}
5720
5721HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5722{
5723 ComPtr<IInternalSessionControl> directControl;
5724 {
5725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5726
5727 if (mData->mSession.mState != SessionState_Locked)
5728 return setError(E_FAIL,
5729 tr("Machine is not locked for session (session state: %s)"),
5730 Global::stringifySessionState(mData->mSession.mState));
5731
5732 if (mData->mSession.mLockType == LockType_VM)
5733 directControl = mData->mSession.mDirectControl;
5734 }
5735
5736 /* ignore calls made after #OnSessionEnd() is called */
5737 if (!directControl)
5738 return S_OK;
5739
5740 BOOL dummy;
5741 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5742}
5743
5744#ifdef VBOX_WITH_GUEST_PROPS
5745/**
5746 * Look up a guest property in VBoxSVC's internal structures.
5747 */
5748HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5749 com::Utf8Str &aValue,
5750 LONG64 *aTimestamp,
5751 com::Utf8Str &aFlags) const
5752{
5753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5754
5755 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5756 if (it != mHWData->mGuestProperties.end())
5757 {
5758 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5759 aValue = it->second.strValue;
5760 *aTimestamp = it->second.mTimestamp;
5761 GuestPropWriteFlags(it->second.mFlags, szFlags);
5762 aFlags = Utf8Str(szFlags);
5763 }
5764
5765 return S_OK;
5766}
5767
5768/**
5769 * Query the VM that a guest property belongs to for the property.
5770 * @returns E_ACCESSDENIED if the VM process is not available or not
5771 * currently handling queries and the lookup should then be done in
5772 * VBoxSVC.
5773 */
5774HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5775 com::Utf8Str &aValue,
5776 LONG64 *aTimestamp,
5777 com::Utf8Str &aFlags) const
5778{
5779 HRESULT rc = S_OK;
5780 BSTR bValue = NULL;
5781 BSTR bFlags = NULL;
5782
5783 ComPtr<IInternalSessionControl> directControl;
5784 {
5785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5786 if (mData->mSession.mLockType == LockType_VM)
5787 directControl = mData->mSession.mDirectControl;
5788 }
5789
5790 /* ignore calls made after #OnSessionEnd() is called */
5791 if (!directControl)
5792 rc = E_ACCESSDENIED;
5793 else
5794 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5795 0 /* accessMode */,
5796 &bValue, aTimestamp, &bFlags);
5797
5798 aValue = bValue;
5799 aFlags = bFlags;
5800
5801 return rc;
5802}
5803#endif // VBOX_WITH_GUEST_PROPS
5804
5805HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5806 com::Utf8Str &aValue,
5807 LONG64 *aTimestamp,
5808 com::Utf8Str &aFlags)
5809{
5810#ifndef VBOX_WITH_GUEST_PROPS
5811 ReturnComNotImplemented();
5812#else // VBOX_WITH_GUEST_PROPS
5813
5814 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5815
5816 if (rc == E_ACCESSDENIED)
5817 /* The VM is not running or the service is not (yet) accessible */
5818 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5819 return rc;
5820#endif // VBOX_WITH_GUEST_PROPS
5821}
5822
5823HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5824{
5825 LONG64 dummyTimestamp;
5826 com::Utf8Str dummyFlags;
5827 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5828 return rc;
5829
5830}
5831HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5832{
5833 com::Utf8Str dummyFlags;
5834 com::Utf8Str dummyValue;
5835 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5836 return rc;
5837}
5838
5839#ifdef VBOX_WITH_GUEST_PROPS
5840/**
5841 * Set a guest property in VBoxSVC's internal structures.
5842 */
5843HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5844 const com::Utf8Str &aFlags, bool fDelete)
5845{
5846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5847 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5848 if (FAILED(rc)) return rc;
5849
5850 try
5851 {
5852 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5853 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5854 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5855
5856 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5857 if (it == mHWData->mGuestProperties.end())
5858 {
5859 if (!fDelete)
5860 {
5861 i_setModified(IsModified_MachineData);
5862 mHWData.backupEx();
5863
5864 RTTIMESPEC time;
5865 HWData::GuestProperty prop;
5866 prop.strValue = Bstr(aValue).raw();
5867 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5868 prop.mFlags = fFlags;
5869 mHWData->mGuestProperties[aName] = prop;
5870 }
5871 }
5872 else
5873 {
5874 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5875 {
5876 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5877 }
5878 else
5879 {
5880 i_setModified(IsModified_MachineData);
5881 mHWData.backupEx();
5882
5883 /* The backupEx() operation invalidates our iterator,
5884 * so get a new one. */
5885 it = mHWData->mGuestProperties.find(aName);
5886 Assert(it != mHWData->mGuestProperties.end());
5887
5888 if (!fDelete)
5889 {
5890 RTTIMESPEC time;
5891 it->second.strValue = aValue;
5892 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5893 it->second.mFlags = fFlags;
5894 }
5895 else
5896 mHWData->mGuestProperties.erase(it);
5897 }
5898 }
5899
5900 if (SUCCEEDED(rc))
5901 {
5902 alock.release();
5903
5904 mParent->i_onGuestPropertyChange(mData->mUuid,
5905 Bstr(aName).raw(),
5906 Bstr(aValue).raw(),
5907 Bstr(aFlags).raw());
5908 }
5909 }
5910 catch (std::bad_alloc &)
5911 {
5912 rc = E_OUTOFMEMORY;
5913 }
5914
5915 return rc;
5916}
5917
5918/**
5919 * Set a property on the VM that that property belongs to.
5920 * @returns E_ACCESSDENIED if the VM process is not available or not
5921 * currently handling queries and the setting should then be done in
5922 * VBoxSVC.
5923 */
5924HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5925 const com::Utf8Str &aFlags, bool fDelete)
5926{
5927 HRESULT rc;
5928
5929 try
5930 {
5931 ComPtr<IInternalSessionControl> directControl;
5932 {
5933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5934 if (mData->mSession.mLockType == LockType_VM)
5935 directControl = mData->mSession.mDirectControl;
5936 }
5937
5938 BSTR dummy = NULL; /* will not be changed (setter) */
5939 LONG64 dummy64;
5940 if (!directControl)
5941 rc = E_ACCESSDENIED;
5942 else
5943 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5944 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5945 fDelete? 2: 1 /* accessMode */,
5946 &dummy, &dummy64, &dummy);
5947 }
5948 catch (std::bad_alloc &)
5949 {
5950 rc = E_OUTOFMEMORY;
5951 }
5952
5953 return rc;
5954}
5955#endif // VBOX_WITH_GUEST_PROPS
5956
5957HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5958 const com::Utf8Str &aFlags)
5959{
5960#ifndef VBOX_WITH_GUEST_PROPS
5961 ReturnComNotImplemented();
5962#else // VBOX_WITH_GUEST_PROPS
5963 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5964 if (rc == E_ACCESSDENIED)
5965 /* The VM is not running or the service is not (yet) accessible */
5966 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5967 return rc;
5968#endif // VBOX_WITH_GUEST_PROPS
5969}
5970
5971HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5972{
5973 return setGuestProperty(aProperty, aValue, "");
5974}
5975
5976HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5977{
5978#ifndef VBOX_WITH_GUEST_PROPS
5979 ReturnComNotImplemented();
5980#else // VBOX_WITH_GUEST_PROPS
5981 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5982 if (rc == E_ACCESSDENIED)
5983 /* The VM is not running or the service is not (yet) accessible */
5984 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5985 return rc;
5986#endif // VBOX_WITH_GUEST_PROPS
5987}
5988
5989#ifdef VBOX_WITH_GUEST_PROPS
5990/**
5991 * Enumerate the guest properties in VBoxSVC's internal structures.
5992 */
5993HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5994 std::vector<com::Utf8Str> &aNames,
5995 std::vector<com::Utf8Str> &aValues,
5996 std::vector<LONG64> &aTimestamps,
5997 std::vector<com::Utf8Str> &aFlags)
5998{
5999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6000 Utf8Str strPatterns(aPatterns);
6001
6002 /*
6003 * Look for matching patterns and build up a list.
6004 */
6005 HWData::GuestPropertyMap propMap;
6006 for (HWData::GuestPropertyMap::const_iterator
6007 it = mHWData->mGuestProperties.begin();
6008 it != mHWData->mGuestProperties.end();
6009 ++it)
6010 {
6011 if ( strPatterns.isEmpty()
6012 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6013 RTSTR_MAX,
6014 it->first.c_str(),
6015 RTSTR_MAX,
6016 NULL)
6017 )
6018 propMap.insert(*it);
6019 }
6020
6021 alock.release();
6022
6023 /*
6024 * And build up the arrays for returning the property information.
6025 */
6026 size_t cEntries = propMap.size();
6027
6028 aNames.resize(cEntries);
6029 aValues.resize(cEntries);
6030 aTimestamps.resize(cEntries);
6031 aFlags.resize(cEntries);
6032
6033 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6034 size_t i = 0;
6035 for (HWData::GuestPropertyMap::const_iterator
6036 it = propMap.begin();
6037 it != propMap.end();
6038 ++it, ++i)
6039 {
6040 aNames[i] = it->first;
6041 aValues[i] = it->second.strValue;
6042 aTimestamps[i] = it->second.mTimestamp;
6043 GuestPropWriteFlags(it->second.mFlags, szFlags);
6044 aFlags[i] = Utf8Str(szFlags);
6045 }
6046
6047 return S_OK;
6048}
6049
6050/**
6051 * Enumerate the properties managed by a VM.
6052 * @returns E_ACCESSDENIED if the VM process is not available or not
6053 * currently handling queries and the setting should then be done in
6054 * VBoxSVC.
6055 */
6056HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6057 std::vector<com::Utf8Str> &aNames,
6058 std::vector<com::Utf8Str> &aValues,
6059 std::vector<LONG64> &aTimestamps,
6060 std::vector<com::Utf8Str> &aFlags)
6061{
6062 HRESULT rc;
6063 ComPtr<IInternalSessionControl> directControl;
6064 {
6065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6066 if (mData->mSession.mLockType == LockType_VM)
6067 directControl = mData->mSession.mDirectControl;
6068 }
6069
6070 com::SafeArray<BSTR> bNames;
6071 com::SafeArray<BSTR> bValues;
6072 com::SafeArray<LONG64> bTimestamps;
6073 com::SafeArray<BSTR> bFlags;
6074
6075 if (!directControl)
6076 rc = E_ACCESSDENIED;
6077 else
6078 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6079 ComSafeArrayAsOutParam(bNames),
6080 ComSafeArrayAsOutParam(bValues),
6081 ComSafeArrayAsOutParam(bTimestamps),
6082 ComSafeArrayAsOutParam(bFlags));
6083 size_t i;
6084 aNames.resize(bNames.size());
6085 for (i = 0; i < bNames.size(); ++i)
6086 aNames[i] = Utf8Str(bNames[i]);
6087 aValues.resize(bValues.size());
6088 for (i = 0; i < bValues.size(); ++i)
6089 aValues[i] = Utf8Str(bValues[i]);
6090 aTimestamps.resize(bTimestamps.size());
6091 for (i = 0; i < bTimestamps.size(); ++i)
6092 aTimestamps[i] = bTimestamps[i];
6093 aFlags.resize(bFlags.size());
6094 for (i = 0; i < bFlags.size(); ++i)
6095 aFlags[i] = Utf8Str(bFlags[i]);
6096
6097 return rc;
6098}
6099#endif // VBOX_WITH_GUEST_PROPS
6100HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6101 std::vector<com::Utf8Str> &aNames,
6102 std::vector<com::Utf8Str> &aValues,
6103 std::vector<LONG64> &aTimestamps,
6104 std::vector<com::Utf8Str> &aFlags)
6105{
6106#ifndef VBOX_WITH_GUEST_PROPS
6107 ReturnComNotImplemented();
6108#else // VBOX_WITH_GUEST_PROPS
6109
6110 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6111
6112 if (rc == E_ACCESSDENIED)
6113 /* The VM is not running or the service is not (yet) accessible */
6114 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6115 return rc;
6116#endif // VBOX_WITH_GUEST_PROPS
6117}
6118
6119HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6120 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6121{
6122 MediumAttachmentList atts;
6123
6124 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6125 if (FAILED(rc)) return rc;
6126
6127 aMediumAttachments.resize(atts.size());
6128 size_t i = 0;
6129 for (MediumAttachmentList::const_iterator
6130 it = atts.begin();
6131 it != atts.end();
6132 ++it, ++i)
6133 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6134
6135 return S_OK;
6136}
6137
6138HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6139 LONG aControllerPort,
6140 LONG aDevice,
6141 ComPtr<IMediumAttachment> &aAttachment)
6142{
6143 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6144 aName.c_str(), aControllerPort, aDevice));
6145
6146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6147
6148 aAttachment = NULL;
6149
6150 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6151 aName,
6152 aControllerPort,
6153 aDevice);
6154 if (pAttach.isNull())
6155 return setError(VBOX_E_OBJECT_NOT_FOUND,
6156 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6157 aDevice, aControllerPort, aName.c_str());
6158
6159 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6160
6161 return S_OK;
6162}
6163
6164
6165HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6166 StorageBus_T aConnectionType,
6167 ComPtr<IStorageController> &aController)
6168{
6169 if ( (aConnectionType <= StorageBus_Null)
6170 || (aConnectionType > StorageBus_PCIe))
6171 return setError(E_INVALIDARG,
6172 tr("Invalid connection type: %d"),
6173 aConnectionType);
6174
6175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 HRESULT rc = i_checkStateDependency(MutableStateDep);
6178 if (FAILED(rc)) return rc;
6179
6180 /* try to find one with the name first. */
6181 ComObjPtr<StorageController> ctrl;
6182
6183 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6184 if (SUCCEEDED(rc))
6185 return setError(VBOX_E_OBJECT_IN_USE,
6186 tr("Storage controller named '%s' already exists"),
6187 aName.c_str());
6188
6189 ctrl.createObject();
6190
6191 /* get a new instance number for the storage controller */
6192 ULONG ulInstance = 0;
6193 bool fBootable = true;
6194 for (StorageControllerList::const_iterator
6195 it = mStorageControllers->begin();
6196 it != mStorageControllers->end();
6197 ++it)
6198 {
6199 if ((*it)->i_getStorageBus() == aConnectionType)
6200 {
6201 ULONG ulCurInst = (*it)->i_getInstance();
6202
6203 if (ulCurInst >= ulInstance)
6204 ulInstance = ulCurInst + 1;
6205
6206 /* Only one controller of each type can be marked as bootable. */
6207 if ((*it)->i_getBootable())
6208 fBootable = false;
6209 }
6210 }
6211
6212 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6213 if (FAILED(rc)) return rc;
6214
6215 i_setModified(IsModified_Storage);
6216 mStorageControllers.backup();
6217 mStorageControllers->push_back(ctrl);
6218
6219 ctrl.queryInterfaceTo(aController.asOutParam());
6220
6221 /* inform the direct session if any */
6222 alock.release();
6223 i_onStorageControllerChange();
6224
6225 return S_OK;
6226}
6227
6228HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6229 ComPtr<IStorageController> &aStorageController)
6230{
6231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6232
6233 ComObjPtr<StorageController> ctrl;
6234
6235 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6236 if (SUCCEEDED(rc))
6237 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6238
6239 return rc;
6240}
6241
6242HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6243 ULONG aInstance,
6244 ComPtr<IStorageController> &aStorageController)
6245{
6246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 for (StorageControllerList::const_iterator
6249 it = mStorageControllers->begin();
6250 it != mStorageControllers->end();
6251 ++it)
6252 {
6253 if ( (*it)->i_getStorageBus() == aConnectionType
6254 && (*it)->i_getInstance() == aInstance)
6255 {
6256 (*it).queryInterfaceTo(aStorageController.asOutParam());
6257 return S_OK;
6258 }
6259 }
6260
6261 return setError(VBOX_E_OBJECT_NOT_FOUND,
6262 tr("Could not find a storage controller with instance number '%lu'"),
6263 aInstance);
6264}
6265
6266HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6267{
6268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6269
6270 HRESULT rc = i_checkStateDependency(MutableStateDep);
6271 if (FAILED(rc)) return rc;
6272
6273 ComObjPtr<StorageController> ctrl;
6274
6275 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6276 if (SUCCEEDED(rc))
6277 {
6278 /* Ensure that only one controller of each type is marked as bootable. */
6279 if (aBootable == TRUE)
6280 {
6281 for (StorageControllerList::const_iterator
6282 it = mStorageControllers->begin();
6283 it != mStorageControllers->end();
6284 ++it)
6285 {
6286 ComObjPtr<StorageController> aCtrl = (*it);
6287
6288 if ( (aCtrl->i_getName() != aName)
6289 && aCtrl->i_getBootable() == TRUE
6290 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6291 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6292 {
6293 aCtrl->i_setBootable(FALSE);
6294 break;
6295 }
6296 }
6297 }
6298
6299 if (SUCCEEDED(rc))
6300 {
6301 ctrl->i_setBootable(aBootable);
6302 i_setModified(IsModified_Storage);
6303 }
6304 }
6305
6306 if (SUCCEEDED(rc))
6307 {
6308 /* inform the direct session if any */
6309 alock.release();
6310 i_onStorageControllerChange();
6311 }
6312
6313 return rc;
6314}
6315
6316HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6317{
6318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 HRESULT rc = i_checkStateDependency(MutableStateDep);
6321 if (FAILED(rc)) return rc;
6322
6323 ComObjPtr<StorageController> ctrl;
6324 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6325 if (FAILED(rc)) return rc;
6326
6327 {
6328 /* find all attached devices to the appropriate storage controller and detach them all */
6329 // make a temporary list because detachDevice invalidates iterators into
6330 // mMediumAttachments
6331 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6332
6333 for (MediumAttachmentList::const_iterator
6334 it = llAttachments2.begin();
6335 it != llAttachments2.end();
6336 ++it)
6337 {
6338 MediumAttachment *pAttachTemp = *it;
6339
6340 AutoCaller localAutoCaller(pAttachTemp);
6341 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6342
6343 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6344
6345 if (pAttachTemp->i_getControllerName() == aName)
6346 {
6347 rc = i_detachDevice(pAttachTemp, alock, NULL);
6348 if (FAILED(rc)) return rc;
6349 }
6350 }
6351 }
6352
6353 /* We can remove it now. */
6354 i_setModified(IsModified_Storage);
6355 mStorageControllers.backup();
6356
6357 ctrl->i_unshare();
6358
6359 mStorageControllers->remove(ctrl);
6360
6361 /* inform the direct session if any */
6362 alock.release();
6363 i_onStorageControllerChange();
6364
6365 return S_OK;
6366}
6367
6368HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6369 ComPtr<IUSBController> &aController)
6370{
6371 if ( (aType <= USBControllerType_Null)
6372 || (aType >= USBControllerType_Last))
6373 return setError(E_INVALIDARG,
6374 tr("Invalid USB controller type: %d"),
6375 aType);
6376
6377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 HRESULT rc = i_checkStateDependency(MutableStateDep);
6380 if (FAILED(rc)) return rc;
6381
6382 /* try to find one with the same type first. */
6383 ComObjPtr<USBController> ctrl;
6384
6385 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6386 if (SUCCEEDED(rc))
6387 return setError(VBOX_E_OBJECT_IN_USE,
6388 tr("USB controller named '%s' already exists"),
6389 aName.c_str());
6390
6391 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6392 ULONG maxInstances;
6393 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6394 if (FAILED(rc))
6395 return rc;
6396
6397 ULONG cInstances = i_getUSBControllerCountByType(aType);
6398 if (cInstances >= maxInstances)
6399 return setError(E_INVALIDARG,
6400 tr("Too many USB controllers of this type"));
6401
6402 ctrl.createObject();
6403
6404 rc = ctrl->init(this, aName, aType);
6405 if (FAILED(rc)) return rc;
6406
6407 i_setModified(IsModified_USB);
6408 mUSBControllers.backup();
6409 mUSBControllers->push_back(ctrl);
6410
6411 ctrl.queryInterfaceTo(aController.asOutParam());
6412
6413 /* inform the direct session if any */
6414 alock.release();
6415 i_onUSBControllerChange();
6416
6417 return S_OK;
6418}
6419
6420HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6421{
6422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6423
6424 ComObjPtr<USBController> ctrl;
6425
6426 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6427 if (SUCCEEDED(rc))
6428 ctrl.queryInterfaceTo(aController.asOutParam());
6429
6430 return rc;
6431}
6432
6433HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6434 ULONG *aControllers)
6435{
6436 if ( (aType <= USBControllerType_Null)
6437 || (aType >= USBControllerType_Last))
6438 return setError(E_INVALIDARG,
6439 tr("Invalid USB controller type: %d"),
6440 aType);
6441
6442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6443
6444 ComObjPtr<USBController> ctrl;
6445
6446 *aControllers = i_getUSBControllerCountByType(aType);
6447
6448 return S_OK;
6449}
6450
6451HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6452{
6453
6454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6455
6456 HRESULT rc = i_checkStateDependency(MutableStateDep);
6457 if (FAILED(rc)) return rc;
6458
6459 ComObjPtr<USBController> ctrl;
6460 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6461 if (FAILED(rc)) return rc;
6462
6463 i_setModified(IsModified_USB);
6464 mUSBControllers.backup();
6465
6466 ctrl->i_unshare();
6467
6468 mUSBControllers->remove(ctrl);
6469
6470 /* inform the direct session if any */
6471 alock.release();
6472 i_onUSBControllerChange();
6473
6474 return S_OK;
6475}
6476
6477HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6478 ULONG *aOriginX,
6479 ULONG *aOriginY,
6480 ULONG *aWidth,
6481 ULONG *aHeight,
6482 BOOL *aEnabled)
6483{
6484 uint32_t u32OriginX= 0;
6485 uint32_t u32OriginY= 0;
6486 uint32_t u32Width = 0;
6487 uint32_t u32Height = 0;
6488 uint16_t u16Flags = 0;
6489
6490 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6491 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6492 if (RT_FAILURE(vrc))
6493 {
6494#ifdef RT_OS_WINDOWS
6495 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6496 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6497 * So just assign fEnable to TRUE again.
6498 * The right fix would be to change GUI API wrappers to make sure that parameters
6499 * are changed only if API succeeds.
6500 */
6501 *aEnabled = TRUE;
6502#endif
6503 return setError(VBOX_E_IPRT_ERROR,
6504 tr("Saved guest size is not available (%Rrc)"),
6505 vrc);
6506 }
6507
6508 *aOriginX = u32OriginX;
6509 *aOriginY = u32OriginY;
6510 *aWidth = u32Width;
6511 *aHeight = u32Height;
6512 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6513
6514 return S_OK;
6515}
6516
6517HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6518 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6519{
6520 if (aScreenId != 0)
6521 return E_NOTIMPL;
6522
6523 if ( aBitmapFormat != BitmapFormat_BGR0
6524 && aBitmapFormat != BitmapFormat_BGRA
6525 && aBitmapFormat != BitmapFormat_RGBA
6526 && aBitmapFormat != BitmapFormat_PNG)
6527 return setError(E_NOTIMPL,
6528 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6529
6530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 uint8_t *pu8Data = NULL;
6533 uint32_t cbData = 0;
6534 uint32_t u32Width = 0;
6535 uint32_t u32Height = 0;
6536
6537 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6538
6539 if (RT_FAILURE(vrc))
6540 return setError(VBOX_E_IPRT_ERROR,
6541 tr("Saved thumbnail data is not available (%Rrc)"),
6542 vrc);
6543
6544 HRESULT hr = S_OK;
6545
6546 *aWidth = u32Width;
6547 *aHeight = u32Height;
6548
6549 if (cbData > 0)
6550 {
6551 /* Convert pixels to the format expected by the API caller. */
6552 if (aBitmapFormat == BitmapFormat_BGR0)
6553 {
6554 /* [0] B, [1] G, [2] R, [3] 0. */
6555 aData.resize(cbData);
6556 memcpy(&aData.front(), pu8Data, cbData);
6557 }
6558 else if (aBitmapFormat == BitmapFormat_BGRA)
6559 {
6560 /* [0] B, [1] G, [2] R, [3] A. */
6561 aData.resize(cbData);
6562 for (uint32_t i = 0; i < cbData; i += 4)
6563 {
6564 aData[i] = pu8Data[i];
6565 aData[i + 1] = pu8Data[i + 1];
6566 aData[i + 2] = pu8Data[i + 2];
6567 aData[i + 3] = 0xff;
6568 }
6569 }
6570 else if (aBitmapFormat == BitmapFormat_RGBA)
6571 {
6572 /* [0] R, [1] G, [2] B, [3] A. */
6573 aData.resize(cbData);
6574 for (uint32_t i = 0; i < cbData; i += 4)
6575 {
6576 aData[i] = pu8Data[i + 2];
6577 aData[i + 1] = pu8Data[i + 1];
6578 aData[i + 2] = pu8Data[i];
6579 aData[i + 3] = 0xff;
6580 }
6581 }
6582 else if (aBitmapFormat == BitmapFormat_PNG)
6583 {
6584 uint8_t *pu8PNG = NULL;
6585 uint32_t cbPNG = 0;
6586 uint32_t cxPNG = 0;
6587 uint32_t cyPNG = 0;
6588
6589 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6590
6591 if (RT_SUCCESS(vrc))
6592 {
6593 aData.resize(cbPNG);
6594 if (cbPNG)
6595 memcpy(&aData.front(), pu8PNG, cbPNG);
6596 }
6597 else
6598 hr = setError(VBOX_E_IPRT_ERROR,
6599 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6600 vrc);
6601
6602 RTMemFree(pu8PNG);
6603 }
6604 }
6605
6606 freeSavedDisplayScreenshot(pu8Data);
6607
6608 return hr;
6609}
6610
6611HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6612 ULONG *aWidth,
6613 ULONG *aHeight,
6614 std::vector<BitmapFormat_T> &aBitmapFormats)
6615{
6616 if (aScreenId != 0)
6617 return E_NOTIMPL;
6618
6619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6620
6621 uint8_t *pu8Data = NULL;
6622 uint32_t cbData = 0;
6623 uint32_t u32Width = 0;
6624 uint32_t u32Height = 0;
6625
6626 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6627
6628 if (RT_FAILURE(vrc))
6629 return setError(VBOX_E_IPRT_ERROR,
6630 tr("Saved screenshot data is not available (%Rrc)"),
6631 vrc);
6632
6633 *aWidth = u32Width;
6634 *aHeight = u32Height;
6635 aBitmapFormats.resize(1);
6636 aBitmapFormats[0] = BitmapFormat_PNG;
6637
6638 freeSavedDisplayScreenshot(pu8Data);
6639
6640 return S_OK;
6641}
6642
6643HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6644 BitmapFormat_T aBitmapFormat,
6645 ULONG *aWidth,
6646 ULONG *aHeight,
6647 std::vector<BYTE> &aData)
6648{
6649 if (aScreenId != 0)
6650 return E_NOTIMPL;
6651
6652 if (aBitmapFormat != BitmapFormat_PNG)
6653 return E_NOTIMPL;
6654
6655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 uint8_t *pu8Data = NULL;
6658 uint32_t cbData = 0;
6659 uint32_t u32Width = 0;
6660 uint32_t u32Height = 0;
6661
6662 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6663
6664 if (RT_FAILURE(vrc))
6665 return setError(VBOX_E_IPRT_ERROR,
6666 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6667 vrc);
6668
6669 *aWidth = u32Width;
6670 *aHeight = u32Height;
6671
6672 aData.resize(cbData);
6673 if (cbData)
6674 memcpy(&aData.front(), pu8Data, cbData);
6675
6676 freeSavedDisplayScreenshot(pu8Data);
6677
6678 return S_OK;
6679}
6680
6681HRESULT Machine::hotPlugCPU(ULONG aCpu)
6682{
6683 HRESULT rc = S_OK;
6684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6685
6686 if (!mHWData->mCPUHotPlugEnabled)
6687 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6688
6689 if (aCpu >= mHWData->mCPUCount)
6690 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6691
6692 if (mHWData->mCPUAttached[aCpu])
6693 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6694
6695 alock.release();
6696 rc = i_onCPUChange(aCpu, false);
6697 alock.acquire();
6698 if (FAILED(rc)) return rc;
6699
6700 i_setModified(IsModified_MachineData);
6701 mHWData.backup();
6702 mHWData->mCPUAttached[aCpu] = true;
6703
6704 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6705 if (Global::IsOnline(mData->mMachineState))
6706 i_saveSettings(NULL);
6707
6708 return S_OK;
6709}
6710
6711HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6712{
6713 HRESULT rc = S_OK;
6714
6715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6716
6717 if (!mHWData->mCPUHotPlugEnabled)
6718 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6719
6720 if (aCpu >= SchemaDefs::MaxCPUCount)
6721 return setError(E_INVALIDARG,
6722 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6723 SchemaDefs::MaxCPUCount);
6724
6725 if (!mHWData->mCPUAttached[aCpu])
6726 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6727
6728 /* CPU 0 can't be detached */
6729 if (aCpu == 0)
6730 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6731
6732 alock.release();
6733 rc = i_onCPUChange(aCpu, true);
6734 alock.acquire();
6735 if (FAILED(rc)) return rc;
6736
6737 i_setModified(IsModified_MachineData);
6738 mHWData.backup();
6739 mHWData->mCPUAttached[aCpu] = false;
6740
6741 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6742 if (Global::IsOnline(mData->mMachineState))
6743 i_saveSettings(NULL);
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6749{
6750 *aAttached = false;
6751
6752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6753
6754 /* If hotplug is enabled the CPU is always enabled. */
6755 if (!mHWData->mCPUHotPlugEnabled)
6756 {
6757 if (aCpu < mHWData->mCPUCount)
6758 *aAttached = true;
6759 }
6760 else
6761 {
6762 if (aCpu < SchemaDefs::MaxCPUCount)
6763 *aAttached = mHWData->mCPUAttached[aCpu];
6764 }
6765
6766 return S_OK;
6767}
6768
6769HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6770{
6771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6772
6773 Utf8Str log = i_getLogFilename(aIdx);
6774 if (!RTFileExists(log.c_str()))
6775 log.setNull();
6776 aFilename = log;
6777
6778 return S_OK;
6779}
6780
6781HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6782{
6783 if (aSize < 0)
6784 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6785
6786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6787
6788 HRESULT rc = S_OK;
6789 Utf8Str log = i_getLogFilename(aIdx);
6790
6791 /* do not unnecessarily hold the lock while doing something which does
6792 * not need the lock and potentially takes a long time. */
6793 alock.release();
6794
6795 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6796 * keeps the SOAP reply size under 1M for the webservice (we're using
6797 * base64 encoded strings for binary data for years now, avoiding the
6798 * expansion of each byte array element to approx. 25 bytes of XML. */
6799 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6800 aData.resize(cbData);
6801
6802 RTFILE LogFile;
6803 int vrc = RTFileOpen(&LogFile, log.c_str(),
6804 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6805 if (RT_SUCCESS(vrc))
6806 {
6807 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6808 if (RT_SUCCESS(vrc))
6809 aData.resize(cbData);
6810 else
6811 rc = setError(VBOX_E_IPRT_ERROR,
6812 tr("Could not read log file '%s' (%Rrc)"),
6813 log.c_str(), vrc);
6814 RTFileClose(LogFile);
6815 }
6816 else
6817 rc = setError(VBOX_E_IPRT_ERROR,
6818 tr("Could not open log file '%s' (%Rrc)"),
6819 log.c_str(), vrc);
6820
6821 if (FAILED(rc))
6822 aData.resize(0);
6823
6824 return rc;
6825}
6826
6827
6828/**
6829 * Currently this method doesn't attach device to the running VM,
6830 * just makes sure it's plugged on next VM start.
6831 */
6832HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6833{
6834 // lock scope
6835 {
6836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 HRESULT rc = i_checkStateDependency(MutableStateDep);
6839 if (FAILED(rc)) return rc;
6840
6841 ChipsetType_T aChipset = ChipsetType_PIIX3;
6842 COMGETTER(ChipsetType)(&aChipset);
6843
6844 if (aChipset != ChipsetType_ICH9)
6845 {
6846 return setError(E_INVALIDARG,
6847 tr("Host PCI attachment only supported with ICH9 chipset"));
6848 }
6849
6850 // check if device with this host PCI address already attached
6851 for (HWData::PCIDeviceAssignmentList::const_iterator
6852 it = mHWData->mPCIDeviceAssignments.begin();
6853 it != mHWData->mPCIDeviceAssignments.end();
6854 ++it)
6855 {
6856 LONG iHostAddress = -1;
6857 ComPtr<PCIDeviceAttachment> pAttach;
6858 pAttach = *it;
6859 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6860 if (iHostAddress == aHostAddress)
6861 return setError(E_INVALIDARG,
6862 tr("Device with host PCI address already attached to this VM"));
6863 }
6864
6865 ComObjPtr<PCIDeviceAttachment> pda;
6866 char name[32];
6867
6868 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6869 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6870 pda.createObject();
6871 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6872 i_setModified(IsModified_MachineData);
6873 mHWData.backup();
6874 mHWData->mPCIDeviceAssignments.push_back(pda);
6875 }
6876
6877 return S_OK;
6878}
6879
6880/**
6881 * Currently this method doesn't detach device from the running VM,
6882 * just makes sure it's not plugged on next VM start.
6883 */
6884HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6885{
6886 ComObjPtr<PCIDeviceAttachment> pAttach;
6887 bool fRemoved = false;
6888 HRESULT rc;
6889
6890 // lock scope
6891 {
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893
6894 rc = i_checkStateDependency(MutableStateDep);
6895 if (FAILED(rc)) return rc;
6896
6897 for (HWData::PCIDeviceAssignmentList::const_iterator
6898 it = mHWData->mPCIDeviceAssignments.begin();
6899 it != mHWData->mPCIDeviceAssignments.end();
6900 ++it)
6901 {
6902 LONG iHostAddress = -1;
6903 pAttach = *it;
6904 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6905 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6906 {
6907 i_setModified(IsModified_MachineData);
6908 mHWData.backup();
6909 mHWData->mPCIDeviceAssignments.remove(pAttach);
6910 fRemoved = true;
6911 break;
6912 }
6913 }
6914 }
6915
6916
6917 /* Fire event outside of the lock */
6918 if (fRemoved)
6919 {
6920 Assert(!pAttach.isNull());
6921 ComPtr<IEventSource> es;
6922 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6923 Assert(SUCCEEDED(rc));
6924 Bstr mid;
6925 rc = this->COMGETTER(Id)(mid.asOutParam());
6926 Assert(SUCCEEDED(rc));
6927 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6928 }
6929
6930 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6931 tr("No host PCI device %08x attached"),
6932 aHostAddress
6933 );
6934}
6935
6936HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6937{
6938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6939
6940 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6941 size_t i = 0;
6942 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6943 it = mHWData->mPCIDeviceAssignments.begin();
6944 it != mHWData->mPCIDeviceAssignments.end();
6945 ++it, ++i)
6946 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6947
6948 return S_OK;
6949}
6950
6951HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6952{
6953 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6954
6955 return S_OK;
6956}
6957
6958HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6959{
6960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6961
6962 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6963
6964 return S_OK;
6965}
6966
6967HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6968{
6969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6970 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6971 if (SUCCEEDED(hrc))
6972 {
6973 hrc = mHWData.backupEx();
6974 if (SUCCEEDED(hrc))
6975 {
6976 i_setModified(IsModified_MachineData);
6977 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6978 }
6979 }
6980 return hrc;
6981}
6982
6983HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6984{
6985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6986 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6987 return S_OK;
6988}
6989
6990HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6991{
6992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6993 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6994 if (SUCCEEDED(hrc))
6995 {
6996 hrc = mHWData.backupEx();
6997 if (SUCCEEDED(hrc))
6998 {
6999 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7000 if (SUCCEEDED(hrc))
7001 i_setModified(IsModified_MachineData);
7002 }
7003 }
7004 return hrc;
7005}
7006
7007HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7008{
7009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7012
7013 return S_OK;
7014}
7015
7016HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7017{
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7020 if (SUCCEEDED(hrc))
7021 {
7022 hrc = mHWData.backupEx();
7023 if (SUCCEEDED(hrc))
7024 {
7025 i_setModified(IsModified_MachineData);
7026 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7027 }
7028 }
7029 return hrc;
7030}
7031
7032HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7033{
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035
7036 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7037
7038 return S_OK;
7039}
7040
7041HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7042{
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7046 if ( SUCCEEDED(hrc)
7047 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7048 {
7049 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7050 int vrc;
7051
7052 if (aAutostartEnabled)
7053 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7054 else
7055 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7056
7057 if (RT_SUCCESS(vrc))
7058 {
7059 hrc = mHWData.backupEx();
7060 if (SUCCEEDED(hrc))
7061 {
7062 i_setModified(IsModified_MachineData);
7063 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7064 }
7065 }
7066 else if (vrc == VERR_NOT_SUPPORTED)
7067 hrc = setError(VBOX_E_NOT_SUPPORTED,
7068 tr("The VM autostart feature is not supported on this platform"));
7069 else if (vrc == VERR_PATH_NOT_FOUND)
7070 hrc = setError(E_FAIL,
7071 tr("The path to the autostart database is not set"));
7072 else
7073 hrc = setError(E_UNEXPECTED,
7074 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7075 aAutostartEnabled ? "Adding" : "Removing",
7076 mUserData->s.strName.c_str(), vrc);
7077 }
7078 return hrc;
7079}
7080
7081HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7082{
7083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7086
7087 return S_OK;
7088}
7089
7090HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7091{
7092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7093 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7094 if (SUCCEEDED(hrc))
7095 {
7096 hrc = mHWData.backupEx();
7097 if (SUCCEEDED(hrc))
7098 {
7099 i_setModified(IsModified_MachineData);
7100 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7101 }
7102 }
7103 return hrc;
7104}
7105
7106HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7107{
7108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7109
7110 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7111
7112 return S_OK;
7113}
7114
7115HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7116{
7117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7118 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7119 if ( SUCCEEDED(hrc)
7120 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7121 {
7122 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7123 int vrc;
7124
7125 if (aAutostopType != AutostopType_Disabled)
7126 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7127 else
7128 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7129
7130 if (RT_SUCCESS(vrc))
7131 {
7132 hrc = mHWData.backupEx();
7133 if (SUCCEEDED(hrc))
7134 {
7135 i_setModified(IsModified_MachineData);
7136 mHWData->mAutostart.enmAutostopType = aAutostopType;
7137 }
7138 }
7139 else if (vrc == VERR_NOT_SUPPORTED)
7140 hrc = setError(VBOX_E_NOT_SUPPORTED,
7141 tr("The VM autostop feature is not supported on this platform"));
7142 else if (vrc == VERR_PATH_NOT_FOUND)
7143 hrc = setError(E_FAIL,
7144 tr("The path to the autostart database is not set"));
7145 else
7146 hrc = setError(E_UNEXPECTED,
7147 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7148 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7149 mUserData->s.strName.c_str(), vrc);
7150 }
7151 return hrc;
7152}
7153
7154HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7155{
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157
7158 aDefaultFrontend = mHWData->mDefaultFrontend;
7159
7160 return S_OK;
7161}
7162
7163HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7164{
7165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7166 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7167 if (SUCCEEDED(hrc))
7168 {
7169 hrc = mHWData.backupEx();
7170 if (SUCCEEDED(hrc))
7171 {
7172 i_setModified(IsModified_MachineData);
7173 mHWData->mDefaultFrontend = aDefaultFrontend;
7174 }
7175 }
7176 return hrc;
7177}
7178
7179HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7180{
7181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7182 size_t cbIcon = mUserData->s.ovIcon.size();
7183 aIcon.resize(cbIcon);
7184 if (cbIcon)
7185 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7186 return S_OK;
7187}
7188
7189HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7190{
7191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7192 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7193 if (SUCCEEDED(hrc))
7194 {
7195 i_setModified(IsModified_MachineData);
7196 mUserData.backup();
7197 size_t cbIcon = aIcon.size();
7198 mUserData->s.ovIcon.resize(cbIcon);
7199 if (cbIcon)
7200 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7201 }
7202 return hrc;
7203}
7204
7205HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7206{
7207#ifdef VBOX_WITH_USB
7208 *aUSBProxyAvailable = true;
7209#else
7210 *aUSBProxyAvailable = false;
7211#endif
7212 return S_OK;
7213}
7214
7215HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7216{
7217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7218
7219 aVMProcessPriority = mUserData->s.strVMPriority;
7220
7221 return S_OK;
7222}
7223
7224HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7225{
7226 RT_NOREF(aVMProcessPriority);
7227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7228 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7229 if (SUCCEEDED(hrc))
7230 {
7231 /** @todo r=klaus: currently this is marked as not implemented, as
7232 * the code for setting the priority of the process is not there
7233 * (neither when starting the VM nor at runtime). */
7234 ReturnComNotImplemented();
7235#if 0
7236 hrc = mUserData.backupEx();
7237 if (SUCCEEDED(hrc))
7238 {
7239 i_setModified(IsModified_MachineData);
7240 mUserData->s.strVMPriority = aVMProcessPriority;
7241 }
7242#endif
7243 }
7244 return hrc;
7245}
7246
7247HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7248 ComPtr<IProgress> &aProgress)
7249{
7250 ComObjPtr<Progress> pP;
7251 Progress *ppP = pP;
7252 IProgress *iP = static_cast<IProgress *>(ppP);
7253 IProgress **pProgress = &iP;
7254
7255 IMachine *pTarget = aTarget;
7256
7257 /* Convert the options. */
7258 RTCList<CloneOptions_T> optList;
7259 if (aOptions.size())
7260 for (size_t i = 0; i < aOptions.size(); ++i)
7261 optList.append(aOptions[i]);
7262
7263 if (optList.contains(CloneOptions_Link))
7264 {
7265 if (!i_isSnapshotMachine())
7266 return setError(E_INVALIDARG,
7267 tr("Linked clone can only be created from a snapshot"));
7268 if (aMode != CloneMode_MachineState)
7269 return setError(E_INVALIDARG,
7270 tr("Linked clone can only be created for a single machine state"));
7271 }
7272 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7273
7274 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7275
7276 HRESULT rc = pWorker->start(pProgress);
7277
7278 pP = static_cast<Progress *>(*pProgress);
7279 pP.queryInterfaceTo(aProgress.asOutParam());
7280
7281 return rc;
7282
7283}
7284
7285HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7286 const com::Utf8Str &aType,
7287 ComPtr<IProgress> &aProgress)
7288{
7289 LogFlowThisFuncEnter();
7290
7291 ComObjPtr<Progress> progress;
7292
7293 progress.createObject();
7294
7295 HRESULT rc = S_OK;
7296 Utf8Str targetPath = aTargetPath;
7297 Utf8Str type = aType;
7298
7299 /* Initialize our worker task */
7300 MachineMoveVM* task = NULL;
7301 try
7302 {
7303 task = new MachineMoveVM(this, targetPath, type, progress);
7304 }
7305 catch(...)
7306 {
7307 delete task;
7308 return rc;
7309 }
7310
7311 /*
7312 * task pointer will be owned by the ThreadTask class.
7313 * There is no need to call operator "delete" in the end.
7314 */
7315 rc = task->init();
7316 if (SUCCEEDED(rc))
7317 {
7318 rc = task->createThread();
7319 if (FAILED(rc))
7320 {
7321 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7322 }
7323
7324 /* Return progress to the caller */
7325 progress.queryInterfaceTo(aProgress.asOutParam());
7326 }
7327
7328 LogFlowThisFuncLeave();
7329 return rc;
7330
7331}
7332
7333HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7334{
7335 NOREF(aProgress);
7336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7337
7338 // This check should always fail.
7339 HRESULT rc = i_checkStateDependency(MutableStateDep);
7340 if (FAILED(rc)) return rc;
7341
7342 AssertFailedReturn(E_NOTIMPL);
7343}
7344
7345HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7346{
7347 NOREF(aSavedStateFile);
7348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7349
7350 // This check should always fail.
7351 HRESULT rc = i_checkStateDependency(MutableStateDep);
7352 if (FAILED(rc)) return rc;
7353
7354 AssertFailedReturn(E_NOTIMPL);
7355}
7356
7357HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7358{
7359 NOREF(aFRemoveFile);
7360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7361
7362 // This check should always fail.
7363 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7364 if (FAILED(rc)) return rc;
7365
7366 AssertFailedReturn(E_NOTIMPL);
7367}
7368
7369// public methods for internal purposes
7370/////////////////////////////////////////////////////////////////////////////
7371
7372/**
7373 * Adds the given IsModified_* flag to the dirty flags of the machine.
7374 * This must be called either during i_loadSettings or under the machine write lock.
7375 * @param fl Flag
7376 * @param fAllowStateModification If state modifications are allowed.
7377 */
7378void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7379{
7380 mData->flModifications |= fl;
7381 if (fAllowStateModification && i_isStateModificationAllowed())
7382 mData->mCurrentStateModified = true;
7383}
7384
7385/**
7386 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7387 * care of the write locking.
7388 *
7389 * @param fModification The flag to add.
7390 * @param fAllowStateModification If state modifications are allowed.
7391 */
7392void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7393{
7394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7395 i_setModified(fModification, fAllowStateModification);
7396}
7397
7398/**
7399 * Saves the registry entry of this machine to the given configuration node.
7400 *
7401 * @param data Machine registry data.
7402 *
7403 * @note locks this object for reading.
7404 */
7405HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7406{
7407 AutoLimitedCaller autoCaller(this);
7408 AssertComRCReturnRC(autoCaller.rc());
7409
7410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7411
7412 data.uuid = mData->mUuid;
7413 data.strSettingsFile = mData->m_strConfigFile;
7414
7415 return S_OK;
7416}
7417
7418/**
7419 * Calculates the absolute path of the given path taking the directory of the
7420 * machine settings file as the current directory.
7421 *
7422 * @param strPath Path to calculate the absolute path for.
7423 * @param aResult Where to put the result (used only on success, can be the
7424 * same Utf8Str instance as passed in @a aPath).
7425 * @return IPRT result.
7426 *
7427 * @note Locks this object for reading.
7428 */
7429int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7430{
7431 AutoCaller autoCaller(this);
7432 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7433
7434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7435
7436 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7437
7438 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7439
7440 strSettingsDir.stripFilename();
7441 char folder[RTPATH_MAX];
7442 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7443 if (RT_SUCCESS(vrc))
7444 aResult = folder;
7445
7446 return vrc;
7447}
7448
7449/**
7450 * Copies strSource to strTarget, making it relative to the machine folder
7451 * if it is a subdirectory thereof, or simply copying it otherwise.
7452 *
7453 * @param strSource Path to evaluate and copy.
7454 * @param strTarget Buffer to receive target path.
7455 *
7456 * @note Locks this object for reading.
7457 */
7458void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7459 Utf8Str &strTarget)
7460{
7461 AutoCaller autoCaller(this);
7462 AssertComRCReturn(autoCaller.rc(), (void)0);
7463
7464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7465
7466 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7467 // use strTarget as a temporary buffer to hold the machine settings dir
7468 strTarget = mData->m_strConfigFileFull;
7469 strTarget.stripFilename();
7470 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7471 {
7472 // is relative: then append what's left
7473 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7474 // for empty paths (only possible for subdirs) use "." to avoid
7475 // triggering default settings for not present config attributes.
7476 if (strTarget.isEmpty())
7477 strTarget = ".";
7478 }
7479 else
7480 // is not relative: then overwrite
7481 strTarget = strSource;
7482}
7483
7484/**
7485 * Returns the full path to the machine's log folder in the
7486 * \a aLogFolder argument.
7487 */
7488void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7489{
7490 AutoCaller autoCaller(this);
7491 AssertComRCReturnVoid(autoCaller.rc());
7492
7493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7494
7495 char szTmp[RTPATH_MAX];
7496 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7497 if (RT_SUCCESS(vrc))
7498 {
7499 if (szTmp[0] && !mUserData.isNull())
7500 {
7501 char szTmp2[RTPATH_MAX];
7502 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7503 if (RT_SUCCESS(vrc))
7504 aLogFolder = Utf8StrFmt("%s%c%s",
7505 szTmp2,
7506 RTPATH_DELIMITER,
7507 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7508 }
7509 else
7510 vrc = VERR_PATH_IS_RELATIVE;
7511 }
7512
7513 if (RT_FAILURE(vrc))
7514 {
7515 // fallback if VBOX_USER_LOGHOME is not set or invalid
7516 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7517 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7518 aLogFolder.append(RTPATH_DELIMITER);
7519 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7520 }
7521}
7522
7523/**
7524 * Returns the full path to the machine's log file for an given index.
7525 */
7526Utf8Str Machine::i_getLogFilename(ULONG idx)
7527{
7528 Utf8Str logFolder;
7529 getLogFolder(logFolder);
7530 Assert(logFolder.length());
7531
7532 Utf8Str log;
7533 if (idx == 0)
7534 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7535#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7536 else if (idx == 1)
7537 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7538 else
7539 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7540#else
7541 else
7542 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7543#endif
7544 return log;
7545}
7546
7547/**
7548 * Returns the full path to the machine's hardened log file.
7549 */
7550Utf8Str Machine::i_getHardeningLogFilename(void)
7551{
7552 Utf8Str strFilename;
7553 getLogFolder(strFilename);
7554 Assert(strFilename.length());
7555 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7556 return strFilename;
7557}
7558
7559
7560/**
7561 * Composes a unique saved state filename based on the current system time. The filename is
7562 * granular to the second so this will work so long as no more than one snapshot is taken on
7563 * a machine per second.
7564 *
7565 * Before version 4.1, we used this formula for saved state files:
7566 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7567 * which no longer works because saved state files can now be shared between the saved state of the
7568 * "saved" machine and an online snapshot, and the following would cause problems:
7569 * 1) save machine
7570 * 2) create online snapshot from that machine state --> reusing saved state file
7571 * 3) save machine again --> filename would be reused, breaking the online snapshot
7572 *
7573 * So instead we now use a timestamp.
7574 *
7575 * @param strStateFilePath
7576 */
7577
7578void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7579{
7580 AutoCaller autoCaller(this);
7581 AssertComRCReturnVoid(autoCaller.rc());
7582
7583 {
7584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7585 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7586 }
7587
7588 RTTIMESPEC ts;
7589 RTTimeNow(&ts);
7590 RTTIME time;
7591 RTTimeExplode(&time, &ts);
7592
7593 strStateFilePath += RTPATH_DELIMITER;
7594 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7595 time.i32Year, time.u8Month, time.u8MonthDay,
7596 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7597}
7598
7599/**
7600 * Returns the full path to the default video capture file.
7601 */
7602void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7603{
7604 AutoCaller autoCaller(this);
7605 AssertComRCReturnVoid(autoCaller.rc());
7606
7607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7608
7609 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7610 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7611 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7612}
7613
7614/**
7615 * Returns whether at least one USB controller is present for the VM.
7616 */
7617bool Machine::i_isUSBControllerPresent()
7618{
7619 AutoCaller autoCaller(this);
7620 AssertComRCReturn(autoCaller.rc(), false);
7621
7622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7623
7624 return (mUSBControllers->size() > 0);
7625}
7626
7627/**
7628 * @note Locks this object for writing, calls the client process
7629 * (inside the lock).
7630 */
7631HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7632 const Utf8Str &strFrontend,
7633 const Utf8Str &strEnvironment,
7634 ProgressProxy *aProgress)
7635{
7636 LogFlowThisFuncEnter();
7637
7638 AssertReturn(aControl, E_FAIL);
7639 AssertReturn(aProgress, E_FAIL);
7640 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7641
7642 AutoCaller autoCaller(this);
7643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7644
7645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7646
7647 if (!mData->mRegistered)
7648 return setError(E_UNEXPECTED,
7649 tr("The machine '%s' is not registered"),
7650 mUserData->s.strName.c_str());
7651
7652 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7653
7654 /* The process started when launching a VM with separate UI/VM processes is always
7655 * the UI process, i.e. needs special handling as it won't claim the session. */
7656 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7657
7658 if (fSeparate)
7659 {
7660 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7661 return setError(VBOX_E_INVALID_OBJECT_STATE,
7662 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7663 mUserData->s.strName.c_str());
7664 }
7665 else
7666 {
7667 if ( mData->mSession.mState == SessionState_Locked
7668 || mData->mSession.mState == SessionState_Spawning
7669 || mData->mSession.mState == SessionState_Unlocking)
7670 return setError(VBOX_E_INVALID_OBJECT_STATE,
7671 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7672 mUserData->s.strName.c_str());
7673
7674 /* may not be busy */
7675 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7676 }
7677
7678 /* get the path to the executable */
7679 char szPath[RTPATH_MAX];
7680 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7681 size_t cchBufLeft = strlen(szPath);
7682 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7683 szPath[cchBufLeft] = 0;
7684 char *pszNamePart = szPath + cchBufLeft;
7685 cchBufLeft = sizeof(szPath) - cchBufLeft;
7686
7687 int vrc = VINF_SUCCESS;
7688 RTPROCESS pid = NIL_RTPROCESS;
7689
7690 RTENV env = RTENV_DEFAULT;
7691
7692 if (!strEnvironment.isEmpty())
7693 {
7694 char *newEnvStr = NULL;
7695
7696 do
7697 {
7698 /* clone the current environment */
7699 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7700 AssertRCBreakStmt(vrc2, vrc = vrc2);
7701
7702 newEnvStr = RTStrDup(strEnvironment.c_str());
7703 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7704
7705 /* put new variables to the environment
7706 * (ignore empty variable names here since RTEnv API
7707 * intentionally doesn't do that) */
7708 char *var = newEnvStr;
7709 for (char *p = newEnvStr; *p; ++p)
7710 {
7711 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7712 {
7713 *p = '\0';
7714 if (*var)
7715 {
7716 char *val = strchr(var, '=');
7717 if (val)
7718 {
7719 *val++ = '\0';
7720 vrc2 = RTEnvSetEx(env, var, val);
7721 }
7722 else
7723 vrc2 = RTEnvUnsetEx(env, var);
7724 if (RT_FAILURE(vrc2))
7725 break;
7726 }
7727 var = p + 1;
7728 }
7729 }
7730 if (RT_SUCCESS(vrc2) && *var)
7731 vrc2 = RTEnvPutEx(env, var);
7732
7733 AssertRCBreakStmt(vrc2, vrc = vrc2);
7734 }
7735 while (0);
7736
7737 if (newEnvStr != NULL)
7738 RTStrFree(newEnvStr);
7739 }
7740
7741 /* Hardening logging */
7742#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7743 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7744 {
7745 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7746 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7747 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7748 {
7749 Utf8Str strStartupLogDir = strHardeningLogFile;
7750 strStartupLogDir.stripFilename();
7751 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7752 file without stripping the file. */
7753 }
7754 strSupHardeningLogArg.append(strHardeningLogFile);
7755
7756 /* Remove legacy log filename to avoid confusion. */
7757 Utf8Str strOldStartupLogFile;
7758 getLogFolder(strOldStartupLogFile);
7759 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7760 RTFileDelete(strOldStartupLogFile.c_str());
7761 }
7762 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7763#else
7764 const char *pszSupHardeningLogArg = NULL;
7765#endif
7766
7767 Utf8Str strCanonicalName;
7768
7769#ifdef VBOX_WITH_QTGUI
7770 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7771 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7772 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7773 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7774 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7775 {
7776 strCanonicalName = "GUI/Qt";
7777# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7778 /* Modify the base path so that we don't need to use ".." below. */
7779 RTPathStripTrailingSlash(szPath);
7780 RTPathStripFilename(szPath);
7781 cchBufLeft = strlen(szPath);
7782 pszNamePart = szPath + cchBufLeft;
7783 cchBufLeft = sizeof(szPath) - cchBufLeft;
7784
7785# define OSX_APP_NAME "VirtualBoxVM"
7786# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7787
7788 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7789 if ( strAppOverride.contains(".")
7790 || strAppOverride.contains("/")
7791 || strAppOverride.contains("\\")
7792 || strAppOverride.contains(":"))
7793 strAppOverride.setNull();
7794 Utf8Str strAppPath;
7795 if (!strAppOverride.isEmpty())
7796 {
7797 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7798 Utf8Str strFullPath(szPath);
7799 strFullPath.append(strAppPath);
7800 /* there is a race, but people using this deserve the failure */
7801 if (!RTFileExists(strFullPath.c_str()))
7802 strAppOverride.setNull();
7803 }
7804 if (strAppOverride.isEmpty())
7805 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7806 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7807 strcpy(pszNamePart, strAppPath.c_str());
7808# else
7809# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7810 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7811# else
7812 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7813# endif
7814 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7815 strcpy(pszNamePart, s_szVirtualBox_exe);
7816# endif
7817
7818 Utf8Str idStr = mData->mUuid.toString();
7819 const char *apszArgs[] =
7820 {
7821 szPath,
7822 "--comment", mUserData->s.strName.c_str(),
7823 "--startvm", idStr.c_str(),
7824 "--no-startvm-errormsgbox",
7825 NULL, /* For "--separate". */
7826 NULL, /* For "--sup-startup-log". */
7827 NULL
7828 };
7829 unsigned iArg = 6;
7830 if (fSeparate)
7831 apszArgs[iArg++] = "--separate";
7832 apszArgs[iArg++] = pszSupHardeningLogArg;
7833
7834 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7835 }
7836#else /* !VBOX_WITH_QTGUI */
7837 if (0)
7838 ;
7839#endif /* VBOX_WITH_QTGUI */
7840
7841 else
7842
7843#ifdef VBOX_WITH_VBOXSDL
7844 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7845 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7846 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7847 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7848 {
7849 strCanonicalName = "GUI/SDL";
7850 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7851 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7852 strcpy(pszNamePart, s_szVBoxSDL_exe);
7853
7854 Utf8Str idStr = mData->mUuid.toString();
7855 const char *apszArgs[] =
7856 {
7857 szPath,
7858 "--comment", mUserData->s.strName.c_str(),
7859 "--startvm", idStr.c_str(),
7860 NULL, /* For "--separate". */
7861 NULL, /* For "--sup-startup-log". */
7862 NULL
7863 };
7864 unsigned iArg = 5;
7865 if (fSeparate)
7866 apszArgs[iArg++] = "--separate";
7867 apszArgs[iArg++] = pszSupHardeningLogArg;
7868
7869 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7870 }
7871#else /* !VBOX_WITH_VBOXSDL */
7872 if (0)
7873 ;
7874#endif /* !VBOX_WITH_VBOXSDL */
7875
7876 else
7877
7878#ifdef VBOX_WITH_HEADLESS
7879 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7880 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7881 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7882 )
7883 {
7884 strCanonicalName = "headless";
7885 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7886 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7887 * and a VM works even if the server has not been installed.
7888 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7889 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7890 * differently in 4.0 and 3.x.
7891 */
7892 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7893 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7894 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7895
7896 Utf8Str idStr = mData->mUuid.toString();
7897 const char *apszArgs[] =
7898 {
7899 szPath,
7900 "--comment", mUserData->s.strName.c_str(),
7901 "--startvm", idStr.c_str(),
7902 "--vrde", "config",
7903 NULL, /* For "--capture". */
7904 NULL, /* For "--sup-startup-log". */
7905 NULL
7906 };
7907 unsigned iArg = 7;
7908 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7909 apszArgs[iArg++] = "--capture";
7910 apszArgs[iArg++] = pszSupHardeningLogArg;
7911
7912# ifdef RT_OS_WINDOWS
7913 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7914# else
7915 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7916# endif
7917 }
7918#else /* !VBOX_WITH_HEADLESS */
7919 if (0)
7920 ;
7921#endif /* !VBOX_WITH_HEADLESS */
7922 else
7923 {
7924 RTEnvDestroy(env);
7925 return setError(E_INVALIDARG,
7926 tr("Invalid frontend name: '%s'"),
7927 strFrontend.c_str());
7928 }
7929
7930 RTEnvDestroy(env);
7931
7932 if (RT_FAILURE(vrc))
7933 return setError(VBOX_E_IPRT_ERROR,
7934 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7935 mUserData->s.strName.c_str(), vrc);
7936
7937 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7938
7939 if (!fSeparate)
7940 {
7941 /*
7942 * Note that we don't release the lock here before calling the client,
7943 * because it doesn't need to call us back if called with a NULL argument.
7944 * Releasing the lock here is dangerous because we didn't prepare the
7945 * launch data yet, but the client we've just started may happen to be
7946 * too fast and call LockMachine() that will fail (because of PID, etc.),
7947 * so that the Machine will never get out of the Spawning session state.
7948 */
7949
7950 /* inform the session that it will be a remote one */
7951 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7952#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7953 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7954#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7955 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7956#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7957 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7958
7959 if (FAILED(rc))
7960 {
7961 /* restore the session state */
7962 mData->mSession.mState = SessionState_Unlocked;
7963 alock.release();
7964 mParent->i_addProcessToReap(pid);
7965 /* The failure may occur w/o any error info (from RPC), so provide one */
7966 return setError(VBOX_E_VM_ERROR,
7967 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7968 }
7969
7970 /* attach launch data to the machine */
7971 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7972 mData->mSession.mRemoteControls.push_back(aControl);
7973 mData->mSession.mProgress = aProgress;
7974 mData->mSession.mPID = pid;
7975 mData->mSession.mState = SessionState_Spawning;
7976 Assert(strCanonicalName.isNotEmpty());
7977 mData->mSession.mName = strCanonicalName;
7978 }
7979 else
7980 {
7981 /* For separate UI process we declare the launch as completed instantly, as the
7982 * actual headless VM start may or may not come. No point in remembering anything
7983 * yet, as what matters for us is when the headless VM gets started. */
7984 aProgress->i_notifyComplete(S_OK);
7985 }
7986
7987 alock.release();
7988 mParent->i_addProcessToReap(pid);
7989
7990 LogFlowThisFuncLeave();
7991 return S_OK;
7992}
7993
7994/**
7995 * Returns @c true if the given session machine instance has an open direct
7996 * session (and optionally also for direct sessions which are closing) and
7997 * returns the session control machine instance if so.
7998 *
7999 * Note that when the method returns @c false, the arguments remain unchanged.
8000 *
8001 * @param aMachine Session machine object.
8002 * @param aControl Direct session control object (optional).
8003 * @param aRequireVM If true then only allow VM sessions.
8004 * @param aAllowClosing If true then additionally a session which is currently
8005 * being closed will also be allowed.
8006 *
8007 * @note locks this object for reading.
8008 */
8009bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8010 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8011 bool aRequireVM /*= false*/,
8012 bool aAllowClosing /*= false*/)
8013{
8014 AutoLimitedCaller autoCaller(this);
8015 AssertComRCReturn(autoCaller.rc(), false);
8016
8017 /* just return false for inaccessible machines */
8018 if (getObjectState().getState() != ObjectState::Ready)
8019 return false;
8020
8021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8022
8023 if ( ( mData->mSession.mState == SessionState_Locked
8024 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8025 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8026 )
8027 {
8028 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8029
8030 aMachine = mData->mSession.mMachine;
8031
8032 if (aControl != NULL)
8033 *aControl = mData->mSession.mDirectControl;
8034
8035 return true;
8036 }
8037
8038 return false;
8039}
8040
8041/**
8042 * Returns @c true if the given machine has an spawning direct session.
8043 *
8044 * @note locks this object for reading.
8045 */
8046bool Machine::i_isSessionSpawning()
8047{
8048 AutoLimitedCaller autoCaller(this);
8049 AssertComRCReturn(autoCaller.rc(), false);
8050
8051 /* just return false for inaccessible machines */
8052 if (getObjectState().getState() != ObjectState::Ready)
8053 return false;
8054
8055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8056
8057 if (mData->mSession.mState == SessionState_Spawning)
8058 return true;
8059
8060 return false;
8061}
8062
8063/**
8064 * Called from the client watcher thread to check for unexpected client process
8065 * death during Session_Spawning state (e.g. before it successfully opened a
8066 * direct session).
8067 *
8068 * On Win32 and on OS/2, this method is called only when we've got the
8069 * direct client's process termination notification, so it always returns @c
8070 * true.
8071 *
8072 * On other platforms, this method returns @c true if the client process is
8073 * terminated and @c false if it's still alive.
8074 *
8075 * @note Locks this object for writing.
8076 */
8077bool Machine::i_checkForSpawnFailure()
8078{
8079 AutoCaller autoCaller(this);
8080 if (!autoCaller.isOk())
8081 {
8082 /* nothing to do */
8083 LogFlowThisFunc(("Already uninitialized!\n"));
8084 return true;
8085 }
8086
8087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8088
8089 if (mData->mSession.mState != SessionState_Spawning)
8090 {
8091 /* nothing to do */
8092 LogFlowThisFunc(("Not spawning any more!\n"));
8093 return true;
8094 }
8095
8096 HRESULT rc = S_OK;
8097
8098 /* PID not yet initialized, skip check. */
8099 if (mData->mSession.mPID == NIL_RTPROCESS)
8100 return false;
8101
8102 RTPROCSTATUS status;
8103 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8104
8105 if (vrc != VERR_PROCESS_RUNNING)
8106 {
8107 Utf8Str strExtraInfo;
8108
8109#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8110 /* If the startup logfile exists and is of non-zero length, tell the
8111 user to look there for more details to encourage them to attach it
8112 when reporting startup issues. */
8113 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8114 uint64_t cbStartupLogFile = 0;
8115 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8116 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8117 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8118#endif
8119
8120 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8121 rc = setError(E_FAIL,
8122 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8123 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8124 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8125 rc = setError(E_FAIL,
8126 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8127 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8128 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8129 rc = setError(E_FAIL,
8130 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8131 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8132 else
8133 rc = setError(E_FAIL,
8134 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8135 i_getName().c_str(), vrc, strExtraInfo.c_str());
8136 }
8137
8138 if (FAILED(rc))
8139 {
8140 /* Close the remote session, remove the remote control from the list
8141 * and reset session state to Closed (@note keep the code in sync with
8142 * the relevant part in LockMachine()). */
8143
8144 Assert(mData->mSession.mRemoteControls.size() == 1);
8145 if (mData->mSession.mRemoteControls.size() == 1)
8146 {
8147 ErrorInfoKeeper eik;
8148 mData->mSession.mRemoteControls.front()->Uninitialize();
8149 }
8150
8151 mData->mSession.mRemoteControls.clear();
8152 mData->mSession.mState = SessionState_Unlocked;
8153
8154 /* finalize the progress after setting the state */
8155 if (!mData->mSession.mProgress.isNull())
8156 {
8157 mData->mSession.mProgress->notifyComplete(rc);
8158 mData->mSession.mProgress.setNull();
8159 }
8160
8161 mData->mSession.mPID = NIL_RTPROCESS;
8162
8163 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8164 return true;
8165 }
8166
8167 return false;
8168}
8169
8170/**
8171 * Checks whether the machine can be registered. If so, commits and saves
8172 * all settings.
8173 *
8174 * @note Must be called from mParent's write lock. Locks this object and
8175 * children for writing.
8176 */
8177HRESULT Machine::i_prepareRegister()
8178{
8179 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8180
8181 AutoLimitedCaller autoCaller(this);
8182 AssertComRCReturnRC(autoCaller.rc());
8183
8184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8185
8186 /* wait for state dependents to drop to zero */
8187 i_ensureNoStateDependencies();
8188
8189 if (!mData->mAccessible)
8190 return setError(VBOX_E_INVALID_OBJECT_STATE,
8191 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8192 mUserData->s.strName.c_str(),
8193 mData->mUuid.toString().c_str());
8194
8195 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8196
8197 if (mData->mRegistered)
8198 return setError(VBOX_E_INVALID_OBJECT_STATE,
8199 tr("The machine '%s' with UUID {%s} is already registered"),
8200 mUserData->s.strName.c_str(),
8201 mData->mUuid.toString().c_str());
8202
8203 HRESULT rc = S_OK;
8204
8205 // Ensure the settings are saved. If we are going to be registered and
8206 // no config file exists yet, create it by calling i_saveSettings() too.
8207 if ( (mData->flModifications)
8208 || (!mData->pMachineConfigFile->fileExists())
8209 )
8210 {
8211 rc = i_saveSettings(NULL);
8212 // no need to check whether VirtualBox.xml needs saving too since
8213 // we can't have a machine XML file rename pending
8214 if (FAILED(rc)) return rc;
8215 }
8216
8217 /* more config checking goes here */
8218
8219 if (SUCCEEDED(rc))
8220 {
8221 /* we may have had implicit modifications we want to fix on success */
8222 i_commit();
8223
8224 mData->mRegistered = true;
8225 }
8226 else
8227 {
8228 /* we may have had implicit modifications we want to cancel on failure*/
8229 i_rollback(false /* aNotify */);
8230 }
8231
8232 return rc;
8233}
8234
8235/**
8236 * Increases the number of objects dependent on the machine state or on the
8237 * registered state. Guarantees that these two states will not change at least
8238 * until #i_releaseStateDependency() is called.
8239 *
8240 * Depending on the @a aDepType value, additional state checks may be made.
8241 * These checks will set extended error info on failure. See
8242 * #i_checkStateDependency() for more info.
8243 *
8244 * If this method returns a failure, the dependency is not added and the caller
8245 * is not allowed to rely on any particular machine state or registration state
8246 * value and may return the failed result code to the upper level.
8247 *
8248 * @param aDepType Dependency type to add.
8249 * @param aState Current machine state (NULL if not interested).
8250 * @param aRegistered Current registered state (NULL if not interested).
8251 *
8252 * @note Locks this object for writing.
8253 */
8254HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8255 MachineState_T *aState /* = NULL */,
8256 BOOL *aRegistered /* = NULL */)
8257{
8258 AutoCaller autoCaller(this);
8259 AssertComRCReturnRC(autoCaller.rc());
8260
8261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8262
8263 HRESULT rc = i_checkStateDependency(aDepType);
8264 if (FAILED(rc)) return rc;
8265
8266 {
8267 if (mData->mMachineStateChangePending != 0)
8268 {
8269 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8270 * drop to zero so don't add more. It may make sense to wait a bit
8271 * and retry before reporting an error (since the pending state
8272 * transition should be really quick) but let's just assert for
8273 * now to see if it ever happens on practice. */
8274
8275 AssertFailed();
8276
8277 return setError(E_ACCESSDENIED,
8278 tr("Machine state change is in progress. Please retry the operation later."));
8279 }
8280
8281 ++mData->mMachineStateDeps;
8282 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8283 }
8284
8285 if (aState)
8286 *aState = mData->mMachineState;
8287 if (aRegistered)
8288 *aRegistered = mData->mRegistered;
8289
8290 return S_OK;
8291}
8292
8293/**
8294 * Decreases the number of objects dependent on the machine state.
8295 * Must always complete the #i_addStateDependency() call after the state
8296 * dependency is no more necessary.
8297 */
8298void Machine::i_releaseStateDependency()
8299{
8300 AutoCaller autoCaller(this);
8301 AssertComRCReturnVoid(autoCaller.rc());
8302
8303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8304
8305 /* releaseStateDependency() w/o addStateDependency()? */
8306 AssertReturnVoid(mData->mMachineStateDeps != 0);
8307 -- mData->mMachineStateDeps;
8308
8309 if (mData->mMachineStateDeps == 0)
8310 {
8311 /* inform i_ensureNoStateDependencies() that there are no more deps */
8312 if (mData->mMachineStateChangePending != 0)
8313 {
8314 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8315 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8316 }
8317 }
8318}
8319
8320Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8321{
8322 /* start with nothing found */
8323 Utf8Str strResult("");
8324
8325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8326
8327 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8328 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8329 // found:
8330 strResult = it->second; // source is a Utf8Str
8331
8332 return strResult;
8333}
8334
8335// protected methods
8336/////////////////////////////////////////////////////////////////////////////
8337
8338/**
8339 * Performs machine state checks based on the @a aDepType value. If a check
8340 * fails, this method will set extended error info, otherwise it will return
8341 * S_OK. It is supposed, that on failure, the caller will immediately return
8342 * the return value of this method to the upper level.
8343 *
8344 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8345 *
8346 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8347 * current state of this machine object allows to change settings of the
8348 * machine (i.e. the machine is not registered, or registered but not running
8349 * and not saved). It is useful to call this method from Machine setters
8350 * before performing any change.
8351 *
8352 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8353 * as for MutableStateDep except that if the machine is saved, S_OK is also
8354 * returned. This is useful in setters which allow changing machine
8355 * properties when it is in the saved state.
8356 *
8357 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8358 * if the current state of this machine object allows to change runtime
8359 * changeable settings of the machine (i.e. the machine is not registered, or
8360 * registered but either running or not running and not saved). It is useful
8361 * to call this method from Machine setters before performing any changes to
8362 * runtime changeable settings.
8363 *
8364 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8365 * the same as for MutableOrRunningStateDep except that if the machine is
8366 * saved, S_OK is also returned. This is useful in setters which allow
8367 * changing runtime and saved state changeable machine properties.
8368 *
8369 * @param aDepType Dependency type to check.
8370 *
8371 * @note Non Machine based classes should use #i_addStateDependency() and
8372 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8373 * template.
8374 *
8375 * @note This method must be called from under this object's read or write
8376 * lock.
8377 */
8378HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8379{
8380 switch (aDepType)
8381 {
8382 case AnyStateDep:
8383 {
8384 break;
8385 }
8386 case MutableStateDep:
8387 {
8388 if ( mData->mRegistered
8389 && ( !i_isSessionMachine()
8390 || ( mData->mMachineState != MachineState_Aborted
8391 && mData->mMachineState != MachineState_Teleported
8392 && mData->mMachineState != MachineState_PoweredOff
8393 )
8394 )
8395 )
8396 return setError(VBOX_E_INVALID_VM_STATE,
8397 tr("The machine is not mutable (state is %s)"),
8398 Global::stringifyMachineState(mData->mMachineState));
8399 break;
8400 }
8401 case MutableOrSavedStateDep:
8402 {
8403 if ( mData->mRegistered
8404 && ( !i_isSessionMachine()
8405 || ( mData->mMachineState != MachineState_Aborted
8406 && mData->mMachineState != MachineState_Teleported
8407 && mData->mMachineState != MachineState_Saved
8408 && mData->mMachineState != MachineState_PoweredOff
8409 )
8410 )
8411 )
8412 return setError(VBOX_E_INVALID_VM_STATE,
8413 tr("The machine is not mutable or saved (state is %s)"),
8414 Global::stringifyMachineState(mData->mMachineState));
8415 break;
8416 }
8417 case MutableOrRunningStateDep:
8418 {
8419 if ( mData->mRegistered
8420 && ( !i_isSessionMachine()
8421 || ( mData->mMachineState != MachineState_Aborted
8422 && mData->mMachineState != MachineState_Teleported
8423 && mData->mMachineState != MachineState_PoweredOff
8424 && !Global::IsOnline(mData->mMachineState)
8425 )
8426 )
8427 )
8428 return setError(VBOX_E_INVALID_VM_STATE,
8429 tr("The machine is not mutable or running (state is %s)"),
8430 Global::stringifyMachineState(mData->mMachineState));
8431 break;
8432 }
8433 case MutableOrSavedOrRunningStateDep:
8434 {
8435 if ( mData->mRegistered
8436 && ( !i_isSessionMachine()
8437 || ( mData->mMachineState != MachineState_Aborted
8438 && mData->mMachineState != MachineState_Teleported
8439 && mData->mMachineState != MachineState_Saved
8440 && mData->mMachineState != MachineState_PoweredOff
8441 && !Global::IsOnline(mData->mMachineState)
8442 )
8443 )
8444 )
8445 return setError(VBOX_E_INVALID_VM_STATE,
8446 tr("The machine is not mutable, saved or running (state is %s)"),
8447 Global::stringifyMachineState(mData->mMachineState));
8448 break;
8449 }
8450 }
8451
8452 return S_OK;
8453}
8454
8455/**
8456 * Helper to initialize all associated child objects and allocate data
8457 * structures.
8458 *
8459 * This method must be called as a part of the object's initialization procedure
8460 * (usually done in the #init() method).
8461 *
8462 * @note Must be called only from #init() or from #i_registeredInit().
8463 */
8464HRESULT Machine::initDataAndChildObjects()
8465{
8466 AutoCaller autoCaller(this);
8467 AssertComRCReturnRC(autoCaller.rc());
8468 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8469 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8470
8471 AssertReturn(!mData->mAccessible, E_FAIL);
8472
8473 /* allocate data structures */
8474 mSSData.allocate();
8475 mUserData.allocate();
8476 mHWData.allocate();
8477 mMediumAttachments.allocate();
8478 mStorageControllers.allocate();
8479 mUSBControllers.allocate();
8480
8481 /* initialize mOSTypeId */
8482 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8483
8484/** @todo r=bird: init() methods never fails, right? Why don't we make them
8485 * return void then! */
8486
8487 /* create associated BIOS settings object */
8488 unconst(mBIOSSettings).createObject();
8489 mBIOSSettings->init(this);
8490
8491 /* create an associated VRDE object (default is disabled) */
8492 unconst(mVRDEServer).createObject();
8493 mVRDEServer->init(this);
8494
8495 /* create associated serial port objects */
8496 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8497 {
8498 unconst(mSerialPorts[slot]).createObject();
8499 mSerialPorts[slot]->init(this, slot);
8500 }
8501
8502 /* create associated parallel port objects */
8503 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8504 {
8505 unconst(mParallelPorts[slot]).createObject();
8506 mParallelPorts[slot]->init(this, slot);
8507 }
8508
8509 /* create the audio adapter object (always present, default is disabled) */
8510 unconst(mAudioAdapter).createObject();
8511 mAudioAdapter->init(this);
8512
8513 /* create the USB device filters object (always present) */
8514 unconst(mUSBDeviceFilters).createObject();
8515 mUSBDeviceFilters->init(this);
8516
8517 /* create associated network adapter objects */
8518 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8519 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8520 {
8521 unconst(mNetworkAdapters[slot]).createObject();
8522 mNetworkAdapters[slot]->init(this, slot);
8523 }
8524
8525 /* create the bandwidth control */
8526 unconst(mBandwidthControl).createObject();
8527 mBandwidthControl->init(this);
8528
8529 return S_OK;
8530}
8531
8532/**
8533 * Helper to uninitialize all associated child objects and to free all data
8534 * structures.
8535 *
8536 * This method must be called as a part of the object's uninitialization
8537 * procedure (usually done in the #uninit() method).
8538 *
8539 * @note Must be called only from #uninit() or from #i_registeredInit().
8540 */
8541void Machine::uninitDataAndChildObjects()
8542{
8543 AutoCaller autoCaller(this);
8544 AssertComRCReturnVoid(autoCaller.rc());
8545 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8546 || getObjectState().getState() == ObjectState::Limited);
8547
8548 /* tell all our other child objects we've been uninitialized */
8549 if (mBandwidthControl)
8550 {
8551 mBandwidthControl->uninit();
8552 unconst(mBandwidthControl).setNull();
8553 }
8554
8555 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8556 {
8557 if (mNetworkAdapters[slot])
8558 {
8559 mNetworkAdapters[slot]->uninit();
8560 unconst(mNetworkAdapters[slot]).setNull();
8561 }
8562 }
8563
8564 if (mUSBDeviceFilters)
8565 {
8566 mUSBDeviceFilters->uninit();
8567 unconst(mUSBDeviceFilters).setNull();
8568 }
8569
8570 if (mAudioAdapter)
8571 {
8572 mAudioAdapter->uninit();
8573 unconst(mAudioAdapter).setNull();
8574 }
8575
8576 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8577 {
8578 if (mParallelPorts[slot])
8579 {
8580 mParallelPorts[slot]->uninit();
8581 unconst(mParallelPorts[slot]).setNull();
8582 }
8583 }
8584
8585 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8586 {
8587 if (mSerialPorts[slot])
8588 {
8589 mSerialPorts[slot]->uninit();
8590 unconst(mSerialPorts[slot]).setNull();
8591 }
8592 }
8593
8594 if (mVRDEServer)
8595 {
8596 mVRDEServer->uninit();
8597 unconst(mVRDEServer).setNull();
8598 }
8599
8600 if (mBIOSSettings)
8601 {
8602 mBIOSSettings->uninit();
8603 unconst(mBIOSSettings).setNull();
8604 }
8605
8606 /* Deassociate media (only when a real Machine or a SnapshotMachine
8607 * instance is uninitialized; SessionMachine instances refer to real
8608 * Machine media). This is necessary for a clean re-initialization of
8609 * the VM after successfully re-checking the accessibility state. Note
8610 * that in case of normal Machine or SnapshotMachine uninitialization (as
8611 * a result of unregistering or deleting the snapshot), outdated media
8612 * attachments will already be uninitialized and deleted, so this
8613 * code will not affect them. */
8614 if ( !mMediumAttachments.isNull()
8615 && !i_isSessionMachine()
8616 )
8617 {
8618 for (MediumAttachmentList::const_iterator
8619 it = mMediumAttachments->begin();
8620 it != mMediumAttachments->end();
8621 ++it)
8622 {
8623 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8624 if (pMedium.isNull())
8625 continue;
8626 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8627 AssertComRC(rc);
8628 }
8629 }
8630
8631 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8632 {
8633 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8634 if (mData->mFirstSnapshot)
8635 {
8636 // snapshots tree is protected by machine write lock; strictly
8637 // this isn't necessary here since we're deleting the entire
8638 // machine, but otherwise we assert in Snapshot::uninit()
8639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8640 mData->mFirstSnapshot->uninit();
8641 mData->mFirstSnapshot.setNull();
8642 }
8643
8644 mData->mCurrentSnapshot.setNull();
8645 }
8646
8647 /* free data structures (the essential mData structure is not freed here
8648 * since it may be still in use) */
8649 mMediumAttachments.free();
8650 mStorageControllers.free();
8651 mUSBControllers.free();
8652 mHWData.free();
8653 mUserData.free();
8654 mSSData.free();
8655}
8656
8657/**
8658 * Returns a pointer to the Machine object for this machine that acts like a
8659 * parent for complex machine data objects such as shared folders, etc.
8660 *
8661 * For primary Machine objects and for SnapshotMachine objects, returns this
8662 * object's pointer itself. For SessionMachine objects, returns the peer
8663 * (primary) machine pointer.
8664 */
8665Machine *Machine::i_getMachine()
8666{
8667 if (i_isSessionMachine())
8668 return (Machine*)mPeer;
8669 return this;
8670}
8671
8672/**
8673 * Makes sure that there are no machine state dependents. If necessary, waits
8674 * for the number of dependents to drop to zero.
8675 *
8676 * Make sure this method is called from under this object's write lock to
8677 * guarantee that no new dependents may be added when this method returns
8678 * control to the caller.
8679 *
8680 * @note Locks this object for writing. The lock will be released while waiting
8681 * (if necessary).
8682 *
8683 * @warning To be used only in methods that change the machine state!
8684 */
8685void Machine::i_ensureNoStateDependencies()
8686{
8687 AssertReturnVoid(isWriteLockOnCurrentThread());
8688
8689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8690
8691 /* Wait for all state dependents if necessary */
8692 if (mData->mMachineStateDeps != 0)
8693 {
8694 /* lazy semaphore creation */
8695 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8696 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8697
8698 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8699 mData->mMachineStateDeps));
8700
8701 ++mData->mMachineStateChangePending;
8702
8703 /* reset the semaphore before waiting, the last dependent will signal
8704 * it */
8705 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8706
8707 alock.release();
8708
8709 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8710
8711 alock.acquire();
8712
8713 -- mData->mMachineStateChangePending;
8714 }
8715}
8716
8717/**
8718 * Changes the machine state and informs callbacks.
8719 *
8720 * This method is not intended to fail so it either returns S_OK or asserts (and
8721 * returns a failure).
8722 *
8723 * @note Locks this object for writing.
8724 */
8725HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8726{
8727 LogFlowThisFuncEnter();
8728 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8729 Assert(aMachineState != MachineState_Null);
8730
8731 AutoCaller autoCaller(this);
8732 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8733
8734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8735
8736 /* wait for state dependents to drop to zero */
8737 i_ensureNoStateDependencies();
8738
8739 MachineState_T const enmOldState = mData->mMachineState;
8740 if (enmOldState != aMachineState)
8741 {
8742 mData->mMachineState = aMachineState;
8743 RTTimeNow(&mData->mLastStateChange);
8744
8745#ifdef VBOX_WITH_DTRACE_R3_MAIN
8746 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8747#endif
8748 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8749 }
8750
8751 LogFlowThisFuncLeave();
8752 return S_OK;
8753}
8754
8755/**
8756 * Searches for a shared folder with the given logical name
8757 * in the collection of shared folders.
8758 *
8759 * @param aName logical name of the shared folder
8760 * @param aSharedFolder where to return the found object
8761 * @param aSetError whether to set the error info if the folder is
8762 * not found
8763 * @return
8764 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8765 *
8766 * @note
8767 * must be called from under the object's lock!
8768 */
8769HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8770 ComObjPtr<SharedFolder> &aSharedFolder,
8771 bool aSetError /* = false */)
8772{
8773 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8774 for (HWData::SharedFolderList::const_iterator
8775 it = mHWData->mSharedFolders.begin();
8776 it != mHWData->mSharedFolders.end();
8777 ++it)
8778 {
8779 SharedFolder *pSF = *it;
8780 AutoCaller autoCaller(pSF);
8781 if (pSF->i_getName() == aName)
8782 {
8783 aSharedFolder = pSF;
8784 rc = S_OK;
8785 break;
8786 }
8787 }
8788
8789 if (aSetError && FAILED(rc))
8790 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8791
8792 return rc;
8793}
8794
8795/**
8796 * Initializes all machine instance data from the given settings structures
8797 * from XML. The exception is the machine UUID which needs special handling
8798 * depending on the caller's use case, so the caller needs to set that herself.
8799 *
8800 * This gets called in several contexts during machine initialization:
8801 *
8802 * -- When machine XML exists on disk already and needs to be loaded into memory,
8803 * for example, from #i_registeredInit() to load all registered machines on
8804 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8805 * attached to the machine should be part of some media registry already.
8806 *
8807 * -- During OVF import, when a machine config has been constructed from an
8808 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8809 * ensure that the media listed as attachments in the config (which have
8810 * been imported from the OVF) receive the correct registry ID.
8811 *
8812 * -- During VM cloning.
8813 *
8814 * @param config Machine settings from XML.
8815 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8816 * for each attached medium in the config.
8817 * @return
8818 */
8819HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8820 const Guid *puuidRegistry)
8821{
8822 // copy name, description, OS type, teleporter, UTC etc.
8823 mUserData->s = config.machineUserData;
8824
8825 // look up the object by Id to check it is valid
8826 ComObjPtr<GuestOSType> pGuestOSType;
8827 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8828 pGuestOSType);
8829 if (FAILED(rc)) return rc;
8830 mUserData->s.strOsType = pGuestOSType->i_id();
8831
8832 // stateFile (optional)
8833 if (config.strStateFile.isEmpty())
8834 mSSData->strStateFilePath.setNull();
8835 else
8836 {
8837 Utf8Str stateFilePathFull(config.strStateFile);
8838 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8839 if (RT_FAILURE(vrc))
8840 return setError(E_FAIL,
8841 tr("Invalid saved state file path '%s' (%Rrc)"),
8842 config.strStateFile.c_str(),
8843 vrc);
8844 mSSData->strStateFilePath = stateFilePathFull;
8845 }
8846
8847 // snapshot folder needs special processing so set it again
8848 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8849 if (FAILED(rc)) return rc;
8850
8851 /* Copy the extra data items (config may or may not be the same as
8852 * mData->pMachineConfigFile) if necessary. When loading the XML files
8853 * from disk they are the same, but not for OVF import. */
8854 if (mData->pMachineConfigFile != &config)
8855 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8856
8857 /* currentStateModified (optional, default is true) */
8858 mData->mCurrentStateModified = config.fCurrentStateModified;
8859
8860 mData->mLastStateChange = config.timeLastStateChange;
8861
8862 /*
8863 * note: all mUserData members must be assigned prior this point because
8864 * we need to commit changes in order to let mUserData be shared by all
8865 * snapshot machine instances.
8866 */
8867 mUserData.commitCopy();
8868
8869 // machine registry, if present (must be loaded before snapshots)
8870 if (config.canHaveOwnMediaRegistry())
8871 {
8872 // determine machine folder
8873 Utf8Str strMachineFolder = i_getSettingsFileFull();
8874 strMachineFolder.stripFilename();
8875 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8876 config.mediaRegistry,
8877 strMachineFolder);
8878 if (FAILED(rc)) return rc;
8879 }
8880
8881 /* Snapshot node (optional) */
8882 size_t cRootSnapshots;
8883 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8884 {
8885 // there must be only one root snapshot
8886 Assert(cRootSnapshots == 1);
8887
8888 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8889
8890 rc = i_loadSnapshot(snap,
8891 config.uuidCurrentSnapshot,
8892 NULL); // no parent == first snapshot
8893 if (FAILED(rc)) return rc;
8894 }
8895
8896 // hardware data
8897 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8898 if (FAILED(rc)) return rc;
8899
8900 /*
8901 * NOTE: the assignment below must be the last thing to do,
8902 * otherwise it will be not possible to change the settings
8903 * somewhere in the code above because all setters will be
8904 * blocked by i_checkStateDependency(MutableStateDep).
8905 */
8906
8907 /* set the machine state to Aborted or Saved when appropriate */
8908 if (config.fAborted)
8909 {
8910 mSSData->strStateFilePath.setNull();
8911
8912 /* no need to use i_setMachineState() during init() */
8913 mData->mMachineState = MachineState_Aborted;
8914 }
8915 else if (!mSSData->strStateFilePath.isEmpty())
8916 {
8917 /* no need to use i_setMachineState() during init() */
8918 mData->mMachineState = MachineState_Saved;
8919 }
8920
8921 // after loading settings, we are no longer different from the XML on disk
8922 mData->flModifications = 0;
8923
8924 return S_OK;
8925}
8926
8927/**
8928 * Recursively loads all snapshots starting from the given.
8929 *
8930 * @param data snapshot settings.
8931 * @param aCurSnapshotId Current snapshot ID from the settings file.
8932 * @param aParentSnapshot Parent snapshot.
8933 */
8934HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8935 const Guid &aCurSnapshotId,
8936 Snapshot *aParentSnapshot)
8937{
8938 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8939 AssertReturn(!i_isSessionMachine(), E_FAIL);
8940
8941 HRESULT rc = S_OK;
8942
8943 Utf8Str strStateFile;
8944 if (!data.strStateFile.isEmpty())
8945 {
8946 /* optional */
8947 strStateFile = data.strStateFile;
8948 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8949 if (RT_FAILURE(vrc))
8950 return setError(E_FAIL,
8951 tr("Invalid saved state file path '%s' (%Rrc)"),
8952 strStateFile.c_str(),
8953 vrc);
8954 }
8955
8956 /* create a snapshot machine object */
8957 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8958 pSnapshotMachine.createObject();
8959 rc = pSnapshotMachine->initFromSettings(this,
8960 data.hardware,
8961 &data.debugging,
8962 &data.autostart,
8963 data.uuid.ref(),
8964 strStateFile);
8965 if (FAILED(rc)) return rc;
8966
8967 /* create a snapshot object */
8968 ComObjPtr<Snapshot> pSnapshot;
8969 pSnapshot.createObject();
8970 /* initialize the snapshot */
8971 rc = pSnapshot->init(mParent, // VirtualBox object
8972 data.uuid,
8973 data.strName,
8974 data.strDescription,
8975 data.timestamp,
8976 pSnapshotMachine,
8977 aParentSnapshot);
8978 if (FAILED(rc)) return rc;
8979
8980 /* memorize the first snapshot if necessary */
8981 if (!mData->mFirstSnapshot)
8982 mData->mFirstSnapshot = pSnapshot;
8983
8984 /* memorize the current snapshot when appropriate */
8985 if ( !mData->mCurrentSnapshot
8986 && pSnapshot->i_getId() == aCurSnapshotId
8987 )
8988 mData->mCurrentSnapshot = pSnapshot;
8989
8990 // now create the children
8991 for (settings::SnapshotsList::const_iterator
8992 it = data.llChildSnapshots.begin();
8993 it != data.llChildSnapshots.end();
8994 ++it)
8995 {
8996 const settings::Snapshot &childData = *it;
8997 // recurse
8998 rc = i_loadSnapshot(childData,
8999 aCurSnapshotId,
9000 pSnapshot); // parent = the one we created above
9001 if (FAILED(rc)) return rc;
9002 }
9003
9004 return rc;
9005}
9006
9007/**
9008 * Loads settings into mHWData.
9009 *
9010 * @param puuidRegistry Registry ID.
9011 * @param puuidSnapshot Snapshot ID
9012 * @param data Reference to the hardware settings.
9013 * @param pDbg Pointer to the debugging settings.
9014 * @param pAutostart Pointer to the autostart settings.
9015 */
9016HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9017 const Guid *puuidSnapshot,
9018 const settings::Hardware &data,
9019 const settings::Debugging *pDbg,
9020 const settings::Autostart *pAutostart)
9021{
9022 AssertReturn(!i_isSessionMachine(), E_FAIL);
9023
9024 HRESULT rc = S_OK;
9025
9026 try
9027 {
9028 ComObjPtr<GuestOSType> pGuestOSType;
9029 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9030 pGuestOSType);
9031 if (FAILED(rc))
9032 return rc;
9033
9034 /* The hardware version attribute (optional). */
9035 mHWData->mHWVersion = data.strVersion;
9036 mHWData->mHardwareUUID = data.uuid;
9037
9038 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9039 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9040 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9041 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9042 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9043 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9044 mHWData->mPAEEnabled = data.fPAE;
9045 mHWData->mLongMode = data.enmLongMode;
9046 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9047 mHWData->mAPIC = data.fAPIC;
9048 mHWData->mX2APIC = data.fX2APIC;
9049 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9050 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9051 mHWData->mSpecCtrl = data.fSpecCtrl;
9052 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9053 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9054 mHWData->mCPUCount = data.cCPUs;
9055 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9056 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9057 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9058 mHWData->mCpuProfile = data.strCpuProfile;
9059
9060 // cpu
9061 if (mHWData->mCPUHotPlugEnabled)
9062 {
9063 for (settings::CpuList::const_iterator
9064 it = data.llCpus.begin();
9065 it != data.llCpus.end();
9066 ++it)
9067 {
9068 const settings::Cpu &cpu = *it;
9069
9070 mHWData->mCPUAttached[cpu.ulId] = true;
9071 }
9072 }
9073
9074 // cpuid leafs
9075 for (settings::CpuIdLeafsList::const_iterator
9076 it = data.llCpuIdLeafs.begin();
9077 it != data.llCpuIdLeafs.end();
9078 ++it)
9079 {
9080 const settings::CpuIdLeaf &rLeaf= *it;
9081 if ( rLeaf.idx < UINT32_C(0x20)
9082 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9083 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9084 mHWData->mCpuIdLeafList.push_back(rLeaf);
9085 /* else: just ignore */
9086 }
9087
9088 mHWData->mMemorySize = data.ulMemorySizeMB;
9089 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9090
9091 // boot order
9092 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9093 {
9094 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9095 if (it == data.mapBootOrder.end())
9096 mHWData->mBootOrder[i] = DeviceType_Null;
9097 else
9098 mHWData->mBootOrder[i] = it->second;
9099 }
9100
9101 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9102 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9103 mHWData->mMonitorCount = data.cMonitors;
9104 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9105 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9106 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9107 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9108 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9109 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9110 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9111 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9112 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9113 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9114 if (!data.strVideoCaptureFile.isEmpty())
9115 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9116 else
9117 mHWData->mVideoCaptureFile.setNull();
9118 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9119 mHWData->mFirmwareType = data.firmwareType;
9120 mHWData->mPointingHIDType = data.pointingHIDType;
9121 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9122 mHWData->mChipsetType = data.chipsetType;
9123 mHWData->mParavirtProvider = data.paravirtProvider;
9124 mHWData->mParavirtDebug = data.strParavirtDebug;
9125 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9126 mHWData->mHPETEnabled = data.fHPETEnabled;
9127
9128 /* VRDEServer */
9129 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9130 if (FAILED(rc)) return rc;
9131
9132 /* BIOS */
9133 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9134 if (FAILED(rc)) return rc;
9135
9136 // Bandwidth control (must come before network adapters)
9137 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9138 if (FAILED(rc)) return rc;
9139
9140 /* Shared folders */
9141 for (settings::USBControllerList::const_iterator
9142 it = data.usbSettings.llUSBControllers.begin();
9143 it != data.usbSettings.llUSBControllers.end();
9144 ++it)
9145 {
9146 const settings::USBController &settingsCtrl = *it;
9147 ComObjPtr<USBController> newCtrl;
9148
9149 newCtrl.createObject();
9150 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9151 mUSBControllers->push_back(newCtrl);
9152 }
9153
9154 /* USB device filters */
9155 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9156 if (FAILED(rc)) return rc;
9157
9158 // network adapters (establish array size first and apply defaults, to
9159 // ensure reading the same settings as we saved, since the list skips
9160 // adapters having defaults)
9161 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9162 size_t oldCount = mNetworkAdapters.size();
9163 if (newCount > oldCount)
9164 {
9165 mNetworkAdapters.resize(newCount);
9166 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9167 {
9168 unconst(mNetworkAdapters[slot]).createObject();
9169 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9170 }
9171 }
9172 else if (newCount < oldCount)
9173 mNetworkAdapters.resize(newCount);
9174 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9175 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9176 for (settings::NetworkAdaptersList::const_iterator
9177 it = data.llNetworkAdapters.begin();
9178 it != data.llNetworkAdapters.end();
9179 ++it)
9180 {
9181 const settings::NetworkAdapter &nic = *it;
9182
9183 /* slot uniqueness is guaranteed by XML Schema */
9184 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9185 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9186 if (FAILED(rc)) return rc;
9187 }
9188
9189 // serial ports (establish defaults first, to ensure reading the same
9190 // settings as we saved, since the list skips ports having defaults)
9191 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9192 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9193 for (settings::SerialPortsList::const_iterator
9194 it = data.llSerialPorts.begin();
9195 it != data.llSerialPorts.end();
9196 ++it)
9197 {
9198 const settings::SerialPort &s = *it;
9199
9200 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9201 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9202 if (FAILED(rc)) return rc;
9203 }
9204
9205 // parallel ports (establish defaults first, to ensure reading the same
9206 // settings as we saved, since the list skips ports having defaults)
9207 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9208 mParallelPorts[i]->i_applyDefaults();
9209 for (settings::ParallelPortsList::const_iterator
9210 it = data.llParallelPorts.begin();
9211 it != data.llParallelPorts.end();
9212 ++it)
9213 {
9214 const settings::ParallelPort &p = *it;
9215
9216 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9217 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9218 if (FAILED(rc)) return rc;
9219 }
9220
9221 /* AudioAdapter */
9222 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9223 if (FAILED(rc)) return rc;
9224
9225 /* storage controllers */
9226 rc = i_loadStorageControllers(data.storage,
9227 puuidRegistry,
9228 puuidSnapshot);
9229 if (FAILED(rc)) return rc;
9230
9231 /* Shared folders */
9232 for (settings::SharedFoldersList::const_iterator
9233 it = data.llSharedFolders.begin();
9234 it != data.llSharedFolders.end();
9235 ++it)
9236 {
9237 const settings::SharedFolder &sf = *it;
9238
9239 ComObjPtr<SharedFolder> sharedFolder;
9240 /* Check for double entries. Not allowed! */
9241 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9242 if (SUCCEEDED(rc))
9243 return setError(VBOX_E_OBJECT_IN_USE,
9244 tr("Shared folder named '%s' already exists"),
9245 sf.strName.c_str());
9246
9247 /* Create the new shared folder. Don't break on error. This will be
9248 * reported when the machine starts. */
9249 sharedFolder.createObject();
9250 rc = sharedFolder->init(i_getMachine(),
9251 sf.strName,
9252 sf.strHostPath,
9253 RT_BOOL(sf.fWritable),
9254 RT_BOOL(sf.fAutoMount),
9255 false /* fFailOnError */);
9256 if (FAILED(rc)) return rc;
9257 mHWData->mSharedFolders.push_back(sharedFolder);
9258 }
9259
9260 // Clipboard
9261 mHWData->mClipboardMode = data.clipboardMode;
9262
9263 // drag'n'drop
9264 mHWData->mDnDMode = data.dndMode;
9265
9266 // guest settings
9267 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9268
9269 // IO settings
9270 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9271 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9272
9273 // Host PCI devices
9274 for (settings::HostPCIDeviceAttachmentList::const_iterator
9275 it = data.pciAttachments.begin();
9276 it != data.pciAttachments.end();
9277 ++it)
9278 {
9279 const settings::HostPCIDeviceAttachment &hpda = *it;
9280 ComObjPtr<PCIDeviceAttachment> pda;
9281
9282 pda.createObject();
9283 pda->i_loadSettings(this, hpda);
9284 mHWData->mPCIDeviceAssignments.push_back(pda);
9285 }
9286
9287 /*
9288 * (The following isn't really real hardware, but it lives in HWData
9289 * for reasons of convenience.)
9290 */
9291
9292#ifdef VBOX_WITH_GUEST_PROPS
9293 /* Guest properties (optional) */
9294
9295 /* Only load transient guest properties for configs which have saved
9296 * state, because there shouldn't be any for powered off VMs. The same
9297 * logic applies for snapshots, as offline snapshots shouldn't have
9298 * any such properties. They confuse the code in various places.
9299 * Note: can't rely on the machine state, as it isn't set yet. */
9300 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9301 /* apologies for the hacky unconst() usage, but this needs hacking
9302 * actually inconsistent settings into consistency, otherwise there
9303 * will be some corner cases where the inconsistency survives
9304 * surprisingly long without getting fixed, especially for snapshots
9305 * as there are no config changes. */
9306 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9307 for (settings::GuestPropertiesList::iterator
9308 it = llGuestProperties.begin();
9309 it != llGuestProperties.end();
9310 /*nothing*/)
9311 {
9312 const settings::GuestProperty &prop = *it;
9313 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9314 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9315 if ( fSkipTransientGuestProperties
9316 && ( fFlags & GUEST_PROP_F_TRANSIENT
9317 || fFlags & GUEST_PROP_F_TRANSRESET))
9318 {
9319 it = llGuestProperties.erase(it);
9320 continue;
9321 }
9322 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9323 mHWData->mGuestProperties[prop.strName] = property;
9324 ++it;
9325 }
9326#endif /* VBOX_WITH_GUEST_PROPS defined */
9327
9328 rc = i_loadDebugging(pDbg);
9329 if (FAILED(rc))
9330 return rc;
9331
9332 mHWData->mAutostart = *pAutostart;
9333
9334 /* default frontend */
9335 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9336 }
9337 catch (std::bad_alloc &)
9338 {
9339 return E_OUTOFMEMORY;
9340 }
9341
9342 AssertComRC(rc);
9343 return rc;
9344}
9345
9346/**
9347 * Called from i_loadHardware() to load the debugging settings of the
9348 * machine.
9349 *
9350 * @param pDbg Pointer to the settings.
9351 */
9352HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9353{
9354 mHWData->mDebugging = *pDbg;
9355 /* no more processing currently required, this will probably change. */
9356 return S_OK;
9357}
9358
9359/**
9360 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9361 *
9362 * @param data storage settings.
9363 * @param puuidRegistry media registry ID to set media to or NULL;
9364 * see Machine::i_loadMachineDataFromSettings()
9365 * @param puuidSnapshot snapshot ID
9366 * @return
9367 */
9368HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9369 const Guid *puuidRegistry,
9370 const Guid *puuidSnapshot)
9371{
9372 AssertReturn(!i_isSessionMachine(), E_FAIL);
9373
9374 HRESULT rc = S_OK;
9375
9376 for (settings::StorageControllersList::const_iterator
9377 it = data.llStorageControllers.begin();
9378 it != data.llStorageControllers.end();
9379 ++it)
9380 {
9381 const settings::StorageController &ctlData = *it;
9382
9383 ComObjPtr<StorageController> pCtl;
9384 /* Try to find one with the name first. */
9385 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9386 if (SUCCEEDED(rc))
9387 return setError(VBOX_E_OBJECT_IN_USE,
9388 tr("Storage controller named '%s' already exists"),
9389 ctlData.strName.c_str());
9390
9391 pCtl.createObject();
9392 rc = pCtl->init(this,
9393 ctlData.strName,
9394 ctlData.storageBus,
9395 ctlData.ulInstance,
9396 ctlData.fBootable);
9397 if (FAILED(rc)) return rc;
9398
9399 mStorageControllers->push_back(pCtl);
9400
9401 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9402 if (FAILED(rc)) return rc;
9403
9404 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9405 if (FAILED(rc)) return rc;
9406
9407 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9408 if (FAILED(rc)) return rc;
9409
9410 /* Load the attached devices now. */
9411 rc = i_loadStorageDevices(pCtl,
9412 ctlData,
9413 puuidRegistry,
9414 puuidSnapshot);
9415 if (FAILED(rc)) return rc;
9416 }
9417
9418 return S_OK;
9419}
9420
9421/**
9422 * Called from i_loadStorageControllers for a controller's devices.
9423 *
9424 * @param aStorageController
9425 * @param data
9426 * @param puuidRegistry media registry ID to set media to or NULL; see
9427 * Machine::i_loadMachineDataFromSettings()
9428 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9429 * @return
9430 */
9431HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9432 const settings::StorageController &data,
9433 const Guid *puuidRegistry,
9434 const Guid *puuidSnapshot)
9435{
9436 HRESULT rc = S_OK;
9437
9438 /* paranoia: detect duplicate attachments */
9439 for (settings::AttachedDevicesList::const_iterator
9440 it = data.llAttachedDevices.begin();
9441 it != data.llAttachedDevices.end();
9442 ++it)
9443 {
9444 const settings::AttachedDevice &ad = *it;
9445
9446 for (settings::AttachedDevicesList::const_iterator it2 = it;
9447 it2 != data.llAttachedDevices.end();
9448 ++it2)
9449 {
9450 if (it == it2)
9451 continue;
9452
9453 const settings::AttachedDevice &ad2 = *it2;
9454
9455 if ( ad.lPort == ad2.lPort
9456 && ad.lDevice == ad2.lDevice)
9457 {
9458 return setError(E_FAIL,
9459 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9460 aStorageController->i_getName().c_str(),
9461 ad.lPort,
9462 ad.lDevice,
9463 mUserData->s.strName.c_str());
9464 }
9465 }
9466 }
9467
9468 for (settings::AttachedDevicesList::const_iterator
9469 it = data.llAttachedDevices.begin();
9470 it != data.llAttachedDevices.end();
9471 ++it)
9472 {
9473 const settings::AttachedDevice &dev = *it;
9474 ComObjPtr<Medium> medium;
9475
9476 switch (dev.deviceType)
9477 {
9478 case DeviceType_Floppy:
9479 case DeviceType_DVD:
9480 if (dev.strHostDriveSrc.isNotEmpty())
9481 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9482 false /* fRefresh */, medium);
9483 else
9484 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9485 dev.uuid,
9486 false /* fRefresh */,
9487 false /* aSetError */,
9488 medium);
9489 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9490 // This is not an error. The host drive or UUID might have vanished, so just go
9491 // ahead without this removeable medium attachment
9492 rc = S_OK;
9493 break;
9494
9495 case DeviceType_HardDisk:
9496 {
9497 /* find a hard disk by UUID */
9498 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9499 if (FAILED(rc))
9500 {
9501 if (i_isSnapshotMachine())
9502 {
9503 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9504 // so the user knows that the bad disk is in a snapshot somewhere
9505 com::ErrorInfo info;
9506 return setError(E_FAIL,
9507 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9508 puuidSnapshot->raw(),
9509 info.getText().raw());
9510 }
9511 else
9512 return rc;
9513 }
9514
9515 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9516
9517 if (medium->i_getType() == MediumType_Immutable)
9518 {
9519 if (i_isSnapshotMachine())
9520 return setError(E_FAIL,
9521 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9522 "of the virtual machine '%s' ('%s')"),
9523 medium->i_getLocationFull().c_str(),
9524 dev.uuid.raw(),
9525 puuidSnapshot->raw(),
9526 mUserData->s.strName.c_str(),
9527 mData->m_strConfigFileFull.c_str());
9528
9529 return setError(E_FAIL,
9530 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9531 medium->i_getLocationFull().c_str(),
9532 dev.uuid.raw(),
9533 mUserData->s.strName.c_str(),
9534 mData->m_strConfigFileFull.c_str());
9535 }
9536
9537 if (medium->i_getType() == MediumType_MultiAttach)
9538 {
9539 if (i_isSnapshotMachine())
9540 return setError(E_FAIL,
9541 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9542 "of the virtual machine '%s' ('%s')"),
9543 medium->i_getLocationFull().c_str(),
9544 dev.uuid.raw(),
9545 puuidSnapshot->raw(),
9546 mUserData->s.strName.c_str(),
9547 mData->m_strConfigFileFull.c_str());
9548
9549 return setError(E_FAIL,
9550 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9551 medium->i_getLocationFull().c_str(),
9552 dev.uuid.raw(),
9553 mUserData->s.strName.c_str(),
9554 mData->m_strConfigFileFull.c_str());
9555 }
9556
9557 if ( !i_isSnapshotMachine()
9558 && medium->i_getChildren().size() != 0
9559 )
9560 return setError(E_FAIL,
9561 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9562 "because it has %d differencing child hard disks"),
9563 medium->i_getLocationFull().c_str(),
9564 dev.uuid.raw(),
9565 mUserData->s.strName.c_str(),
9566 mData->m_strConfigFileFull.c_str(),
9567 medium->i_getChildren().size());
9568
9569 if (i_findAttachment(*mMediumAttachments.data(),
9570 medium))
9571 return setError(E_FAIL,
9572 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9573 medium->i_getLocationFull().c_str(),
9574 dev.uuid.raw(),
9575 mUserData->s.strName.c_str(),
9576 mData->m_strConfigFileFull.c_str());
9577
9578 break;
9579 }
9580
9581 default:
9582 return setError(E_FAIL,
9583 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9584 medium->i_getLocationFull().c_str(),
9585 mUserData->s.strName.c_str(),
9586 mData->m_strConfigFileFull.c_str());
9587 }
9588
9589 if (FAILED(rc))
9590 break;
9591
9592 /* Bandwidth groups are loaded at this point. */
9593 ComObjPtr<BandwidthGroup> pBwGroup;
9594
9595 if (!dev.strBwGroup.isEmpty())
9596 {
9597 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9598 if (FAILED(rc))
9599 return setError(E_FAIL,
9600 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9601 medium->i_getLocationFull().c_str(),
9602 dev.strBwGroup.c_str(),
9603 mUserData->s.strName.c_str(),
9604 mData->m_strConfigFileFull.c_str());
9605 pBwGroup->i_reference();
9606 }
9607
9608 const Utf8Str controllerName = aStorageController->i_getName();
9609 ComObjPtr<MediumAttachment> pAttachment;
9610 pAttachment.createObject();
9611 rc = pAttachment->init(this,
9612 medium,
9613 controllerName,
9614 dev.lPort,
9615 dev.lDevice,
9616 dev.deviceType,
9617 false,
9618 dev.fPassThrough,
9619 dev.fTempEject,
9620 dev.fNonRotational,
9621 dev.fDiscard,
9622 dev.fHotPluggable,
9623 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9624 if (FAILED(rc)) break;
9625
9626 /* associate the medium with this machine and snapshot */
9627 if (!medium.isNull())
9628 {
9629 AutoCaller medCaller(medium);
9630 if (FAILED(medCaller.rc())) return medCaller.rc();
9631 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9632
9633 if (i_isSnapshotMachine())
9634 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9635 else
9636 rc = medium->i_addBackReference(mData->mUuid);
9637 /* If the medium->addBackReference fails it sets an appropriate
9638 * error message, so no need to do any guesswork here. */
9639
9640 if (puuidRegistry)
9641 // caller wants registry ID to be set on all attached media (OVF import case)
9642 medium->i_addRegistry(*puuidRegistry);
9643 }
9644
9645 if (FAILED(rc))
9646 break;
9647
9648 /* back up mMediumAttachments to let registeredInit() properly rollback
9649 * on failure (= limited accessibility) */
9650 i_setModified(IsModified_Storage);
9651 mMediumAttachments.backup();
9652 mMediumAttachments->push_back(pAttachment);
9653 }
9654
9655 return rc;
9656}
9657
9658/**
9659 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9660 *
9661 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9662 * @param aSnapshot where to return the found snapshot
9663 * @param aSetError true to set extended error info on failure
9664 */
9665HRESULT Machine::i_findSnapshotById(const Guid &aId,
9666 ComObjPtr<Snapshot> &aSnapshot,
9667 bool aSetError /* = false */)
9668{
9669 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9670
9671 if (!mData->mFirstSnapshot)
9672 {
9673 if (aSetError)
9674 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9675 return E_FAIL;
9676 }
9677
9678 if (aId.isZero())
9679 aSnapshot = mData->mFirstSnapshot;
9680 else
9681 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9682
9683 if (!aSnapshot)
9684 {
9685 if (aSetError)
9686 return setError(E_FAIL,
9687 tr("Could not find a snapshot with UUID {%s}"),
9688 aId.toString().c_str());
9689 return E_FAIL;
9690 }
9691
9692 return S_OK;
9693}
9694
9695/**
9696 * Returns the snapshot with the given name or fails of no such snapshot.
9697 *
9698 * @param strName snapshot name to find
9699 * @param aSnapshot where to return the found snapshot
9700 * @param aSetError true to set extended error info on failure
9701 */
9702HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9703 ComObjPtr<Snapshot> &aSnapshot,
9704 bool aSetError /* = false */)
9705{
9706 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9707
9708 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9709
9710 if (!mData->mFirstSnapshot)
9711 {
9712 if (aSetError)
9713 return setError(VBOX_E_OBJECT_NOT_FOUND,
9714 tr("This machine does not have any snapshots"));
9715 return VBOX_E_OBJECT_NOT_FOUND;
9716 }
9717
9718 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9719
9720 if (!aSnapshot)
9721 {
9722 if (aSetError)
9723 return setError(VBOX_E_OBJECT_NOT_FOUND,
9724 tr("Could not find a snapshot named '%s'"), strName.c_str());
9725 return VBOX_E_OBJECT_NOT_FOUND;
9726 }
9727
9728 return S_OK;
9729}
9730
9731/**
9732 * Returns a storage controller object with the given name.
9733 *
9734 * @param aName storage controller name to find
9735 * @param aStorageController where to return the found storage controller
9736 * @param aSetError true to set extended error info on failure
9737 */
9738HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9739 ComObjPtr<StorageController> &aStorageController,
9740 bool aSetError /* = false */)
9741{
9742 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9743
9744 for (StorageControllerList::const_iterator
9745 it = mStorageControllers->begin();
9746 it != mStorageControllers->end();
9747 ++it)
9748 {
9749 if ((*it)->i_getName() == aName)
9750 {
9751 aStorageController = (*it);
9752 return S_OK;
9753 }
9754 }
9755
9756 if (aSetError)
9757 return setError(VBOX_E_OBJECT_NOT_FOUND,
9758 tr("Could not find a storage controller named '%s'"),
9759 aName.c_str());
9760 return VBOX_E_OBJECT_NOT_FOUND;
9761}
9762
9763/**
9764 * Returns a USB controller object with the given name.
9765 *
9766 * @param aName USB controller name to find
9767 * @param aUSBController where to return the found USB controller
9768 * @param aSetError true to set extended error info on failure
9769 */
9770HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9771 ComObjPtr<USBController> &aUSBController,
9772 bool aSetError /* = false */)
9773{
9774 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9775
9776 for (USBControllerList::const_iterator
9777 it = mUSBControllers->begin();
9778 it != mUSBControllers->end();
9779 ++it)
9780 {
9781 if ((*it)->i_getName() == aName)
9782 {
9783 aUSBController = (*it);
9784 return S_OK;
9785 }
9786 }
9787
9788 if (aSetError)
9789 return setError(VBOX_E_OBJECT_NOT_FOUND,
9790 tr("Could not find a storage controller named '%s'"),
9791 aName.c_str());
9792 return VBOX_E_OBJECT_NOT_FOUND;
9793}
9794
9795/**
9796 * Returns the number of USB controller instance of the given type.
9797 *
9798 * @param enmType USB controller type.
9799 */
9800ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9801{
9802 ULONG cCtrls = 0;
9803
9804 for (USBControllerList::const_iterator
9805 it = mUSBControllers->begin();
9806 it != mUSBControllers->end();
9807 ++it)
9808 {
9809 if ((*it)->i_getControllerType() == enmType)
9810 cCtrls++;
9811 }
9812
9813 return cCtrls;
9814}
9815
9816HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9817 MediumAttachmentList &atts)
9818{
9819 AutoCaller autoCaller(this);
9820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9821
9822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9823
9824 for (MediumAttachmentList::const_iterator
9825 it = mMediumAttachments->begin();
9826 it != mMediumAttachments->end();
9827 ++it)
9828 {
9829 const ComObjPtr<MediumAttachment> &pAtt = *it;
9830 // should never happen, but deal with NULL pointers in the list.
9831 AssertContinue(!pAtt.isNull());
9832
9833 // getControllerName() needs caller+read lock
9834 AutoCaller autoAttCaller(pAtt);
9835 if (FAILED(autoAttCaller.rc()))
9836 {
9837 atts.clear();
9838 return autoAttCaller.rc();
9839 }
9840 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9841
9842 if (pAtt->i_getControllerName() == aName)
9843 atts.push_back(pAtt);
9844 }
9845
9846 return S_OK;
9847}
9848
9849
9850/**
9851 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9852 * file if the machine name was changed and about creating a new settings file
9853 * if this is a new machine.
9854 *
9855 * @note Must be never called directly but only from #saveSettings().
9856 */
9857HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9858{
9859 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9860
9861 HRESULT rc = S_OK;
9862
9863 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9864
9865 /// @todo need to handle primary group change, too
9866
9867 /* attempt to rename the settings file if machine name is changed */
9868 if ( mUserData->s.fNameSync
9869 && mUserData.isBackedUp()
9870 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9871 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9872 )
9873 {
9874 bool dirRenamed = false;
9875 bool fileRenamed = false;
9876
9877 Utf8Str configFile, newConfigFile;
9878 Utf8Str configFilePrev, newConfigFilePrev;
9879 Utf8Str configDir, newConfigDir;
9880
9881 do
9882 {
9883 int vrc = VINF_SUCCESS;
9884
9885 Utf8Str name = mUserData.backedUpData()->s.strName;
9886 Utf8Str newName = mUserData->s.strName;
9887 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9888 if (group == "/")
9889 group.setNull();
9890 Utf8Str newGroup = mUserData->s.llGroups.front();
9891 if (newGroup == "/")
9892 newGroup.setNull();
9893
9894 configFile = mData->m_strConfigFileFull;
9895
9896 /* first, rename the directory if it matches the group and machine name */
9897 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9898 group.c_str(), RTPATH_DELIMITER, name.c_str());
9899 /** @todo hack, make somehow use of ComposeMachineFilename */
9900 if (mUserData->s.fDirectoryIncludesUUID)
9901 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9902 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9903 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9904 /** @todo hack, make somehow use of ComposeMachineFilename */
9905 if (mUserData->s.fDirectoryIncludesUUID)
9906 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9907 configDir = configFile;
9908 configDir.stripFilename();
9909 newConfigDir = configDir;
9910 if ( configDir.length() >= groupPlusName.length()
9911 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9912 groupPlusName.c_str()))
9913 {
9914 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9915 Utf8Str newConfigBaseDir(newConfigDir);
9916 newConfigDir.append(newGroupPlusName);
9917 /* consistency: use \ if appropriate on the platform */
9918 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9919 /* new dir and old dir cannot be equal here because of 'if'
9920 * above and because name != newName */
9921 Assert(configDir != newConfigDir);
9922 if (!fSettingsFileIsNew)
9923 {
9924 /* perform real rename only if the machine is not new */
9925 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9926 if ( vrc == VERR_FILE_NOT_FOUND
9927 || vrc == VERR_PATH_NOT_FOUND)
9928 {
9929 /* create the parent directory, then retry renaming */
9930 Utf8Str parent(newConfigDir);
9931 parent.stripFilename();
9932 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9933 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9934 }
9935 if (RT_FAILURE(vrc))
9936 {
9937 rc = setError(E_FAIL,
9938 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9939 configDir.c_str(),
9940 newConfigDir.c_str(),
9941 vrc);
9942 break;
9943 }
9944 /* delete subdirectories which are no longer needed */
9945 Utf8Str dir(configDir);
9946 dir.stripFilename();
9947 while (dir != newConfigBaseDir && dir != ".")
9948 {
9949 vrc = RTDirRemove(dir.c_str());
9950 if (RT_FAILURE(vrc))
9951 break;
9952 dir.stripFilename();
9953 }
9954 dirRenamed = true;
9955 }
9956 }
9957
9958 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9959 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9960
9961 /* then try to rename the settings file itself */
9962 if (newConfigFile != configFile)
9963 {
9964 /* get the path to old settings file in renamed directory */
9965 configFile = Utf8StrFmt("%s%c%s",
9966 newConfigDir.c_str(),
9967 RTPATH_DELIMITER,
9968 RTPathFilename(configFile.c_str()));
9969 if (!fSettingsFileIsNew)
9970 {
9971 /* perform real rename only if the machine is not new */
9972 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9973 if (RT_FAILURE(vrc))
9974 {
9975 rc = setError(E_FAIL,
9976 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9977 configFile.c_str(),
9978 newConfigFile.c_str(),
9979 vrc);
9980 break;
9981 }
9982 fileRenamed = true;
9983 configFilePrev = configFile;
9984 configFilePrev += "-prev";
9985 newConfigFilePrev = newConfigFile;
9986 newConfigFilePrev += "-prev";
9987 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9988 }
9989 }
9990
9991 // update m_strConfigFileFull amd mConfigFile
9992 mData->m_strConfigFileFull = newConfigFile;
9993 // compute the relative path too
9994 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9995
9996 // store the old and new so that VirtualBox::i_saveSettings() can update
9997 // the media registry
9998 if ( mData->mRegistered
9999 && (configDir != newConfigDir || configFile != newConfigFile))
10000 {
10001 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10002
10003 if (pfNeedsGlobalSaveSettings)
10004 *pfNeedsGlobalSaveSettings = true;
10005 }
10006
10007 // in the saved state file path, replace the old directory with the new directory
10008 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10009 {
10010 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10011 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10012 }
10013
10014 // and do the same thing for the saved state file paths of all the online snapshots
10015 if (mData->mFirstSnapshot)
10016 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10017 newConfigDir.c_str());
10018 }
10019 while (0);
10020
10021 if (FAILED(rc))
10022 {
10023 /* silently try to rename everything back */
10024 if (fileRenamed)
10025 {
10026 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10027 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10028 }
10029 if (dirRenamed)
10030 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10031 }
10032
10033 if (FAILED(rc)) return rc;
10034 }
10035
10036 if (fSettingsFileIsNew)
10037 {
10038 /* create a virgin config file */
10039 int vrc = VINF_SUCCESS;
10040
10041 /* ensure the settings directory exists */
10042 Utf8Str path(mData->m_strConfigFileFull);
10043 path.stripFilename();
10044 if (!RTDirExists(path.c_str()))
10045 {
10046 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10047 if (RT_FAILURE(vrc))
10048 {
10049 return setError(E_FAIL,
10050 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10051 path.c_str(),
10052 vrc);
10053 }
10054 }
10055
10056 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10057 path = Utf8Str(mData->m_strConfigFileFull);
10058 RTFILE f = NIL_RTFILE;
10059 vrc = RTFileOpen(&f, path.c_str(),
10060 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10061 if (RT_FAILURE(vrc))
10062 return setError(E_FAIL,
10063 tr("Could not create the settings file '%s' (%Rrc)"),
10064 path.c_str(),
10065 vrc);
10066 RTFileClose(f);
10067 }
10068
10069 return rc;
10070}
10071
10072/**
10073 * Saves and commits machine data, user data and hardware data.
10074 *
10075 * Note that on failure, the data remains uncommitted.
10076 *
10077 * @a aFlags may combine the following flags:
10078 *
10079 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10080 * Used when saving settings after an operation that makes them 100%
10081 * correspond to the settings from the current snapshot.
10082 * - SaveS_Force: settings will be saved without doing a deep compare of the
10083 * settings structures. This is used when this is called because snapshots
10084 * have changed to avoid the overhead of the deep compare.
10085 *
10086 * @note Must be called from under this object's write lock. Locks children for
10087 * writing.
10088 *
10089 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10090 * initialized to false and that will be set to true by this function if
10091 * the caller must invoke VirtualBox::i_saveSettings() because the global
10092 * settings have changed. This will happen if a machine rename has been
10093 * saved and the global machine and media registries will therefore need
10094 * updating.
10095 * @param aFlags Flags.
10096 */
10097HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10098 int aFlags /*= 0*/)
10099{
10100 LogFlowThisFuncEnter();
10101
10102 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10103
10104 /* make sure child objects are unable to modify the settings while we are
10105 * saving them */
10106 i_ensureNoStateDependencies();
10107
10108 AssertReturn(!i_isSnapshotMachine(),
10109 E_FAIL);
10110
10111 HRESULT rc = S_OK;
10112 bool fNeedsWrite = false;
10113
10114 /* First, prepare to save settings. It will care about renaming the
10115 * settings directory and file if the machine name was changed and about
10116 * creating a new settings file if this is a new machine. */
10117 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10118 if (FAILED(rc)) return rc;
10119
10120 // keep a pointer to the current settings structures
10121 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10122 settings::MachineConfigFile *pNewConfig = NULL;
10123
10124 try
10125 {
10126 // make a fresh one to have everyone write stuff into
10127 pNewConfig = new settings::MachineConfigFile(NULL);
10128 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10129
10130 // now go and copy all the settings data from COM to the settings structures
10131 // (this calls i_saveSettings() on all the COM objects in the machine)
10132 i_copyMachineDataToSettings(*pNewConfig);
10133
10134 if (aFlags & SaveS_ResetCurStateModified)
10135 {
10136 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10137 mData->mCurrentStateModified = FALSE;
10138 fNeedsWrite = true; // always, no need to compare
10139 }
10140 else if (aFlags & SaveS_Force)
10141 {
10142 fNeedsWrite = true; // always, no need to compare
10143 }
10144 else
10145 {
10146 if (!mData->mCurrentStateModified)
10147 {
10148 // do a deep compare of the settings that we just saved with the settings
10149 // previously stored in the config file; this invokes MachineConfigFile::operator==
10150 // which does a deep compare of all the settings, which is expensive but less expensive
10151 // than writing out XML in vain
10152 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10153
10154 // could still be modified if any settings changed
10155 mData->mCurrentStateModified = fAnySettingsChanged;
10156
10157 fNeedsWrite = fAnySettingsChanged;
10158 }
10159 else
10160 fNeedsWrite = true;
10161 }
10162
10163 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10164
10165 if (fNeedsWrite)
10166 // now spit it all out!
10167 pNewConfig->write(mData->m_strConfigFileFull);
10168
10169 mData->pMachineConfigFile = pNewConfig;
10170 delete pOldConfig;
10171 i_commit();
10172
10173 // after saving settings, we are no longer different from the XML on disk
10174 mData->flModifications = 0;
10175 }
10176 catch (HRESULT err)
10177 {
10178 // we assume that error info is set by the thrower
10179 rc = err;
10180
10181 // restore old config
10182 delete pNewConfig;
10183 mData->pMachineConfigFile = pOldConfig;
10184 }
10185 catch (...)
10186 {
10187 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10188 }
10189
10190 if (fNeedsWrite)
10191 {
10192 /* Fire the data change event, even on failure (since we've already
10193 * committed all data). This is done only for SessionMachines because
10194 * mutable Machine instances are always not registered (i.e. private
10195 * to the client process that creates them) and thus don't need to
10196 * inform callbacks. */
10197 if (i_isSessionMachine())
10198 mParent->i_onMachineDataChange(mData->mUuid);
10199 }
10200
10201 LogFlowThisFunc(("rc=%08X\n", rc));
10202 LogFlowThisFuncLeave();
10203 return rc;
10204}
10205
10206/**
10207 * Implementation for saving the machine settings into the given
10208 * settings::MachineConfigFile instance. This copies machine extradata
10209 * from the previous machine config file in the instance data, if any.
10210 *
10211 * This gets called from two locations:
10212 *
10213 * -- Machine::i_saveSettings(), during the regular XML writing;
10214 *
10215 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10216 * exported to OVF and we write the VirtualBox proprietary XML
10217 * into a <vbox:Machine> tag.
10218 *
10219 * This routine fills all the fields in there, including snapshots, *except*
10220 * for the following:
10221 *
10222 * -- fCurrentStateModified. There is some special logic associated with that.
10223 *
10224 * The caller can then call MachineConfigFile::write() or do something else
10225 * with it.
10226 *
10227 * Caller must hold the machine lock!
10228 *
10229 * This throws XML errors and HRESULT, so the caller must have a catch block!
10230 */
10231void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10232{
10233 // deep copy extradata, being extra careful with self assignment (the STL
10234 // map assignment on Mac OS X clang based Xcode isn't checking)
10235 if (&config != mData->pMachineConfigFile)
10236 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10237
10238 config.uuid = mData->mUuid;
10239
10240 // copy name, description, OS type, teleport, UTC etc.
10241 config.machineUserData = mUserData->s;
10242
10243 if ( mData->mMachineState == MachineState_Saved
10244 || mData->mMachineState == MachineState_Restoring
10245 // when doing certain snapshot operations we may or may not have
10246 // a saved state in the current state, so keep everything as is
10247 || ( ( mData->mMachineState == MachineState_Snapshotting
10248 || mData->mMachineState == MachineState_DeletingSnapshot
10249 || mData->mMachineState == MachineState_RestoringSnapshot)
10250 && (!mSSData->strStateFilePath.isEmpty())
10251 )
10252 )
10253 {
10254 Assert(!mSSData->strStateFilePath.isEmpty());
10255 /* try to make the file name relative to the settings file dir */
10256 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10257 }
10258 else
10259 {
10260 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10261 config.strStateFile.setNull();
10262 }
10263
10264 if (mData->mCurrentSnapshot)
10265 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10266 else
10267 config.uuidCurrentSnapshot.clear();
10268
10269 config.timeLastStateChange = mData->mLastStateChange;
10270 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10271 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10272
10273 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10274 if (FAILED(rc)) throw rc;
10275
10276 // save machine's media registry if this is VirtualBox 4.0 or later
10277 if (config.canHaveOwnMediaRegistry())
10278 {
10279 // determine machine folder
10280 Utf8Str strMachineFolder = i_getSettingsFileFull();
10281 strMachineFolder.stripFilename();
10282 mParent->i_saveMediaRegistry(config.mediaRegistry,
10283 i_getId(), // only media with registry ID == machine UUID
10284 strMachineFolder);
10285 // this throws HRESULT
10286 }
10287
10288 // save snapshots
10289 rc = i_saveAllSnapshots(config);
10290 if (FAILED(rc)) throw rc;
10291}
10292
10293/**
10294 * Saves all snapshots of the machine into the given machine config file. Called
10295 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10296 * @param config
10297 * @return
10298 */
10299HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10300{
10301 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10302
10303 HRESULT rc = S_OK;
10304
10305 try
10306 {
10307 config.llFirstSnapshot.clear();
10308
10309 if (mData->mFirstSnapshot)
10310 {
10311 // the settings use a list for "the first snapshot"
10312 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10313
10314 // get reference to the snapshot on the list and work on that
10315 // element straight in the list to avoid excessive copying later
10316 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10317 if (FAILED(rc)) throw rc;
10318 }
10319
10320// if (mType == IsSessionMachine)
10321// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10322
10323 }
10324 catch (HRESULT err)
10325 {
10326 /* we assume that error info is set by the thrower */
10327 rc = err;
10328 }
10329 catch (...)
10330 {
10331 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10332 }
10333
10334 return rc;
10335}
10336
10337/**
10338 * Saves the VM hardware configuration. It is assumed that the
10339 * given node is empty.
10340 *
10341 * @param data Reference to the settings object for the hardware config.
10342 * @param pDbg Pointer to the settings object for the debugging config
10343 * which happens to live in mHWData.
10344 * @param pAutostart Pointer to the settings object for the autostart config
10345 * which happens to live in mHWData.
10346 */
10347HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10348 settings::Autostart *pAutostart)
10349{
10350 HRESULT rc = S_OK;
10351
10352 try
10353 {
10354 /* The hardware version attribute (optional).
10355 Automatically upgrade from 1 to current default hardware version
10356 when there is no saved state. (ugly!) */
10357 if ( mHWData->mHWVersion == "1"
10358 && mSSData->strStateFilePath.isEmpty()
10359 )
10360 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10361
10362 data.strVersion = mHWData->mHWVersion;
10363 data.uuid = mHWData->mHardwareUUID;
10364
10365 // CPU
10366 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10367 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10368 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10369 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10370 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10371 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10372 data.fPAE = !!mHWData->mPAEEnabled;
10373 data.enmLongMode = mHWData->mLongMode;
10374 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10375 data.fAPIC = !!mHWData->mAPIC;
10376 data.fX2APIC = !!mHWData->mX2APIC;
10377 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10378 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10379 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10380 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10381 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10382 data.cCPUs = mHWData->mCPUCount;
10383 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10384 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10385 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10386 data.strCpuProfile = mHWData->mCpuProfile;
10387
10388 data.llCpus.clear();
10389 if (data.fCpuHotPlug)
10390 {
10391 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10392 {
10393 if (mHWData->mCPUAttached[idx])
10394 {
10395 settings::Cpu cpu;
10396 cpu.ulId = idx;
10397 data.llCpus.push_back(cpu);
10398 }
10399 }
10400 }
10401
10402 /* Standard and Extended CPUID leafs. */
10403 data.llCpuIdLeafs.clear();
10404 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10405
10406 // memory
10407 data.ulMemorySizeMB = mHWData->mMemorySize;
10408 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10409
10410 // firmware
10411 data.firmwareType = mHWData->mFirmwareType;
10412
10413 // HID
10414 data.pointingHIDType = mHWData->mPointingHIDType;
10415 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10416
10417 // chipset
10418 data.chipsetType = mHWData->mChipsetType;
10419
10420 // paravirt
10421 data.paravirtProvider = mHWData->mParavirtProvider;
10422 data.strParavirtDebug = mHWData->mParavirtDebug;
10423
10424 // emulated USB card reader
10425 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10426
10427 // HPET
10428 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10429
10430 // boot order
10431 data.mapBootOrder.clear();
10432 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10433 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10434
10435 // display
10436 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10437 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10438 data.cMonitors = mHWData->mMonitorCount;
10439 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10440 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10441 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10442 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10443 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10444 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10445 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10446 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10447 {
10448 if (mHWData->maVideoCaptureScreens[i])
10449 ASMBitSet(&data.u64VideoCaptureScreens, i);
10450 else
10451 ASMBitClear(&data.u64VideoCaptureScreens, i);
10452 }
10453 /* store relative video capture file if possible */
10454 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10455 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10456
10457 /* VRDEServer settings (optional) */
10458 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10459 if (FAILED(rc)) throw rc;
10460
10461 /* BIOS (required) */
10462 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10463 if (FAILED(rc)) throw rc;
10464
10465 /* USB Controller (required) */
10466 data.usbSettings.llUSBControllers.clear();
10467 for (USBControllerList::const_iterator
10468 it = mUSBControllers->begin();
10469 it != mUSBControllers->end();
10470 ++it)
10471 {
10472 ComObjPtr<USBController> ctrl = *it;
10473 settings::USBController settingsCtrl;
10474
10475 settingsCtrl.strName = ctrl->i_getName();
10476 settingsCtrl.enmType = ctrl->i_getControllerType();
10477
10478 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10479 }
10480
10481 /* USB device filters (required) */
10482 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10483 if (FAILED(rc)) throw rc;
10484
10485 /* Network adapters (required) */
10486 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10487 data.llNetworkAdapters.clear();
10488 /* Write out only the nominal number of network adapters for this
10489 * chipset type. Since Machine::commit() hasn't been called there
10490 * may be extra NIC settings in the vector. */
10491 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10492 {
10493 settings::NetworkAdapter nic;
10494 nic.ulSlot = (uint32_t)slot;
10495 /* paranoia check... must not be NULL, but must not crash either. */
10496 if (mNetworkAdapters[slot])
10497 {
10498 if (mNetworkAdapters[slot]->i_hasDefaults())
10499 continue;
10500
10501 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10502 if (FAILED(rc)) throw rc;
10503
10504 data.llNetworkAdapters.push_back(nic);
10505 }
10506 }
10507
10508 /* Serial ports */
10509 data.llSerialPorts.clear();
10510 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10511 {
10512 if (mSerialPorts[slot]->i_hasDefaults())
10513 continue;
10514
10515 settings::SerialPort s;
10516 s.ulSlot = slot;
10517 rc = mSerialPorts[slot]->i_saveSettings(s);
10518 if (FAILED(rc)) return rc;
10519
10520 data.llSerialPorts.push_back(s);
10521 }
10522
10523 /* Parallel ports */
10524 data.llParallelPorts.clear();
10525 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10526 {
10527 if (mParallelPorts[slot]->i_hasDefaults())
10528 continue;
10529
10530 settings::ParallelPort p;
10531 p.ulSlot = slot;
10532 rc = mParallelPorts[slot]->i_saveSettings(p);
10533 if (FAILED(rc)) return rc;
10534
10535 data.llParallelPorts.push_back(p);
10536 }
10537
10538 /* Audio adapter */
10539 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10540 if (FAILED(rc)) return rc;
10541
10542 rc = i_saveStorageControllers(data.storage);
10543 if (FAILED(rc)) return rc;
10544
10545 /* Shared folders */
10546 data.llSharedFolders.clear();
10547 for (HWData::SharedFolderList::const_iterator
10548 it = mHWData->mSharedFolders.begin();
10549 it != mHWData->mSharedFolders.end();
10550 ++it)
10551 {
10552 SharedFolder *pSF = *it;
10553 AutoCaller sfCaller(pSF);
10554 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10555 settings::SharedFolder sf;
10556 sf.strName = pSF->i_getName();
10557 sf.strHostPath = pSF->i_getHostPath();
10558 sf.fWritable = !!pSF->i_isWritable();
10559 sf.fAutoMount = !!pSF->i_isAutoMounted();
10560
10561 data.llSharedFolders.push_back(sf);
10562 }
10563
10564 // clipboard
10565 data.clipboardMode = mHWData->mClipboardMode;
10566
10567 // drag'n'drop
10568 data.dndMode = mHWData->mDnDMode;
10569
10570 /* Guest */
10571 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10572
10573 // IO settings
10574 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10575 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10576
10577 /* BandwidthControl (required) */
10578 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10579 if (FAILED(rc)) throw rc;
10580
10581 /* Host PCI devices */
10582 data.pciAttachments.clear();
10583 for (HWData::PCIDeviceAssignmentList::const_iterator
10584 it = mHWData->mPCIDeviceAssignments.begin();
10585 it != mHWData->mPCIDeviceAssignments.end();
10586 ++it)
10587 {
10588 ComObjPtr<PCIDeviceAttachment> pda = *it;
10589 settings::HostPCIDeviceAttachment hpda;
10590
10591 rc = pda->i_saveSettings(hpda);
10592 if (FAILED(rc)) throw rc;
10593
10594 data.pciAttachments.push_back(hpda);
10595 }
10596
10597 // guest properties
10598 data.llGuestProperties.clear();
10599#ifdef VBOX_WITH_GUEST_PROPS
10600 for (HWData::GuestPropertyMap::const_iterator
10601 it = mHWData->mGuestProperties.begin();
10602 it != mHWData->mGuestProperties.end();
10603 ++it)
10604 {
10605 HWData::GuestProperty property = it->second;
10606
10607 /* Remove transient guest properties at shutdown unless we
10608 * are saving state. Note that restoring snapshot intentionally
10609 * keeps them, they will be removed if appropriate once the final
10610 * machine state is set (as crashes etc. need to work). */
10611 if ( ( mData->mMachineState == MachineState_PoweredOff
10612 || mData->mMachineState == MachineState_Aborted
10613 || mData->mMachineState == MachineState_Teleported)
10614 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10615 continue;
10616 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10617 prop.strName = it->first;
10618 prop.strValue = property.strValue;
10619 prop.timestamp = property.mTimestamp;
10620 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10621 GuestPropWriteFlags(property.mFlags, szFlags);
10622 prop.strFlags = szFlags;
10623
10624 data.llGuestProperties.push_back(prop);
10625 }
10626
10627 /* I presume this doesn't require a backup(). */
10628 mData->mGuestPropertiesModified = FALSE;
10629#endif /* VBOX_WITH_GUEST_PROPS defined */
10630
10631 *pDbg = mHWData->mDebugging;
10632 *pAutostart = mHWData->mAutostart;
10633
10634 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10635 }
10636 catch (std::bad_alloc &)
10637 {
10638 return E_OUTOFMEMORY;
10639 }
10640
10641 AssertComRC(rc);
10642 return rc;
10643}
10644
10645/**
10646 * Saves the storage controller configuration.
10647 *
10648 * @param data storage settings.
10649 */
10650HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10651{
10652 data.llStorageControllers.clear();
10653
10654 for (StorageControllerList::const_iterator
10655 it = mStorageControllers->begin();
10656 it != mStorageControllers->end();
10657 ++it)
10658 {
10659 HRESULT rc;
10660 ComObjPtr<StorageController> pCtl = *it;
10661
10662 settings::StorageController ctl;
10663 ctl.strName = pCtl->i_getName();
10664 ctl.controllerType = pCtl->i_getControllerType();
10665 ctl.storageBus = pCtl->i_getStorageBus();
10666 ctl.ulInstance = pCtl->i_getInstance();
10667 ctl.fBootable = pCtl->i_getBootable();
10668
10669 /* Save the port count. */
10670 ULONG portCount;
10671 rc = pCtl->COMGETTER(PortCount)(&portCount);
10672 ComAssertComRCRet(rc, rc);
10673 ctl.ulPortCount = portCount;
10674
10675 /* Save fUseHostIOCache */
10676 BOOL fUseHostIOCache;
10677 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10678 ComAssertComRCRet(rc, rc);
10679 ctl.fUseHostIOCache = !!fUseHostIOCache;
10680
10681 /* save the devices now. */
10682 rc = i_saveStorageDevices(pCtl, ctl);
10683 ComAssertComRCRet(rc, rc);
10684
10685 data.llStorageControllers.push_back(ctl);
10686 }
10687
10688 return S_OK;
10689}
10690
10691/**
10692 * Saves the hard disk configuration.
10693 */
10694HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10695 settings::StorageController &data)
10696{
10697 MediumAttachmentList atts;
10698
10699 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10700 if (FAILED(rc)) return rc;
10701
10702 data.llAttachedDevices.clear();
10703 for (MediumAttachmentList::const_iterator
10704 it = atts.begin();
10705 it != atts.end();
10706 ++it)
10707 {
10708 settings::AttachedDevice dev;
10709 IMediumAttachment *iA = *it;
10710 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10711 Medium *pMedium = pAttach->i_getMedium();
10712
10713 dev.deviceType = pAttach->i_getType();
10714 dev.lPort = pAttach->i_getPort();
10715 dev.lDevice = pAttach->i_getDevice();
10716 dev.fPassThrough = pAttach->i_getPassthrough();
10717 dev.fHotPluggable = pAttach->i_getHotPluggable();
10718 if (pMedium)
10719 {
10720 if (pMedium->i_isHostDrive())
10721 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10722 else
10723 dev.uuid = pMedium->i_getId();
10724 dev.fTempEject = pAttach->i_getTempEject();
10725 dev.fNonRotational = pAttach->i_getNonRotational();
10726 dev.fDiscard = pAttach->i_getDiscard();
10727 }
10728
10729 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10730
10731 data.llAttachedDevices.push_back(dev);
10732 }
10733
10734 return S_OK;
10735}
10736
10737/**
10738 * Saves machine state settings as defined by aFlags
10739 * (SaveSTS_* values).
10740 *
10741 * @param aFlags Combination of SaveSTS_* flags.
10742 *
10743 * @note Locks objects for writing.
10744 */
10745HRESULT Machine::i_saveStateSettings(int aFlags)
10746{
10747 if (aFlags == 0)
10748 return S_OK;
10749
10750 AutoCaller autoCaller(this);
10751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10752
10753 /* This object's write lock is also necessary to serialize file access
10754 * (prevent concurrent reads and writes) */
10755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10756
10757 HRESULT rc = S_OK;
10758
10759 Assert(mData->pMachineConfigFile);
10760
10761 try
10762 {
10763 if (aFlags & SaveSTS_CurStateModified)
10764 mData->pMachineConfigFile->fCurrentStateModified = true;
10765
10766 if (aFlags & SaveSTS_StateFilePath)
10767 {
10768 if (!mSSData->strStateFilePath.isEmpty())
10769 /* try to make the file name relative to the settings file dir */
10770 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10771 else
10772 mData->pMachineConfigFile->strStateFile.setNull();
10773 }
10774
10775 if (aFlags & SaveSTS_StateTimeStamp)
10776 {
10777 Assert( mData->mMachineState != MachineState_Aborted
10778 || mSSData->strStateFilePath.isEmpty());
10779
10780 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10781
10782 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10783/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10784 }
10785
10786 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10787 }
10788 catch (...)
10789 {
10790 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10791 }
10792
10793 return rc;
10794}
10795
10796/**
10797 * Ensures that the given medium is added to a media registry. If this machine
10798 * was created with 4.0 or later, then the machine registry is used. Otherwise
10799 * the global VirtualBox media registry is used.
10800 *
10801 * Caller must NOT hold machine lock, media tree or any medium locks!
10802 *
10803 * @param pMedium
10804 */
10805void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10806{
10807 /* Paranoia checks: do not hold machine or media tree locks. */
10808 AssertReturnVoid(!isWriteLockOnCurrentThread());
10809 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10810
10811 ComObjPtr<Medium> pBase;
10812 {
10813 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10814 pBase = pMedium->i_getBase();
10815 }
10816
10817 /* Paranoia checks: do not hold medium locks. */
10818 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10819 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10820
10821 // decide which medium registry to use now that the medium is attached:
10822 Guid uuid;
10823 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10824 if (fCanHaveOwnMediaRegistry)
10825 // machine XML is VirtualBox 4.0 or higher:
10826 uuid = i_getId(); // machine UUID
10827 else
10828 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10829
10830 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10831 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10832 if (pMedium->i_addRegistry(uuid))
10833 mParent->i_markRegistryModified(uuid);
10834
10835 /* For more complex hard disk structures it can happen that the base
10836 * medium isn't yet associated with any medium registry. Do that now. */
10837 if (pMedium != pBase)
10838 {
10839 /* Tree lock needed by Medium::addRegistry when recursing. */
10840 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10841 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10842 {
10843 treeLock.release();
10844 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10845 treeLock.acquire();
10846 }
10847 if (pBase->i_addRegistryRecursive(uuid))
10848 {
10849 treeLock.release();
10850 mParent->i_markRegistryModified(uuid);
10851 }
10852 }
10853}
10854
10855/**
10856 * Creates differencing hard disks for all normal hard disks attached to this
10857 * machine and a new set of attachments to refer to created disks.
10858 *
10859 * Used when taking a snapshot or when deleting the current state. Gets called
10860 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10861 *
10862 * This method assumes that mMediumAttachments contains the original hard disk
10863 * attachments it needs to create diffs for. On success, these attachments will
10864 * be replaced with the created diffs.
10865 *
10866 * Attachments with non-normal hard disks are left as is.
10867 *
10868 * If @a aOnline is @c false then the original hard disks that require implicit
10869 * diffs will be locked for reading. Otherwise it is assumed that they are
10870 * already locked for writing (when the VM was started). Note that in the latter
10871 * case it is responsibility of the caller to lock the newly created diffs for
10872 * writing if this method succeeds.
10873 *
10874 * @param aProgress Progress object to run (must contain at least as
10875 * many operations left as the number of hard disks
10876 * attached).
10877 * @param aWeight Weight of this operation.
10878 * @param aOnline Whether the VM was online prior to this operation.
10879 *
10880 * @note The progress object is not marked as completed, neither on success nor
10881 * on failure. This is a responsibility of the caller.
10882 *
10883 * @note Locks this object and the media tree for writing.
10884 */
10885HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10886 ULONG aWeight,
10887 bool aOnline)
10888{
10889 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10890
10891 AutoCaller autoCaller(this);
10892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10893
10894 AutoMultiWriteLock2 alock(this->lockHandle(),
10895 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10896
10897 /* must be in a protective state because we release the lock below */
10898 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10899 || mData->mMachineState == MachineState_OnlineSnapshotting
10900 || mData->mMachineState == MachineState_LiveSnapshotting
10901 || mData->mMachineState == MachineState_RestoringSnapshot
10902 || mData->mMachineState == MachineState_DeletingSnapshot
10903 , E_FAIL);
10904
10905 HRESULT rc = S_OK;
10906
10907 // use appropriate locked media map (online or offline)
10908 MediumLockListMap lockedMediaOffline;
10909 MediumLockListMap *lockedMediaMap;
10910 if (aOnline)
10911 lockedMediaMap = &mData->mSession.mLockedMedia;
10912 else
10913 lockedMediaMap = &lockedMediaOffline;
10914
10915 try
10916 {
10917 if (!aOnline)
10918 {
10919 /* lock all attached hard disks early to detect "in use"
10920 * situations before creating actual diffs */
10921 for (MediumAttachmentList::const_iterator
10922 it = mMediumAttachments->begin();
10923 it != mMediumAttachments->end();
10924 ++it)
10925 {
10926 MediumAttachment *pAtt = *it;
10927 if (pAtt->i_getType() == DeviceType_HardDisk)
10928 {
10929 Medium *pMedium = pAtt->i_getMedium();
10930 Assert(pMedium);
10931
10932 MediumLockList *pMediumLockList(new MediumLockList());
10933 alock.release();
10934 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10935 NULL /* pToLockWrite */,
10936 false /* fMediumLockWriteAll */,
10937 NULL,
10938 *pMediumLockList);
10939 alock.acquire();
10940 if (FAILED(rc))
10941 {
10942 delete pMediumLockList;
10943 throw rc;
10944 }
10945 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10946 if (FAILED(rc))
10947 {
10948 throw setError(rc,
10949 tr("Collecting locking information for all attached media failed"));
10950 }
10951 }
10952 }
10953
10954 /* Now lock all media. If this fails, nothing is locked. */
10955 alock.release();
10956 rc = lockedMediaMap->Lock();
10957 alock.acquire();
10958 if (FAILED(rc))
10959 {
10960 throw setError(rc,
10961 tr("Locking of attached media failed"));
10962 }
10963 }
10964
10965 /* remember the current list (note that we don't use backup() since
10966 * mMediumAttachments may be already backed up) */
10967 MediumAttachmentList atts = *mMediumAttachments.data();
10968
10969 /* start from scratch */
10970 mMediumAttachments->clear();
10971
10972 /* go through remembered attachments and create diffs for normal hard
10973 * disks and attach them */
10974 for (MediumAttachmentList::const_iterator
10975 it = atts.begin();
10976 it != atts.end();
10977 ++it)
10978 {
10979 MediumAttachment *pAtt = *it;
10980
10981 DeviceType_T devType = pAtt->i_getType();
10982 Medium *pMedium = pAtt->i_getMedium();
10983
10984 if ( devType != DeviceType_HardDisk
10985 || pMedium == NULL
10986 || pMedium->i_getType() != MediumType_Normal)
10987 {
10988 /* copy the attachment as is */
10989
10990 /** @todo the progress object created in SessionMachine::TakeSnaphot
10991 * only expects operations for hard disks. Later other
10992 * device types need to show up in the progress as well. */
10993 if (devType == DeviceType_HardDisk)
10994 {
10995 if (pMedium == NULL)
10996 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10997 aWeight); // weight
10998 else
10999 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11000 pMedium->i_getBase()->i_getName().c_str()).raw(),
11001 aWeight); // weight
11002 }
11003
11004 mMediumAttachments->push_back(pAtt);
11005 continue;
11006 }
11007
11008 /* need a diff */
11009 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11010 pMedium->i_getBase()->i_getName().c_str()).raw(),
11011 aWeight); // weight
11012
11013 Utf8Str strFullSnapshotFolder;
11014 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11015
11016 ComObjPtr<Medium> diff;
11017 diff.createObject();
11018 // store the diff in the same registry as the parent
11019 // (this cannot fail here because we can't create implicit diffs for
11020 // unregistered images)
11021 Guid uuidRegistryParent;
11022 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11023 Assert(fInRegistry); NOREF(fInRegistry);
11024 rc = diff->init(mParent,
11025 pMedium->i_getPreferredDiffFormat(),
11026 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11027 uuidRegistryParent,
11028 DeviceType_HardDisk);
11029 if (FAILED(rc)) throw rc;
11030
11031 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11032 * the push_back? Looks like we're going to release medium with the
11033 * wrong kind of lock (general issue with if we fail anywhere at all)
11034 * and an orphaned VDI in the snapshots folder. */
11035
11036 /* update the appropriate lock list */
11037 MediumLockList *pMediumLockList;
11038 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11039 AssertComRCThrowRC(rc);
11040 if (aOnline)
11041 {
11042 alock.release();
11043 /* The currently attached medium will be read-only, change
11044 * the lock type to read. */
11045 rc = pMediumLockList->Update(pMedium, false);
11046 alock.acquire();
11047 AssertComRCThrowRC(rc);
11048 }
11049
11050 /* release the locks before the potentially lengthy operation */
11051 alock.release();
11052 rc = pMedium->i_createDiffStorage(diff,
11053 pMedium->i_getPreferredDiffVariant(),
11054 pMediumLockList,
11055 NULL /* aProgress */,
11056 true /* aWait */);
11057 alock.acquire();
11058 if (FAILED(rc)) throw rc;
11059
11060 /* actual lock list update is done in Machine::i_commitMedia */
11061
11062 rc = diff->i_addBackReference(mData->mUuid);
11063 AssertComRCThrowRC(rc);
11064
11065 /* add a new attachment */
11066 ComObjPtr<MediumAttachment> attachment;
11067 attachment.createObject();
11068 rc = attachment->init(this,
11069 diff,
11070 pAtt->i_getControllerName(),
11071 pAtt->i_getPort(),
11072 pAtt->i_getDevice(),
11073 DeviceType_HardDisk,
11074 true /* aImplicit */,
11075 false /* aPassthrough */,
11076 false /* aTempEject */,
11077 pAtt->i_getNonRotational(),
11078 pAtt->i_getDiscard(),
11079 pAtt->i_getHotPluggable(),
11080 pAtt->i_getBandwidthGroup());
11081 if (FAILED(rc)) throw rc;
11082
11083 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11084 AssertComRCThrowRC(rc);
11085 mMediumAttachments->push_back(attachment);
11086 }
11087 }
11088 catch (HRESULT aRC) { rc = aRC; }
11089
11090 /* unlock all hard disks we locked when there is no VM */
11091 if (!aOnline)
11092 {
11093 ErrorInfoKeeper eik;
11094
11095 HRESULT rc1 = lockedMediaMap->Clear();
11096 AssertComRC(rc1);
11097 }
11098
11099 return rc;
11100}
11101
11102/**
11103 * Deletes implicit differencing hard disks created either by
11104 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11105 * mMediumAttachments.
11106 *
11107 * Note that to delete hard disks created by #attachDevice() this method is
11108 * called from #i_rollbackMedia() when the changes are rolled back.
11109 *
11110 * @note Locks this object and the media tree for writing.
11111 */
11112HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11113{
11114 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11115
11116 AutoCaller autoCaller(this);
11117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11118
11119 AutoMultiWriteLock2 alock(this->lockHandle(),
11120 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11121
11122 /* We absolutely must have backed up state. */
11123 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11124
11125 /* Check if there are any implicitly created diff images. */
11126 bool fImplicitDiffs = false;
11127 for (MediumAttachmentList::const_iterator
11128 it = mMediumAttachments->begin();
11129 it != mMediumAttachments->end();
11130 ++it)
11131 {
11132 const ComObjPtr<MediumAttachment> &pAtt = *it;
11133 if (pAtt->i_isImplicit())
11134 {
11135 fImplicitDiffs = true;
11136 break;
11137 }
11138 }
11139 /* If there is nothing to do, leave early. This saves lots of image locking
11140 * effort. It also avoids a MachineStateChanged event without real reason.
11141 * This is important e.g. when loading a VM config, because there should be
11142 * no events. Otherwise API clients can become thoroughly confused for
11143 * inaccessible VMs (the code for loading VM configs uses this method for
11144 * cleanup if the config makes no sense), as they take such events as an
11145 * indication that the VM is alive, and they would force the VM config to
11146 * be reread, leading to an endless loop. */
11147 if (!fImplicitDiffs)
11148 return S_OK;
11149
11150 HRESULT rc = S_OK;
11151 MachineState_T oldState = mData->mMachineState;
11152
11153 /* will release the lock before the potentially lengthy operation,
11154 * so protect with the special state (unless already protected) */
11155 if ( oldState != MachineState_Snapshotting
11156 && oldState != MachineState_OnlineSnapshotting
11157 && oldState != MachineState_LiveSnapshotting
11158 && oldState != MachineState_RestoringSnapshot
11159 && oldState != MachineState_DeletingSnapshot
11160 && oldState != MachineState_DeletingSnapshotOnline
11161 && oldState != MachineState_DeletingSnapshotPaused
11162 )
11163 i_setMachineState(MachineState_SettingUp);
11164
11165 // use appropriate locked media map (online or offline)
11166 MediumLockListMap lockedMediaOffline;
11167 MediumLockListMap *lockedMediaMap;
11168 if (aOnline)
11169 lockedMediaMap = &mData->mSession.mLockedMedia;
11170 else
11171 lockedMediaMap = &lockedMediaOffline;
11172
11173 try
11174 {
11175 if (!aOnline)
11176 {
11177 /* lock all attached hard disks early to detect "in use"
11178 * situations before deleting actual diffs */
11179 for (MediumAttachmentList::const_iterator
11180 it = mMediumAttachments->begin();
11181 it != mMediumAttachments->end();
11182 ++it)
11183 {
11184 MediumAttachment *pAtt = *it;
11185 if (pAtt->i_getType() == DeviceType_HardDisk)
11186 {
11187 Medium *pMedium = pAtt->i_getMedium();
11188 Assert(pMedium);
11189
11190 MediumLockList *pMediumLockList(new MediumLockList());
11191 alock.release();
11192 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11193 NULL /* pToLockWrite */,
11194 false /* fMediumLockWriteAll */,
11195 NULL,
11196 *pMediumLockList);
11197 alock.acquire();
11198
11199 if (FAILED(rc))
11200 {
11201 delete pMediumLockList;
11202 throw rc;
11203 }
11204
11205 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11206 if (FAILED(rc))
11207 throw rc;
11208 }
11209 }
11210
11211 if (FAILED(rc))
11212 throw rc;
11213 } // end of offline
11214
11215 /* Lock lists are now up to date and include implicitly created media */
11216
11217 /* Go through remembered attachments and delete all implicitly created
11218 * diffs and fix up the attachment information */
11219 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11220 MediumAttachmentList implicitAtts;
11221 for (MediumAttachmentList::const_iterator
11222 it = mMediumAttachments->begin();
11223 it != mMediumAttachments->end();
11224 ++it)
11225 {
11226 ComObjPtr<MediumAttachment> pAtt = *it;
11227 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11228 if (pMedium.isNull())
11229 continue;
11230
11231 // Implicit attachments go on the list for deletion and back references are removed.
11232 if (pAtt->i_isImplicit())
11233 {
11234 /* Deassociate and mark for deletion */
11235 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11236 rc = pMedium->i_removeBackReference(mData->mUuid);
11237 if (FAILED(rc))
11238 throw rc;
11239 implicitAtts.push_back(pAtt);
11240 continue;
11241 }
11242
11243 /* Was this medium attached before? */
11244 if (!i_findAttachment(oldAtts, pMedium))
11245 {
11246 /* no: de-associate */
11247 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11248 rc = pMedium->i_removeBackReference(mData->mUuid);
11249 if (FAILED(rc))
11250 throw rc;
11251 continue;
11252 }
11253 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11254 }
11255
11256 /* If there are implicit attachments to delete, throw away the lock
11257 * map contents (which will unlock all media) since the medium
11258 * attachments will be rolled back. Below we need to completely
11259 * recreate the lock map anyway since it is infinitely complex to
11260 * do this incrementally (would need reconstructing each attachment
11261 * change, which would be extremely hairy). */
11262 if (implicitAtts.size() != 0)
11263 {
11264 ErrorInfoKeeper eik;
11265
11266 HRESULT rc1 = lockedMediaMap->Clear();
11267 AssertComRC(rc1);
11268 }
11269
11270 /* rollback hard disk changes */
11271 mMediumAttachments.rollback();
11272
11273 MultiResult mrc(S_OK);
11274
11275 // Delete unused implicit diffs.
11276 if (implicitAtts.size() != 0)
11277 {
11278 alock.release();
11279
11280 for (MediumAttachmentList::const_iterator
11281 it = implicitAtts.begin();
11282 it != implicitAtts.end();
11283 ++it)
11284 {
11285 // Remove medium associated with this attachment.
11286 ComObjPtr<MediumAttachment> pAtt = *it;
11287 Assert(pAtt);
11288 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11289 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11290 Assert(pMedium);
11291
11292 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11293 // continue on delete failure, just collect error messages
11294 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11295 pMedium->i_getLocationFull().c_str() ));
11296 mrc = rc;
11297 }
11298 // Clear the list of deleted implicit attachments now, while not
11299 // holding the lock, as it will ultimately trigger Medium::uninit()
11300 // calls which assume that the media tree lock isn't held.
11301 implicitAtts.clear();
11302
11303 alock.acquire();
11304
11305 /* if there is a VM recreate media lock map as mentioned above,
11306 * otherwise it is a waste of time and we leave things unlocked */
11307 if (aOnline)
11308 {
11309 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11310 /* must never be NULL, but better safe than sorry */
11311 if (!pMachine.isNull())
11312 {
11313 alock.release();
11314 rc = mData->mSession.mMachine->i_lockMedia();
11315 alock.acquire();
11316 if (FAILED(rc))
11317 throw rc;
11318 }
11319 }
11320 }
11321 }
11322 catch (HRESULT aRC) {rc = aRC;}
11323
11324 if (mData->mMachineState == MachineState_SettingUp)
11325 i_setMachineState(oldState);
11326
11327 /* unlock all hard disks we locked when there is no VM */
11328 if (!aOnline)
11329 {
11330 ErrorInfoKeeper eik;
11331
11332 HRESULT rc1 = lockedMediaMap->Clear();
11333 AssertComRC(rc1);
11334 }
11335
11336 return rc;
11337}
11338
11339
11340/**
11341 * Looks through the given list of media attachments for one with the given parameters
11342 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11343 * can be searched as well if needed.
11344 *
11345 * @param ll
11346 * @param aControllerName
11347 * @param aControllerPort
11348 * @param aDevice
11349 * @return
11350 */
11351MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11352 const Utf8Str &aControllerName,
11353 LONG aControllerPort,
11354 LONG aDevice)
11355{
11356 for (MediumAttachmentList::const_iterator
11357 it = ll.begin();
11358 it != ll.end();
11359 ++it)
11360 {
11361 MediumAttachment *pAttach = *it;
11362 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11363 return pAttach;
11364 }
11365
11366 return NULL;
11367}
11368
11369/**
11370 * Looks through the given list of media attachments for one with the given parameters
11371 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11372 * can be searched as well if needed.
11373 *
11374 * @param ll
11375 * @param pMedium
11376 * @return
11377 */
11378MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11379 ComObjPtr<Medium> pMedium)
11380{
11381 for (MediumAttachmentList::const_iterator
11382 it = ll.begin();
11383 it != ll.end();
11384 ++it)
11385 {
11386 MediumAttachment *pAttach = *it;
11387 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11388 if (pMediumThis == pMedium)
11389 return pAttach;
11390 }
11391
11392 return NULL;
11393}
11394
11395/**
11396 * Looks through the given list of media attachments for one with the given parameters
11397 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11398 * can be searched as well if needed.
11399 *
11400 * @param ll
11401 * @param id
11402 * @return
11403 */
11404MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11405 Guid &id)
11406{
11407 for (MediumAttachmentList::const_iterator
11408 it = ll.begin();
11409 it != ll.end();
11410 ++it)
11411 {
11412 MediumAttachment *pAttach = *it;
11413 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11414 if (pMediumThis->i_getId() == id)
11415 return pAttach;
11416 }
11417
11418 return NULL;
11419}
11420
11421/**
11422 * Main implementation for Machine::DetachDevice. This also gets called
11423 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11424 *
11425 * @param pAttach Medium attachment to detach.
11426 * @param writeLock Machine write lock which the caller must have locked once.
11427 * This may be released temporarily in here.
11428 * @param pSnapshot If NULL, then the detachment is for the current machine.
11429 * Otherwise this is for a SnapshotMachine, and this must be
11430 * its snapshot.
11431 * @return
11432 */
11433HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11434 AutoWriteLock &writeLock,
11435 Snapshot *pSnapshot)
11436{
11437 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11438 DeviceType_T mediumType = pAttach->i_getType();
11439
11440 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11441
11442 if (pAttach->i_isImplicit())
11443 {
11444 /* attempt to implicitly delete the implicitly created diff */
11445
11446 /// @todo move the implicit flag from MediumAttachment to Medium
11447 /// and forbid any hard disk operation when it is implicit. Or maybe
11448 /// a special media state for it to make it even more simple.
11449
11450 Assert(mMediumAttachments.isBackedUp());
11451
11452 /* will release the lock before the potentially lengthy operation, so
11453 * protect with the special state */
11454 MachineState_T oldState = mData->mMachineState;
11455 i_setMachineState(MachineState_SettingUp);
11456
11457 writeLock.release();
11458
11459 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11460 true /*aWait*/);
11461
11462 writeLock.acquire();
11463
11464 i_setMachineState(oldState);
11465
11466 if (FAILED(rc)) return rc;
11467 }
11468
11469 i_setModified(IsModified_Storage);
11470 mMediumAttachments.backup();
11471 mMediumAttachments->remove(pAttach);
11472
11473 if (!oldmedium.isNull())
11474 {
11475 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11476 if (pSnapshot)
11477 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11478 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11479 else if (mediumType != DeviceType_HardDisk)
11480 oldmedium->i_removeBackReference(mData->mUuid);
11481 }
11482
11483 return S_OK;
11484}
11485
11486/**
11487 * Goes thru all media of the given list and
11488 *
11489 * 1) calls i_detachDevice() on each of them for this machine and
11490 * 2) adds all Medium objects found in the process to the given list,
11491 * depending on cleanupMode.
11492 *
11493 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11494 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11495 * media to the list.
11496 *
11497 * This gets called from Machine::Unregister, both for the actual Machine and
11498 * the SnapshotMachine objects that might be found in the snapshots.
11499 *
11500 * Requires caller and locking. The machine lock must be passed in because it
11501 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11502 *
11503 * @param writeLock Machine lock from top-level caller; this gets passed to
11504 * i_detachDevice.
11505 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11506 * object if called for a SnapshotMachine.
11507 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11508 * added to llMedia; if Full, then all media get added;
11509 * otherwise no media get added.
11510 * @param llMedia Caller's list to receive Medium objects which got detached so
11511 * caller can close() them, depending on cleanupMode.
11512 * @return
11513 */
11514HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11515 Snapshot *pSnapshot,
11516 CleanupMode_T cleanupMode,
11517 MediaList &llMedia)
11518{
11519 Assert(isWriteLockOnCurrentThread());
11520
11521 HRESULT rc;
11522
11523 // make a temporary list because i_detachDevice invalidates iterators into
11524 // mMediumAttachments
11525 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11526
11527 for (MediumAttachmentList::iterator
11528 it = llAttachments2.begin();
11529 it != llAttachments2.end();
11530 ++it)
11531 {
11532 ComObjPtr<MediumAttachment> &pAttach = *it;
11533 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11534
11535 if (!pMedium.isNull())
11536 {
11537 AutoCaller mac(pMedium);
11538 if (FAILED(mac.rc())) return mac.rc();
11539 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11540 DeviceType_T devType = pMedium->i_getDeviceType();
11541 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11542 && devType == DeviceType_HardDisk)
11543 || (cleanupMode == CleanupMode_Full)
11544 )
11545 {
11546 llMedia.push_back(pMedium);
11547 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11548 /* Not allowed to keep this lock as below we need the parent
11549 * medium lock, and the lock order is parent to child. */
11550 lock.release();
11551 /*
11552 * Search for medias which are not attached to any machine, but
11553 * in the chain to an attached disk. Mediums are only consided
11554 * if they are:
11555 * - have only one child
11556 * - no references to any machines
11557 * - are of normal medium type
11558 */
11559 while (!pParent.isNull())
11560 {
11561 AutoCaller mac1(pParent);
11562 if (FAILED(mac1.rc())) return mac1.rc();
11563 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11564 if (pParent->i_getChildren().size() == 1)
11565 {
11566 if ( pParent->i_getMachineBackRefCount() == 0
11567 && pParent->i_getType() == MediumType_Normal
11568 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11569 llMedia.push_back(pParent);
11570 }
11571 else
11572 break;
11573 pParent = pParent->i_getParent();
11574 }
11575 }
11576 }
11577
11578 // real machine: then we need to use the proper method
11579 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11580
11581 if (FAILED(rc))
11582 return rc;
11583 }
11584
11585 return S_OK;
11586}
11587
11588/**
11589 * Perform deferred hard disk detachments.
11590 *
11591 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11592 * changed (not backed up).
11593 *
11594 * If @a aOnline is @c true then this method will also unlock the old hard
11595 * disks for which the new implicit diffs were created and will lock these new
11596 * diffs for writing.
11597 *
11598 * @param aOnline Whether the VM was online prior to this operation.
11599 *
11600 * @note Locks this object for writing!
11601 */
11602void Machine::i_commitMedia(bool aOnline /*= false*/)
11603{
11604 AutoCaller autoCaller(this);
11605 AssertComRCReturnVoid(autoCaller.rc());
11606
11607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11608
11609 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11610
11611 HRESULT rc = S_OK;
11612
11613 /* no attach/detach operations -- nothing to do */
11614 if (!mMediumAttachments.isBackedUp())
11615 return;
11616
11617 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11618 bool fMediaNeedsLocking = false;
11619
11620 /* enumerate new attachments */
11621 for (MediumAttachmentList::const_iterator
11622 it = mMediumAttachments->begin();
11623 it != mMediumAttachments->end();
11624 ++it)
11625 {
11626 MediumAttachment *pAttach = *it;
11627
11628 pAttach->i_commit();
11629
11630 Medium *pMedium = pAttach->i_getMedium();
11631 bool fImplicit = pAttach->i_isImplicit();
11632
11633 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11634 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11635 fImplicit));
11636
11637 /** @todo convert all this Machine-based voodoo to MediumAttachment
11638 * based commit logic. */
11639 if (fImplicit)
11640 {
11641 /* convert implicit attachment to normal */
11642 pAttach->i_setImplicit(false);
11643
11644 if ( aOnline
11645 && pMedium
11646 && pAttach->i_getType() == DeviceType_HardDisk
11647 )
11648 {
11649 /* update the appropriate lock list */
11650 MediumLockList *pMediumLockList;
11651 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11652 AssertComRC(rc);
11653 if (pMediumLockList)
11654 {
11655 /* unlock if there's a need to change the locking */
11656 if (!fMediaNeedsLocking)
11657 {
11658 rc = mData->mSession.mLockedMedia.Unlock();
11659 AssertComRC(rc);
11660 fMediaNeedsLocking = true;
11661 }
11662 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11663 AssertComRC(rc);
11664 rc = pMediumLockList->Append(pMedium, true);
11665 AssertComRC(rc);
11666 }
11667 }
11668
11669 continue;
11670 }
11671
11672 if (pMedium)
11673 {
11674 /* was this medium attached before? */
11675 for (MediumAttachmentList::iterator
11676 oldIt = oldAtts.begin();
11677 oldIt != oldAtts.end();
11678 ++oldIt)
11679 {
11680 MediumAttachment *pOldAttach = *oldIt;
11681 if (pOldAttach->i_getMedium() == pMedium)
11682 {
11683 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11684
11685 /* yes: remove from old to avoid de-association */
11686 oldAtts.erase(oldIt);
11687 break;
11688 }
11689 }
11690 }
11691 }
11692
11693 /* enumerate remaining old attachments and de-associate from the
11694 * current machine state */
11695 for (MediumAttachmentList::const_iterator
11696 it = oldAtts.begin();
11697 it != oldAtts.end();
11698 ++it)
11699 {
11700 MediumAttachment *pAttach = *it;
11701 Medium *pMedium = pAttach->i_getMedium();
11702
11703 /* Detach only hard disks, since DVD/floppy media is detached
11704 * instantly in MountMedium. */
11705 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11706 {
11707 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11708
11709 /* now de-associate from the current machine state */
11710 rc = pMedium->i_removeBackReference(mData->mUuid);
11711 AssertComRC(rc);
11712
11713 if (aOnline)
11714 {
11715 /* unlock since medium is not used anymore */
11716 MediumLockList *pMediumLockList;
11717 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11718 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11719 {
11720 /* this happens for online snapshots, there the attachment
11721 * is changing, but only to a diff image created under
11722 * the old one, so there is no separate lock list */
11723 Assert(!pMediumLockList);
11724 }
11725 else
11726 {
11727 AssertComRC(rc);
11728 if (pMediumLockList)
11729 {
11730 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11731 AssertComRC(rc);
11732 }
11733 }
11734 }
11735 }
11736 }
11737
11738 /* take media locks again so that the locking state is consistent */
11739 if (fMediaNeedsLocking)
11740 {
11741 Assert(aOnline);
11742 rc = mData->mSession.mLockedMedia.Lock();
11743 AssertComRC(rc);
11744 }
11745
11746 /* commit the hard disk changes */
11747 mMediumAttachments.commit();
11748
11749 if (i_isSessionMachine())
11750 {
11751 /*
11752 * Update the parent machine to point to the new owner.
11753 * This is necessary because the stored parent will point to the
11754 * session machine otherwise and cause crashes or errors later
11755 * when the session machine gets invalid.
11756 */
11757 /** @todo Change the MediumAttachment class to behave like any other
11758 * class in this regard by creating peer MediumAttachment
11759 * objects for session machines and share the data with the peer
11760 * machine.
11761 */
11762 for (MediumAttachmentList::const_iterator
11763 it = mMediumAttachments->begin();
11764 it != mMediumAttachments->end();
11765 ++it)
11766 (*it)->i_updateParentMachine(mPeer);
11767
11768 /* attach new data to the primary machine and reshare it */
11769 mPeer->mMediumAttachments.attach(mMediumAttachments);
11770 }
11771
11772 return;
11773}
11774
11775/**
11776 * Perform deferred deletion of implicitly created diffs.
11777 *
11778 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11779 * changed (not backed up).
11780 *
11781 * @note Locks this object for writing!
11782 */
11783void Machine::i_rollbackMedia()
11784{
11785 AutoCaller autoCaller(this);
11786 AssertComRCReturnVoid(autoCaller.rc());
11787
11788 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11789 LogFlowThisFunc(("Entering rollbackMedia\n"));
11790
11791 HRESULT rc = S_OK;
11792
11793 /* no attach/detach operations -- nothing to do */
11794 if (!mMediumAttachments.isBackedUp())
11795 return;
11796
11797 /* enumerate new attachments */
11798 for (MediumAttachmentList::const_iterator
11799 it = mMediumAttachments->begin();
11800 it != mMediumAttachments->end();
11801 ++it)
11802 {
11803 MediumAttachment *pAttach = *it;
11804 /* Fix up the backrefs for DVD/floppy media. */
11805 if (pAttach->i_getType() != DeviceType_HardDisk)
11806 {
11807 Medium *pMedium = pAttach->i_getMedium();
11808 if (pMedium)
11809 {
11810 rc = pMedium->i_removeBackReference(mData->mUuid);
11811 AssertComRC(rc);
11812 }
11813 }
11814
11815 (*it)->i_rollback();
11816
11817 pAttach = *it;
11818 /* Fix up the backrefs for DVD/floppy media. */
11819 if (pAttach->i_getType() != DeviceType_HardDisk)
11820 {
11821 Medium *pMedium = pAttach->i_getMedium();
11822 if (pMedium)
11823 {
11824 rc = pMedium->i_addBackReference(mData->mUuid);
11825 AssertComRC(rc);
11826 }
11827 }
11828 }
11829
11830 /** @todo convert all this Machine-based voodoo to MediumAttachment
11831 * based rollback logic. */
11832 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11833
11834 return;
11835}
11836
11837/**
11838 * Returns true if the settings file is located in the directory named exactly
11839 * as the machine; this means, among other things, that the machine directory
11840 * should be auto-renamed.
11841 *
11842 * @param aSettingsDir if not NULL, the full machine settings file directory
11843 * name will be assigned there.
11844 *
11845 * @note Doesn't lock anything.
11846 * @note Not thread safe (must be called from this object's lock).
11847 */
11848bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11849{
11850 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11851 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11852 if (aSettingsDir)
11853 *aSettingsDir = strMachineDirName;
11854 strMachineDirName.stripPath(); // vmname
11855 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11856 strConfigFileOnly.stripPath() // vmname.vbox
11857 .stripSuffix(); // vmname
11858 /** @todo hack, make somehow use of ComposeMachineFilename */
11859 if (mUserData->s.fDirectoryIncludesUUID)
11860 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11861
11862 AssertReturn(!strMachineDirName.isEmpty(), false);
11863 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11864
11865 return strMachineDirName == strConfigFileOnly;
11866}
11867
11868/**
11869 * Discards all changes to machine settings.
11870 *
11871 * @param aNotify Whether to notify the direct session about changes or not.
11872 *
11873 * @note Locks objects for writing!
11874 */
11875void Machine::i_rollback(bool aNotify)
11876{
11877 AutoCaller autoCaller(this);
11878 AssertComRCReturn(autoCaller.rc(), (void)0);
11879
11880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11881
11882 if (!mStorageControllers.isNull())
11883 {
11884 if (mStorageControllers.isBackedUp())
11885 {
11886 /* unitialize all new devices (absent in the backed up list). */
11887 StorageControllerList *backedList = mStorageControllers.backedUpData();
11888 for (StorageControllerList::const_iterator
11889 it = mStorageControllers->begin();
11890 it != mStorageControllers->end();
11891 ++it)
11892 {
11893 if ( std::find(backedList->begin(), backedList->end(), *it)
11894 == backedList->end()
11895 )
11896 {
11897 (*it)->uninit();
11898 }
11899 }
11900
11901 /* restore the list */
11902 mStorageControllers.rollback();
11903 }
11904
11905 /* rollback any changes to devices after restoring the list */
11906 if (mData->flModifications & IsModified_Storage)
11907 {
11908 for (StorageControllerList::const_iterator
11909 it = mStorageControllers->begin();
11910 it != mStorageControllers->end();
11911 ++it)
11912 {
11913 (*it)->i_rollback();
11914 }
11915 }
11916 }
11917
11918 if (!mUSBControllers.isNull())
11919 {
11920 if (mUSBControllers.isBackedUp())
11921 {
11922 /* unitialize all new devices (absent in the backed up list). */
11923 USBControllerList *backedList = mUSBControllers.backedUpData();
11924 for (USBControllerList::const_iterator
11925 it = mUSBControllers->begin();
11926 it != mUSBControllers->end();
11927 ++it)
11928 {
11929 if ( std::find(backedList->begin(), backedList->end(), *it)
11930 == backedList->end()
11931 )
11932 {
11933 (*it)->uninit();
11934 }
11935 }
11936
11937 /* restore the list */
11938 mUSBControllers.rollback();
11939 }
11940
11941 /* rollback any changes to devices after restoring the list */
11942 if (mData->flModifications & IsModified_USB)
11943 {
11944 for (USBControllerList::const_iterator
11945 it = mUSBControllers->begin();
11946 it != mUSBControllers->end();
11947 ++it)
11948 {
11949 (*it)->i_rollback();
11950 }
11951 }
11952 }
11953
11954 mUserData.rollback();
11955
11956 mHWData.rollback();
11957
11958 if (mData->flModifications & IsModified_Storage)
11959 i_rollbackMedia();
11960
11961 if (mBIOSSettings)
11962 mBIOSSettings->i_rollback();
11963
11964 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11965 mVRDEServer->i_rollback();
11966
11967 if (mAudioAdapter)
11968 mAudioAdapter->i_rollback();
11969
11970 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11971 mUSBDeviceFilters->i_rollback();
11972
11973 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11974 mBandwidthControl->i_rollback();
11975
11976 if (!mHWData.isNull())
11977 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11978 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11979 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11980 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11981
11982 if (mData->flModifications & IsModified_NetworkAdapters)
11983 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11984 if ( mNetworkAdapters[slot]
11985 && mNetworkAdapters[slot]->i_isModified())
11986 {
11987 mNetworkAdapters[slot]->i_rollback();
11988 networkAdapters[slot] = mNetworkAdapters[slot];
11989 }
11990
11991 if (mData->flModifications & IsModified_SerialPorts)
11992 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11993 if ( mSerialPorts[slot]
11994 && mSerialPorts[slot]->i_isModified())
11995 {
11996 mSerialPorts[slot]->i_rollback();
11997 serialPorts[slot] = mSerialPorts[slot];
11998 }
11999
12000 if (mData->flModifications & IsModified_ParallelPorts)
12001 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12002 if ( mParallelPorts[slot]
12003 && mParallelPorts[slot]->i_isModified())
12004 {
12005 mParallelPorts[slot]->i_rollback();
12006 parallelPorts[slot] = mParallelPorts[slot];
12007 }
12008
12009 if (aNotify)
12010 {
12011 /* inform the direct session about changes */
12012
12013 ComObjPtr<Machine> that = this;
12014 uint32_t flModifications = mData->flModifications;
12015 alock.release();
12016
12017 if (flModifications & IsModified_SharedFolders)
12018 that->i_onSharedFolderChange();
12019
12020 if (flModifications & IsModified_VRDEServer)
12021 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12022 if (flModifications & IsModified_USB)
12023 that->i_onUSBControllerChange();
12024
12025 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12026 if (networkAdapters[slot])
12027 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12028 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12029 if (serialPorts[slot])
12030 that->i_onSerialPortChange(serialPorts[slot]);
12031 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12032 if (parallelPorts[slot])
12033 that->i_onParallelPortChange(parallelPorts[slot]);
12034
12035 if (flModifications & IsModified_Storage)
12036 that->i_onStorageControllerChange();
12037
12038#if 0
12039 if (flModifications & IsModified_BandwidthControl)
12040 that->onBandwidthControlChange();
12041#endif
12042 }
12043}
12044
12045/**
12046 * Commits all the changes to machine settings.
12047 *
12048 * Note that this operation is supposed to never fail.
12049 *
12050 * @note Locks this object and children for writing.
12051 */
12052void Machine::i_commit()
12053{
12054 AutoCaller autoCaller(this);
12055 AssertComRCReturnVoid(autoCaller.rc());
12056
12057 AutoCaller peerCaller(mPeer);
12058 AssertComRCReturnVoid(peerCaller.rc());
12059
12060 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12061
12062 /*
12063 * use safe commit to ensure Snapshot machines (that share mUserData)
12064 * will still refer to a valid memory location
12065 */
12066 mUserData.commitCopy();
12067
12068 mHWData.commit();
12069
12070 if (mMediumAttachments.isBackedUp())
12071 i_commitMedia(Global::IsOnline(mData->mMachineState));
12072
12073 mBIOSSettings->i_commit();
12074 mVRDEServer->i_commit();
12075 mAudioAdapter->i_commit();
12076 mUSBDeviceFilters->i_commit();
12077 mBandwidthControl->i_commit();
12078
12079 /* Since mNetworkAdapters is a list which might have been changed (resized)
12080 * without using the Backupable<> template we need to handle the copying
12081 * of the list entries manually, including the creation of peers for the
12082 * new objects. */
12083 bool commitNetworkAdapters = false;
12084 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12085 if (mPeer)
12086 {
12087 /* commit everything, even the ones which will go away */
12088 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12089 mNetworkAdapters[slot]->i_commit();
12090 /* copy over the new entries, creating a peer and uninit the original */
12091 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12092 for (size_t slot = 0; slot < newSize; slot++)
12093 {
12094 /* look if this adapter has a peer device */
12095 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12096 if (!peer)
12097 {
12098 /* no peer means the adapter is a newly created one;
12099 * create a peer owning data this data share it with */
12100 peer.createObject();
12101 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12102 }
12103 mPeer->mNetworkAdapters[slot] = peer;
12104 }
12105 /* uninit any no longer needed network adapters */
12106 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12107 mNetworkAdapters[slot]->uninit();
12108 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12109 {
12110 if (mPeer->mNetworkAdapters[slot])
12111 mPeer->mNetworkAdapters[slot]->uninit();
12112 }
12113 /* Keep the original network adapter count until this point, so that
12114 * discarding a chipset type change will not lose settings. */
12115 mNetworkAdapters.resize(newSize);
12116 mPeer->mNetworkAdapters.resize(newSize);
12117 }
12118 else
12119 {
12120 /* we have no peer (our parent is the newly created machine);
12121 * just commit changes to the network adapters */
12122 commitNetworkAdapters = true;
12123 }
12124 if (commitNetworkAdapters)
12125 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12126 mNetworkAdapters[slot]->i_commit();
12127
12128 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12129 mSerialPorts[slot]->i_commit();
12130 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12131 mParallelPorts[slot]->i_commit();
12132
12133 bool commitStorageControllers = false;
12134
12135 if (mStorageControllers.isBackedUp())
12136 {
12137 mStorageControllers.commit();
12138
12139 if (mPeer)
12140 {
12141 /* Commit all changes to new controllers (this will reshare data with
12142 * peers for those who have peers) */
12143 StorageControllerList *newList = new StorageControllerList();
12144 for (StorageControllerList::const_iterator
12145 it = mStorageControllers->begin();
12146 it != mStorageControllers->end();
12147 ++it)
12148 {
12149 (*it)->i_commit();
12150
12151 /* look if this controller has a peer device */
12152 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12153 if (!peer)
12154 {
12155 /* no peer means the device is a newly created one;
12156 * create a peer owning data this device share it with */
12157 peer.createObject();
12158 peer->init(mPeer, *it, true /* aReshare */);
12159 }
12160 else
12161 {
12162 /* remove peer from the old list */
12163 mPeer->mStorageControllers->remove(peer);
12164 }
12165 /* and add it to the new list */
12166 newList->push_back(peer);
12167 }
12168
12169 /* uninit old peer's controllers that are left */
12170 for (StorageControllerList::const_iterator
12171 it = mPeer->mStorageControllers->begin();
12172 it != mPeer->mStorageControllers->end();
12173 ++it)
12174 {
12175 (*it)->uninit();
12176 }
12177
12178 /* attach new list of controllers to our peer */
12179 mPeer->mStorageControllers.attach(newList);
12180 }
12181 else
12182 {
12183 /* we have no peer (our parent is the newly created machine);
12184 * just commit changes to devices */
12185 commitStorageControllers = true;
12186 }
12187 }
12188 else
12189 {
12190 /* the list of controllers itself is not changed,
12191 * just commit changes to controllers themselves */
12192 commitStorageControllers = true;
12193 }
12194
12195 if (commitStorageControllers)
12196 {
12197 for (StorageControllerList::const_iterator
12198 it = mStorageControllers->begin();
12199 it != mStorageControllers->end();
12200 ++it)
12201 {
12202 (*it)->i_commit();
12203 }
12204 }
12205
12206 bool commitUSBControllers = false;
12207
12208 if (mUSBControllers.isBackedUp())
12209 {
12210 mUSBControllers.commit();
12211
12212 if (mPeer)
12213 {
12214 /* Commit all changes to new controllers (this will reshare data with
12215 * peers for those who have peers) */
12216 USBControllerList *newList = new USBControllerList();
12217 for (USBControllerList::const_iterator
12218 it = mUSBControllers->begin();
12219 it != mUSBControllers->end();
12220 ++it)
12221 {
12222 (*it)->i_commit();
12223
12224 /* look if this controller has a peer device */
12225 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12226 if (!peer)
12227 {
12228 /* no peer means the device is a newly created one;
12229 * create a peer owning data this device share it with */
12230 peer.createObject();
12231 peer->init(mPeer, *it, true /* aReshare */);
12232 }
12233 else
12234 {
12235 /* remove peer from the old list */
12236 mPeer->mUSBControllers->remove(peer);
12237 }
12238 /* and add it to the new list */
12239 newList->push_back(peer);
12240 }
12241
12242 /* uninit old peer's controllers that are left */
12243 for (USBControllerList::const_iterator
12244 it = mPeer->mUSBControllers->begin();
12245 it != mPeer->mUSBControllers->end();
12246 ++it)
12247 {
12248 (*it)->uninit();
12249 }
12250
12251 /* attach new list of controllers to our peer */
12252 mPeer->mUSBControllers.attach(newList);
12253 }
12254 else
12255 {
12256 /* we have no peer (our parent is the newly created machine);
12257 * just commit changes to devices */
12258 commitUSBControllers = true;
12259 }
12260 }
12261 else
12262 {
12263 /* the list of controllers itself is not changed,
12264 * just commit changes to controllers themselves */
12265 commitUSBControllers = true;
12266 }
12267
12268 if (commitUSBControllers)
12269 {
12270 for (USBControllerList::const_iterator
12271 it = mUSBControllers->begin();
12272 it != mUSBControllers->end();
12273 ++it)
12274 {
12275 (*it)->i_commit();
12276 }
12277 }
12278
12279 if (i_isSessionMachine())
12280 {
12281 /* attach new data to the primary machine and reshare it */
12282 mPeer->mUserData.attach(mUserData);
12283 mPeer->mHWData.attach(mHWData);
12284 /* mmMediumAttachments is reshared by fixupMedia */
12285 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12286 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12287 }
12288}
12289
12290/**
12291 * Copies all the hardware data from the given machine.
12292 *
12293 * Currently, only called when the VM is being restored from a snapshot. In
12294 * particular, this implies that the VM is not running during this method's
12295 * call.
12296 *
12297 * @note This method must be called from under this object's lock.
12298 *
12299 * @note This method doesn't call #i_commit(), so all data remains backed up and
12300 * unsaved.
12301 */
12302void Machine::i_copyFrom(Machine *aThat)
12303{
12304 AssertReturnVoid(!i_isSnapshotMachine());
12305 AssertReturnVoid(aThat->i_isSnapshotMachine());
12306
12307 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12308
12309 mHWData.assignCopy(aThat->mHWData);
12310
12311 // create copies of all shared folders (mHWData after attaching a copy
12312 // contains just references to original objects)
12313 for (HWData::SharedFolderList::iterator
12314 it = mHWData->mSharedFolders.begin();
12315 it != mHWData->mSharedFolders.end();
12316 ++it)
12317 {
12318 ComObjPtr<SharedFolder> folder;
12319 folder.createObject();
12320 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12321 AssertComRC(rc);
12322 *it = folder;
12323 }
12324
12325 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12326 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12327 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12328 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12329 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12330
12331 /* create private copies of all controllers */
12332 mStorageControllers.backup();
12333 mStorageControllers->clear();
12334 for (StorageControllerList::const_iterator
12335 it = aThat->mStorageControllers->begin();
12336 it != aThat->mStorageControllers->end();
12337 ++it)
12338 {
12339 ComObjPtr<StorageController> ctrl;
12340 ctrl.createObject();
12341 ctrl->initCopy(this, *it);
12342 mStorageControllers->push_back(ctrl);
12343 }
12344
12345 /* create private copies of all USB controllers */
12346 mUSBControllers.backup();
12347 mUSBControllers->clear();
12348 for (USBControllerList::const_iterator
12349 it = aThat->mUSBControllers->begin();
12350 it != aThat->mUSBControllers->end();
12351 ++it)
12352 {
12353 ComObjPtr<USBController> ctrl;
12354 ctrl.createObject();
12355 ctrl->initCopy(this, *it);
12356 mUSBControllers->push_back(ctrl);
12357 }
12358
12359 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12360 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12361 {
12362 if (mNetworkAdapters[slot].isNotNull())
12363 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12364 else
12365 {
12366 unconst(mNetworkAdapters[slot]).createObject();
12367 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12368 }
12369 }
12370 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12371 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12372 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12373 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12374}
12375
12376/**
12377 * Returns whether the given storage controller is hotplug capable.
12378 *
12379 * @returns true if the controller supports hotplugging
12380 * false otherwise.
12381 * @param enmCtrlType The controller type to check for.
12382 */
12383bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12384{
12385 ComPtr<ISystemProperties> systemProperties;
12386 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12387 if (FAILED(rc))
12388 return false;
12389
12390 BOOL aHotplugCapable = FALSE;
12391 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12392
12393 return RT_BOOL(aHotplugCapable);
12394}
12395
12396#ifdef VBOX_WITH_RESOURCE_USAGE_API
12397
12398void Machine::i_getDiskList(MediaList &list)
12399{
12400 for (MediumAttachmentList::const_iterator
12401 it = mMediumAttachments->begin();
12402 it != mMediumAttachments->end();
12403 ++it)
12404 {
12405 MediumAttachment *pAttach = *it;
12406 /* just in case */
12407 AssertContinue(pAttach);
12408
12409 AutoCaller localAutoCallerA(pAttach);
12410 if (FAILED(localAutoCallerA.rc())) continue;
12411
12412 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12413
12414 if (pAttach->i_getType() == DeviceType_HardDisk)
12415 list.push_back(pAttach->i_getMedium());
12416 }
12417}
12418
12419void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12420{
12421 AssertReturnVoid(isWriteLockOnCurrentThread());
12422 AssertPtrReturnVoid(aCollector);
12423
12424 pm::CollectorHAL *hal = aCollector->getHAL();
12425 /* Create sub metrics */
12426 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12427 "Percentage of processor time spent in user mode by the VM process.");
12428 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12429 "Percentage of processor time spent in kernel mode by the VM process.");
12430 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12431 "Size of resident portion of VM process in memory.");
12432 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12433 "Actual size of all VM disks combined.");
12434 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12435 "Network receive rate.");
12436 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12437 "Network transmit rate.");
12438 /* Create and register base metrics */
12439 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12440 cpuLoadUser, cpuLoadKernel);
12441 aCollector->registerBaseMetric(cpuLoad);
12442 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12443 ramUsageUsed);
12444 aCollector->registerBaseMetric(ramUsage);
12445 MediaList disks;
12446 i_getDiskList(disks);
12447 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12448 diskUsageUsed);
12449 aCollector->registerBaseMetric(diskUsage);
12450
12451 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12452 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12453 new pm::AggregateAvg()));
12454 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12455 new pm::AggregateMin()));
12456 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12457 new pm::AggregateMax()));
12458 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12459 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12460 new pm::AggregateAvg()));
12461 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12462 new pm::AggregateMin()));
12463 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12464 new pm::AggregateMax()));
12465
12466 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12467 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12468 new pm::AggregateAvg()));
12469 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12470 new pm::AggregateMin()));
12471 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12472 new pm::AggregateMax()));
12473
12474 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12475 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12476 new pm::AggregateAvg()));
12477 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12478 new pm::AggregateMin()));
12479 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12480 new pm::AggregateMax()));
12481
12482
12483 /* Guest metrics collector */
12484 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12485 aCollector->registerGuest(mCollectorGuest);
12486 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12487
12488 /* Create sub metrics */
12489 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12490 "Percentage of processor time spent in user mode as seen by the guest.");
12491 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12492 "Percentage of processor time spent in kernel mode as seen by the guest.");
12493 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12494 "Percentage of processor time spent idling as seen by the guest.");
12495
12496 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12497 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12498 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12499 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12500 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12501 pm::SubMetric *guestMemCache = new pm::SubMetric(
12502 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12503
12504 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12505 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12506
12507 /* Create and register base metrics */
12508 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12509 machineNetRx, machineNetTx);
12510 aCollector->registerBaseMetric(machineNetRate);
12511
12512 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12513 guestLoadUser, guestLoadKernel, guestLoadIdle);
12514 aCollector->registerBaseMetric(guestCpuLoad);
12515
12516 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12517 guestMemTotal, guestMemFree,
12518 guestMemBalloon, guestMemShared,
12519 guestMemCache, guestPagedTotal);
12520 aCollector->registerBaseMetric(guestCpuMem);
12521
12522 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12523 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12524 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12525 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12526
12527 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12528 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12529 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12530 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12531
12532 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12533 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12534 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12535 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12536
12537 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12538 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12539 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12540 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12541
12542 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12543 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12544 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12545 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12546
12547 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12548 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12549 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12550 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12551
12552 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12553 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12554 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12555 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12556
12557 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12558 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12559 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12560 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12561
12562 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12563 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12564 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12565 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12566
12567 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12568 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12569 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12570 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12571
12572 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12573 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12574 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12575 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12576}
12577
12578void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12579{
12580 AssertReturnVoid(isWriteLockOnCurrentThread());
12581
12582 if (aCollector)
12583 {
12584 aCollector->unregisterMetricsFor(aMachine);
12585 aCollector->unregisterBaseMetricsFor(aMachine);
12586 }
12587}
12588
12589#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12590
12591
12592////////////////////////////////////////////////////////////////////////////////
12593
12594DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12595
12596HRESULT SessionMachine::FinalConstruct()
12597{
12598 LogFlowThisFunc(("\n"));
12599
12600 mClientToken = NULL;
12601
12602 return BaseFinalConstruct();
12603}
12604
12605void SessionMachine::FinalRelease()
12606{
12607 LogFlowThisFunc(("\n"));
12608
12609 Assert(!mClientToken);
12610 /* paranoia, should not hang around any more */
12611 if (mClientToken)
12612 {
12613 delete mClientToken;
12614 mClientToken = NULL;
12615 }
12616
12617 uninit(Uninit::Unexpected);
12618
12619 BaseFinalRelease();
12620}
12621
12622/**
12623 * @note Must be called only by Machine::LockMachine() from its own write lock.
12624 */
12625HRESULT SessionMachine::init(Machine *aMachine)
12626{
12627 LogFlowThisFuncEnter();
12628 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12629
12630 AssertReturn(aMachine, E_INVALIDARG);
12631
12632 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12633
12634 /* Enclose the state transition NotReady->InInit->Ready */
12635 AutoInitSpan autoInitSpan(this);
12636 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12637
12638 HRESULT rc = S_OK;
12639
12640 RT_ZERO(mAuthLibCtx);
12641
12642 /* create the machine client token */
12643 try
12644 {
12645 mClientToken = new ClientToken(aMachine, this);
12646 if (!mClientToken->isReady())
12647 {
12648 delete mClientToken;
12649 mClientToken = NULL;
12650 rc = E_FAIL;
12651 }
12652 }
12653 catch (std::bad_alloc &)
12654 {
12655 rc = E_OUTOFMEMORY;
12656 }
12657 if (FAILED(rc))
12658 return rc;
12659
12660 /* memorize the peer Machine */
12661 unconst(mPeer) = aMachine;
12662 /* share the parent pointer */
12663 unconst(mParent) = aMachine->mParent;
12664
12665 /* take the pointers to data to share */
12666 mData.share(aMachine->mData);
12667 mSSData.share(aMachine->mSSData);
12668
12669 mUserData.share(aMachine->mUserData);
12670 mHWData.share(aMachine->mHWData);
12671 mMediumAttachments.share(aMachine->mMediumAttachments);
12672
12673 mStorageControllers.allocate();
12674 for (StorageControllerList::const_iterator
12675 it = aMachine->mStorageControllers->begin();
12676 it != aMachine->mStorageControllers->end();
12677 ++it)
12678 {
12679 ComObjPtr<StorageController> ctl;
12680 ctl.createObject();
12681 ctl->init(this, *it);
12682 mStorageControllers->push_back(ctl);
12683 }
12684
12685 mUSBControllers.allocate();
12686 for (USBControllerList::const_iterator
12687 it = aMachine->mUSBControllers->begin();
12688 it != aMachine->mUSBControllers->end();
12689 ++it)
12690 {
12691 ComObjPtr<USBController> ctl;
12692 ctl.createObject();
12693 ctl->init(this, *it);
12694 mUSBControllers->push_back(ctl);
12695 }
12696
12697 unconst(mBIOSSettings).createObject();
12698 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12699 /* create another VRDEServer object that will be mutable */
12700 unconst(mVRDEServer).createObject();
12701 mVRDEServer->init(this, aMachine->mVRDEServer);
12702 /* create another audio adapter object that will be mutable */
12703 unconst(mAudioAdapter).createObject();
12704 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12705 /* create a list of serial ports that will be mutable */
12706 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12707 {
12708 unconst(mSerialPorts[slot]).createObject();
12709 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12710 }
12711 /* create a list of parallel ports that will be mutable */
12712 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12713 {
12714 unconst(mParallelPorts[slot]).createObject();
12715 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12716 }
12717
12718 /* create another USB device filters object that will be mutable */
12719 unconst(mUSBDeviceFilters).createObject();
12720 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12721
12722 /* create a list of network adapters that will be mutable */
12723 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12724 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12725 {
12726 unconst(mNetworkAdapters[slot]).createObject();
12727 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12728 }
12729
12730 /* create another bandwidth control object that will be mutable */
12731 unconst(mBandwidthControl).createObject();
12732 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12733
12734 /* default is to delete saved state on Saved -> PoweredOff transition */
12735 mRemoveSavedState = true;
12736
12737 /* Confirm a successful initialization when it's the case */
12738 autoInitSpan.setSucceeded();
12739
12740 miNATNetworksStarted = 0;
12741
12742 LogFlowThisFuncLeave();
12743 return rc;
12744}
12745
12746/**
12747 * Uninitializes this session object. If the reason is other than
12748 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12749 * or the client watcher code.
12750 *
12751 * @param aReason uninitialization reason
12752 *
12753 * @note Locks mParent + this object for writing.
12754 */
12755void SessionMachine::uninit(Uninit::Reason aReason)
12756{
12757 LogFlowThisFuncEnter();
12758 LogFlowThisFunc(("reason=%d\n", aReason));
12759
12760 /*
12761 * Strongly reference ourselves to prevent this object deletion after
12762 * mData->mSession.mMachine.setNull() below (which can release the last
12763 * reference and call the destructor). Important: this must be done before
12764 * accessing any members (and before AutoUninitSpan that does it as well).
12765 * This self reference will be released as the very last step on return.
12766 */
12767 ComObjPtr<SessionMachine> selfRef;
12768 if (aReason != Uninit::Unexpected)
12769 selfRef = this;
12770
12771 /* Enclose the state transition Ready->InUninit->NotReady */
12772 AutoUninitSpan autoUninitSpan(this);
12773 if (autoUninitSpan.uninitDone())
12774 {
12775 LogFlowThisFunc(("Already uninitialized\n"));
12776 LogFlowThisFuncLeave();
12777 return;
12778 }
12779
12780 if (autoUninitSpan.initFailed())
12781 {
12782 /* We've been called by init() because it's failed. It's not really
12783 * necessary (nor it's safe) to perform the regular uninit sequence
12784 * below, the following is enough.
12785 */
12786 LogFlowThisFunc(("Initialization failed.\n"));
12787 /* destroy the machine client token */
12788 if (mClientToken)
12789 {
12790 delete mClientToken;
12791 mClientToken = NULL;
12792 }
12793 uninitDataAndChildObjects();
12794 mData.free();
12795 unconst(mParent) = NULL;
12796 unconst(mPeer) = NULL;
12797 LogFlowThisFuncLeave();
12798 return;
12799 }
12800
12801 MachineState_T lastState;
12802 {
12803 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12804 lastState = mData->mMachineState;
12805 }
12806 NOREF(lastState);
12807
12808#ifdef VBOX_WITH_USB
12809 // release all captured USB devices, but do this before requesting the locks below
12810 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12811 {
12812 /* Console::captureUSBDevices() is called in the VM process only after
12813 * setting the machine state to Starting or Restoring.
12814 * Console::detachAllUSBDevices() will be called upon successful
12815 * termination. So, we need to release USB devices only if there was
12816 * an abnormal termination of a running VM.
12817 *
12818 * This is identical to SessionMachine::DetachAllUSBDevices except
12819 * for the aAbnormal argument. */
12820 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12821 AssertComRC(rc);
12822 NOREF(rc);
12823
12824 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12825 if (service)
12826 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12827 }
12828#endif /* VBOX_WITH_USB */
12829
12830 // we need to lock this object in uninit() because the lock is shared
12831 // with mPeer (as well as data we modify below). mParent lock is needed
12832 // by several calls to it.
12833 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12834
12835#ifdef VBOX_WITH_RESOURCE_USAGE_API
12836 /*
12837 * It is safe to call Machine::i_unregisterMetrics() here because
12838 * PerformanceCollector::samplerCallback no longer accesses guest methods
12839 * holding the lock.
12840 */
12841 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12842 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12843 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12844 if (mCollectorGuest)
12845 {
12846 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12847 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12848 mCollectorGuest = NULL;
12849 }
12850#endif
12851
12852 if (aReason == Uninit::Abnormal)
12853 {
12854 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12855
12856 /* reset the state to Aborted */
12857 if (mData->mMachineState != MachineState_Aborted)
12858 i_setMachineState(MachineState_Aborted);
12859 }
12860
12861 // any machine settings modified?
12862 if (mData->flModifications)
12863 {
12864 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12865 i_rollback(false /* aNotify */);
12866 }
12867
12868 mData->mSession.mPID = NIL_RTPROCESS;
12869
12870 if (aReason == Uninit::Unexpected)
12871 {
12872 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12873 * client watcher thread to update the set of machines that have open
12874 * sessions. */
12875 mParent->i_updateClientWatcher();
12876 }
12877
12878 /* uninitialize all remote controls */
12879 if (mData->mSession.mRemoteControls.size())
12880 {
12881 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12882 mData->mSession.mRemoteControls.size()));
12883
12884 /* Always restart a the beginning, since the iterator is invalidated
12885 * by using erase(). */
12886 for (Data::Session::RemoteControlList::iterator
12887 it = mData->mSession.mRemoteControls.begin();
12888 it != mData->mSession.mRemoteControls.end();
12889 it = mData->mSession.mRemoteControls.begin())
12890 {
12891 ComPtr<IInternalSessionControl> pControl = *it;
12892 mData->mSession.mRemoteControls.erase(it);
12893 multilock.release();
12894 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12895 HRESULT rc = pControl->Uninitialize();
12896 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12897 if (FAILED(rc))
12898 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12899 multilock.acquire();
12900 }
12901 mData->mSession.mRemoteControls.clear();
12902 }
12903
12904 /* Remove all references to the NAT network service. The service will stop
12905 * if all references (also from other VMs) are removed. */
12906 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12907 {
12908 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12909 {
12910 BOOL enabled;
12911 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12912 if ( FAILED(hrc)
12913 || !enabled)
12914 continue;
12915
12916 NetworkAttachmentType_T type;
12917 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12918 if ( SUCCEEDED(hrc)
12919 && type == NetworkAttachmentType_NATNetwork)
12920 {
12921 Bstr name;
12922 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12923 if (SUCCEEDED(hrc))
12924 {
12925 multilock.release();
12926 Utf8Str strName(name);
12927 LogRel(("VM '%s' stops using NAT network '%s'\n",
12928 mUserData->s.strName.c_str(), strName.c_str()));
12929 mParent->i_natNetworkRefDec(strName);
12930 multilock.acquire();
12931 }
12932 }
12933 }
12934 }
12935
12936 /*
12937 * An expected uninitialization can come only from #i_checkForDeath().
12938 * Otherwise it means that something's gone really wrong (for example,
12939 * the Session implementation has released the VirtualBox reference
12940 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12941 * etc). However, it's also possible, that the client releases the IPC
12942 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12943 * but the VirtualBox release event comes first to the server process.
12944 * This case is practically possible, so we should not assert on an
12945 * unexpected uninit, just log a warning.
12946 */
12947
12948 if (aReason == Uninit::Unexpected)
12949 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12950
12951 if (aReason != Uninit::Normal)
12952 {
12953 mData->mSession.mDirectControl.setNull();
12954 }
12955 else
12956 {
12957 /* this must be null here (see #OnSessionEnd()) */
12958 Assert(mData->mSession.mDirectControl.isNull());
12959 Assert(mData->mSession.mState == SessionState_Unlocking);
12960 Assert(!mData->mSession.mProgress.isNull());
12961 }
12962 if (mData->mSession.mProgress)
12963 {
12964 if (aReason == Uninit::Normal)
12965 mData->mSession.mProgress->i_notifyComplete(S_OK);
12966 else
12967 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12968 COM_IIDOF(ISession),
12969 getComponentName(),
12970 tr("The VM session was aborted"));
12971 mData->mSession.mProgress.setNull();
12972 }
12973
12974 if (mConsoleTaskData.mProgress)
12975 {
12976 Assert(aReason == Uninit::Abnormal);
12977 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12978 COM_IIDOF(ISession),
12979 getComponentName(),
12980 tr("The VM session was aborted"));
12981 mConsoleTaskData.mProgress.setNull();
12982 }
12983
12984 /* remove the association between the peer machine and this session machine */
12985 Assert( (SessionMachine*)mData->mSession.mMachine == this
12986 || aReason == Uninit::Unexpected);
12987
12988 /* reset the rest of session data */
12989 mData->mSession.mLockType = LockType_Null;
12990 mData->mSession.mMachine.setNull();
12991 mData->mSession.mState = SessionState_Unlocked;
12992 mData->mSession.mName.setNull();
12993
12994 /* destroy the machine client token before leaving the exclusive lock */
12995 if (mClientToken)
12996 {
12997 delete mClientToken;
12998 mClientToken = NULL;
12999 }
13000
13001 /* fire an event */
13002 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13003
13004 uninitDataAndChildObjects();
13005
13006 /* free the essential data structure last */
13007 mData.free();
13008
13009 /* release the exclusive lock before setting the below two to NULL */
13010 multilock.release();
13011
13012 unconst(mParent) = NULL;
13013 unconst(mPeer) = NULL;
13014
13015 AuthLibUnload(&mAuthLibCtx);
13016
13017 LogFlowThisFuncLeave();
13018}
13019
13020// util::Lockable interface
13021////////////////////////////////////////////////////////////////////////////////
13022
13023/**
13024 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13025 * with the primary Machine instance (mPeer).
13026 */
13027RWLockHandle *SessionMachine::lockHandle() const
13028{
13029 AssertReturn(mPeer != NULL, NULL);
13030 return mPeer->lockHandle();
13031}
13032
13033// IInternalMachineControl methods
13034////////////////////////////////////////////////////////////////////////////////
13035
13036/**
13037 * Passes collected guest statistics to performance collector object
13038 */
13039HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13040 ULONG aCpuKernel, ULONG aCpuIdle,
13041 ULONG aMemTotal, ULONG aMemFree,
13042 ULONG aMemBalloon, ULONG aMemShared,
13043 ULONG aMemCache, ULONG aPageTotal,
13044 ULONG aAllocVMM, ULONG aFreeVMM,
13045 ULONG aBalloonedVMM, ULONG aSharedVMM,
13046 ULONG aVmNetRx, ULONG aVmNetTx)
13047{
13048#ifdef VBOX_WITH_RESOURCE_USAGE_API
13049 if (mCollectorGuest)
13050 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13051 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13052 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13053 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13054
13055 return S_OK;
13056#else
13057 NOREF(aValidStats);
13058 NOREF(aCpuUser);
13059 NOREF(aCpuKernel);
13060 NOREF(aCpuIdle);
13061 NOREF(aMemTotal);
13062 NOREF(aMemFree);
13063 NOREF(aMemBalloon);
13064 NOREF(aMemShared);
13065 NOREF(aMemCache);
13066 NOREF(aPageTotal);
13067 NOREF(aAllocVMM);
13068 NOREF(aFreeVMM);
13069 NOREF(aBalloonedVMM);
13070 NOREF(aSharedVMM);
13071 NOREF(aVmNetRx);
13072 NOREF(aVmNetTx);
13073 return E_NOTIMPL;
13074#endif
13075}
13076
13077////////////////////////////////////////////////////////////////////////////////
13078//
13079// SessionMachine task records
13080//
13081////////////////////////////////////////////////////////////////////////////////
13082
13083/**
13084 * Task record for saving the machine state.
13085 */
13086class SessionMachine::SaveStateTask
13087 : public Machine::Task
13088{
13089public:
13090 SaveStateTask(SessionMachine *m,
13091 Progress *p,
13092 const Utf8Str &t,
13093 Reason_T enmReason,
13094 const Utf8Str &strStateFilePath)
13095 : Task(m, p, t),
13096 m_enmReason(enmReason),
13097 m_strStateFilePath(strStateFilePath)
13098 {}
13099
13100private:
13101 void handler()
13102 {
13103 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13104 }
13105
13106 Reason_T m_enmReason;
13107 Utf8Str m_strStateFilePath;
13108
13109 friend class SessionMachine;
13110};
13111
13112/**
13113 * Task thread implementation for SessionMachine::SaveState(), called from
13114 * SessionMachine::taskHandler().
13115 *
13116 * @note Locks this object for writing.
13117 *
13118 * @param task
13119 * @return
13120 */
13121void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13122{
13123 LogFlowThisFuncEnter();
13124
13125 AutoCaller autoCaller(this);
13126 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13127 if (FAILED(autoCaller.rc()))
13128 {
13129 /* we might have been uninitialized because the session was accidentally
13130 * closed by the client, so don't assert */
13131 HRESULT rc = setError(E_FAIL,
13132 tr("The session has been accidentally closed"));
13133 task.m_pProgress->i_notifyComplete(rc);
13134 LogFlowThisFuncLeave();
13135 return;
13136 }
13137
13138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13139
13140 HRESULT rc = S_OK;
13141
13142 try
13143 {
13144 ComPtr<IInternalSessionControl> directControl;
13145 if (mData->mSession.mLockType == LockType_VM)
13146 directControl = mData->mSession.mDirectControl;
13147 if (directControl.isNull())
13148 throw setError(VBOX_E_INVALID_VM_STATE,
13149 tr("Trying to save state without a running VM"));
13150 alock.release();
13151 BOOL fSuspendedBySave;
13152 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13153 Assert(!fSuspendedBySave);
13154 alock.acquire();
13155
13156 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13157 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13158 throw E_FAIL);
13159
13160 if (SUCCEEDED(rc))
13161 {
13162 mSSData->strStateFilePath = task.m_strStateFilePath;
13163
13164 /* save all VM settings */
13165 rc = i_saveSettings(NULL);
13166 // no need to check whether VirtualBox.xml needs saving also since
13167 // we can't have a name change pending at this point
13168 }
13169 else
13170 {
13171 // On failure, set the state to the state we had at the beginning.
13172 i_setMachineState(task.m_machineStateBackup);
13173 i_updateMachineStateOnClient();
13174
13175 // Delete the saved state file (might have been already created).
13176 // No need to check whether this is shared with a snapshot here
13177 // because we certainly created a fresh saved state file here.
13178 RTFileDelete(task.m_strStateFilePath.c_str());
13179 }
13180 }
13181 catch (HRESULT aRC) { rc = aRC; }
13182
13183 task.m_pProgress->i_notifyComplete(rc);
13184
13185 LogFlowThisFuncLeave();
13186}
13187
13188/**
13189 * @note Locks this object for writing.
13190 */
13191HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13192{
13193 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13194}
13195
13196HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13197{
13198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13199
13200 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13201 if (FAILED(rc)) return rc;
13202
13203 if ( mData->mMachineState != MachineState_Running
13204 && mData->mMachineState != MachineState_Paused
13205 )
13206 return setError(VBOX_E_INVALID_VM_STATE,
13207 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13208 Global::stringifyMachineState(mData->mMachineState));
13209
13210 ComObjPtr<Progress> pProgress;
13211 pProgress.createObject();
13212 rc = pProgress->init(i_getVirtualBox(),
13213 static_cast<IMachine *>(this) /* aInitiator */,
13214 tr("Saving the execution state of the virtual machine"),
13215 FALSE /* aCancelable */);
13216 if (FAILED(rc))
13217 return rc;
13218
13219 Utf8Str strStateFilePath;
13220 i_composeSavedStateFilename(strStateFilePath);
13221
13222 /* create and start the task on a separate thread (note that it will not
13223 * start working until we release alock) */
13224 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13225 rc = pTask->createThread();
13226 if (FAILED(rc))
13227 return rc;
13228
13229 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13230 i_setMachineState(MachineState_Saving);
13231 i_updateMachineStateOnClient();
13232
13233 pProgress.queryInterfaceTo(aProgress.asOutParam());
13234
13235 return S_OK;
13236}
13237
13238/**
13239 * @note Locks this object for writing.
13240 */
13241HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13242{
13243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13244
13245 HRESULT rc = i_checkStateDependency(MutableStateDep);
13246 if (FAILED(rc)) return rc;
13247
13248 if ( mData->mMachineState != MachineState_PoweredOff
13249 && mData->mMachineState != MachineState_Teleported
13250 && mData->mMachineState != MachineState_Aborted
13251 )
13252 return setError(VBOX_E_INVALID_VM_STATE,
13253 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13254 Global::stringifyMachineState(mData->mMachineState));
13255
13256 com::Utf8Str stateFilePathFull;
13257 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13258 if (RT_FAILURE(vrc))
13259 return setError(VBOX_E_FILE_ERROR,
13260 tr("Invalid saved state file path '%s' (%Rrc)"),
13261 aSavedStateFile.c_str(),
13262 vrc);
13263
13264 mSSData->strStateFilePath = stateFilePathFull;
13265
13266 /* The below i_setMachineState() will detect the state transition and will
13267 * update the settings file */
13268
13269 return i_setMachineState(MachineState_Saved);
13270}
13271
13272/**
13273 * @note Locks this object for writing.
13274 */
13275HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13276{
13277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13278
13279 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13280 if (FAILED(rc)) return rc;
13281
13282 if (mData->mMachineState != MachineState_Saved)
13283 return setError(VBOX_E_INVALID_VM_STATE,
13284 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13285 Global::stringifyMachineState(mData->mMachineState));
13286
13287 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13288
13289 /*
13290 * Saved -> PoweredOff transition will be detected in the SessionMachine
13291 * and properly handled.
13292 */
13293 rc = i_setMachineState(MachineState_PoweredOff);
13294 return rc;
13295}
13296
13297
13298/**
13299 * @note Locks the same as #i_setMachineState() does.
13300 */
13301HRESULT SessionMachine::updateState(MachineState_T aState)
13302{
13303 return i_setMachineState(aState);
13304}
13305
13306/**
13307 * @note Locks this object for writing.
13308 */
13309HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13310{
13311 IProgress *pProgress(aProgress);
13312
13313 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13314
13315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13316
13317 if (mData->mSession.mState != SessionState_Locked)
13318 return VBOX_E_INVALID_OBJECT_STATE;
13319
13320 if (!mData->mSession.mProgress.isNull())
13321 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13322
13323 /* If we didn't reference the NAT network service yet, add a reference to
13324 * force a start */
13325 if (miNATNetworksStarted < 1)
13326 {
13327 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13328 {
13329 BOOL enabled;
13330 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13331 if ( FAILED(hrc)
13332 || !enabled)
13333 continue;
13334
13335 NetworkAttachmentType_T type;
13336 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13337 if ( SUCCEEDED(hrc)
13338 && type == NetworkAttachmentType_NATNetwork)
13339 {
13340 Bstr name;
13341 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13342 if (SUCCEEDED(hrc))
13343 {
13344 Utf8Str strName(name);
13345 LogRel(("VM '%s' starts using NAT network '%s'\n",
13346 mUserData->s.strName.c_str(), strName.c_str()));
13347 mPeer->lockHandle()->unlockWrite();
13348 mParent->i_natNetworkRefInc(strName);
13349#ifdef RT_LOCK_STRICT
13350 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13351#else
13352 mPeer->lockHandle()->lockWrite();
13353#endif
13354 }
13355 }
13356 }
13357 miNATNetworksStarted++;
13358 }
13359
13360 LogFlowThisFunc(("returns S_OK.\n"));
13361 return S_OK;
13362}
13363
13364/**
13365 * @note Locks this object for writing.
13366 */
13367HRESULT SessionMachine::endPowerUp(LONG aResult)
13368{
13369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13370
13371 if (mData->mSession.mState != SessionState_Locked)
13372 return VBOX_E_INVALID_OBJECT_STATE;
13373
13374 /* Finalize the LaunchVMProcess progress object. */
13375 if (mData->mSession.mProgress)
13376 {
13377 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13378 mData->mSession.mProgress.setNull();
13379 }
13380
13381 if (SUCCEEDED((HRESULT)aResult))
13382 {
13383#ifdef VBOX_WITH_RESOURCE_USAGE_API
13384 /* The VM has been powered up successfully, so it makes sense
13385 * now to offer the performance metrics for a running machine
13386 * object. Doing it earlier wouldn't be safe. */
13387 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13388 mData->mSession.mPID);
13389#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13390 }
13391
13392 return S_OK;
13393}
13394
13395/**
13396 * @note Locks this object for writing.
13397 */
13398HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13399{
13400 LogFlowThisFuncEnter();
13401
13402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13403
13404 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13405 E_FAIL);
13406
13407 /* create a progress object to track operation completion */
13408 ComObjPtr<Progress> pProgress;
13409 pProgress.createObject();
13410 pProgress->init(i_getVirtualBox(),
13411 static_cast<IMachine *>(this) /* aInitiator */,
13412 tr("Stopping the virtual machine"),
13413 FALSE /* aCancelable */);
13414
13415 /* fill in the console task data */
13416 mConsoleTaskData.mLastState = mData->mMachineState;
13417 mConsoleTaskData.mProgress = pProgress;
13418
13419 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13420 i_setMachineState(MachineState_Stopping);
13421
13422 pProgress.queryInterfaceTo(aProgress.asOutParam());
13423
13424 return S_OK;
13425}
13426
13427/**
13428 * @note Locks this object for writing.
13429 */
13430HRESULT SessionMachine::endPoweringDown(LONG aResult,
13431 const com::Utf8Str &aErrMsg)
13432{
13433 LogFlowThisFuncEnter();
13434
13435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13436
13437 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13438 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13439 && mConsoleTaskData.mLastState != MachineState_Null,
13440 E_FAIL);
13441
13442 /*
13443 * On failure, set the state to the state we had when BeginPoweringDown()
13444 * was called (this is expected by Console::PowerDown() and the associated
13445 * task). On success the VM process already changed the state to
13446 * MachineState_PoweredOff, so no need to do anything.
13447 */
13448 if (FAILED(aResult))
13449 i_setMachineState(mConsoleTaskData.mLastState);
13450
13451 /* notify the progress object about operation completion */
13452 Assert(mConsoleTaskData.mProgress);
13453 if (SUCCEEDED(aResult))
13454 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13455 else
13456 {
13457 if (aErrMsg.length())
13458 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13459 COM_IIDOF(ISession),
13460 getComponentName(),
13461 aErrMsg.c_str());
13462 else
13463 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13464 }
13465
13466 /* clear out the temporary saved state data */
13467 mConsoleTaskData.mLastState = MachineState_Null;
13468 mConsoleTaskData.mProgress.setNull();
13469
13470 LogFlowThisFuncLeave();
13471 return S_OK;
13472}
13473
13474
13475/**
13476 * Goes through the USB filters of the given machine to see if the given
13477 * device matches any filter or not.
13478 *
13479 * @note Locks the same as USBController::hasMatchingFilter() does.
13480 */
13481HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13482 BOOL *aMatched,
13483 ULONG *aMaskedInterfaces)
13484{
13485 LogFlowThisFunc(("\n"));
13486
13487#ifdef VBOX_WITH_USB
13488 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13489#else
13490 NOREF(aDevice);
13491 NOREF(aMaskedInterfaces);
13492 *aMatched = FALSE;
13493#endif
13494
13495 return S_OK;
13496}
13497
13498/**
13499 * @note Locks the same as Host::captureUSBDevice() does.
13500 */
13501HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13502{
13503 LogFlowThisFunc(("\n"));
13504
13505#ifdef VBOX_WITH_USB
13506 /* if captureDeviceForVM() fails, it must have set extended error info */
13507 clearError();
13508 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13509 if (FAILED(rc)) return rc;
13510
13511 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13512 AssertReturn(service, E_FAIL);
13513 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13514#else
13515 NOREF(aId);
13516 return E_NOTIMPL;
13517#endif
13518}
13519
13520/**
13521 * @note Locks the same as Host::detachUSBDevice() does.
13522 */
13523HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13524 BOOL aDone)
13525{
13526 LogFlowThisFunc(("\n"));
13527
13528#ifdef VBOX_WITH_USB
13529 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13530 AssertReturn(service, E_FAIL);
13531 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13532#else
13533 NOREF(aId);
13534 NOREF(aDone);
13535 return E_NOTIMPL;
13536#endif
13537}
13538
13539/**
13540 * Inserts all machine filters to the USB proxy service and then calls
13541 * Host::autoCaptureUSBDevices().
13542 *
13543 * Called by Console from the VM process upon VM startup.
13544 *
13545 * @note Locks what called methods lock.
13546 */
13547HRESULT SessionMachine::autoCaptureUSBDevices()
13548{
13549 LogFlowThisFunc(("\n"));
13550
13551#ifdef VBOX_WITH_USB
13552 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13553 AssertComRC(rc);
13554 NOREF(rc);
13555
13556 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13557 AssertReturn(service, E_FAIL);
13558 return service->autoCaptureDevicesForVM(this);
13559#else
13560 return S_OK;
13561#endif
13562}
13563
13564/**
13565 * Removes all machine filters from the USB proxy service and then calls
13566 * Host::detachAllUSBDevices().
13567 *
13568 * Called by Console from the VM process upon normal VM termination or by
13569 * SessionMachine::uninit() upon abnormal VM termination (from under the
13570 * Machine/SessionMachine lock).
13571 *
13572 * @note Locks what called methods lock.
13573 */
13574HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13575{
13576 LogFlowThisFunc(("\n"));
13577
13578#ifdef VBOX_WITH_USB
13579 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13580 AssertComRC(rc);
13581 NOREF(rc);
13582
13583 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13584 AssertReturn(service, E_FAIL);
13585 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13586#else
13587 NOREF(aDone);
13588 return S_OK;
13589#endif
13590}
13591
13592/**
13593 * @note Locks this object for writing.
13594 */
13595HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13596 ComPtr<IProgress> &aProgress)
13597{
13598 LogFlowThisFuncEnter();
13599
13600 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13601 /*
13602 * We don't assert below because it might happen that a non-direct session
13603 * informs us it is closed right after we've been uninitialized -- it's ok.
13604 */
13605
13606 /* get IInternalSessionControl interface */
13607 ComPtr<IInternalSessionControl> control(aSession);
13608
13609 ComAssertRet(!control.isNull(), E_INVALIDARG);
13610
13611 /* Creating a Progress object requires the VirtualBox lock, and
13612 * thus locking it here is required by the lock order rules. */
13613 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13614
13615 if (control == mData->mSession.mDirectControl)
13616 {
13617 /* The direct session is being normally closed by the client process
13618 * ----------------------------------------------------------------- */
13619
13620 /* go to the closing state (essential for all open*Session() calls and
13621 * for #i_checkForDeath()) */
13622 Assert(mData->mSession.mState == SessionState_Locked);
13623 mData->mSession.mState = SessionState_Unlocking;
13624
13625 /* set direct control to NULL to release the remote instance */
13626 mData->mSession.mDirectControl.setNull();
13627 LogFlowThisFunc(("Direct control is set to NULL\n"));
13628
13629 if (mData->mSession.mProgress)
13630 {
13631 /* finalize the progress, someone might wait if a frontend
13632 * closes the session before powering on the VM. */
13633 mData->mSession.mProgress->notifyComplete(E_FAIL,
13634 COM_IIDOF(ISession),
13635 getComponentName(),
13636 tr("The VM session was closed before any attempt to power it on"));
13637 mData->mSession.mProgress.setNull();
13638 }
13639
13640 /* Create the progress object the client will use to wait until
13641 * #i_checkForDeath() is called to uninitialize this session object after
13642 * it releases the IPC semaphore.
13643 * Note! Because we're "reusing" mProgress here, this must be a proxy
13644 * object just like for LaunchVMProcess. */
13645 Assert(mData->mSession.mProgress.isNull());
13646 ComObjPtr<ProgressProxy> progress;
13647 progress.createObject();
13648 ComPtr<IUnknown> pPeer(mPeer);
13649 progress->init(mParent, pPeer,
13650 Bstr(tr("Closing session")).raw(),
13651 FALSE /* aCancelable */);
13652 progress.queryInterfaceTo(aProgress.asOutParam());
13653 mData->mSession.mProgress = progress;
13654 }
13655 else
13656 {
13657 /* the remote session is being normally closed */
13658 bool found = false;
13659 for (Data::Session::RemoteControlList::iterator
13660 it = mData->mSession.mRemoteControls.begin();
13661 it != mData->mSession.mRemoteControls.end();
13662 ++it)
13663 {
13664 if (control == *it)
13665 {
13666 found = true;
13667 // This MUST be erase(it), not remove(*it) as the latter
13668 // triggers a very nasty use after free due to the place where
13669 // the value "lives".
13670 mData->mSession.mRemoteControls.erase(it);
13671 break;
13672 }
13673 }
13674 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13675 E_INVALIDARG);
13676 }
13677
13678 /* signal the client watcher thread, because the client is going away */
13679 mParent->i_updateClientWatcher();
13680
13681 LogFlowThisFuncLeave();
13682 return S_OK;
13683}
13684
13685HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13686 std::vector<com::Utf8Str> &aValues,
13687 std::vector<LONG64> &aTimestamps,
13688 std::vector<com::Utf8Str> &aFlags)
13689{
13690 LogFlowThisFunc(("\n"));
13691
13692#ifdef VBOX_WITH_GUEST_PROPS
13693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13694
13695 size_t cEntries = mHWData->mGuestProperties.size();
13696 aNames.resize(cEntries);
13697 aValues.resize(cEntries);
13698 aTimestamps.resize(cEntries);
13699 aFlags.resize(cEntries);
13700
13701 size_t i = 0;
13702 for (HWData::GuestPropertyMap::const_iterator
13703 it = mHWData->mGuestProperties.begin();
13704 it != mHWData->mGuestProperties.end();
13705 ++it, ++i)
13706 {
13707 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13708 aNames[i] = it->first;
13709 aValues[i] = it->second.strValue;
13710 aTimestamps[i] = it->second.mTimestamp;
13711
13712 /* If it is NULL, keep it NULL. */
13713 if (it->second.mFlags)
13714 {
13715 GuestPropWriteFlags(it->second.mFlags, szFlags);
13716 aFlags[i] = szFlags;
13717 }
13718 else
13719 aFlags[i] = "";
13720 }
13721 return S_OK;
13722#else
13723 ReturnComNotImplemented();
13724#endif
13725}
13726
13727HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13728 const com::Utf8Str &aValue,
13729 LONG64 aTimestamp,
13730 const com::Utf8Str &aFlags)
13731{
13732 LogFlowThisFunc(("\n"));
13733
13734#ifdef VBOX_WITH_GUEST_PROPS
13735 try
13736 {
13737 /*
13738 * Convert input up front.
13739 */
13740 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13741 if (aFlags.length())
13742 {
13743 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13744 AssertRCReturn(vrc, E_INVALIDARG);
13745 }
13746
13747 /*
13748 * Now grab the object lock, validate the state and do the update.
13749 */
13750
13751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13752
13753 if (!Global::IsOnline(mData->mMachineState))
13754 {
13755 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13756 VBOX_E_INVALID_VM_STATE);
13757 }
13758
13759 i_setModified(IsModified_MachineData);
13760 mHWData.backup();
13761
13762 bool fDelete = !aValue.length();
13763 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13764 if (it != mHWData->mGuestProperties.end())
13765 {
13766 if (!fDelete)
13767 {
13768 it->second.strValue = aValue;
13769 it->second.mTimestamp = aTimestamp;
13770 it->second.mFlags = fFlags;
13771 }
13772 else
13773 mHWData->mGuestProperties.erase(it);
13774
13775 mData->mGuestPropertiesModified = TRUE;
13776 }
13777 else if (!fDelete)
13778 {
13779 HWData::GuestProperty prop;
13780 prop.strValue = aValue;
13781 prop.mTimestamp = aTimestamp;
13782 prop.mFlags = fFlags;
13783
13784 mHWData->mGuestProperties[aName] = prop;
13785 mData->mGuestPropertiesModified = TRUE;
13786 }
13787
13788 alock.release();
13789
13790 mParent->i_onGuestPropertyChange(mData->mUuid,
13791 Bstr(aName).raw(),
13792 Bstr(aValue).raw(),
13793 Bstr(aFlags).raw());
13794 }
13795 catch (...)
13796 {
13797 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13798 }
13799 return S_OK;
13800#else
13801 ReturnComNotImplemented();
13802#endif
13803}
13804
13805
13806HRESULT SessionMachine::lockMedia()
13807{
13808 AutoMultiWriteLock2 alock(this->lockHandle(),
13809 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13810
13811 AssertReturn( mData->mMachineState == MachineState_Starting
13812 || mData->mMachineState == MachineState_Restoring
13813 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13814
13815 clearError();
13816 alock.release();
13817 return i_lockMedia();
13818}
13819
13820HRESULT SessionMachine::unlockMedia()
13821{
13822 HRESULT hrc = i_unlockMedia();
13823 return hrc;
13824}
13825
13826HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13827 ComPtr<IMediumAttachment> &aNewAttachment)
13828{
13829 // request the host lock first, since might be calling Host methods for getting host drives;
13830 // next, protect the media tree all the while we're in here, as well as our member variables
13831 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13832 this->lockHandle(),
13833 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13834
13835 IMediumAttachment *iAttach = aAttachment;
13836 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13837
13838 Utf8Str ctrlName;
13839 LONG lPort;
13840 LONG lDevice;
13841 bool fTempEject;
13842 {
13843 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13844
13845 /* Need to query the details first, as the IMediumAttachment reference
13846 * might be to the original settings, which we are going to change. */
13847 ctrlName = pAttach->i_getControllerName();
13848 lPort = pAttach->i_getPort();
13849 lDevice = pAttach->i_getDevice();
13850 fTempEject = pAttach->i_getTempEject();
13851 }
13852
13853 if (!fTempEject)
13854 {
13855 /* Remember previously mounted medium. The medium before taking the
13856 * backup is not necessarily the same thing. */
13857 ComObjPtr<Medium> oldmedium;
13858 oldmedium = pAttach->i_getMedium();
13859
13860 i_setModified(IsModified_Storage);
13861 mMediumAttachments.backup();
13862
13863 // The backup operation makes the pAttach reference point to the
13864 // old settings. Re-get the correct reference.
13865 pAttach = i_findAttachment(*mMediumAttachments.data(),
13866 ctrlName,
13867 lPort,
13868 lDevice);
13869
13870 {
13871 AutoCaller autoAttachCaller(this);
13872 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13873
13874 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13875 if (!oldmedium.isNull())
13876 oldmedium->i_removeBackReference(mData->mUuid);
13877
13878 pAttach->i_updateMedium(NULL);
13879 pAttach->i_updateEjected();
13880 }
13881
13882 i_setModified(IsModified_Storage);
13883 }
13884 else
13885 {
13886 {
13887 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13888 pAttach->i_updateEjected();
13889 }
13890 }
13891
13892 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13893
13894 return S_OK;
13895}
13896
13897HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13898 com::Utf8Str &aResult)
13899{
13900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13901
13902 HRESULT hr = S_OK;
13903
13904 if (!mAuthLibCtx.hAuthLibrary)
13905 {
13906 /* Load the external authentication library. */
13907 Bstr authLibrary;
13908 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13909
13910 Utf8Str filename = authLibrary;
13911
13912 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13913 if (RT_FAILURE(rc))
13914 {
13915 hr = setError(E_FAIL,
13916 tr("Could not load the external authentication library '%s' (%Rrc)"),
13917 filename.c_str(), rc);
13918 }
13919 }
13920
13921 /* The auth library might need the machine lock. */
13922 alock.release();
13923
13924 if (FAILED(hr))
13925 return hr;
13926
13927 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13928 {
13929 enum VRDEAuthParams
13930 {
13931 parmUuid = 1,
13932 parmGuestJudgement,
13933 parmUser,
13934 parmPassword,
13935 parmDomain,
13936 parmClientId
13937 };
13938
13939 AuthResult result = AuthResultAccessDenied;
13940
13941 Guid uuid(aAuthParams[parmUuid]);
13942 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13943 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13944
13945 result = AuthLibAuthenticate(&mAuthLibCtx,
13946 uuid.raw(), guestJudgement,
13947 aAuthParams[parmUser].c_str(),
13948 aAuthParams[parmPassword].c_str(),
13949 aAuthParams[parmDomain].c_str(),
13950 u32ClientId);
13951
13952 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13953 size_t cbPassword = aAuthParams[parmPassword].length();
13954 if (cbPassword)
13955 {
13956 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13957 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13958 }
13959
13960 if (result == AuthResultAccessGranted)
13961 aResult = "granted";
13962 else
13963 aResult = "denied";
13964
13965 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13966 aAuthParams[parmUser].c_str(), aResult.c_str()));
13967 }
13968 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13969 {
13970 enum VRDEAuthDisconnectParams
13971 {
13972 parmUuid = 1,
13973 parmClientId
13974 };
13975
13976 Guid uuid(aAuthParams[parmUuid]);
13977 uint32_t u32ClientId = 0;
13978 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13979 }
13980 else
13981 {
13982 hr = E_INVALIDARG;
13983 }
13984
13985 return hr;
13986}
13987
13988// public methods only for internal purposes
13989/////////////////////////////////////////////////////////////////////////////
13990
13991#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13992/**
13993 * Called from the client watcher thread to check for expected or unexpected
13994 * death of the client process that has a direct session to this machine.
13995 *
13996 * On Win32 and on OS/2, this method is called only when we've got the
13997 * mutex (i.e. the client has either died or terminated normally) so it always
13998 * returns @c true (the client is terminated, the session machine is
13999 * uninitialized).
14000 *
14001 * On other platforms, the method returns @c true if the client process has
14002 * terminated normally or abnormally and the session machine was uninitialized,
14003 * and @c false if the client process is still alive.
14004 *
14005 * @note Locks this object for writing.
14006 */
14007bool SessionMachine::i_checkForDeath()
14008{
14009 Uninit::Reason reason;
14010 bool terminated = false;
14011
14012 /* Enclose autoCaller with a block because calling uninit() from under it
14013 * will deadlock. */
14014 {
14015 AutoCaller autoCaller(this);
14016 if (!autoCaller.isOk())
14017 {
14018 /* return true if not ready, to cause the client watcher to exclude
14019 * the corresponding session from watching */
14020 LogFlowThisFunc(("Already uninitialized!\n"));
14021 return true;
14022 }
14023
14024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14025
14026 /* Determine the reason of death: if the session state is Closing here,
14027 * everything is fine. Otherwise it means that the client did not call
14028 * OnSessionEnd() before it released the IPC semaphore. This may happen
14029 * either because the client process has abnormally terminated, or
14030 * because it simply forgot to call ISession::Close() before exiting. We
14031 * threat the latter also as an abnormal termination (see
14032 * Session::uninit() for details). */
14033 reason = mData->mSession.mState == SessionState_Unlocking ?
14034 Uninit::Normal :
14035 Uninit::Abnormal;
14036
14037 if (mClientToken)
14038 terminated = mClientToken->release();
14039 } /* AutoCaller block */
14040
14041 if (terminated)
14042 uninit(reason);
14043
14044 return terminated;
14045}
14046
14047void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14048{
14049 LogFlowThisFunc(("\n"));
14050
14051 strTokenId.setNull();
14052
14053 AutoCaller autoCaller(this);
14054 AssertComRCReturnVoid(autoCaller.rc());
14055
14056 Assert(mClientToken);
14057 if (mClientToken)
14058 mClientToken->getId(strTokenId);
14059}
14060#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14061IToken *SessionMachine::i_getToken()
14062{
14063 LogFlowThisFunc(("\n"));
14064
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.rc(), NULL);
14067
14068 Assert(mClientToken);
14069 if (mClientToken)
14070 return mClientToken->getToken();
14071 else
14072 return NULL;
14073}
14074#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14075
14076Machine::ClientToken *SessionMachine::i_getClientToken()
14077{
14078 LogFlowThisFunc(("\n"));
14079
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(), NULL);
14082
14083 return mClientToken;
14084}
14085
14086
14087/**
14088 * @note Locks this object for reading.
14089 */
14090HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
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->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14115 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14116 IN_BSTR aGuestIp, LONG aGuestPort)
14117{
14118 LogFlowThisFunc(("\n"));
14119
14120 AutoCaller autoCaller(this);
14121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14122
14123 ComPtr<IInternalSessionControl> directControl;
14124 {
14125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14126 if (mData->mSession.mLockType == LockType_VM)
14127 directControl = mData->mSession.mDirectControl;
14128 }
14129
14130 /* ignore notifications sent after #OnSessionEnd() is called */
14131 if (!directControl)
14132 return S_OK;
14133 /*
14134 * instead acting like callback we ask IVirtualBox deliver corresponding event
14135 */
14136
14137 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14138 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14139 return S_OK;
14140}
14141
14142/**
14143 * @note Locks this object for reading.
14144 */
14145HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14146{
14147 LogFlowThisFunc(("\n"));
14148
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14151
14152 ComPtr<IInternalSessionControl> directControl;
14153 {
14154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14155 if (mData->mSession.mLockType == LockType_VM)
14156 directControl = mData->mSession.mDirectControl;
14157 }
14158
14159 /* ignore notifications sent after #OnSessionEnd() is called */
14160 if (!directControl)
14161 return S_OK;
14162
14163 return directControl->OnAudioAdapterChange(audioAdapter);
14164}
14165
14166/**
14167 * @note Locks this object for reading.
14168 */
14169HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14170{
14171 LogFlowThisFunc(("\n"));
14172
14173 AutoCaller autoCaller(this);
14174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14175
14176 ComPtr<IInternalSessionControl> directControl;
14177 {
14178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14179 if (mData->mSession.mLockType == LockType_VM)
14180 directControl = mData->mSession.mDirectControl;
14181 }
14182
14183 /* ignore notifications sent after #OnSessionEnd() is called */
14184 if (!directControl)
14185 return S_OK;
14186
14187 return directControl->OnSerialPortChange(serialPort);
14188}
14189
14190/**
14191 * @note Locks this object for reading.
14192 */
14193HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14194{
14195 LogFlowThisFunc(("\n"));
14196
14197 AutoCaller autoCaller(this);
14198 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14199
14200 ComPtr<IInternalSessionControl> directControl;
14201 {
14202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14203 if (mData->mSession.mLockType == LockType_VM)
14204 directControl = mData->mSession.mDirectControl;
14205 }
14206
14207 /* ignore notifications sent after #OnSessionEnd() is called */
14208 if (!directControl)
14209 return S_OK;
14210
14211 return directControl->OnParallelPortChange(parallelPort);
14212}
14213
14214/**
14215 * @note Locks this object for reading.
14216 */
14217HRESULT SessionMachine::i_onStorageControllerChange()
14218{
14219 LogFlowThisFunc(("\n"));
14220
14221 AutoCaller autoCaller(this);
14222 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14223
14224 ComPtr<IInternalSessionControl> directControl;
14225 {
14226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14227 if (mData->mSession.mLockType == LockType_VM)
14228 directControl = mData->mSession.mDirectControl;
14229 }
14230
14231 /* ignore notifications sent after #OnSessionEnd() is called */
14232 if (!directControl)
14233 return S_OK;
14234
14235 return directControl->OnStorageControllerChange();
14236}
14237
14238/**
14239 * @note Locks this object for reading.
14240 */
14241HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14242{
14243 LogFlowThisFunc(("\n"));
14244
14245 AutoCaller autoCaller(this);
14246 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14247
14248 ComPtr<IInternalSessionControl> directControl;
14249 {
14250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14251 if (mData->mSession.mLockType == LockType_VM)
14252 directControl = mData->mSession.mDirectControl;
14253 }
14254
14255 /* ignore notifications sent after #OnSessionEnd() is called */
14256 if (!directControl)
14257 return S_OK;
14258
14259 return directControl->OnMediumChange(aAttachment, aForce);
14260}
14261
14262/**
14263 * @note Locks this object for reading.
14264 */
14265HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14266{
14267 LogFlowThisFunc(("\n"));
14268
14269 AutoCaller autoCaller(this);
14270 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14271
14272 ComPtr<IInternalSessionControl> directControl;
14273 {
14274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14275 if (mData->mSession.mLockType == LockType_VM)
14276 directControl = mData->mSession.mDirectControl;
14277 }
14278
14279 /* ignore notifications sent after #OnSessionEnd() is called */
14280 if (!directControl)
14281 return S_OK;
14282
14283 return directControl->OnCPUChange(aCPU, aRemove);
14284}
14285
14286HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14287{
14288 LogFlowThisFunc(("\n"));
14289
14290 AutoCaller autoCaller(this);
14291 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14292
14293 ComPtr<IInternalSessionControl> directControl;
14294 {
14295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14296 if (mData->mSession.mLockType == LockType_VM)
14297 directControl = mData->mSession.mDirectControl;
14298 }
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14305}
14306
14307/**
14308 * @note Locks this object for reading.
14309 */
14310HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14316
14317 ComPtr<IInternalSessionControl> directControl;
14318 {
14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14320 if (mData->mSession.mLockType == LockType_VM)
14321 directControl = mData->mSession.mDirectControl;
14322 }
14323
14324 /* ignore notifications sent after #OnSessionEnd() is called */
14325 if (!directControl)
14326 return S_OK;
14327
14328 return directControl->OnVRDEServerChange(aRestart);
14329}
14330
14331/**
14332 * @note Locks this object for reading.
14333 */
14334HRESULT SessionMachine::i_onVideoCaptureChange()
14335{
14336 LogFlowThisFunc(("\n"));
14337
14338 AutoCaller autoCaller(this);
14339 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14340
14341 ComPtr<IInternalSessionControl> directControl;
14342 {
14343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14344 if (mData->mSession.mLockType == LockType_VM)
14345 directControl = mData->mSession.mDirectControl;
14346 }
14347
14348 /* ignore notifications sent after #OnSessionEnd() is called */
14349 if (!directControl)
14350 return S_OK;
14351
14352 return directControl->OnVideoCaptureChange();
14353}
14354
14355/**
14356 * @note Locks this object for reading.
14357 */
14358HRESULT SessionMachine::i_onUSBControllerChange()
14359{
14360 LogFlowThisFunc(("\n"));
14361
14362 AutoCaller autoCaller(this);
14363 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14364
14365 ComPtr<IInternalSessionControl> directControl;
14366 {
14367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14368 if (mData->mSession.mLockType == LockType_VM)
14369 directControl = mData->mSession.mDirectControl;
14370 }
14371
14372 /* ignore notifications sent after #OnSessionEnd() is called */
14373 if (!directControl)
14374 return S_OK;
14375
14376 return directControl->OnUSBControllerChange();
14377}
14378
14379/**
14380 * @note Locks this object for reading.
14381 */
14382HRESULT SessionMachine::i_onSharedFolderChange()
14383{
14384 LogFlowThisFunc(("\n"));
14385
14386 AutoCaller autoCaller(this);
14387 AssertComRCReturnRC(autoCaller.rc());
14388
14389 ComPtr<IInternalSessionControl> directControl;
14390 {
14391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14392 if (mData->mSession.mLockType == LockType_VM)
14393 directControl = mData->mSession.mDirectControl;
14394 }
14395
14396 /* ignore notifications sent after #OnSessionEnd() is called */
14397 if (!directControl)
14398 return S_OK;
14399
14400 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14401}
14402
14403/**
14404 * @note Locks this object for reading.
14405 */
14406HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14407{
14408 LogFlowThisFunc(("\n"));
14409
14410 AutoCaller autoCaller(this);
14411 AssertComRCReturnRC(autoCaller.rc());
14412
14413 ComPtr<IInternalSessionControl> directControl;
14414 {
14415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14416 if (mData->mSession.mLockType == LockType_VM)
14417 directControl = mData->mSession.mDirectControl;
14418 }
14419
14420 /* ignore notifications sent after #OnSessionEnd() is called */
14421 if (!directControl)
14422 return S_OK;
14423
14424 return directControl->OnClipboardModeChange(aClipboardMode);
14425}
14426
14427/**
14428 * @note Locks this object for reading.
14429 */
14430HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14431{
14432 LogFlowThisFunc(("\n"));
14433
14434 AutoCaller autoCaller(this);
14435 AssertComRCReturnRC(autoCaller.rc());
14436
14437 ComPtr<IInternalSessionControl> directControl;
14438 {
14439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14440 if (mData->mSession.mLockType == LockType_VM)
14441 directControl = mData->mSession.mDirectControl;
14442 }
14443
14444 /* ignore notifications sent after #OnSessionEnd() is called */
14445 if (!directControl)
14446 return S_OK;
14447
14448 return directControl->OnDnDModeChange(aDnDMode);
14449}
14450
14451/**
14452 * @note Locks this object for reading.
14453 */
14454HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14455{
14456 LogFlowThisFunc(("\n"));
14457
14458 AutoCaller autoCaller(this);
14459 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14460
14461 ComPtr<IInternalSessionControl> directControl;
14462 {
14463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14464 if (mData->mSession.mLockType == LockType_VM)
14465 directControl = mData->mSession.mDirectControl;
14466 }
14467
14468 /* ignore notifications sent after #OnSessionEnd() is called */
14469 if (!directControl)
14470 return S_OK;
14471
14472 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14473}
14474
14475/**
14476 * @note Locks this object for reading.
14477 */
14478HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14479{
14480 LogFlowThisFunc(("\n"));
14481
14482 AutoCaller autoCaller(this);
14483 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14484
14485 ComPtr<IInternalSessionControl> directControl;
14486 {
14487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14488 if (mData->mSession.mLockType == LockType_VM)
14489 directControl = mData->mSession.mDirectControl;
14490 }
14491
14492 /* ignore notifications sent after #OnSessionEnd() is called */
14493 if (!directControl)
14494 return S_OK;
14495
14496 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14497}
14498
14499/**
14500 * Returns @c true if this machine's USB controller reports it has a matching
14501 * filter for the given USB device and @c false otherwise.
14502 *
14503 * @note locks this object for reading.
14504 */
14505bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14506{
14507 AutoCaller autoCaller(this);
14508 /* silently return if not ready -- this method may be called after the
14509 * direct machine session has been called */
14510 if (!autoCaller.isOk())
14511 return false;
14512
14513#ifdef VBOX_WITH_USB
14514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14515
14516 switch (mData->mMachineState)
14517 {
14518 case MachineState_Starting:
14519 case MachineState_Restoring:
14520 case MachineState_TeleportingIn:
14521 case MachineState_Paused:
14522 case MachineState_Running:
14523 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14524 * elsewhere... */
14525 alock.release();
14526 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14527 default: break;
14528 }
14529#else
14530 NOREF(aDevice);
14531 NOREF(aMaskedIfs);
14532#endif
14533 return false;
14534}
14535
14536/**
14537 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14538 */
14539HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14540 IVirtualBoxErrorInfo *aError,
14541 ULONG aMaskedIfs,
14542 const com::Utf8Str &aCaptureFilename)
14543{
14544 LogFlowThisFunc(("\n"));
14545
14546 AutoCaller autoCaller(this);
14547
14548 /* This notification may happen after the machine object has been
14549 * uninitialized (the session was closed), so don't assert. */
14550 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14551
14552 ComPtr<IInternalSessionControl> directControl;
14553 {
14554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14555 if (mData->mSession.mLockType == LockType_VM)
14556 directControl = mData->mSession.mDirectControl;
14557 }
14558
14559 /* fail on notifications sent after #OnSessionEnd() is called, it is
14560 * expected by the caller */
14561 if (!directControl)
14562 return E_FAIL;
14563
14564 /* No locks should be held at this point. */
14565 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14566 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14567
14568 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14569}
14570
14571/**
14572 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14573 */
14574HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14575 IVirtualBoxErrorInfo *aError)
14576{
14577 LogFlowThisFunc(("\n"));
14578
14579 AutoCaller autoCaller(this);
14580
14581 /* This notification may happen after the machine object has been
14582 * uninitialized (the session was closed), so don't assert. */
14583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14584
14585 ComPtr<IInternalSessionControl> directControl;
14586 {
14587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14588 if (mData->mSession.mLockType == LockType_VM)
14589 directControl = mData->mSession.mDirectControl;
14590 }
14591
14592 /* fail on notifications sent after #OnSessionEnd() is called, it is
14593 * expected by the caller */
14594 if (!directControl)
14595 return E_FAIL;
14596
14597 /* No locks should be held at this point. */
14598 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14599 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14600
14601 return directControl->OnUSBDeviceDetach(aId, aError);
14602}
14603
14604// protected methods
14605/////////////////////////////////////////////////////////////////////////////
14606
14607/**
14608 * Deletes the given file if it is no longer in use by either the current machine state
14609 * (if the machine is "saved") or any of the machine's snapshots.
14610 *
14611 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14612 * but is different for each SnapshotMachine. When calling this, the order of calling this
14613 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14614 * is therefore critical. I know, it's all rather messy.
14615 *
14616 * @param strStateFile
14617 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14618 * the test for whether the saved state file is in use.
14619 */
14620void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14621 Snapshot *pSnapshotToIgnore)
14622{
14623 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14624 if ( (strStateFile.isNotEmpty())
14625 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14626 )
14627 // ... and it must also not be shared with other snapshots
14628 if ( !mData->mFirstSnapshot
14629 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14630 // this checks the SnapshotMachine's state file paths
14631 )
14632 RTFileDelete(strStateFile.c_str());
14633}
14634
14635/**
14636 * Locks the attached media.
14637 *
14638 * All attached hard disks are locked for writing and DVD/floppy are locked for
14639 * reading. Parents of attached hard disks (if any) are locked for reading.
14640 *
14641 * This method also performs accessibility check of all media it locks: if some
14642 * media is inaccessible, the method will return a failure and a bunch of
14643 * extended error info objects per each inaccessible medium.
14644 *
14645 * Note that this method is atomic: if it returns a success, all media are
14646 * locked as described above; on failure no media is locked at all (all
14647 * succeeded individual locks will be undone).
14648 *
14649 * The caller is responsible for doing the necessary state sanity checks.
14650 *
14651 * The locks made by this method must be undone by calling #unlockMedia() when
14652 * no more needed.
14653 */
14654HRESULT SessionMachine::i_lockMedia()
14655{
14656 AutoCaller autoCaller(this);
14657 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14658
14659 AutoMultiWriteLock2 alock(this->lockHandle(),
14660 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14661
14662 /* bail out if trying to lock things with already set up locking */
14663 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14664
14665 MultiResult mrc(S_OK);
14666
14667 /* Collect locking information for all medium objects attached to the VM. */
14668 for (MediumAttachmentList::const_iterator
14669 it = mMediumAttachments->begin();
14670 it != mMediumAttachments->end();
14671 ++it)
14672 {
14673 MediumAttachment *pAtt = *it;
14674 DeviceType_T devType = pAtt->i_getType();
14675 Medium *pMedium = pAtt->i_getMedium();
14676
14677 MediumLockList *pMediumLockList(new MediumLockList());
14678 // There can be attachments without a medium (floppy/dvd), and thus
14679 // it's impossible to create a medium lock list. It still makes sense
14680 // to have the empty medium lock list in the map in case a medium is
14681 // attached later.
14682 if (pMedium != NULL)
14683 {
14684 MediumType_T mediumType = pMedium->i_getType();
14685 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14686 || mediumType == MediumType_Shareable;
14687 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14688
14689 alock.release();
14690 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14691 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14692 false /* fMediumLockWriteAll */,
14693 NULL,
14694 *pMediumLockList);
14695 alock.acquire();
14696 if (FAILED(mrc))
14697 {
14698 delete pMediumLockList;
14699 mData->mSession.mLockedMedia.Clear();
14700 break;
14701 }
14702 }
14703
14704 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14705 if (FAILED(rc))
14706 {
14707 mData->mSession.mLockedMedia.Clear();
14708 mrc = setError(rc,
14709 tr("Collecting locking information for all attached media failed"));
14710 break;
14711 }
14712 }
14713
14714 if (SUCCEEDED(mrc))
14715 {
14716 /* Now lock all media. If this fails, nothing is locked. */
14717 alock.release();
14718 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14719 alock.acquire();
14720 if (FAILED(rc))
14721 {
14722 mrc = setError(rc,
14723 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14724 }
14725 }
14726
14727 return mrc;
14728}
14729
14730/**
14731 * Undoes the locks made by by #lockMedia().
14732 */
14733HRESULT SessionMachine::i_unlockMedia()
14734{
14735 AutoCaller autoCaller(this);
14736 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14737
14738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14739
14740 /* we may be holding important error info on the current thread;
14741 * preserve it */
14742 ErrorInfoKeeper eik;
14743
14744 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14745 AssertComRC(rc);
14746 return rc;
14747}
14748
14749/**
14750 * Helper to change the machine state (reimplementation).
14751 *
14752 * @note Locks this object for writing.
14753 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14754 * it can cause crashes in random places due to unexpectedly committing
14755 * the current settings. The caller is responsible for that. The call
14756 * to saveStateSettings is fine, because this method does not commit.
14757 */
14758HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14759{
14760 LogFlowThisFuncEnter();
14761 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14762
14763 AutoCaller autoCaller(this);
14764 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14765
14766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14767
14768 MachineState_T oldMachineState = mData->mMachineState;
14769
14770 AssertMsgReturn(oldMachineState != aMachineState,
14771 ("oldMachineState=%s, aMachineState=%s\n",
14772 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14773 E_FAIL);
14774
14775 HRESULT rc = S_OK;
14776
14777 int stsFlags = 0;
14778 bool deleteSavedState = false;
14779
14780 /* detect some state transitions */
14781
14782 if ( ( oldMachineState == MachineState_Saved
14783 && aMachineState == MachineState_Restoring)
14784 || ( ( oldMachineState == MachineState_PoweredOff
14785 || oldMachineState == MachineState_Teleported
14786 || oldMachineState == MachineState_Aborted
14787 )
14788 && ( aMachineState == MachineState_TeleportingIn
14789 || aMachineState == MachineState_Starting
14790 )
14791 )
14792 )
14793 {
14794 /* The EMT thread is about to start */
14795
14796 /* Nothing to do here for now... */
14797
14798 /// @todo NEWMEDIA don't let mDVDDrive and other children
14799 /// change anything when in the Starting/Restoring state
14800 }
14801 else if ( ( oldMachineState == MachineState_Running
14802 || oldMachineState == MachineState_Paused
14803 || oldMachineState == MachineState_Teleporting
14804 || oldMachineState == MachineState_OnlineSnapshotting
14805 || oldMachineState == MachineState_LiveSnapshotting
14806 || oldMachineState == MachineState_Stuck
14807 || oldMachineState == MachineState_Starting
14808 || oldMachineState == MachineState_Stopping
14809 || oldMachineState == MachineState_Saving
14810 || oldMachineState == MachineState_Restoring
14811 || oldMachineState == MachineState_TeleportingPausedVM
14812 || oldMachineState == MachineState_TeleportingIn
14813 )
14814 && ( aMachineState == MachineState_PoweredOff
14815 || aMachineState == MachineState_Saved
14816 || aMachineState == MachineState_Teleported
14817 || aMachineState == MachineState_Aborted
14818 )
14819 )
14820 {
14821 /* The EMT thread has just stopped, unlock attached media. Note that as
14822 * opposed to locking that is done from Console, we do unlocking here
14823 * because the VM process may have aborted before having a chance to
14824 * properly unlock all media it locked. */
14825
14826 unlockMedia();
14827 }
14828
14829 if (oldMachineState == MachineState_Restoring)
14830 {
14831 if (aMachineState != MachineState_Saved)
14832 {
14833 /*
14834 * delete the saved state file once the machine has finished
14835 * restoring from it (note that Console sets the state from
14836 * Restoring to Saved if the VM couldn't restore successfully,
14837 * to give the user an ability to fix an error and retry --
14838 * we keep the saved state file in this case)
14839 */
14840 deleteSavedState = true;
14841 }
14842 }
14843 else if ( oldMachineState == MachineState_Saved
14844 && ( aMachineState == MachineState_PoweredOff
14845 || aMachineState == MachineState_Aborted
14846 || aMachineState == MachineState_Teleported
14847 )
14848 )
14849 {
14850 /*
14851 * delete the saved state after SessionMachine::ForgetSavedState() is called
14852 * or if the VM process (owning a direct VM session) crashed while the
14853 * VM was Saved
14854 */
14855
14856 /// @todo (dmik)
14857 // Not sure that deleting the saved state file just because of the
14858 // client death before it attempted to restore the VM is a good
14859 // thing. But when it crashes we need to go to the Aborted state
14860 // which cannot have the saved state file associated... The only
14861 // way to fix this is to make the Aborted condition not a VM state
14862 // but a bool flag: i.e., when a crash occurs, set it to true and
14863 // change the state to PoweredOff or Saved depending on the
14864 // saved state presence.
14865
14866 deleteSavedState = true;
14867 mData->mCurrentStateModified = TRUE;
14868 stsFlags |= SaveSTS_CurStateModified;
14869 }
14870
14871 if ( aMachineState == MachineState_Starting
14872 || aMachineState == MachineState_Restoring
14873 || aMachineState == MachineState_TeleportingIn
14874 )
14875 {
14876 /* set the current state modified flag to indicate that the current
14877 * state is no more identical to the state in the
14878 * current snapshot */
14879 if (!mData->mCurrentSnapshot.isNull())
14880 {
14881 mData->mCurrentStateModified = TRUE;
14882 stsFlags |= SaveSTS_CurStateModified;
14883 }
14884 }
14885
14886 if (deleteSavedState)
14887 {
14888 if (mRemoveSavedState)
14889 {
14890 Assert(!mSSData->strStateFilePath.isEmpty());
14891
14892 // it is safe to delete the saved state file if ...
14893 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14894 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14895 // ... none of the snapshots share the saved state file
14896 )
14897 RTFileDelete(mSSData->strStateFilePath.c_str());
14898 }
14899
14900 mSSData->strStateFilePath.setNull();
14901 stsFlags |= SaveSTS_StateFilePath;
14902 }
14903
14904 /* redirect to the underlying peer machine */
14905 mPeer->i_setMachineState(aMachineState);
14906
14907 if ( oldMachineState != MachineState_RestoringSnapshot
14908 && ( aMachineState == MachineState_PoweredOff
14909 || aMachineState == MachineState_Teleported
14910 || aMachineState == MachineState_Aborted
14911 || aMachineState == MachineState_Saved))
14912 {
14913 /* the machine has stopped execution
14914 * (or the saved state file was adopted) */
14915 stsFlags |= SaveSTS_StateTimeStamp;
14916 }
14917
14918 if ( ( oldMachineState == MachineState_PoweredOff
14919 || oldMachineState == MachineState_Aborted
14920 || oldMachineState == MachineState_Teleported
14921 )
14922 && aMachineState == MachineState_Saved)
14923 {
14924 /* the saved state file was adopted */
14925 Assert(!mSSData->strStateFilePath.isEmpty());
14926 stsFlags |= SaveSTS_StateFilePath;
14927 }
14928
14929#ifdef VBOX_WITH_GUEST_PROPS
14930 if ( aMachineState == MachineState_PoweredOff
14931 || aMachineState == MachineState_Aborted
14932 || aMachineState == MachineState_Teleported)
14933 {
14934 /* Make sure any transient guest properties get removed from the
14935 * property store on shutdown. */
14936 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14937
14938 /* remove it from the settings representation */
14939 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14940 for (settings::GuestPropertiesList::iterator
14941 it = llGuestProperties.begin();
14942 it != llGuestProperties.end();
14943 /*nothing*/)
14944 {
14945 const settings::GuestProperty &prop = *it;
14946 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14947 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14948 {
14949 it = llGuestProperties.erase(it);
14950 fNeedsSaving = true;
14951 }
14952 else
14953 {
14954 ++it;
14955 }
14956 }
14957
14958 /* Additionally remove it from the HWData representation. Required to
14959 * keep everything in sync, as this is what the API keeps using. */
14960 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14961 for (HWData::GuestPropertyMap::iterator
14962 it = llHWGuestProperties.begin();
14963 it != llHWGuestProperties.end();
14964 /*nothing*/)
14965 {
14966 uint32_t fFlags = it->second.mFlags;
14967 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14968 {
14969 /* iterator where we need to continue after the erase call
14970 * (C++03 is a fact still, and it doesn't return the iterator
14971 * which would allow continuing) */
14972 HWData::GuestPropertyMap::iterator it2 = it;
14973 ++it2;
14974 llHWGuestProperties.erase(it);
14975 it = it2;
14976 fNeedsSaving = true;
14977 }
14978 else
14979 {
14980 ++it;
14981 }
14982 }
14983
14984 if (fNeedsSaving)
14985 {
14986 mData->mCurrentStateModified = TRUE;
14987 stsFlags |= SaveSTS_CurStateModified;
14988 }
14989 }
14990#endif /* VBOX_WITH_GUEST_PROPS */
14991
14992 rc = i_saveStateSettings(stsFlags);
14993
14994 if ( ( oldMachineState != MachineState_PoweredOff
14995 && oldMachineState != MachineState_Aborted
14996 && oldMachineState != MachineState_Teleported
14997 )
14998 && ( aMachineState == MachineState_PoweredOff
14999 || aMachineState == MachineState_Aborted
15000 || aMachineState == MachineState_Teleported
15001 )
15002 )
15003 {
15004 /* we've been shut down for any reason */
15005 /* no special action so far */
15006 }
15007
15008 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15009 LogFlowThisFuncLeave();
15010 return rc;
15011}
15012
15013/**
15014 * Sends the current machine state value to the VM process.
15015 *
15016 * @note Locks this object for reading, then calls a client process.
15017 */
15018HRESULT SessionMachine::i_updateMachineStateOnClient()
15019{
15020 AutoCaller autoCaller(this);
15021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15022
15023 ComPtr<IInternalSessionControl> directControl;
15024 {
15025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15026 AssertReturn(!!mData, E_FAIL);
15027 if (mData->mSession.mLockType == LockType_VM)
15028 directControl = mData->mSession.mDirectControl;
15029
15030 /* directControl may be already set to NULL here in #OnSessionEnd()
15031 * called too early by the direct session process while there is still
15032 * some operation (like deleting the snapshot) in progress. The client
15033 * process in this case is waiting inside Session::close() for the
15034 * "end session" process object to complete, while #uninit() called by
15035 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15036 * operation to complete. For now, we accept this inconsistent behavior
15037 * and simply do nothing here. */
15038
15039 if (mData->mSession.mState == SessionState_Unlocking)
15040 return S_OK;
15041 }
15042
15043 /* ignore notifications sent after #OnSessionEnd() is called */
15044 if (!directControl)
15045 return S_OK;
15046
15047 return directControl->UpdateMachineState(mData->mMachineState);
15048}
15049
15050
15051/*static*/
15052HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15053{
15054 va_list args;
15055 va_start(args, pcszMsg);
15056 HRESULT rc = setErrorInternal(aResultCode,
15057 getStaticClassIID(),
15058 getStaticComponentName(),
15059 Utf8Str(pcszMsg, args),
15060 false /* aWarning */,
15061 true /* aLogIt */);
15062 va_end(args);
15063 return rc;
15064}
15065
15066
15067HRESULT Machine::updateState(MachineState_T aState)
15068{
15069 NOREF(aState);
15070 ReturnComNotImplemented();
15071}
15072
15073HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15074{
15075 NOREF(aProgress);
15076 ReturnComNotImplemented();
15077}
15078
15079HRESULT Machine::endPowerUp(LONG aResult)
15080{
15081 NOREF(aResult);
15082 ReturnComNotImplemented();
15083}
15084
15085HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15086{
15087 NOREF(aProgress);
15088 ReturnComNotImplemented();
15089}
15090
15091HRESULT Machine::endPoweringDown(LONG aResult,
15092 const com::Utf8Str &aErrMsg)
15093{
15094 NOREF(aResult);
15095 NOREF(aErrMsg);
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15100 BOOL *aMatched,
15101 ULONG *aMaskedInterfaces)
15102{
15103 NOREF(aDevice);
15104 NOREF(aMatched);
15105 NOREF(aMaskedInterfaces);
15106 ReturnComNotImplemented();
15107
15108}
15109
15110HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15111{
15112 NOREF(aId); NOREF(aCaptureFilename);
15113 ReturnComNotImplemented();
15114}
15115
15116HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15117 BOOL aDone)
15118{
15119 NOREF(aId);
15120 NOREF(aDone);
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::autoCaptureUSBDevices()
15125{
15126 ReturnComNotImplemented();
15127}
15128
15129HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15130{
15131 NOREF(aDone);
15132 ReturnComNotImplemented();
15133}
15134
15135HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15136 ComPtr<IProgress> &aProgress)
15137{
15138 NOREF(aSession);
15139 NOREF(aProgress);
15140 ReturnComNotImplemented();
15141}
15142
15143HRESULT Machine::finishOnlineMergeMedium()
15144{
15145 ReturnComNotImplemented();
15146}
15147
15148HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15149 std::vector<com::Utf8Str> &aValues,
15150 std::vector<LONG64> &aTimestamps,
15151 std::vector<com::Utf8Str> &aFlags)
15152{
15153 NOREF(aNames);
15154 NOREF(aValues);
15155 NOREF(aTimestamps);
15156 NOREF(aFlags);
15157 ReturnComNotImplemented();
15158}
15159
15160HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15161 const com::Utf8Str &aValue,
15162 LONG64 aTimestamp,
15163 const com::Utf8Str &aFlags)
15164{
15165 NOREF(aName);
15166 NOREF(aValue);
15167 NOREF(aTimestamp);
15168 NOREF(aFlags);
15169 ReturnComNotImplemented();
15170}
15171
15172HRESULT Machine::lockMedia()
15173{
15174 ReturnComNotImplemented();
15175}
15176
15177HRESULT Machine::unlockMedia()
15178{
15179 ReturnComNotImplemented();
15180}
15181
15182HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15183 ComPtr<IMediumAttachment> &aNewAttachment)
15184{
15185 NOREF(aAttachment);
15186 NOREF(aNewAttachment);
15187 ReturnComNotImplemented();
15188}
15189
15190HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15191 ULONG aCpuUser,
15192 ULONG aCpuKernel,
15193 ULONG aCpuIdle,
15194 ULONG aMemTotal,
15195 ULONG aMemFree,
15196 ULONG aMemBalloon,
15197 ULONG aMemShared,
15198 ULONG aMemCache,
15199 ULONG aPagedTotal,
15200 ULONG aMemAllocTotal,
15201 ULONG aMemFreeTotal,
15202 ULONG aMemBalloonTotal,
15203 ULONG aMemSharedTotal,
15204 ULONG aVmNetRx,
15205 ULONG aVmNetTx)
15206{
15207 NOREF(aValidStats);
15208 NOREF(aCpuUser);
15209 NOREF(aCpuKernel);
15210 NOREF(aCpuIdle);
15211 NOREF(aMemTotal);
15212 NOREF(aMemFree);
15213 NOREF(aMemBalloon);
15214 NOREF(aMemShared);
15215 NOREF(aMemCache);
15216 NOREF(aPagedTotal);
15217 NOREF(aMemAllocTotal);
15218 NOREF(aMemFreeTotal);
15219 NOREF(aMemBalloonTotal);
15220 NOREF(aMemSharedTotal);
15221 NOREF(aVmNetRx);
15222 NOREF(aVmNetTx);
15223 ReturnComNotImplemented();
15224}
15225
15226HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15227 com::Utf8Str &aResult)
15228{
15229 NOREF(aAuthParams);
15230 NOREF(aResult);
15231 ReturnComNotImplemented();
15232}
15233
15234HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15235{
15236 NOREF(aFlags);
15237 ReturnComNotImplemented();
15238}
15239
15240/* This isn't handled entirely by the wrapper generator yet. */
15241#ifdef VBOX_WITH_XPCOM
15242NS_DECL_CLASSINFO(SessionMachine)
15243NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15244
15245NS_DECL_CLASSINFO(SnapshotMachine)
15246NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15247#endif
Note: See TracBrowser for help on using the repository browser.

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