VirtualBox

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

Last change on this file since 55993 was 55988, checked in by vboxsync, 10 years ago

iprt/log.h,SUPDrv: Replaced the 'personal' logging groups with 6 more generic logging levels (7 thru 12) and a 'Warn' level. The 'Warn' level is enabled by 'group.e' together with level 1 logging. Modified the new RTLog[Rel][Get]DefaultInstanceEx functions to only take one 32-bit parameter to minimize call setup time and size. Major support driver version bump. LogAleksey=Log7, LogBird=Log8, LogSunlover=Log9, none of the other personal macros was used. Log*Warning got renamed to Log1*Warning so as to not confuse it with the LogWarn/LogRelWarn macros.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 511.6 KB
Line 
1/* $Id: MachineImpl.cpp 55988 2015-05-20 23:24:44Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.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 = "2"; /** @todo get the default from the schema if that is possible. */
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 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209 mGuestPropertyNotificationPatterns = "";
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine::HDData structure
231/////////////////////////////////////////////////////////////////////////////
232
233Machine::MediaData::MediaData()
234{
235}
236
237Machine::MediaData::~MediaData()
238{
239}
240
241/////////////////////////////////////////////////////////////////////////////
242// Machine class
243/////////////////////////////////////////////////////////////////////////////
244
245// constructor / destructor
246/////////////////////////////////////////////////////////////////////////////
247
248Machine::Machine() :
249#ifdef VBOX_WITH_RESOURCE_USAGE_API
250 mCollectorGuest(NULL),
251#endif
252 mPeer(NULL),
253 mParent(NULL),
254 mSerialPorts(),
255 mParallelPorts(),
256 uRegistryNeedsSaving(0)
257{}
258
259Machine::~Machine()
260{}
261
262HRESULT Machine::FinalConstruct()
263{
264 LogFlowThisFunc(("\n"));
265 return BaseFinalConstruct();
266}
267
268void Machine::FinalRelease()
269{
270 LogFlowThisFunc(("\n"));
271 uninit();
272 BaseFinalRelease();
273}
274
275/**
276 * Initializes a new machine instance; this init() variant creates a new, empty machine.
277 * This gets called from VirtualBox::CreateMachine().
278 *
279 * @param aParent Associated parent object
280 * @param strConfigFile Local file system path to the VM settings file (can
281 * be relative to the VirtualBox config directory).
282 * @param strName name for the machine
283 * @param llGroups list of groups for the machine
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->i_applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->i_applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->i_is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360 }
361
362 /* At this point the changing of the current state modification
363 * flag is allowed. */
364 i_allowStateModification();
365
366 /* commit all changes made during the initialization */
367 i_commit();
368 }
369
370 /* Confirm a successful initialization when it's the case */
371 if (SUCCEEDED(rc))
372 {
373 if (mData->mAccessible)
374 autoInitSpan.setSucceeded();
375 else
376 autoInitSpan.setLimited();
377 }
378
379 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
380 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
381 mData->mRegistered,
382 mData->mAccessible,
383 rc));
384
385 LogFlowThisFuncLeave();
386
387 return rc;
388}
389
390/**
391 * Initializes a new instance with data from machine XML (formerly Init_Registered).
392 * Gets called in two modes:
393 *
394 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
395 * UUID is specified and we mark the machine as "registered";
396 *
397 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
398 * and the machine remains unregistered until RegisterMachine() is called.
399 *
400 * @param aParent Associated parent object
401 * @param aConfigFile Local file system path to the VM settings file (can
402 * be relative to the VirtualBox config directory).
403 * @param aId UUID of the machine or NULL (see above).
404 *
405 * @return Success indicator. if not S_OK, the machine object is invalid
406 */
407HRESULT Machine::initFromSettings(VirtualBox *aParent,
408 const Utf8Str &strConfigFile,
409 const Guid *aId)
410{
411 LogFlowThisFuncEnter();
412 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
413
414 /* Enclose the state transition NotReady->InInit->Ready */
415 AutoInitSpan autoInitSpan(this);
416 AssertReturn(autoInitSpan.isOk(), E_FAIL);
417
418 HRESULT rc = initImpl(aParent, strConfigFile);
419 if (FAILED(rc)) return rc;
420
421 if (aId)
422 {
423 // loading a registered VM:
424 unconst(mData->mUuid) = *aId;
425 mData->mRegistered = TRUE;
426 // now load the settings from XML:
427 rc = i_registeredInit();
428 // this calls initDataAndChildObjects() and loadSettings()
429 }
430 else
431 {
432 // opening an unregistered VM (VirtualBox::OpenMachine()):
433 rc = initDataAndChildObjects();
434
435 if (SUCCEEDED(rc))
436 {
437 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
438 mData->mAccessible = TRUE;
439
440 try
441 {
442 // load and parse machine XML; this will throw on XML or logic errors
443 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
444
445 // reject VM UUID duplicates, they can happen if someone
446 // tries to register an already known VM config again
447 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
448 true /* fPermitInaccessible */,
449 false /* aDoSetError */,
450 NULL) != VBOX_E_OBJECT_NOT_FOUND)
451 {
452 throw setError(E_FAIL,
453 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
454 mData->m_strConfigFile.c_str());
455 }
456
457 // use UUID from machine config
458 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
459
460 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
461 NULL /* puuidRegistry */);
462 if (FAILED(rc)) throw rc;
463
464 /* At this point the changing of the current state modification
465 * flag is allowed. */
466 i_allowStateModification();
467
468 i_commit();
469 }
470 catch (HRESULT err)
471 {
472 /* we assume that error info is set by the thrower */
473 rc = err;
474 }
475 catch (...)
476 {
477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
478 }
479 }
480 }
481
482 /* Confirm a successful initialization when it's the case */
483 if (SUCCEEDED(rc))
484 {
485 if (mData->mAccessible)
486 autoInitSpan.setSucceeded();
487 else
488 {
489 autoInitSpan.setLimited();
490
491 // uninit media from this machine's media registry, or else
492 // reloading the settings will fail
493 mParent->i_unregisterMachineMedia(i_getId());
494 }
495 }
496
497 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
498 "rc=%08X\n",
499 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
500 mData->mRegistered, mData->mAccessible, rc));
501
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507/**
508 * Initializes a new instance from a machine config that is already in memory
509 * (import OVF case). Since we are importing, the UUID in the machine
510 * config is ignored and we always generate a fresh one.
511 *
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->i_getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = i_loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 i_allowStateModification();
566
567 /* commit all changes made during the initialization */
568 i_commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 /* Ignore all errors from unregistering, they would destroy
580- * the more interesting error information we already have,
581- * pinpointing the issue with the VM config. */
582 ErrorInfoKeeper eik;
583
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->i_unregisterMachineMedia(i_getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 Log1WarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 rc = i_checkStateDependency(MutableOrSavedStateDep);
1064 if (FAILED(rc)) return rc;
1065
1066 i_setModified(IsModified_MachineData);
1067 mUserData.backup();
1068 mUserData->s.llGroups = llGroups;
1069
1070 return S_OK;
1071}
1072
1073HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1074{
1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1076
1077 aOSTypeId = mUserData->s.strOsType;
1078
1079 return S_OK;
1080}
1081
1082HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1083{
1084 /* look up the object by Id to check it is valid */
1085 ComPtr<IGuestOSType> guestOSType;
1086 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1087 if (FAILED(rc)) return rc;
1088
1089 /* when setting, always use the "etalon" value for consistency -- lookup
1090 * by ID is case-insensitive and the input value may have different case */
1091 Bstr osTypeId;
1092 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1093 if (FAILED(rc)) return rc;
1094
1095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1096
1097 rc = i_checkStateDependency(MutableStateDep);
1098 if (FAILED(rc)) return rc;
1099
1100 i_setModified(IsModified_MachineData);
1101 mUserData.backup();
1102 mUserData->s.strOsType = osTypeId;
1103
1104 return S_OK;
1105}
1106
1107HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1108{
1109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 *aFirmwareType = mHWData->mFirmwareType;
1112
1113 return S_OK;
1114}
1115
1116HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1117{
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 HRESULT rc = i_checkStateDependency(MutableStateDep);
1121 if (FAILED(rc)) return rc;
1122
1123 i_setModified(IsModified_MachineData);
1124 mHWData.backup();
1125 mHWData->mFirmwareType = aFirmwareType;
1126
1127 return S_OK;
1128}
1129
1130HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1131{
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1135
1136 return S_OK;
1137}
1138
1139HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1140{
1141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1142
1143 HRESULT rc = i_checkStateDependency(MutableStateDep);
1144 if (FAILED(rc)) return rc;
1145
1146 i_setModified(IsModified_MachineData);
1147 mHWData.backup();
1148 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1149
1150 return S_OK;
1151}
1152
1153HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1154{
1155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 *aPointingHIDType = mHWData->mPointingHIDType;
1158
1159 return S_OK;
1160}
1161
1162HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1163{
1164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1165
1166 HRESULT rc = i_checkStateDependency(MutableStateDep);
1167 if (FAILED(rc)) return rc;
1168
1169 i_setModified(IsModified_MachineData);
1170 mHWData.backup();
1171 mHWData->mPointingHIDType = aPointingHIDType;
1172
1173 return S_OK;
1174}
1175
1176HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1177{
1178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 *aChipsetType = mHWData->mChipsetType;
1181
1182 return S_OK;
1183}
1184
1185HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1186{
1187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 HRESULT rc = i_checkStateDependency(MutableStateDep);
1190 if (FAILED(rc)) return rc;
1191
1192 if (aChipsetType != mHWData->mChipsetType)
1193 {
1194 i_setModified(IsModified_MachineData);
1195 mHWData.backup();
1196 mHWData->mChipsetType = aChipsetType;
1197
1198 // Resize network adapter array, to be finalized on commit/rollback.
1199 // We must not throw away entries yet, otherwise settings are lost
1200 // without a way to roll back.
1201 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1202 size_t oldCount = mNetworkAdapters.size();
1203 if (newCount > oldCount)
1204 {
1205 mNetworkAdapters.resize(newCount);
1206 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1207 {
1208 unconst(mNetworkAdapters[slot]).createObject();
1209 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1210 }
1211 }
1212 }
1213
1214 return S_OK;
1215}
1216
1217HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1218{
1219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1220
1221 *aParavirtProvider = mHWData->mParavirtProvider;
1222
1223 return S_OK;
1224}
1225
1226HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1227{
1228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1229
1230 HRESULT rc = i_checkStateDependency(MutableStateDep);
1231 if (FAILED(rc)) return rc;
1232
1233 if (aParavirtProvider != mHWData->mParavirtProvider)
1234 {
1235 i_setModified(IsModified_MachineData);
1236 mHWData.backup();
1237 mHWData->mParavirtProvider = aParavirtProvider;
1238 }
1239
1240 return S_OK;
1241}
1242
1243HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1244{
1245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1246
1247 *aParavirtProvider = mHWData->mParavirtProvider;
1248 switch (mHWData->mParavirtProvider)
1249 {
1250 case ParavirtProvider_None:
1251 case ParavirtProvider_HyperV:
1252 case ParavirtProvider_KVM:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "Windows10"
1284 || mUserData->s.strOsType == "Windows10_64"
1285 || mUserData->s.strOsType == "Windows81"
1286 || mUserData->s.strOsType == "Windows81_64"
1287 || mUserData->s.strOsType == "Windows8"
1288 || mUserData->s.strOsType == "Windows8_64"
1289 || mUserData->s.strOsType == "Windows7"
1290 || mUserData->s.strOsType == "Windows7_64"
1291 || mUserData->s.strOsType == "WindowsVista"
1292 || mUserData->s.strOsType == "WindowsVista_64"
1293 || mUserData->s.strOsType == "Windows2012"
1294 || mUserData->s.strOsType == "Windows2012_64"
1295 || mUserData->s.strOsType == "Windows2008"
1296 || mUserData->s.strOsType == "Windows2008_64")
1297 {
1298 *aParavirtProvider = ParavirtProvider_HyperV;
1299 }
1300 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1301 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1302 || mUserData->s.strOsType == "Linux"
1303 || mUserData->s.strOsType == "Linux_64"
1304 || mUserData->s.strOsType == "ArchLinux"
1305 || mUserData->s.strOsType == "ArchLinux_64"
1306 || mUserData->s.strOsType == "Debian"
1307 || mUserData->s.strOsType == "Debian_64"
1308 || mUserData->s.strOsType == "Fedora"
1309 || mUserData->s.strOsType == "Fedora_64"
1310 || mUserData->s.strOsType == "Gentoo"
1311 || mUserData->s.strOsType == "Gentoo_64"
1312 || mUserData->s.strOsType == "Mandriva"
1313 || mUserData->s.strOsType == "Mandriva_64"
1314 || mUserData->s.strOsType == "OpenSUSE"
1315 || mUserData->s.strOsType == "OpenSUSE_64"
1316 || mUserData->s.strOsType == "Oracle"
1317 || mUserData->s.strOsType == "Oracle_64"
1318 || mUserData->s.strOsType == "RedHat"
1319 || mUserData->s.strOsType == "RedHat_64"
1320 || mUserData->s.strOsType == "Turbolinux"
1321 || mUserData->s.strOsType == "Turbolinux_64"
1322 || mUserData->s.strOsType == "Ubuntu"
1323 || mUserData->s.strOsType == "Ubuntu_64"
1324 || mUserData->s.strOsType == "Xandros"
1325 || mUserData->s.strOsType == "Xandros_64")
1326 {
1327 *aParavirtProvider = ParavirtProvider_KVM;
1328 }
1329 else
1330 *aParavirtProvider = ParavirtProvider_None;
1331 break;
1332 }
1333 }
1334 break;
1335 }
1336 }
1337
1338 Assert( *aParavirtProvider == ParavirtProvider_None
1339 || *aParavirtProvider == ParavirtProvider_Minimal
1340 || *aParavirtProvider == ParavirtProvider_HyperV
1341 || *aParavirtProvider == ParavirtProvider_KVM);
1342 return S_OK;
1343}
1344
1345HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1346{
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 aHardwareVersion = mHWData->mHWVersion;
1350
1351 return S_OK;
1352}
1353
1354HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1355{
1356 /* check known version */
1357 Utf8Str hwVersion = aHardwareVersion;
1358 if ( hwVersion.compare("1") != 0
1359 && hwVersion.compare("2") != 0)
1360 return setError(E_INVALIDARG,
1361 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1362
1363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1364
1365 HRESULT rc = i_checkStateDependency(MutableStateDep);
1366 if (FAILED(rc)) return rc;
1367
1368 i_setModified(IsModified_MachineData);
1369 mHWData.backup();
1370 mHWData->mHWVersion = aHardwareVersion;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 if (!mHWData->mHardwareUUID.isZero())
1380 aHardwareUUID = mHWData->mHardwareUUID;
1381 else
1382 aHardwareUUID = mData->mUuid;
1383
1384 return S_OK;
1385}
1386
1387HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1388{
1389 if (!aHardwareUUID.isValid())
1390 return E_INVALIDARG;
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = i_checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 i_setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 if (aHardwareUUID == mData->mUuid)
1400 mHWData->mHardwareUUID.clear();
1401 else
1402 mHWData->mHardwareUUID = aHardwareUUID;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1408{
1409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 *aMemorySize = mHWData->mMemorySize;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setMemorySize(ULONG aMemorySize)
1417{
1418 /* check RAM limits */
1419 if ( aMemorySize < MM_RAM_MIN_IN_MB
1420 || aMemorySize > MM_RAM_MAX_IN_MB
1421 )
1422 return setError(E_INVALIDARG,
1423 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1424 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
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 mHWData->mMemorySize = aMemorySize;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1439{
1440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 *aCPUCount = mHWData->mCPUCount;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setCPUCount(ULONG aCPUCount)
1448{
1449 /* check CPU limits */
1450 if ( aCPUCount < SchemaDefs::MinCPUCount
1451 || aCPUCount > SchemaDefs::MaxCPUCount
1452 )
1453 return setError(E_INVALIDARG,
1454 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1455 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1456
1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1460 if (mHWData->mCPUHotPlugEnabled)
1461 {
1462 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1463 {
1464 if (mHWData->mCPUAttached[idx])
1465 return setError(E_INVALIDARG,
1466 tr("There is still a CPU attached to socket %lu."
1467 "Detach the CPU before removing the socket"),
1468 aCPUCount, idx+1);
1469 }
1470 }
1471
1472 HRESULT rc = i_checkStateDependency(MutableStateDep);
1473 if (FAILED(rc)) return rc;
1474
1475 i_setModified(IsModified_MachineData);
1476 mHWData.backup();
1477 mHWData->mCPUCount = aCPUCount;
1478
1479 return S_OK;
1480}
1481
1482HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1483{
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1492{
1493 HRESULT rc = S_OK;
1494
1495 /* check throttle limits */
1496 if ( aCPUExecutionCap < 1
1497 || aCPUExecutionCap > 100
1498 )
1499 return setError(E_INVALIDARG,
1500 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1501 aCPUExecutionCap, 1, 100);
1502
1503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1504
1505 alock.release();
1506 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1507 alock.acquire();
1508 if (FAILED(rc)) return rc;
1509
1510 i_setModified(IsModified_MachineData);
1511 mHWData.backup();
1512 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1513
1514 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1515 if (Global::IsOnline(mData->mMachineState))
1516 i_saveSettings(NULL);
1517
1518 return S_OK;
1519}
1520
1521HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1522{
1523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1526
1527 return S_OK;
1528}
1529
1530HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1531{
1532 HRESULT rc = S_OK;
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 rc = i_checkStateDependency(MutableStateDep);
1537 if (FAILED(rc)) return rc;
1538
1539 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1540 {
1541 if (aCPUHotPlugEnabled)
1542 {
1543 i_setModified(IsModified_MachineData);
1544 mHWData.backup();
1545
1546 /* Add the amount of CPUs currently attached */
1547 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1548 mHWData->mCPUAttached[i] = true;
1549 }
1550 else
1551 {
1552 /*
1553 * We can disable hotplug only if the amount of maximum CPUs is equal
1554 * to the amount of attached CPUs
1555 */
1556 unsigned cCpusAttached = 0;
1557 unsigned iHighestId = 0;
1558
1559 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1560 {
1561 if (mHWData->mCPUAttached[i])
1562 {
1563 cCpusAttached++;
1564 iHighestId = i;
1565 }
1566 }
1567
1568 if ( (cCpusAttached != mHWData->mCPUCount)
1569 || (iHighestId >= mHWData->mCPUCount))
1570 return setError(E_INVALIDARG,
1571 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1572
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 }
1576 }
1577
1578 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1579
1580 return rc;
1581}
1582
1583HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1584{
1585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1586
1587 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1588
1589 return S_OK;
1590}
1591
1592HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1593{
1594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1597 if (SUCCEEDED(hrc))
1598 {
1599 i_setModified(IsModified_MachineData);
1600 mHWData.backup();
1601 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1602 }
1603 return hrc;
1604}
1605
1606HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1607{
1608#ifdef VBOX_WITH_USB_CARDREADER
1609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1610
1611 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1612
1613 return S_OK;
1614#else
1615 NOREF(aEmulatedUSBCardReaderEnabled);
1616 return E_NOTIMPL;
1617#endif
1618}
1619
1620HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1621{
1622#ifdef VBOX_WITH_USB_CARDREADER
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1626 if (FAILED(rc)) return rc;
1627
1628 i_setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1631
1632 return S_OK;
1633#else
1634 NOREF(aEmulatedUSBCardReaderEnabled);
1635 return E_NOTIMPL;
1636#endif
1637}
1638
1639HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1640{
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *aHPETEnabled = mHWData->mHPETEnabled;
1644
1645 return S_OK;
1646}
1647
1648HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1649{
1650 HRESULT rc = S_OK;
1651
1652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1653
1654 rc = i_checkStateDependency(MutableStateDep);
1655 if (FAILED(rc)) return rc;
1656
1657 i_setModified(IsModified_MachineData);
1658 mHWData.backup();
1659
1660 mHWData->mHPETEnabled = aHPETEnabled;
1661
1662 return rc;
1663}
1664
1665HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1666{
1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1670 return S_OK;
1671}
1672
1673HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1674{
1675 HRESULT rc = S_OK;
1676
1677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1678
1679 i_setModified(IsModified_MachineData);
1680 mHWData.backup();
1681 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1682
1683 alock.release();
1684 rc = i_onVideoCaptureChange();
1685 alock.acquire();
1686 if (FAILED(rc))
1687 {
1688 /*
1689 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1690 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1691 * determine if it should start or stop capturing. Therefore we need to manually
1692 * undo change.
1693 */
1694 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1695 return rc;
1696 }
1697
1698 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1699 if (Global::IsOnline(mData->mMachineState))
1700 i_saveSettings(NULL);
1701
1702 return rc;
1703}
1704
1705HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1706{
1707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1708 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1709 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1710 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1711 return S_OK;
1712}
1713
1714HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1715{
1716 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1717 bool fChanged = false;
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1722 {
1723 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1724 {
1725 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1726 fChanged = true;
1727 }
1728 }
1729 if (fChanged)
1730 {
1731 alock.release();
1732 HRESULT rc = i_onVideoCaptureChange();
1733 alock.acquire();
1734 if (FAILED(rc)) return rc;
1735 i_setModified(IsModified_MachineData);
1736
1737 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1738 if (Global::IsOnline(mData->mMachineState))
1739 i_saveSettings(NULL);
1740 }
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748 if (mHWData->mVideoCaptureFile.isEmpty())
1749 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1750 else
1751 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1752 return S_OK;
1753}
1754
1755HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1756{
1757 Utf8Str strFile(aVideoCaptureFile);
1758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1759
1760 if ( Global::IsOnline(mData->mMachineState)
1761 && mHWData->mVideoCaptureEnabled)
1762 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1763
1764 if (!RTPathStartsWithRoot(strFile.c_str()))
1765 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1766
1767 if (!strFile.isEmpty())
1768 {
1769 Utf8Str defaultFile;
1770 i_getDefaultVideoCaptureFile(defaultFile);
1771 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1772 strFile.setNull();
1773 }
1774
1775 i_setModified(IsModified_MachineData);
1776 mHWData.backup();
1777 mHWData->mVideoCaptureFile = strFile;
1778
1779 return S_OK;
1780}
1781
1782HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1783{
1784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1785 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1786 return S_OK;
1787}
1788
1789HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1790{
1791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 if ( Global::IsOnline(mData->mMachineState)
1794 && mHWData->mVideoCaptureEnabled)
1795 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1796
1797 i_setModified(IsModified_MachineData);
1798 mHWData.backup();
1799 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1800
1801 return S_OK;
1802}
1803
1804HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1805{
1806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1807 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1808 return S_OK;
1809}
1810
1811HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1812{
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 i_setModified(IsModified_MachineData);
1820 mHWData.backup();
1821 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1822
1823 return S_OK;
1824}
1825
1826HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1827{
1828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1829 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1830 return S_OK;
1831}
1832
1833HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1834{
1835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1836
1837 if ( Global::IsOnline(mData->mMachineState)
1838 && mHWData->mVideoCaptureEnabled)
1839 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1840
1841 i_setModified(IsModified_MachineData);
1842 mHWData.backup();
1843 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1844
1845 return S_OK;
1846}
1847
1848HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1849{
1850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1851 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1852 return S_OK;
1853}
1854
1855HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1856{
1857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1858
1859 if ( Global::IsOnline(mData->mMachineState)
1860 && mHWData->mVideoCaptureEnabled)
1861 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1862
1863 i_setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1866
1867 return S_OK;
1868}
1869
1870HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1871{
1872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1873 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1874 return S_OK;
1875}
1876
1877HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1878{
1879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 if ( Global::IsOnline(mData->mMachineState)
1882 && mHWData->mVideoCaptureEnabled)
1883 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1884
1885 i_setModified(IsModified_MachineData);
1886 mHWData.backup();
1887 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1888
1889 return S_OK;
1890}
1891
1892HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1893{
1894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1895 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1896 return S_OK;
1897}
1898
1899HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1900{
1901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1902
1903 if ( Global::IsOnline(mData->mMachineState)
1904 && mHWData->mVideoCaptureEnabled)
1905 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1906
1907 i_setModified(IsModified_MachineData);
1908 mHWData.backup();
1909 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1910
1911 return S_OK;
1912}
1913
1914HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1915{
1916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1919 return S_OK;
1920}
1921
1922HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1923{
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 if ( Global::IsOnline(mData->mMachineState)
1927 && mHWData->mVideoCaptureEnabled)
1928 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1929
1930 i_setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1933
1934 return S_OK;
1935}
1936
1937HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1938{
1939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1940
1941 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1942
1943 return S_OK;
1944}
1945
1946HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1947{
1948 switch (aGraphicsControllerType)
1949 {
1950 case GraphicsControllerType_Null:
1951 case GraphicsControllerType_VBoxVGA:
1952#ifdef VBOX_WITH_VMSVGA
1953 case GraphicsControllerType_VMSVGA:
1954#endif
1955 break;
1956 default:
1957 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1958 }
1959
1960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 HRESULT rc = i_checkStateDependency(MutableStateDep);
1963 if (FAILED(rc)) return rc;
1964
1965 i_setModified(IsModified_MachineData);
1966 mHWData.backup();
1967 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1968
1969 return S_OK;
1970}
1971
1972HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1973{
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 *aVRAMSize = mHWData->mVRAMSize;
1977
1978 return S_OK;
1979}
1980
1981HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1982{
1983 /* check VRAM limits */
1984 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1985 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1986 return setError(E_INVALIDARG,
1987 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1988 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1989
1990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 HRESULT rc = i_checkStateDependency(MutableStateDep);
1993 if (FAILED(rc)) return rc;
1994
1995 i_setModified(IsModified_MachineData);
1996 mHWData.backup();
1997 mHWData->mVRAMSize = aVRAMSize;
1998
1999 return S_OK;
2000}
2001
2002/** @todo this method should not be public */
2003HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2004{
2005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2006
2007 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2008
2009 return S_OK;
2010}
2011
2012/**
2013 * Set the memory balloon size.
2014 *
2015 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2016 * we have to make sure that we never call IGuest from here.
2017 */
2018HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2019{
2020 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2021#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2022 /* check limits */
2023 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2024 return setError(E_INVALIDARG,
2025 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2026 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2033
2034 return S_OK;
2035#else
2036 NOREF(aMemoryBalloonSize);
2037 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2038#endif
2039}
2040
2041HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2042{
2043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2044
2045 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2046 return S_OK;
2047}
2048
2049HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2050{
2051#ifdef VBOX_WITH_PAGE_SHARING
2052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2058 return S_OK;
2059#else
2060 NOREF(aPageFusionEnabled);
2061 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2062#endif
2063}
2064
2065HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2066{
2067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2068
2069 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2070
2071 return S_OK;
2072}
2073
2074HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2075{
2076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 HRESULT rc = i_checkStateDependency(MutableStateDep);
2079 if (FAILED(rc)) return rc;
2080
2081 /** @todo check validity! */
2082
2083 i_setModified(IsModified_MachineData);
2084 mHWData.backup();
2085 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2086
2087 return S_OK;
2088}
2089
2090
2091HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2092{
2093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2094
2095 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2096
2097 return S_OK;
2098}
2099
2100HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2101{
2102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2103
2104 HRESULT rc = i_checkStateDependency(MutableStateDep);
2105 if (FAILED(rc)) return rc;
2106
2107 /** @todo check validity! */
2108 i_setModified(IsModified_MachineData);
2109 mHWData.backup();
2110 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2111
2112 return S_OK;
2113}
2114
2115HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2116{
2117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2118
2119 *aMonitorCount = mHWData->mMonitorCount;
2120
2121 return S_OK;
2122}
2123
2124HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2125{
2126 /* make sure monitor count is a sensible number */
2127 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2128 return setError(E_INVALIDARG,
2129 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2130 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2131
2132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 HRESULT rc = i_checkStateDependency(MutableStateDep);
2135 if (FAILED(rc)) return rc;
2136
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mMonitorCount = aMonitorCount;
2140
2141 return S_OK;
2142}
2143
2144HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2145{
2146 /* mBIOSSettings is constant during life time, no need to lock */
2147 aBIOSSettings = mBIOSSettings;
2148
2149 return S_OK;
2150}
2151
2152HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2153{
2154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2155
2156 switch (aProperty)
2157 {
2158 case CPUPropertyType_PAE:
2159 *aValue = mHWData->mPAEEnabled;
2160 break;
2161
2162 case CPUPropertyType_LongMode:
2163 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2164 *aValue = TRUE;
2165 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2166 *aValue = FALSE;
2167#if HC_ARCH_BITS == 64
2168 else
2169 *aValue = TRUE;
2170#else
2171 else
2172 {
2173 *aValue = FALSE;
2174
2175 ComPtr<IGuestOSType> ptrGuestOSType;
2176 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2177 if (SUCCEEDED(hrc2))
2178 {
2179 BOOL fIs64Bit = FALSE;
2180 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2181 if (SUCCEEDED(hrc2) && fIs64Bit)
2182 {
2183 ComObjPtr<Host> ptrHost = mParent->i_host();
2184 alock.release();
2185
2186 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2187 if (FAILED(hrc2))
2188 *aValue = FALSE;
2189 }
2190 }
2191 }
2192#endif
2193 break;
2194
2195 case CPUPropertyType_TripleFaultReset:
2196 *aValue = mHWData->mTripleFaultReset;
2197 break;
2198
2199 default:
2200 return E_INVALIDARG;
2201 }
2202 return S_OK;
2203}
2204
2205HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2206{
2207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2208
2209 HRESULT rc = i_checkStateDependency(MutableStateDep);
2210 if (FAILED(rc)) return rc;
2211
2212 switch (aProperty)
2213 {
2214 case CPUPropertyType_PAE:
2215 i_setModified(IsModified_MachineData);
2216 mHWData.backup();
2217 mHWData->mPAEEnabled = !!aValue;
2218 break;
2219
2220 case CPUPropertyType_LongMode:
2221 i_setModified(IsModified_MachineData);
2222 mHWData.backup();
2223 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2224 break;
2225
2226 case CPUPropertyType_TripleFaultReset:
2227 i_setModified(IsModified_MachineData);
2228 mHWData.backup();
2229 mHWData->mTripleFaultReset = !!aValue;
2230 break;
2231
2232 default:
2233 return E_INVALIDARG;
2234 }
2235 return S_OK;
2236}
2237
2238HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2239{
2240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2241
2242 switch(aId)
2243 {
2244 case 0x0:
2245 case 0x1:
2246 case 0x2:
2247 case 0x3:
2248 case 0x4:
2249 case 0x5:
2250 case 0x6:
2251 case 0x7:
2252 case 0x8:
2253 case 0x9:
2254 case 0xA:
2255 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2256 return E_INVALIDARG;
2257
2258 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2259 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2260 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2261 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2262 break;
2263
2264 case 0x80000000:
2265 case 0x80000001:
2266 case 0x80000002:
2267 case 0x80000003:
2268 case 0x80000004:
2269 case 0x80000005:
2270 case 0x80000006:
2271 case 0x80000007:
2272 case 0x80000008:
2273 case 0x80000009:
2274 case 0x8000000A:
2275 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2276 return E_INVALIDARG;
2277
2278 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2279 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2280 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2281 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2282 break;
2283
2284 default:
2285 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2286 }
2287 return S_OK;
2288}
2289
2290
2291HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2292{
2293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 HRESULT rc = i_checkStateDependency(MutableStateDep);
2296 if (FAILED(rc)) return rc;
2297
2298 switch(aId)
2299 {
2300 case 0x0:
2301 case 0x1:
2302 case 0x2:
2303 case 0x3:
2304 case 0x4:
2305 case 0x5:
2306 case 0x6:
2307 case 0x7:
2308 case 0x8:
2309 case 0x9:
2310 case 0xA:
2311 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2312 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2316 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2317 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2318 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2319 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2320 break;
2321
2322 case 0x80000000:
2323 case 0x80000001:
2324 case 0x80000002:
2325 case 0x80000003:
2326 case 0x80000004:
2327 case 0x80000005:
2328 case 0x80000006:
2329 case 0x80000007:
2330 case 0x80000008:
2331 case 0x80000009:
2332 case 0x8000000A:
2333 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2334 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2338 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2339 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2340 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2341 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2342 break;
2343
2344 default:
2345 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2346 }
2347 return S_OK;
2348}
2349
2350HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2351{
2352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2353
2354 HRESULT rc = i_checkStateDependency(MutableStateDep);
2355 if (FAILED(rc)) return rc;
2356
2357 switch(aId)
2358 {
2359 case 0x0:
2360 case 0x1:
2361 case 0x2:
2362 case 0x3:
2363 case 0x4:
2364 case 0x5:
2365 case 0x6:
2366 case 0x7:
2367 case 0x8:
2368 case 0x9:
2369 case 0xA:
2370 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2371 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2372 i_setModified(IsModified_MachineData);
2373 mHWData.backup();
2374 /* Invalidate leaf. */
2375 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2376 break;
2377
2378 case 0x80000000:
2379 case 0x80000001:
2380 case 0x80000002:
2381 case 0x80000003:
2382 case 0x80000004:
2383 case 0x80000005:
2384 case 0x80000006:
2385 case 0x80000007:
2386 case 0x80000008:
2387 case 0x80000009:
2388 case 0x8000000A:
2389 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2390 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2391 i_setModified(IsModified_MachineData);
2392 mHWData.backup();
2393 /* Invalidate leaf. */
2394 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2395 break;
2396
2397 default:
2398 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2399 }
2400 return S_OK;
2401}
2402
2403HRESULT Machine::removeAllCPUIDLeaves()
2404{
2405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 HRESULT rc = i_checkStateDependency(MutableStateDep);
2408 if (FAILED(rc)) return rc;
2409
2410 i_setModified(IsModified_MachineData);
2411 mHWData.backup();
2412
2413 /* Invalidate all standard leafs. */
2414 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2415 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2416
2417 /* Invalidate all extended leafs. */
2418 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2419 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2420
2421 return S_OK;
2422}
2423HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2424{
2425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2426
2427 switch(aProperty)
2428 {
2429 case HWVirtExPropertyType_Enabled:
2430 *aValue = mHWData->mHWVirtExEnabled;
2431 break;
2432
2433 case HWVirtExPropertyType_VPID:
2434 *aValue = mHWData->mHWVirtExVPIDEnabled;
2435 break;
2436
2437 case HWVirtExPropertyType_NestedPaging:
2438 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2439 break;
2440
2441 case HWVirtExPropertyType_UnrestrictedExecution:
2442 *aValue = mHWData->mHWVirtExUXEnabled;
2443 break;
2444
2445 case HWVirtExPropertyType_LargePages:
2446 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2447#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2448 *aValue = FALSE;
2449#endif
2450 break;
2451
2452 case HWVirtExPropertyType_Force:
2453 *aValue = mHWData->mHWVirtExForceEnabled;
2454 break;
2455
2456 default:
2457 return E_INVALIDARG;
2458 }
2459 return S_OK;
2460}
2461
2462HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2463{
2464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2465
2466 HRESULT rc = i_checkStateDependency(MutableStateDep);
2467 if (FAILED(rc)) return rc;
2468
2469 switch(aProperty)
2470 {
2471 case HWVirtExPropertyType_Enabled:
2472 i_setModified(IsModified_MachineData);
2473 mHWData.backup();
2474 mHWData->mHWVirtExEnabled = !!aValue;
2475 break;
2476
2477 case HWVirtExPropertyType_VPID:
2478 i_setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2481 break;
2482
2483 case HWVirtExPropertyType_NestedPaging:
2484 i_setModified(IsModified_MachineData);
2485 mHWData.backup();
2486 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2487 break;
2488
2489 case HWVirtExPropertyType_UnrestrictedExecution:
2490 i_setModified(IsModified_MachineData);
2491 mHWData.backup();
2492 mHWData->mHWVirtExUXEnabled = !!aValue;
2493 break;
2494
2495 case HWVirtExPropertyType_LargePages:
2496 i_setModified(IsModified_MachineData);
2497 mHWData.backup();
2498 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2499 break;
2500
2501 case HWVirtExPropertyType_Force:
2502 i_setModified(IsModified_MachineData);
2503 mHWData.backup();
2504 mHWData->mHWVirtExForceEnabled = !!aValue;
2505 break;
2506
2507 default:
2508 return E_INVALIDARG;
2509 }
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2515{
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2519
2520 return S_OK;
2521}
2522
2523HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2524{
2525 /* @todo (r=dmik):
2526 * 1. Allow to change the name of the snapshot folder containing snapshots
2527 * 2. Rename the folder on disk instead of just changing the property
2528 * value (to be smart and not to leave garbage). Note that it cannot be
2529 * done here because the change may be rolled back. Thus, the right
2530 * place is #saveSettings().
2531 */
2532
2533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 HRESULT rc = i_checkStateDependency(MutableStateDep);
2536 if (FAILED(rc)) return rc;
2537
2538 if (!mData->mCurrentSnapshot.isNull())
2539 return setError(E_FAIL,
2540 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2541
2542 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2543
2544 if (strSnapshotFolder.isEmpty())
2545 strSnapshotFolder = "Snapshots";
2546 int vrc = i_calculateFullPath(strSnapshotFolder,
2547 strSnapshotFolder);
2548 if (RT_FAILURE(vrc))
2549 return setError(E_FAIL,
2550 tr("Invalid snapshot folder '%s' (%Rrc)"),
2551 strSnapshotFolder.c_str(), vrc);
2552
2553 i_setModified(IsModified_MachineData);
2554 mUserData.backup();
2555
2556 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2557
2558 return S_OK;
2559}
2560
2561HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2562{
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 aMediumAttachments.resize(mMediaData->mAttachments.size());
2566 size_t i = 0;
2567 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2568 it != mMediaData->mAttachments.end(); ++it, ++i)
2569 aMediumAttachments[i] = *it;
2570
2571 return S_OK;
2572}
2573
2574HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 Assert(!!mVRDEServer);
2579
2580 aVRDEServer = mVRDEServer;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2586{
2587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 aAudioAdapter = mAudioAdapter;
2590
2591 return S_OK;
2592}
2593
2594HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2595{
2596#ifdef VBOX_WITH_VUSB
2597 clearError();
2598 MultiResult rc(S_OK);
2599
2600# ifdef VBOX_WITH_USB
2601 rc = mParent->i_host()->i_checkUSBProxyService();
2602 if (FAILED(rc)) return rc;
2603# endif
2604
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 USBControllerList data = *mUSBControllers.data();
2608 aUSBControllers.resize(data.size());
2609 size_t i = 0;
2610 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2611 aUSBControllers[i] = *it;
2612
2613 return S_OK;
2614#else
2615 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2616 * extended error info to indicate that USB is simply not available
2617 * (w/o treating it as a failure), for example, as in OSE */
2618 NOREF(aUSBControllers);
2619 ReturnComNotImplemented();
2620#endif /* VBOX_WITH_VUSB */
2621}
2622
2623HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2624{
2625#ifdef VBOX_WITH_VUSB
2626 clearError();
2627 MultiResult rc(S_OK);
2628
2629# ifdef VBOX_WITH_USB
2630 rc = mParent->i_host()->i_checkUSBProxyService();
2631 if (FAILED(rc)) return rc;
2632# endif
2633
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 aUSBDeviceFilters = mUSBDeviceFilters;
2637 return rc;
2638#else
2639 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2640 * extended error info to indicate that USB is simply not available
2641 * (w/o treating it as a failure), for example, as in OSE */
2642 NOREF(aUSBDeviceFilters);
2643 ReturnComNotImplemented();
2644#endif /* VBOX_WITH_VUSB */
2645}
2646
2647HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 aSettingsFilePath = mData->m_strConfigFileFull;
2652
2653 return S_OK;
2654}
2655
2656HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2657{
2658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2661 if (FAILED(rc)) return rc;
2662
2663 if (!mData->pMachineConfigFile->fileExists())
2664 // this is a new machine, and no config file exists yet:
2665 *aSettingsModified = TRUE;
2666 else
2667 *aSettingsModified = (mData->flModifications != 0);
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2673{
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 *aSessionState = mData->mSession.mState;
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2682{
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 aSessionName = mData->mSession.mName;
2686
2687 return S_OK;
2688}
2689
2690HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2691{
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 *aSessionPID = mData->mSession.mPID;
2695
2696 return S_OK;
2697}
2698
2699HRESULT Machine::getState(MachineState_T *aState)
2700{
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 *aState = mData->mMachineState;
2704 Assert(mData->mMachineState != MachineState_Null);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2714
2715 return S_OK;
2716}
2717
2718HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2719{
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 aStateFilePath = mSSData->strStateFilePath;
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 i_getLogFolder(aLogFolder);
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 aCurrentSnapshot = mData->mCurrentSnapshot;
2741
2742 return S_OK;
2743}
2744
2745HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2746{
2747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
2749 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2750 ? 0
2751 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2757{
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 /* Note: for machines with no snapshots, we always return FALSE
2761 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2762 * reasons :) */
2763
2764 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2765 ? FALSE
2766 : mData->mCurrentStateModified;
2767
2768 return S_OK;
2769}
2770
2771HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 aSharedFolders.resize(mHWData->mSharedFolders.size());
2776 size_t i = 0;
2777 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2778 it != mHWData->mSharedFolders.end(); ++i, ++it)
2779 aSharedFolders[i] = *it;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 *aClipboardMode = mHWData->mClipboardMode;
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2794{
2795 HRESULT rc = S_OK;
2796
2797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 alock.release();
2800 rc = i_onClipboardModeChange(aClipboardMode);
2801 alock.acquire();
2802 if (FAILED(rc)) return rc;
2803
2804 i_setModified(IsModified_MachineData);
2805 mHWData.backup();
2806 mHWData->mClipboardMode = aClipboardMode;
2807
2808 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2809 if (Global::IsOnline(mData->mMachineState))
2810 i_saveSettings(NULL);
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2816{
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aDnDMode = mHWData->mDnDMode;
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2825{
2826 HRESULT rc = S_OK;
2827
2828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 alock.release();
2831 rc = i_onDnDModeChange(aDnDMode);
2832
2833 alock.acquire();
2834 if (FAILED(rc)) return rc;
2835
2836 i_setModified(IsModified_MachineData);
2837 mHWData.backup();
2838 mHWData->mDnDMode = aDnDMode;
2839
2840 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2841 if (Global::IsOnline(mData->mMachineState))
2842 i_saveSettings(NULL);
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 try
2852 {
2853 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2854 }
2855 catch (...)
2856 {
2857 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2858 }
2859
2860 return S_OK;
2861}
2862
2863HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2864{
2865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2868 if (FAILED(rc)) return rc;
2869
2870 i_setModified(IsModified_MachineData);
2871 mHWData.backup();
2872 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2873 return rc;
2874}
2875
2876HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2877{
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879 StorageControllerList data = *mStorageControllers.data();
2880 size_t i = 0;
2881 aStorageControllers.resize(data.size());
2882 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2883 aStorageControllers[i] = *it;
2884 return S_OK;
2885}
2886
2887HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2888{
2889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 *aEnabled = mUserData->s.fTeleporterEnabled;
2892
2893 return S_OK;
2894}
2895
2896HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2897{
2898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 /* Only allow it to be set to true when PoweredOff or Aborted.
2901 (Clearing it is always permitted.) */
2902 if ( aTeleporterEnabled
2903 && mData->mRegistered
2904 && ( !i_isSessionMachine()
2905 || ( mData->mMachineState != MachineState_PoweredOff
2906 && mData->mMachineState != MachineState_Teleported
2907 && mData->mMachineState != MachineState_Aborted
2908 )
2909 )
2910 )
2911 return setError(VBOX_E_INVALID_VM_STATE,
2912 tr("The machine is not powered off (state is %s)"),
2913 Global::stringifyMachineState(mData->mMachineState));
2914
2915 i_setModified(IsModified_MachineData);
2916 mUserData.backup();
2917 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2932{
2933 if (aTeleporterPort >= _64K)
2934 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2935
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 i_setModified(IsModified_MachineData);
2942 mUserData.backup();
2943 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2949{
2950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2958{
2959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2962 if (FAILED(rc)) return rc;
2963
2964 i_setModified(IsModified_MachineData);
2965 mUserData.backup();
2966 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2972{
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2975
2976 return S_OK;
2977}
2978
2979HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2980{
2981 /*
2982 * Hash the password first.
2983 */
2984 com::Utf8Str aT = aTeleporterPassword;
2985
2986 if (!aT.isEmpty())
2987 {
2988 if (VBoxIsPasswordHashed(&aT))
2989 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2990 VBoxHashPassword(&aT);
2991 }
2992
2993 /*
2994 * Do the update.
2995 */
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2998 if (SUCCEEDED(hrc))
2999 {
3000 i_setModified(IsModified_MachineData);
3001 mUserData.backup();
3002 mUserData->s.strTeleporterPassword = aT;
3003 }
3004
3005 return hrc;
3006}
3007
3008HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3009{
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3017{
3018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 /* @todo deal with running state change. */
3021 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3022 if (FAILED(rc)) return rc;
3023
3024 i_setModified(IsModified_MachineData);
3025 mUserData.backup();
3026 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3027 return S_OK;
3028}
3029
3030HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3031{
3032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3033
3034 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3035 return S_OK;
3036}
3037
3038HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3039{
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 /* @todo deal with running state change. */
3043 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3044 if (FAILED(rc)) return rc;
3045
3046 i_setModified(IsModified_MachineData);
3047 mUserData.backup();
3048 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3049 return S_OK;
3050}
3051
3052HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3053{
3054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3057 return S_OK;
3058}
3059
3060HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3061{
3062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 /* @todo deal with running state change. */
3065 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3066 if (FAILED(rc)) return rc;
3067
3068 i_setModified(IsModified_MachineData);
3069 mUserData.backup();
3070 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3071 return S_OK;
3072}
3073
3074HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3075{
3076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3079
3080 return S_OK;
3081}
3082
3083HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3084{
3085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3086
3087 /* @todo deal with running state change. */
3088 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3089 if (FAILED(rc)) return rc;
3090
3091 i_setModified(IsModified_MachineData);
3092 mUserData.backup();
3093 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3094
3095 return S_OK;
3096}
3097
3098HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3099{
3100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3103 return S_OK;
3104}
3105
3106HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3107{
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 /* @todo deal with running state change. */
3111 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3112 if (FAILED(rc)) return rc;
3113
3114 i_setModified(IsModified_MachineData);
3115 mUserData.backup();
3116 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3117 return S_OK;
3118}
3119
3120HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3121{
3122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3125
3126 return S_OK;
3127}
3128
3129HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3130{
3131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3132
3133 /* Only allow it to be set to true when PoweredOff or Aborted.
3134 (Clearing it is always permitted.) */
3135 if ( aRTCUseUTC
3136 && mData->mRegistered
3137 && ( !i_isSessionMachine()
3138 || ( mData->mMachineState != MachineState_PoweredOff
3139 && mData->mMachineState != MachineState_Teleported
3140 && mData->mMachineState != MachineState_Aborted
3141 )
3142 )
3143 )
3144 return setError(VBOX_E_INVALID_VM_STATE,
3145 tr("The machine is not powered off (state is %s)"),
3146 Global::stringifyMachineState(mData->mMachineState));
3147
3148 i_setModified(IsModified_MachineData);
3149 mUserData.backup();
3150 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3151
3152 return S_OK;
3153}
3154
3155HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3156{
3157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3160
3161 return S_OK;
3162}
3163
3164HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3165{
3166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 HRESULT rc = i_checkStateDependency(MutableStateDep);
3169 if (FAILED(rc)) return rc;
3170
3171 i_setModified(IsModified_MachineData);
3172 mHWData.backup();
3173 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3179{
3180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 *aIOCacheSize = mHWData->mIOCacheSize;
3183
3184 return S_OK;
3185}
3186
3187HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3188{
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 HRESULT rc = i_checkStateDependency(MutableStateDep);
3192 if (FAILED(rc)) return rc;
3193
3194 i_setModified(IsModified_MachineData);
3195 mHWData.backup();
3196 mHWData->mIOCacheSize = aIOCacheSize;
3197
3198 return S_OK;
3199}
3200
3201
3202/**
3203 * @note Locks objects!
3204 */
3205HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3206 LockType_T aLockType)
3207{
3208 /* check the session state */
3209 SessionState_T state;
3210 HRESULT rc = aSession->COMGETTER(State)(&state);
3211 if (FAILED(rc)) return rc;
3212
3213 if (state != SessionState_Unlocked)
3214 return setError(VBOX_E_INVALID_OBJECT_STATE,
3215 tr("The given session is busy"));
3216
3217 // get the client's IInternalSessionControl interface
3218 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3219 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3220 E_INVALIDARG);
3221
3222 // session name (only used in some code paths)
3223 Utf8Str strSessionName;
3224
3225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3226
3227 if (!mData->mRegistered)
3228 return setError(E_UNEXPECTED,
3229 tr("The machine '%s' is not registered"),
3230 mUserData->s.strName.c_str());
3231
3232 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3233
3234 SessionState_T oldState = mData->mSession.mState;
3235 /* Hack: in case the session is closing and there is a progress object
3236 * which allows waiting for the session to be closed, take the opportunity
3237 * and do a limited wait (max. 1 second). This helps a lot when the system
3238 * is busy and thus session closing can take a little while. */
3239 if ( mData->mSession.mState == SessionState_Unlocking
3240 && mData->mSession.mProgress)
3241 {
3242 alock.release();
3243 mData->mSession.mProgress->WaitForCompletion(1000);
3244 alock.acquire();
3245 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3246 }
3247
3248 // try again now
3249 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3250 // (i.e. session machine exists)
3251 && (aLockType == LockType_Shared) // caller wants a shared link to the
3252 // existing session that holds the write lock:
3253 )
3254 {
3255 // OK, share the session... we are now dealing with three processes:
3256 // 1) VBoxSVC (where this code runs);
3257 // 2) process C: the caller's client process (who wants a shared session);
3258 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3259
3260 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3261 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3262 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3263 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3264 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3265
3266 /*
3267 * Release the lock before calling the client process. It's safe here
3268 * since the only thing to do after we get the lock again is to add
3269 * the remote control to the list (which doesn't directly influence
3270 * anything).
3271 */
3272 alock.release();
3273
3274 // get the console of the session holding the write lock (this is a remote call)
3275 ComPtr<IConsole> pConsoleW;
3276 if (mData->mSession.mLockType == LockType_VM)
3277 {
3278 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3279 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3280 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3281 if (FAILED(rc))
3282 // the failure may occur w/o any error info (from RPC), so provide one
3283 return setError(VBOX_E_VM_ERROR,
3284 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3285 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3286 }
3287
3288 // share the session machine and W's console with the caller's session
3289 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3290 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3291 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3292
3293 if (FAILED(rc))
3294 // the failure may occur w/o any error info (from RPC), so provide one
3295 return setError(VBOX_E_VM_ERROR,
3296 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3297 alock.acquire();
3298
3299 // need to revalidate the state after acquiring the lock again
3300 if (mData->mSession.mState != SessionState_Locked)
3301 {
3302 pSessionControl->Uninitialize();
3303 return setError(VBOX_E_INVALID_SESSION_STATE,
3304 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3305 mUserData->s.strName.c_str());
3306 }
3307
3308 // add the caller's session to the list
3309 mData->mSession.mRemoteControls.push_back(pSessionControl);
3310 }
3311 else if ( mData->mSession.mState == SessionState_Locked
3312 || mData->mSession.mState == SessionState_Unlocking
3313 )
3314 {
3315 // sharing not permitted, or machine still unlocking:
3316 return setError(VBOX_E_INVALID_OBJECT_STATE,
3317 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3318 mUserData->s.strName.c_str());
3319 }
3320 else
3321 {
3322 // machine is not locked: then write-lock the machine (create the session machine)
3323
3324 // must not be busy
3325 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3326
3327 // get the caller's session PID
3328 RTPROCESS pid = NIL_RTPROCESS;
3329 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3330 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3331 Assert(pid != NIL_RTPROCESS);
3332
3333 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3334
3335 if (fLaunchingVMProcess)
3336 {
3337 if (mData->mSession.mPID == NIL_RTPROCESS)
3338 {
3339 // two or more clients racing for a lock, the one which set the
3340 // session state to Spawning will win, the others will get an
3341 // error as we can't decide here if waiting a little would help
3342 // (only for shared locks this would avoid an error)
3343 return setError(VBOX_E_INVALID_OBJECT_STATE,
3344 tr("The machine '%s' already has a lock request pending"),
3345 mUserData->s.strName.c_str());
3346 }
3347
3348 // this machine is awaiting for a spawning session to be opened:
3349 // then the calling process must be the one that got started by
3350 // LaunchVMProcess()
3351
3352 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3353 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3354
3355#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3356 /* Hardened windows builds spawns three processes when a VM is
3357 launched, the 3rd one is the one that will end up here. */
3358 RTPROCESS ppid;
3359 int rc = RTProcQueryParent(pid, &ppid);
3360 if (RT_SUCCESS(rc))
3361 rc = RTProcQueryParent(ppid, &ppid);
3362 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3363 || rc == VERR_ACCESS_DENIED)
3364 {
3365 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3366 mData->mSession.mPID = pid;
3367 }
3368#endif
3369
3370 if (mData->mSession.mPID != pid)
3371 return setError(E_ACCESSDENIED,
3372 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3373 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3374 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3375 }
3376
3377 // create the mutable SessionMachine from the current machine
3378 ComObjPtr<SessionMachine> sessionMachine;
3379 sessionMachine.createObject();
3380 rc = sessionMachine->init(this);
3381 AssertComRC(rc);
3382
3383 /* NOTE: doing return from this function after this point but
3384 * before the end is forbidden since it may call SessionMachine::uninit()
3385 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3386 * lock while still holding the Machine lock in alock so that a deadlock
3387 * is possible due to the wrong lock order. */
3388
3389 if (SUCCEEDED(rc))
3390 {
3391 /*
3392 * Set the session state to Spawning to protect against subsequent
3393 * attempts to open a session and to unregister the machine after
3394 * we release the lock.
3395 */
3396 SessionState_T origState = mData->mSession.mState;
3397 mData->mSession.mState = SessionState_Spawning;
3398
3399#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3400 /* Get the client token ID to be passed to the client process */
3401 Utf8Str strTokenId;
3402 sessionMachine->i_getTokenId(strTokenId);
3403 Assert(!strTokenId.isEmpty());
3404#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3405 /* Get the client token to be passed to the client process */
3406 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3407 /* The token is now "owned" by pToken, fix refcount */
3408 if (!pToken.isNull())
3409 pToken->Release();
3410#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3411
3412 /*
3413 * Release the lock before calling the client process -- it will call
3414 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3415 * because the state is Spawning, so that LaunchVMProcess() and
3416 * LockMachine() calls will fail. This method, called before we
3417 * acquire the lock again, will fail because of the wrong PID.
3418 *
3419 * Note that mData->mSession.mRemoteControls accessed outside
3420 * the lock may not be modified when state is Spawning, so it's safe.
3421 */
3422 alock.release();
3423
3424 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3425#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3426 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3427#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3428 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3429 /* Now the token is owned by the client process. */
3430 pToken.setNull();
3431#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3432 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3433
3434 /* The failure may occur w/o any error info (from RPC), so provide one */
3435 if (FAILED(rc))
3436 setError(VBOX_E_VM_ERROR,
3437 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3438
3439 // get session name, either to remember or to compare against
3440 // the already known session name.
3441 {
3442 Bstr bstrSessionName;
3443 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3444 if (SUCCEEDED(rc2))
3445 strSessionName = bstrSessionName;
3446 }
3447
3448 if ( SUCCEEDED(rc)
3449 && fLaunchingVMProcess
3450 )
3451 {
3452 /* complete the remote session initialization */
3453
3454 /* get the console from the direct session */
3455 ComPtr<IConsole> console;
3456 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3457 ComAssertComRC(rc);
3458
3459 if (SUCCEEDED(rc) && !console)
3460 {
3461 ComAssert(!!console);
3462 rc = E_FAIL;
3463 }
3464
3465 /* assign machine & console to the remote session */
3466 if (SUCCEEDED(rc))
3467 {
3468 /*
3469 * after LaunchVMProcess(), the first and the only
3470 * entry in remoteControls is that remote session
3471 */
3472 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3473 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3474 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3475
3476 /* The failure may occur w/o any error info (from RPC), so provide one */
3477 if (FAILED(rc))
3478 setError(VBOX_E_VM_ERROR,
3479 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3480 }
3481
3482 if (FAILED(rc))
3483 pSessionControl->Uninitialize();
3484 }
3485
3486 /* acquire the lock again */
3487 alock.acquire();
3488
3489 /* Restore the session state */
3490 mData->mSession.mState = origState;
3491 }
3492
3493 // finalize spawning anyway (this is why we don't return on errors above)
3494 if (fLaunchingVMProcess)
3495 {
3496 Assert(mData->mSession.mName == strSessionName);
3497 /* Note that the progress object is finalized later */
3498 /** @todo Consider checking mData->mSession.mProgress for cancellation
3499 * around here. */
3500
3501 /* We don't reset mSession.mPID here because it is necessary for
3502 * SessionMachine::uninit() to reap the child process later. */
3503
3504 if (FAILED(rc))
3505 {
3506 /* Close the remote session, remove the remote control from the list
3507 * and reset session state to Closed (@note keep the code in sync
3508 * with the relevant part in checkForSpawnFailure()). */
3509
3510 Assert(mData->mSession.mRemoteControls.size() == 1);
3511 if (mData->mSession.mRemoteControls.size() == 1)
3512 {
3513 ErrorInfoKeeper eik;
3514 mData->mSession.mRemoteControls.front()->Uninitialize();
3515 }
3516
3517 mData->mSession.mRemoteControls.clear();
3518 mData->mSession.mState = SessionState_Unlocked;
3519 }
3520 }
3521 else
3522 {
3523 /* memorize PID of the directly opened session */
3524 if (SUCCEEDED(rc))
3525 mData->mSession.mPID = pid;
3526 }
3527
3528 if (SUCCEEDED(rc))
3529 {
3530 mData->mSession.mLockType = aLockType;
3531 /* memorize the direct session control and cache IUnknown for it */
3532 mData->mSession.mDirectControl = pSessionControl;
3533 mData->mSession.mState = SessionState_Locked;
3534 if (!fLaunchingVMProcess)
3535 mData->mSession.mName = strSessionName;
3536 /* associate the SessionMachine with this Machine */
3537 mData->mSession.mMachine = sessionMachine;
3538
3539 /* request an IUnknown pointer early from the remote party for later
3540 * identity checks (it will be internally cached within mDirectControl
3541 * at least on XPCOM) */
3542 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3543 NOREF(unk);
3544 }
3545
3546 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3547 * would break the lock order */
3548 alock.release();
3549
3550 /* uninitialize the created session machine on failure */
3551 if (FAILED(rc))
3552 sessionMachine->uninit();
3553 }
3554
3555 if (SUCCEEDED(rc))
3556 {
3557 /*
3558 * tell the client watcher thread to update the set of
3559 * machines that have open sessions
3560 */
3561 mParent->i_updateClientWatcher();
3562
3563 if (oldState != SessionState_Locked)
3564 /* fire an event */
3565 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3566 }
3567
3568 return rc;
3569}
3570
3571/**
3572 * @note Locks objects!
3573 */
3574HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3575 const com::Utf8Str &aName,
3576 const com::Utf8Str &aEnvironment,
3577 ComPtr<IProgress> &aProgress)
3578{
3579 Utf8Str strFrontend(aName);
3580 /* "emergencystop" doesn't need the session, so skip the checks/interface
3581 * retrieval. This code doesn't quite fit in here, but introducing a
3582 * special API method would be even more effort, and would require explicit
3583 * support by every API client. It's better to hide the feature a bit. */
3584 if (strFrontend != "emergencystop")
3585 CheckComArgNotNull(aSession);
3586
3587 HRESULT rc = S_OK;
3588 if (strFrontend.isEmpty())
3589 {
3590 Bstr bstrFrontend;
3591 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3592 if (FAILED(rc))
3593 return rc;
3594 strFrontend = bstrFrontend;
3595 if (strFrontend.isEmpty())
3596 {
3597 ComPtr<ISystemProperties> systemProperties;
3598 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3599 if (FAILED(rc))
3600 return rc;
3601 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3602 if (FAILED(rc))
3603 return rc;
3604 strFrontend = bstrFrontend;
3605 }
3606 /* paranoia - emergencystop is not a valid default */
3607 if (strFrontend == "emergencystop")
3608 strFrontend = Utf8Str::Empty;
3609 }
3610 /* default frontend: Qt GUI */
3611 if (strFrontend.isEmpty())
3612 strFrontend = "GUI/Qt";
3613
3614 if (strFrontend != "emergencystop")
3615 {
3616 /* check the session state */
3617 SessionState_T state;
3618 rc = aSession->COMGETTER(State)(&state);
3619 if (FAILED(rc))
3620 return rc;
3621
3622 if (state != SessionState_Unlocked)
3623 return setError(VBOX_E_INVALID_OBJECT_STATE,
3624 tr("The given session is busy"));
3625
3626 /* get the IInternalSessionControl interface */
3627 ComPtr<IInternalSessionControl> control(aSession);
3628 ComAssertMsgRet(!control.isNull(),
3629 ("No IInternalSessionControl interface"),
3630 E_INVALIDARG);
3631
3632 /* get the teleporter enable state for the progress object init. */
3633 BOOL fTeleporterEnabled;
3634 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3635 if (FAILED(rc))
3636 return rc;
3637
3638 /* create a progress object */
3639 ComObjPtr<ProgressProxy> progress;
3640 progress.createObject();
3641 rc = progress->init(mParent,
3642 static_cast<IMachine*>(this),
3643 Bstr(tr("Starting VM")).raw(),
3644 TRUE /* aCancelable */,
3645 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3646 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3647 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3648 2 /* uFirstOperationWeight */,
3649 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3650
3651 if (SUCCEEDED(rc))
3652 {
3653 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3654 if (SUCCEEDED(rc))
3655 {
3656 aProgress = progress;
3657
3658 /* signal the client watcher thread */
3659 mParent->i_updateClientWatcher();
3660
3661 /* fire an event */
3662 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3663 }
3664 }
3665 }
3666 else
3667 {
3668 /* no progress object - either instant success or failure */
3669 aProgress = NULL;
3670
3671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3672
3673 if (mData->mSession.mState != SessionState_Locked)
3674 return setError(VBOX_E_INVALID_OBJECT_STATE,
3675 tr("The machine '%s' is not locked by a session"),
3676 mUserData->s.strName.c_str());
3677
3678 /* must have a VM process associated - do not kill normal API clients
3679 * with an open session */
3680 if (!Global::IsOnline(mData->mMachineState))
3681 return setError(VBOX_E_INVALID_OBJECT_STATE,
3682 tr("The machine '%s' does not have a VM process"),
3683 mUserData->s.strName.c_str());
3684
3685 /* forcibly terminate the VM process */
3686 if (mData->mSession.mPID != NIL_RTPROCESS)
3687 RTProcTerminate(mData->mSession.mPID);
3688
3689 /* signal the client watcher thread, as most likely the client has
3690 * been terminated */
3691 mParent->i_updateClientWatcher();
3692 }
3693
3694 return rc;
3695}
3696
3697HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3698{
3699 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3700 return setError(E_INVALIDARG,
3701 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3702 aPosition, SchemaDefs::MaxBootPosition);
3703
3704 if (aDevice == DeviceType_USB)
3705 return setError(E_NOTIMPL,
3706 tr("Booting from USB device is currently not supported"));
3707
3708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3709
3710 HRESULT rc = i_checkStateDependency(MutableStateDep);
3711 if (FAILED(rc)) return rc;
3712
3713 i_setModified(IsModified_MachineData);
3714 mHWData.backup();
3715 mHWData->mBootOrder[aPosition - 1] = aDevice;
3716
3717 return S_OK;
3718}
3719
3720HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3721{
3722 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3723 return setError(E_INVALIDARG,
3724 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3725 aPosition, SchemaDefs::MaxBootPosition);
3726
3727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3728
3729 *aDevice = mHWData->mBootOrder[aPosition - 1];
3730
3731 return S_OK;
3732}
3733
3734HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3735 LONG aControllerPort,
3736 LONG aDevice,
3737 DeviceType_T aType,
3738 const ComPtr<IMedium> &aMedium)
3739{
3740 IMedium *aM = aMedium;
3741 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3742 aName.c_str(), aControllerPort, aDevice, aType, aM));
3743
3744 // request the host lock first, since might be calling Host methods for getting host drives;
3745 // next, protect the media tree all the while we're in here, as well as our member variables
3746 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3747 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3748
3749 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3750 if (FAILED(rc)) return rc;
3751
3752 /// @todo NEWMEDIA implicit machine registration
3753 if (!mData->mRegistered)
3754 return setError(VBOX_E_INVALID_OBJECT_STATE,
3755 tr("Cannot attach storage devices to an unregistered machine"));
3756
3757 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3758
3759 /* Check for an existing controller. */
3760 ComObjPtr<StorageController> ctl;
3761 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3762 if (FAILED(rc)) return rc;
3763
3764 StorageControllerType_T ctrlType;
3765 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3766 if (FAILED(rc))
3767 return setError(E_FAIL,
3768 tr("Could not get type of controller '%s'"),
3769 aName.c_str());
3770
3771 bool fSilent = false;
3772 Utf8Str strReconfig;
3773
3774 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3775 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3776 if ( mData->mMachineState == MachineState_Paused
3777 && strReconfig == "1")
3778 fSilent = true;
3779
3780 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3781 bool fHotplug = false;
3782 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3783 fHotplug = true;
3784
3785 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3786 return setError(VBOX_E_INVALID_VM_STATE,
3787 tr("Controller '%s' does not support hotplugging"),
3788 aName.c_str());
3789
3790 // check that the port and device are not out of range
3791 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3792 if (FAILED(rc)) return rc;
3793
3794 /* check if the device slot is already busy */
3795 MediumAttachment *pAttachTemp;
3796 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3797 Bstr(aName).raw(),
3798 aControllerPort,
3799 aDevice)))
3800 {
3801 Medium *pMedium = pAttachTemp->i_getMedium();
3802 if (pMedium)
3803 {
3804 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3805 return setError(VBOX_E_OBJECT_IN_USE,
3806 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3807 pMedium->i_getLocationFull().c_str(),
3808 aControllerPort,
3809 aDevice,
3810 aName.c_str());
3811 }
3812 else
3813 return setError(VBOX_E_OBJECT_IN_USE,
3814 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3815 aControllerPort, aDevice, aName.c_str());
3816 }
3817
3818 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3819 if (aMedium && medium.isNull())
3820 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3821
3822 AutoCaller mediumCaller(medium);
3823 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3824
3825 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3826
3827 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3828 && !medium.isNull()
3829 )
3830 return setError(VBOX_E_OBJECT_IN_USE,
3831 tr("Medium '%s' is already attached to this virtual machine"),
3832 medium->i_getLocationFull().c_str());
3833
3834 if (!medium.isNull())
3835 {
3836 MediumType_T mtype = medium->i_getType();
3837 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3838 // For DVDs it's not written to the config file, so needs no global config
3839 // version bump. For floppies it's a new attribute "type", which is ignored
3840 // by older VirtualBox version, so needs no global config version bump either.
3841 // For hard disks this type is not accepted.
3842 if (mtype == MediumType_MultiAttach)
3843 {
3844 // This type is new with VirtualBox 4.0 and therefore requires settings
3845 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3846 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3847 // two reasons: The medium type is a property of the media registry tree, which
3848 // can reside in the global config file (for pre-4.0 media); we would therefore
3849 // possibly need to bump the global config version. We don't want to do that though
3850 // because that might make downgrading to pre-4.0 impossible.
3851 // As a result, we can only use these two new types if the medium is NOT in the
3852 // global registry:
3853 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3854 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3855 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3856 )
3857 return setError(VBOX_E_INVALID_OBJECT_STATE,
3858 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3859 "to machines that were created with VirtualBox 4.0 or later"),
3860 medium->i_getLocationFull().c_str());
3861 }
3862 }
3863
3864 bool fIndirect = false;
3865 if (!medium.isNull())
3866 fIndirect = medium->i_isReadOnly();
3867 bool associate = true;
3868
3869 do
3870 {
3871 if ( aType == DeviceType_HardDisk
3872 && mMediaData.isBackedUp())
3873 {
3874 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3875
3876 /* check if the medium was attached to the VM before we started
3877 * changing attachments in which case the attachment just needs to
3878 * be restored */
3879 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3880 {
3881 AssertReturn(!fIndirect, E_FAIL);
3882
3883 /* see if it's the same bus/channel/device */
3884 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3885 {
3886 /* the simplest case: restore the whole attachment
3887 * and return, nothing else to do */
3888 mMediaData->mAttachments.push_back(pAttachTemp);
3889
3890 /* Reattach the medium to the VM. */
3891 if (fHotplug || fSilent)
3892 {
3893 mediumLock.release();
3894 treeLock.release();
3895 alock.release();
3896
3897 MediumLockList *pMediumLockList(new MediumLockList());
3898
3899 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3900 true /* fMediumLockWrite */,
3901 false /* fMediumLockWriteAll */,
3902 NULL,
3903 *pMediumLockList);
3904 alock.acquire();
3905 if (FAILED(rc))
3906 delete pMediumLockList;
3907 else
3908 {
3909 mData->mSession.mLockedMedia.Unlock();
3910 alock.release();
3911 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3912 mData->mSession.mLockedMedia.Lock();
3913 alock.acquire();
3914 }
3915 alock.release();
3916
3917 if (SUCCEEDED(rc))
3918 {
3919 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3920 /* Remove lock list in case of error. */
3921 if (FAILED(rc))
3922 {
3923 mData->mSession.mLockedMedia.Unlock();
3924 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3925 mData->mSession.mLockedMedia.Lock();
3926 }
3927 }
3928 }
3929
3930 return S_OK;
3931 }
3932
3933 /* bus/channel/device differ; we need a new attachment object,
3934 * but don't try to associate it again */
3935 associate = false;
3936 break;
3937 }
3938 }
3939
3940 /* go further only if the attachment is to be indirect */
3941 if (!fIndirect)
3942 break;
3943
3944 /* perform the so called smart attachment logic for indirect
3945 * attachments. Note that smart attachment is only applicable to base
3946 * hard disks. */
3947
3948 if (medium->i_getParent().isNull())
3949 {
3950 /* first, investigate the backup copy of the current hard disk
3951 * attachments to make it possible to re-attach existing diffs to
3952 * another device slot w/o losing their contents */
3953 if (mMediaData.isBackedUp())
3954 {
3955 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3956
3957 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3958 uint32_t foundLevel = 0;
3959
3960 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3961 {
3962 uint32_t level = 0;
3963 MediumAttachment *pAttach = *it;
3964 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3965 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3966 if (pMedium.isNull())
3967 continue;
3968
3969 if (pMedium->i_getBase(&level) == medium)
3970 {
3971 /* skip the hard disk if its currently attached (we
3972 * cannot attach the same hard disk twice) */
3973 if (i_findAttachment(mMediaData->mAttachments,
3974 pMedium))
3975 continue;
3976
3977 /* matched device, channel and bus (i.e. attached to the
3978 * same place) will win and immediately stop the search;
3979 * otherwise the attachment that has the youngest
3980 * descendant of medium will be used
3981 */
3982 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3983 {
3984 /* the simplest case: restore the whole attachment
3985 * and return, nothing else to do */
3986 mMediaData->mAttachments.push_back(*it);
3987
3988 /* Reattach the medium to the VM. */
3989 if (fHotplug || fSilent)
3990 {
3991 mediumLock.release();
3992 treeLock.release();
3993 alock.release();
3994
3995 MediumLockList *pMediumLockList(new MediumLockList());
3996
3997 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3998 true /* fMediumLockWrite */,
3999 false /* fMediumLockWriteAll */,
4000 NULL,
4001 *pMediumLockList);
4002 alock.acquire();
4003 if (FAILED(rc))
4004 delete pMediumLockList;
4005 else
4006 {
4007 mData->mSession.mLockedMedia.Unlock();
4008 alock.release();
4009 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4010 mData->mSession.mLockedMedia.Lock();
4011 alock.acquire();
4012 }
4013 alock.release();
4014
4015 if (SUCCEEDED(rc))
4016 {
4017 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4018 /* Remove lock list in case of error. */
4019 if (FAILED(rc))
4020 {
4021 mData->mSession.mLockedMedia.Unlock();
4022 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4023 mData->mSession.mLockedMedia.Lock();
4024 }
4025 }
4026 }
4027
4028 return S_OK;
4029 }
4030 else if ( foundIt == oldAtts.end()
4031 || level > foundLevel /* prefer younger */
4032 )
4033 {
4034 foundIt = it;
4035 foundLevel = level;
4036 }
4037 }
4038 }
4039
4040 if (foundIt != oldAtts.end())
4041 {
4042 /* use the previously attached hard disk */
4043 medium = (*foundIt)->i_getMedium();
4044 mediumCaller.attach(medium);
4045 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4046 mediumLock.attach(medium);
4047 /* not implicit, doesn't require association with this VM */
4048 fIndirect = false;
4049 associate = false;
4050 /* go right to the MediumAttachment creation */
4051 break;
4052 }
4053 }
4054
4055 /* must give up the medium lock and medium tree lock as below we
4056 * go over snapshots, which needs a lock with higher lock order. */
4057 mediumLock.release();
4058 treeLock.release();
4059
4060 /* then, search through snapshots for the best diff in the given
4061 * hard disk's chain to base the new diff on */
4062
4063 ComObjPtr<Medium> base;
4064 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4065 while (snap)
4066 {
4067 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4068
4069 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4070
4071 MediumAttachment *pAttachFound = NULL;
4072 uint32_t foundLevel = 0;
4073
4074 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4075 {
4076 MediumAttachment *pAttach = *it;
4077 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4078 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4079 if (pMedium.isNull())
4080 continue;
4081
4082 uint32_t level = 0;
4083 if (pMedium->i_getBase(&level) == medium)
4084 {
4085 /* matched device, channel and bus (i.e. attached to the
4086 * same place) will win and immediately stop the search;
4087 * otherwise the attachment that has the youngest
4088 * descendant of medium will be used
4089 */
4090 if ( pAttach->i_getDevice() == aDevice
4091 && pAttach->i_getPort() == aControllerPort
4092 && pAttach->i_getControllerName() == aName
4093 )
4094 {
4095 pAttachFound = pAttach;
4096 break;
4097 }
4098 else if ( !pAttachFound
4099 || level > foundLevel /* prefer younger */
4100 )
4101 {
4102 pAttachFound = pAttach;
4103 foundLevel = level;
4104 }
4105 }
4106 }
4107
4108 if (pAttachFound)
4109 {
4110 base = pAttachFound->i_getMedium();
4111 break;
4112 }
4113
4114 snap = snap->i_getParent();
4115 }
4116
4117 /* re-lock medium tree and the medium, as we need it below */
4118 treeLock.acquire();
4119 mediumLock.acquire();
4120
4121 /* found a suitable diff, use it as a base */
4122 if (!base.isNull())
4123 {
4124 medium = base;
4125 mediumCaller.attach(medium);
4126 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4127 mediumLock.attach(medium);
4128 }
4129 }
4130
4131 Utf8Str strFullSnapshotFolder;
4132 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4133
4134 ComObjPtr<Medium> diff;
4135 diff.createObject();
4136 // store this diff in the same registry as the parent
4137 Guid uuidRegistryParent;
4138 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4139 {
4140 // parent image has no registry: this can happen if we're attaching a new immutable
4141 // image that has not yet been attached (medium then points to the base and we're
4142 // creating the diff image for the immutable, and the parent is not yet registered);
4143 // put the parent in the machine registry then
4144 mediumLock.release();
4145 treeLock.release();
4146 alock.release();
4147 i_addMediumToRegistry(medium);
4148 alock.acquire();
4149 treeLock.acquire();
4150 mediumLock.acquire();
4151 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4152 }
4153 rc = diff->init(mParent,
4154 medium->i_getPreferredDiffFormat(),
4155 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4156 uuidRegistryParent,
4157 DeviceType_HardDisk);
4158 if (FAILED(rc)) return rc;
4159
4160 /* Apply the normal locking logic to the entire chain. */
4161 MediumLockList *pMediumLockList(new MediumLockList());
4162 mediumLock.release();
4163 treeLock.release();
4164 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4165 true /* fMediumLockWrite */,
4166 false /* fMediumLockWriteAll */,
4167 medium,
4168 *pMediumLockList);
4169 treeLock.acquire();
4170 mediumLock.acquire();
4171 if (SUCCEEDED(rc))
4172 {
4173 mediumLock.release();
4174 treeLock.release();
4175 rc = pMediumLockList->Lock();
4176 treeLock.acquire();
4177 mediumLock.acquire();
4178 if (FAILED(rc))
4179 setError(rc,
4180 tr("Could not lock medium when creating diff '%s'"),
4181 diff->i_getLocationFull().c_str());
4182 else
4183 {
4184 /* will release the lock before the potentially lengthy
4185 * operation, so protect with the special state */
4186 MachineState_T oldState = mData->mMachineState;
4187 i_setMachineState(MachineState_SettingUp);
4188
4189 mediumLock.release();
4190 treeLock.release();
4191 alock.release();
4192
4193 rc = medium->i_createDiffStorage(diff,
4194 MediumVariant_Standard,
4195 pMediumLockList,
4196 NULL /* aProgress */,
4197 true /* aWait */);
4198
4199 alock.acquire();
4200 treeLock.acquire();
4201 mediumLock.acquire();
4202
4203 i_setMachineState(oldState);
4204 }
4205 }
4206
4207 /* Unlock the media and free the associated memory. */
4208 delete pMediumLockList;
4209
4210 if (FAILED(rc)) return rc;
4211
4212 /* use the created diff for the actual attachment */
4213 medium = diff;
4214 mediumCaller.attach(medium);
4215 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4216 mediumLock.attach(medium);
4217 }
4218 while (0);
4219
4220 ComObjPtr<MediumAttachment> attachment;
4221 attachment.createObject();
4222 rc = attachment->init(this,
4223 medium,
4224 aName,
4225 aControllerPort,
4226 aDevice,
4227 aType,
4228 fIndirect,
4229 false /* fPassthrough */,
4230 false /* fTempEject */,
4231 false /* fNonRotational */,
4232 false /* fDiscard */,
4233 fHotplug /* fHotPluggable */,
4234 Utf8Str::Empty);
4235 if (FAILED(rc)) return rc;
4236
4237 if (associate && !medium.isNull())
4238 {
4239 // as the last step, associate the medium to the VM
4240 rc = medium->i_addBackReference(mData->mUuid);
4241 // here we can fail because of Deleting, or being in process of creating a Diff
4242 if (FAILED(rc)) return rc;
4243
4244 mediumLock.release();
4245 treeLock.release();
4246 alock.release();
4247 i_addMediumToRegistry(medium);
4248 alock.acquire();
4249 treeLock.acquire();
4250 mediumLock.acquire();
4251 }
4252
4253 /* success: finally remember the attachment */
4254 i_setModified(IsModified_Storage);
4255 mMediaData.backup();
4256 mMediaData->mAttachments.push_back(attachment);
4257
4258 mediumLock.release();
4259 treeLock.release();
4260 alock.release();
4261
4262 if (fHotplug || fSilent)
4263 {
4264 if (!medium.isNull())
4265 {
4266 MediumLockList *pMediumLockList(new MediumLockList());
4267
4268 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4269 true /* fMediumLockWrite */,
4270 false /* fMediumLockWriteAll */,
4271 NULL,
4272 *pMediumLockList);
4273 alock.acquire();
4274 if (FAILED(rc))
4275 delete pMediumLockList;
4276 else
4277 {
4278 mData->mSession.mLockedMedia.Unlock();
4279 alock.release();
4280 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4281 mData->mSession.mLockedMedia.Lock();
4282 alock.acquire();
4283 }
4284 alock.release();
4285 }
4286
4287 if (SUCCEEDED(rc))
4288 {
4289 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4290 /* Remove lock list in case of error. */
4291 if (FAILED(rc))
4292 {
4293 mData->mSession.mLockedMedia.Unlock();
4294 mData->mSession.mLockedMedia.Remove(attachment);
4295 mData->mSession.mLockedMedia.Lock();
4296 }
4297 }
4298 }
4299
4300 /* Save modified registries, but skip this machine as it's the caller's
4301 * job to save its settings like all other settings changes. */
4302 mParent->i_unmarkRegistryModified(i_getId());
4303 mParent->i_saveModifiedRegistries();
4304
4305 return rc;
4306}
4307
4308HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4309 LONG aDevice)
4310{
4311 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4312 aName.c_str(), aControllerPort, aDevice));
4313
4314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4315
4316 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4317 if (FAILED(rc)) return rc;
4318
4319 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4320
4321 /* Check for an existing controller. */
4322 ComObjPtr<StorageController> ctl;
4323 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4324 if (FAILED(rc)) return rc;
4325
4326 StorageControllerType_T ctrlType;
4327 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4328 if (FAILED(rc))
4329 return setError(E_FAIL,
4330 tr("Could not get type of controller '%s'"),
4331 aName.c_str());
4332
4333 bool fSilent = false;
4334 Utf8Str strReconfig;
4335
4336 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4337 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4338 if ( mData->mMachineState == MachineState_Paused
4339 && strReconfig == "1")
4340 fSilent = true;
4341
4342 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4343 bool fHotplug = false;
4344 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4345 fHotplug = true;
4346
4347 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4348 return setError(VBOX_E_INVALID_VM_STATE,
4349 tr("Controller '%s' does not support hotplugging"),
4350 aName.c_str());
4351
4352 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4353 Bstr(aName).raw(),
4354 aControllerPort,
4355 aDevice);
4356 if (!pAttach)
4357 return setError(VBOX_E_OBJECT_NOT_FOUND,
4358 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4359 aDevice, aControllerPort, aName.c_str());
4360
4361 if (fHotplug && !pAttach->i_getHotPluggable())
4362 return setError(VBOX_E_NOT_SUPPORTED,
4363 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4364 aDevice, aControllerPort, aName.c_str());
4365
4366 /*
4367 * The VM has to detach the device before we delete any implicit diffs.
4368 * If this fails we can roll back without loosing data.
4369 */
4370 if (fHotplug || fSilent)
4371 {
4372 alock.release();
4373 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4374 alock.acquire();
4375 }
4376 if (FAILED(rc)) return rc;
4377
4378 /* If we are here everything went well and we can delete the implicit now. */
4379 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4380
4381 alock.release();
4382
4383 /* Save modified registries, but skip this machine as it's the caller's
4384 * job to save its settings like all other settings changes. */
4385 mParent->i_unmarkRegistryModified(i_getId());
4386 mParent->i_saveModifiedRegistries();
4387
4388 return rc;
4389}
4390
4391HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4392 LONG aDevice, BOOL aPassthrough)
4393{
4394 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4395 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4396
4397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4398
4399 HRESULT rc = i_checkStateDependency(MutableStateDep);
4400 if (FAILED(rc)) return rc;
4401
4402 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4403
4404 if (Global::IsOnlineOrTransient(mData->mMachineState))
4405 return setError(VBOX_E_INVALID_VM_STATE,
4406 tr("Invalid machine state: %s"),
4407 Global::stringifyMachineState(mData->mMachineState));
4408
4409 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4410 Bstr(aName).raw(),
4411 aControllerPort,
4412 aDevice);
4413 if (!pAttach)
4414 return setError(VBOX_E_OBJECT_NOT_FOUND,
4415 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418
4419 i_setModified(IsModified_Storage);
4420 mMediaData.backup();
4421
4422 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4423
4424 if (pAttach->i_getType() != DeviceType_DVD)
4425 return setError(E_INVALIDARG,
4426 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4427 aDevice, aControllerPort, aName.c_str());
4428 pAttach->i_updatePassthrough(!!aPassthrough);
4429
4430 return S_OK;
4431}
4432
4433HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4434 LONG aDevice, BOOL aTemporaryEject)
4435{
4436
4437 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4438 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4439
4440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4441
4442 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4443 if (FAILED(rc)) return rc;
4444
4445 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4446 Bstr(aName).raw(),
4447 aControllerPort,
4448 aDevice);
4449 if (!pAttach)
4450 return setError(VBOX_E_OBJECT_NOT_FOUND,
4451 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4452 aDevice, aControllerPort, aName.c_str());
4453
4454
4455 i_setModified(IsModified_Storage);
4456 mMediaData.backup();
4457
4458 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4459
4460 if (pAttach->i_getType() != DeviceType_DVD)
4461 return setError(E_INVALIDARG,
4462 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4463 aDevice, aControllerPort, aName.c_str());
4464 pAttach->i_updateTempEject(!!aTemporaryEject);
4465
4466 return S_OK;
4467}
4468
4469HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4470 LONG aDevice, BOOL aNonRotational)
4471{
4472
4473 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4474 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4475
4476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4477
4478 HRESULT rc = i_checkStateDependency(MutableStateDep);
4479 if (FAILED(rc)) return rc;
4480
4481 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4482
4483 if (Global::IsOnlineOrTransient(mData->mMachineState))
4484 return setError(VBOX_E_INVALID_VM_STATE,
4485 tr("Invalid machine state: %s"),
4486 Global::stringifyMachineState(mData->mMachineState));
4487
4488 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4489 Bstr(aName).raw(),
4490 aControllerPort,
4491 aDevice);
4492 if (!pAttach)
4493 return setError(VBOX_E_OBJECT_NOT_FOUND,
4494 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4495 aDevice, aControllerPort, aName.c_str());
4496
4497
4498 i_setModified(IsModified_Storage);
4499 mMediaData.backup();
4500
4501 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4502
4503 if (pAttach->i_getType() != DeviceType_HardDisk)
4504 return setError(E_INVALIDARG,
4505 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"),
4506 aDevice, aControllerPort, aName.c_str());
4507 pAttach->i_updateNonRotational(!!aNonRotational);
4508
4509 return S_OK;
4510}
4511
4512HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4513 LONG aDevice, BOOL aDiscard)
4514{
4515
4516 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4517 aName.c_str(), aControllerPort, aDevice, aDiscard));
4518
4519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4520
4521 HRESULT rc = i_checkStateDependency(MutableStateDep);
4522 if (FAILED(rc)) return rc;
4523
4524 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4525
4526 if (Global::IsOnlineOrTransient(mData->mMachineState))
4527 return setError(VBOX_E_INVALID_VM_STATE,
4528 tr("Invalid machine state: %s"),
4529 Global::stringifyMachineState(mData->mMachineState));
4530
4531 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4532 Bstr(aName).raw(),
4533 aControllerPort,
4534 aDevice);
4535 if (!pAttach)
4536 return setError(VBOX_E_OBJECT_NOT_FOUND,
4537 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4538 aDevice, aControllerPort, aName.c_str());
4539
4540
4541 i_setModified(IsModified_Storage);
4542 mMediaData.backup();
4543
4544 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4545
4546 if (pAttach->i_getType() != DeviceType_HardDisk)
4547 return setError(E_INVALIDARG,
4548 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"),
4549 aDevice, aControllerPort, aName.c_str());
4550 pAttach->i_updateDiscard(!!aDiscard);
4551
4552 return S_OK;
4553}
4554
4555HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4556 LONG aDevice, BOOL aHotPluggable)
4557{
4558 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4559 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4560
4561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4562
4563 HRESULT rc = i_checkStateDependency(MutableStateDep);
4564 if (FAILED(rc)) return rc;
4565
4566 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4567
4568 if (Global::IsOnlineOrTransient(mData->mMachineState))
4569 return setError(VBOX_E_INVALID_VM_STATE,
4570 tr("Invalid machine state: %s"),
4571 Global::stringifyMachineState(mData->mMachineState));
4572
4573 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4574 Bstr(aName).raw(),
4575 aControllerPort,
4576 aDevice);
4577 if (!pAttach)
4578 return setError(VBOX_E_OBJECT_NOT_FOUND,
4579 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4580 aDevice, aControllerPort, aName.c_str());
4581
4582 /* Check for an existing controller. */
4583 ComObjPtr<StorageController> ctl;
4584 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4585 if (FAILED(rc)) return rc;
4586
4587 StorageControllerType_T ctrlType;
4588 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4589 if (FAILED(rc))
4590 return setError(E_FAIL,
4591 tr("Could not get type of controller '%s'"),
4592 aName.c_str());
4593
4594 if (!i_isControllerHotplugCapable(ctrlType))
4595 return setError(VBOX_E_NOT_SUPPORTED,
4596 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4597 aName.c_str());
4598
4599 i_setModified(IsModified_Storage);
4600 mMediaData.backup();
4601
4602 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4603
4604 if (pAttach->i_getType() == DeviceType_Floppy)
4605 return setError(E_INVALIDARG,
4606 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"),
4607 aDevice, aControllerPort, aName.c_str());
4608 pAttach->i_updateHotPluggable(!!aHotPluggable);
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4614 LONG aDevice)
4615{
4616 int rc = S_OK;
4617 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4618 aName.c_str(), aControllerPort, aDevice));
4619
4620 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4621
4622 return rc;
4623}
4624
4625HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4626 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4629 aName.c_str(), aControllerPort, aDevice));
4630
4631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4634 if (FAILED(rc)) return rc;
4635
4636 if (Global::IsOnlineOrTransient(mData->mMachineState))
4637 return setError(VBOX_E_INVALID_VM_STATE,
4638 tr("Invalid machine state: %s"),
4639 Global::stringifyMachineState(mData->mMachineState));
4640
4641 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4642 Bstr(aName).raw(),
4643 aControllerPort,
4644 aDevice);
4645 if (!pAttach)
4646 return setError(VBOX_E_OBJECT_NOT_FOUND,
4647 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4648 aDevice, aControllerPort, aName.c_str());
4649
4650
4651 i_setModified(IsModified_Storage);
4652 mMediaData.backup();
4653
4654 IBandwidthGroup *iB = aBandwidthGroup;
4655 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4656 if (aBandwidthGroup && group.isNull())
4657 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4658
4659 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4660
4661 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4662 if (strBandwidthGroupOld.isNotEmpty())
4663 {
4664 /* Get the bandwidth group object and release it - this must not fail. */
4665 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4666 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4667 Assert(SUCCEEDED(rc));
4668
4669 pBandwidthGroupOld->i_release();
4670 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4671 }
4672
4673 if (!group.isNull())
4674 {
4675 group->i_reference();
4676 pAttach->i_updateBandwidthGroup(group->i_getName());
4677 }
4678
4679 return S_OK;
4680}
4681
4682HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4683 LONG aControllerPort,
4684 LONG aDevice,
4685 DeviceType_T aType)
4686{
4687 HRESULT rc = S_OK;
4688
4689 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4690 aName.c_str(), aControllerPort, aDevice, aType));
4691
4692 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4693
4694 return rc;
4695}
4696
4697
4698HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4699 LONG aControllerPort,
4700 LONG aDevice,
4701 BOOL aForce)
4702{
4703 int rc = S_OK;
4704 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4705 aName.c_str(), aControllerPort, aForce));
4706
4707 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4708
4709 return rc;
4710}
4711
4712HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4713 LONG aControllerPort,
4714 LONG aDevice,
4715 const ComPtr<IMedium> &aMedium,
4716 BOOL aForce)
4717{
4718 int rc = S_OK;
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4720 aName.c_str(), aControllerPort, aDevice, aForce));
4721
4722 // request the host lock first, since might be calling Host methods for getting host drives;
4723 // next, protect the media tree all the while we're in here, as well as our member variables
4724 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4725 this->lockHandle(),
4726 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4727
4728 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4729 Bstr(aName).raw(),
4730 aControllerPort,
4731 aDevice);
4732 if (pAttach.isNull())
4733 return setError(VBOX_E_OBJECT_NOT_FOUND,
4734 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4735 aDevice, aControllerPort, aName.c_str());
4736
4737 /* Remember previously mounted medium. The medium before taking the
4738 * backup is not necessarily the same thing. */
4739 ComObjPtr<Medium> oldmedium;
4740 oldmedium = pAttach->i_getMedium();
4741
4742 IMedium *iM = aMedium;
4743 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4744 if (aMedium && pMedium.isNull())
4745 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4746
4747 AutoCaller mediumCaller(pMedium);
4748 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4749
4750 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4751 if (pMedium)
4752 {
4753 DeviceType_T mediumType = pAttach->i_getType();
4754 switch (mediumType)
4755 {
4756 case DeviceType_DVD:
4757 case DeviceType_Floppy:
4758 break;
4759
4760 default:
4761 return setError(VBOX_E_INVALID_OBJECT_STATE,
4762 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4763 aControllerPort,
4764 aDevice,
4765 aName.c_str());
4766 }
4767 }
4768
4769 i_setModified(IsModified_Storage);
4770 mMediaData.backup();
4771
4772 {
4773 // The backup operation makes the pAttach reference point to the
4774 // old settings. Re-get the correct reference.
4775 pAttach = i_findAttachment(mMediaData->mAttachments,
4776 Bstr(aName).raw(),
4777 aControllerPort,
4778 aDevice);
4779 if (!oldmedium.isNull())
4780 oldmedium->i_removeBackReference(mData->mUuid);
4781 if (!pMedium.isNull())
4782 {
4783 pMedium->i_addBackReference(mData->mUuid);
4784
4785 mediumLock.release();
4786 multiLock.release();
4787 i_addMediumToRegistry(pMedium);
4788 multiLock.acquire();
4789 mediumLock.acquire();
4790 }
4791
4792 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4793 pAttach->i_updateMedium(pMedium);
4794 }
4795
4796 i_setModified(IsModified_Storage);
4797
4798 mediumLock.release();
4799 multiLock.release();
4800 rc = i_onMediumChange(pAttach, aForce);
4801 multiLock.acquire();
4802 mediumLock.acquire();
4803
4804 /* On error roll back this change only. */
4805 if (FAILED(rc))
4806 {
4807 if (!pMedium.isNull())
4808 pMedium->i_removeBackReference(mData->mUuid);
4809 pAttach = i_findAttachment(mMediaData->mAttachments,
4810 Bstr(aName).raw(),
4811 aControllerPort,
4812 aDevice);
4813 /* If the attachment is gone in the meantime, bail out. */
4814 if (pAttach.isNull())
4815 return rc;
4816 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4817 if (!oldmedium.isNull())
4818 oldmedium->i_addBackReference(mData->mUuid);
4819 pAttach->i_updateMedium(oldmedium);
4820 }
4821
4822 mediumLock.release();
4823 multiLock.release();
4824
4825 /* Save modified registries, but skip this machine as it's the caller's
4826 * job to save its settings like all other settings changes. */
4827 mParent->i_unmarkRegistryModified(i_getId());
4828 mParent->i_saveModifiedRegistries();
4829
4830 return rc;
4831}
4832HRESULT Machine::getMedium(const com::Utf8Str &aName,
4833 LONG aControllerPort,
4834 LONG aDevice,
4835 ComPtr<IMedium> &aMedium)
4836{
4837 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4838 aName.c_str(), aControllerPort, aDevice));
4839
4840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4841
4842 aMedium = NULL;
4843
4844 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4845 Bstr(aName).raw(),
4846 aControllerPort,
4847 aDevice);
4848 if (pAttach.isNull())
4849 return setError(VBOX_E_OBJECT_NOT_FOUND,
4850 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4851 aDevice, aControllerPort, aName.c_str());
4852
4853 aMedium = pAttach->i_getMedium();
4854
4855 return S_OK;
4856}
4857
4858HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4859{
4860
4861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4862
4863 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4864
4865 return S_OK;
4866}
4867
4868HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4869{
4870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4871
4872 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4873
4874 return S_OK;
4875}
4876
4877HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4878{
4879 /* Do not assert if slot is out of range, just return the advertised
4880 status. testdriver/vbox.py triggers this in logVmInfo. */
4881 if (aSlot >= mNetworkAdapters.size())
4882 return setError(E_INVALIDARG,
4883 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4884 aSlot, mNetworkAdapters.size());
4885
4886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4887
4888 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4889
4890 return S_OK;
4891}
4892
4893HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4894{
4895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4896
4897 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4898 size_t i = 0;
4899 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4900 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4901 ++it, ++i)
4902 aKeys[i] = it->first;
4903
4904 return S_OK;
4905}
4906
4907 /**
4908 * @note Locks this object for reading.
4909 */
4910HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4911 com::Utf8Str &aValue)
4912{
4913 /* start with nothing found */
4914 aValue = "";
4915
4916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4917
4918 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4919 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4920 // found:
4921 aValue = it->second; // source is a Utf8Str
4922
4923 /* return the result to caller (may be empty) */
4924 return S_OK;
4925}
4926
4927 /**
4928 * @note Locks mParent for writing + this object for writing.
4929 */
4930HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4931{
4932 Utf8Str strOldValue; // empty
4933
4934 // locking note: we only hold the read lock briefly to look up the old value,
4935 // then release it and call the onExtraCanChange callbacks. There is a small
4936 // chance of a race insofar as the callback might be called twice if two callers
4937 // change the same key at the same time, but that's a much better solution
4938 // than the deadlock we had here before. The actual changing of the extradata
4939 // is then performed under the write lock and race-free.
4940
4941 // look up the old value first; if nothing has changed then we need not do anything
4942 {
4943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4944
4945 // For snapshots don't even think about allowing changes, extradata
4946 // is global for a machine, so there is nothing snapshot specific.
4947 if (i_isSnapshotMachine())
4948 return setError(VBOX_E_INVALID_VM_STATE,
4949 tr("Cannot set extradata for a snapshot"));
4950
4951 // check if the right IMachine instance is used
4952 if (mData->mRegistered && !i_isSessionMachine())
4953 return setError(VBOX_E_INVALID_VM_STATE,
4954 tr("Cannot set extradata for an immutable machine"));
4955
4956 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4957 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4958 strOldValue = it->second;
4959 }
4960
4961 bool fChanged;
4962 if ((fChanged = (strOldValue != aValue)))
4963 {
4964 // ask for permission from all listeners outside the locks;
4965 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4966 // lock to copy the list of callbacks to invoke
4967 Bstr error;
4968 Bstr bstrValue(aValue);
4969
4970 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4971 {
4972 const char *sep = error.isEmpty() ? "" : ": ";
4973 CBSTR err = error.raw();
4974 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4975 return setError(E_ACCESSDENIED,
4976 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4977 aKey.c_str(),
4978 aValue.c_str(),
4979 sep,
4980 err);
4981 }
4982
4983 // data is changing and change not vetoed: then write it out under the lock
4984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4985
4986 if (aValue.isEmpty())
4987 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4988 else
4989 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4990 // creates a new key if needed
4991
4992 bool fNeedsGlobalSaveSettings = false;
4993 // This saving of settings is tricky: there is no "old state" for the
4994 // extradata items at all (unlike all other settings), so the old/new
4995 // settings comparison would give a wrong result!
4996 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4997
4998 if (fNeedsGlobalSaveSettings)
4999 {
5000 // save the global settings; for that we should hold only the VirtualBox lock
5001 alock.release();
5002 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5003 mParent->i_saveSettings();
5004 }
5005 }
5006
5007 // fire notification outside the lock
5008 if (fChanged)
5009 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5010
5011 return S_OK;
5012}
5013
5014HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5015{
5016 aProgress = NULL;
5017 NOREF(aSettingsFilePath);
5018 ReturnComNotImplemented();
5019}
5020
5021HRESULT Machine::saveSettings()
5022{
5023 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5024
5025 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5026 if (FAILED(rc)) return rc;
5027
5028 /* the settings file path may never be null */
5029 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5030
5031 /* save all VM data excluding snapshots */
5032 bool fNeedsGlobalSaveSettings = false;
5033 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5034 mlock.release();
5035
5036 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5037 {
5038 // save the global settings; for that we should hold only the VirtualBox lock
5039 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5040 rc = mParent->i_saveSettings();
5041 }
5042
5043 return rc;
5044}
5045
5046
5047HRESULT Machine::discardSettings()
5048{
5049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5050
5051 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5052 if (FAILED(rc)) return rc;
5053
5054 /*
5055 * during this rollback, the session will be notified if data has
5056 * been actually changed
5057 */
5058 i_rollback(true /* aNotify */);
5059
5060 return S_OK;
5061}
5062
5063/** @note Locks objects! */
5064HRESULT Machine::unregister(AutoCaller &autoCaller,
5065 CleanupMode_T aCleanupMode,
5066 std::vector<ComPtr<IMedium> > &aMedia)
5067{
5068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5069
5070 Guid id(i_getId());
5071
5072 if (mData->mSession.mState != SessionState_Unlocked)
5073 return setError(VBOX_E_INVALID_OBJECT_STATE,
5074 tr("Cannot unregister the machine '%s' while it is locked"),
5075 mUserData->s.strName.c_str());
5076
5077 // wait for state dependents to drop to zero
5078 i_ensureNoStateDependencies();
5079
5080 if (!mData->mAccessible)
5081 {
5082 // inaccessible maschines can only be unregistered; uninitialize ourselves
5083 // here because currently there may be no unregistered that are inaccessible
5084 // (this state combination is not supported). Note releasing the caller and
5085 // leaving the lock before calling uninit()
5086 alock.release();
5087 autoCaller.release();
5088
5089 uninit();
5090
5091 mParent->i_unregisterMachine(this, id);
5092 // calls VirtualBox::i_saveSettings()
5093
5094 return S_OK;
5095 }
5096
5097 HRESULT rc = S_OK;
5098
5099 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5100 // discard saved state
5101 if (mData->mMachineState == MachineState_Saved)
5102 {
5103 // add the saved state file to the list of files the caller should delete
5104 Assert(!mSSData->strStateFilePath.isEmpty());
5105 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5106
5107 mSSData->strStateFilePath.setNull();
5108
5109 // unconditionally set the machine state to powered off, we now
5110 // know no session has locked the machine
5111 mData->mMachineState = MachineState_PoweredOff;
5112 }
5113
5114 size_t cSnapshots = 0;
5115 if (mData->mFirstSnapshot)
5116 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5117 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5118 // fail now before we start detaching media
5119 return setError(VBOX_E_INVALID_OBJECT_STATE,
5120 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5121 mUserData->s.strName.c_str(), cSnapshots);
5122
5123 // This list collects the medium objects from all medium attachments
5124 // which we will detach from the machine and its snapshots, in a specific
5125 // order which allows for closing all media without getting "media in use"
5126 // errors, simply by going through the list from the front to the back:
5127 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5128 // and must be closed before the parent media from the snapshots, or closing the parents
5129 // will fail because they still have children);
5130 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5131 // the root ("first") snapshot of the machine.
5132 MediaList llMedia;
5133
5134 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5135 && mMediaData->mAttachments.size()
5136 )
5137 {
5138 // we have media attachments: detach them all and add the Medium objects to our list
5139 if (aCleanupMode != CleanupMode_UnregisterOnly)
5140 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5141 else
5142 return setError(VBOX_E_INVALID_OBJECT_STATE,
5143 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5144 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5145 }
5146
5147 if (cSnapshots)
5148 {
5149 // add the media from the medium attachments of the snapshots to llMedia
5150 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5151 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5152 // into the children first
5153
5154 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5155 MachineState_T oldState = mData->mMachineState;
5156 mData->mMachineState = MachineState_DeletingSnapshot;
5157
5158 // make a copy of the first snapshot so the refcount does not drop to 0
5159 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5160 // because of the AutoCaller voodoo)
5161 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5162
5163 // GO!
5164 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5165
5166 mData->mMachineState = oldState;
5167 }
5168
5169 if (FAILED(rc))
5170 {
5171 i_rollbackMedia();
5172 return rc;
5173 }
5174
5175 // commit all the media changes made above
5176 i_commitMedia();
5177
5178 mData->mRegistered = false;
5179
5180 // machine lock no longer needed
5181 alock.release();
5182
5183 // return media to caller
5184 size_t i = 0;
5185 aMedia.resize(llMedia.size());
5186 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5187 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5188
5189 mParent->i_unregisterMachine(this, id);
5190 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5191
5192 return S_OK;
5193}
5194
5195/**
5196 * Task record for deleting a machine config.
5197 */
5198struct Machine::DeleteConfigTask
5199 : public Machine::Task
5200{
5201 DeleteConfigTask(Machine *m,
5202 Progress *p,
5203 const Utf8Str &t,
5204 const RTCList<ComPtr<IMedium> > &llMediums,
5205 const StringsList &llFilesToDelete)
5206 : Task(m, p, t),
5207 m_llMediums(llMediums),
5208 m_llFilesToDelete(llFilesToDelete)
5209 {}
5210
5211 void handler()
5212 {
5213 m_pMachine->i_deleteConfigHandler(*this);
5214 }
5215
5216 RTCList<ComPtr<IMedium> > m_llMediums;
5217 StringsList m_llFilesToDelete;
5218};
5219
5220/**
5221 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5222 * SessionMachine::taskHandler().
5223 *
5224 * @note Locks this object for writing.
5225 *
5226 * @param task
5227 * @return
5228 */
5229void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5230{
5231 LogFlowThisFuncEnter();
5232
5233 AutoCaller autoCaller(this);
5234 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5235 if (FAILED(autoCaller.rc()))
5236 {
5237 /* we might have been uninitialized because the session was accidentally
5238 * closed by the client, so don't assert */
5239 HRESULT rc = setError(E_FAIL,
5240 tr("The session has been accidentally closed"));
5241 task.m_pProgress->i_notifyComplete(rc);
5242 LogFlowThisFuncLeave();
5243 return;
5244 }
5245
5246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5247
5248 HRESULT rc = S_OK;
5249
5250 try
5251 {
5252 ULONG uLogHistoryCount = 3;
5253 ComPtr<ISystemProperties> systemProperties;
5254 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5255 if (FAILED(rc)) throw rc;
5256
5257 if (!systemProperties.isNull())
5258 {
5259 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5260 if (FAILED(rc)) throw rc;
5261 }
5262
5263 MachineState_T oldState = mData->mMachineState;
5264 i_setMachineState(MachineState_SettingUp);
5265 alock.release();
5266 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5267 {
5268 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5269 {
5270 AutoCaller mac(pMedium);
5271 if (FAILED(mac.rc())) throw mac.rc();
5272 Utf8Str strLocation = pMedium->i_getLocationFull();
5273 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5274 if (FAILED(rc)) throw rc;
5275 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5276 }
5277 if (pMedium->i_isMediumFormatFile())
5278 {
5279 ComPtr<IProgress> pProgress2;
5280 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5281 if (FAILED(rc)) throw rc;
5282 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5283 if (FAILED(rc)) throw rc;
5284 /* Check the result of the asynchronous process. */
5285 LONG iRc;
5286 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5287 if (FAILED(rc)) throw rc;
5288 /* If the thread of the progress object has an error, then
5289 * retrieve the error info from there, or it'll be lost. */
5290 if (FAILED(iRc))
5291 throw setError(ProgressErrorInfo(pProgress2));
5292 }
5293
5294 /* Close the medium, deliberately without checking the return
5295 * code, and without leaving any trace in the error info, as
5296 * a failure here is a very minor issue, which shouldn't happen
5297 * as above we even managed to delete the medium. */
5298 {
5299 ErrorInfoKeeper eik;
5300 pMedium->Close();
5301 }
5302 }
5303 i_setMachineState(oldState);
5304 alock.acquire();
5305
5306 // delete the files pushed on the task list by Machine::Delete()
5307 // (this includes saved states of the machine and snapshots and
5308 // medium storage files from the IMedium list passed in, and the
5309 // machine XML file)
5310 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5311 while (it != task.m_llFilesToDelete.end())
5312 {
5313 const Utf8Str &strFile = *it;
5314 LogFunc(("Deleting file %s\n", strFile.c_str()));
5315 int vrc = RTFileDelete(strFile.c_str());
5316 if (RT_FAILURE(vrc))
5317 throw setError(VBOX_E_IPRT_ERROR,
5318 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5319
5320 ++it;
5321 if (it == task.m_llFilesToDelete.end())
5322 {
5323 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5324 if (FAILED(rc)) throw rc;
5325 break;
5326 }
5327
5328 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5329 if (FAILED(rc)) throw rc;
5330 }
5331
5332 /* delete the settings only when the file actually exists */
5333 if (mData->pMachineConfigFile->fileExists())
5334 {
5335 /* Delete any backup or uncommitted XML files. Ignore failures.
5336 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5337 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5338 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5339 RTFileDelete(otherXml.c_str());
5340 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5341 RTFileDelete(otherXml.c_str());
5342
5343 /* delete the Logs folder, nothing important should be left
5344 * there (we don't check for errors because the user might have
5345 * some private files there that we don't want to delete) */
5346 Utf8Str logFolder;
5347 getLogFolder(logFolder);
5348 Assert(logFolder.length());
5349 if (RTDirExists(logFolder.c_str()))
5350 {
5351 /* Delete all VBox.log[.N] files from the Logs folder
5352 * (this must be in sync with the rotation logic in
5353 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5354 * files that may have been created by the GUI. */
5355 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5356 logFolder.c_str(), RTPATH_DELIMITER);
5357 RTFileDelete(log.c_str());
5358 log = Utf8StrFmt("%s%cVBox.png",
5359 logFolder.c_str(), RTPATH_DELIMITER);
5360 RTFileDelete(log.c_str());
5361 for (int i = uLogHistoryCount; i > 0; i--)
5362 {
5363 log = Utf8StrFmt("%s%cVBox.log.%d",
5364 logFolder.c_str(), RTPATH_DELIMITER, i);
5365 RTFileDelete(log.c_str());
5366 log = Utf8StrFmt("%s%cVBox.png.%d",
5367 logFolder.c_str(), RTPATH_DELIMITER, i);
5368 RTFileDelete(log.c_str());
5369 }
5370#if defined(RT_OS_WINDOWS)
5371 log = Utf8StrFmt("%s%cVBoxStartup.log",
5372 logFolder.c_str(), RTPATH_DELIMITER);
5373 RTFileDelete(log.c_str());
5374#endif
5375
5376 RTDirRemove(logFolder.c_str());
5377 }
5378
5379 /* delete the Snapshots folder, nothing important should be left
5380 * there (we don't check for errors because the user might have
5381 * some private files there that we don't want to delete) */
5382 Utf8Str strFullSnapshotFolder;
5383 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5384 Assert(!strFullSnapshotFolder.isEmpty());
5385 if (RTDirExists(strFullSnapshotFolder.c_str()))
5386 RTDirRemove(strFullSnapshotFolder.c_str());
5387
5388 // delete the directory that contains the settings file, but only
5389 // if it matches the VM name
5390 Utf8Str settingsDir;
5391 if (i_isInOwnDir(&settingsDir))
5392 RTDirRemove(settingsDir.c_str());
5393 }
5394
5395 alock.release();
5396
5397 mParent->i_saveModifiedRegistries();
5398 }
5399 catch (HRESULT aRC) { rc = aRC; }
5400
5401 task.m_pProgress->i_notifyComplete(rc);
5402
5403 LogFlowThisFuncLeave();
5404}
5405
5406HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5407{
5408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5409
5410 HRESULT rc = i_checkStateDependency(MutableStateDep);
5411 if (FAILED(rc)) return rc;
5412
5413 if (mData->mRegistered)
5414 return setError(VBOX_E_INVALID_VM_STATE,
5415 tr("Cannot delete settings of a registered machine"));
5416
5417 // collect files to delete
5418 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5419 if (mData->pMachineConfigFile->fileExists())
5420 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5421
5422 RTCList<ComPtr<IMedium> > llMediums;
5423 for (size_t i = 0; i < aMedia.size(); ++i)
5424 {
5425 IMedium *pIMedium(aMedia[i]);
5426 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5427 if (pMedium.isNull())
5428 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5429 SafeArray<BSTR> ids;
5430 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5431 if (FAILED(rc)) return rc;
5432 /* At this point the medium should not have any back references
5433 * anymore. If it has it is attached to another VM and *must* not
5434 * deleted. */
5435 if (ids.size() < 1)
5436 llMediums.append(pMedium);
5437 }
5438
5439 ComObjPtr<Progress> pProgress;
5440 pProgress.createObject();
5441 rc = pProgress->init(i_getVirtualBox(),
5442 static_cast<IMachine*>(this) /* aInitiator */,
5443 Bstr(tr("Deleting files")).raw(),
5444 true /* fCancellable */,
5445 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5446 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5447 if (FAILED(rc))
5448 return rc;
5449
5450 /* create and start the task on a separate thread (note that it will not
5451 * start working until we release alock) */
5452 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5453 rc = pTask->createThread();
5454 if (FAILED(rc))
5455 return rc;
5456
5457 pProgress.queryInterfaceTo(aProgress.asOutParam());
5458
5459 LogFlowFuncLeave();
5460
5461 return S_OK;
5462}
5463
5464HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5465{
5466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5467
5468 ComObjPtr<Snapshot> pSnapshot;
5469 HRESULT rc;
5470
5471 if (aNameOrId.isEmpty())
5472 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5473 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5474 else
5475 {
5476 Guid uuid(aNameOrId);
5477 if (uuid.isValid())
5478 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5479 else
5480 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5481 }
5482 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5483
5484 return rc;
5485}
5486
5487HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5488{
5489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5490
5491 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5492 if (FAILED(rc)) return rc;
5493
5494 ComObjPtr<SharedFolder> sharedFolder;
5495 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5496 if (SUCCEEDED(rc))
5497 return setError(VBOX_E_OBJECT_IN_USE,
5498 tr("Shared folder named '%s' already exists"),
5499 aName.c_str());
5500
5501 sharedFolder.createObject();
5502 rc = sharedFolder->init(i_getMachine(),
5503 aName,
5504 aHostPath,
5505 !!aWritable,
5506 !!aAutomount,
5507 true /* fFailOnError */);
5508 if (FAILED(rc)) return rc;
5509
5510 i_setModified(IsModified_SharedFolders);
5511 mHWData.backup();
5512 mHWData->mSharedFolders.push_back(sharedFolder);
5513
5514 /* inform the direct session if any */
5515 alock.release();
5516 i_onSharedFolderChange();
5517
5518 return S_OK;
5519}
5520
5521HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5522{
5523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5524
5525 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5526 if (FAILED(rc)) return rc;
5527
5528 ComObjPtr<SharedFolder> sharedFolder;
5529 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5530 if (FAILED(rc)) return rc;
5531
5532 i_setModified(IsModified_SharedFolders);
5533 mHWData.backup();
5534 mHWData->mSharedFolders.remove(sharedFolder);
5535
5536 /* inform the direct session if any */
5537 alock.release();
5538 i_onSharedFolderChange();
5539
5540 return S_OK;
5541}
5542
5543HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5544{
5545 /* start with No */
5546 *aCanShow = FALSE;
5547
5548 ComPtr<IInternalSessionControl> directControl;
5549 {
5550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5551
5552 if (mData->mSession.mState != SessionState_Locked)
5553 return setError(VBOX_E_INVALID_VM_STATE,
5554 tr("Machine is not locked for session (session state: %s)"),
5555 Global::stringifySessionState(mData->mSession.mState));
5556
5557 if (mData->mSession.mLockType == LockType_VM)
5558 directControl = mData->mSession.mDirectControl;
5559 }
5560
5561 /* ignore calls made after #OnSessionEnd() is called */
5562 if (!directControl)
5563 return S_OK;
5564
5565 LONG64 dummy;
5566 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5567}
5568
5569HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5570{
5571 ComPtr<IInternalSessionControl> directControl;
5572 {
5573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 if (mData->mSession.mState != SessionState_Locked)
5576 return setError(E_FAIL,
5577 tr("Machine is not locked for session (session state: %s)"),
5578 Global::stringifySessionState(mData->mSession.mState));
5579
5580 if (mData->mSession.mLockType == LockType_VM)
5581 directControl = mData->mSession.mDirectControl;
5582 }
5583
5584 /* ignore calls made after #OnSessionEnd() is called */
5585 if (!directControl)
5586 return S_OK;
5587
5588 BOOL dummy;
5589 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5590}
5591
5592#ifdef VBOX_WITH_GUEST_PROPS
5593/**
5594 * Look up a guest property in VBoxSVC's internal structures.
5595 */
5596HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5597 com::Utf8Str &aValue,
5598 LONG64 *aTimestamp,
5599 com::Utf8Str &aFlags) const
5600{
5601 using namespace guestProp;
5602
5603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5604 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5605
5606 if (it != mHWData->mGuestProperties.end())
5607 {
5608 char szFlags[MAX_FLAGS_LEN + 1];
5609 aValue = it->second.strValue;
5610 *aTimestamp = it->second.mTimestamp;
5611 writeFlags(it->second.mFlags, szFlags);
5612 aFlags = Utf8Str(szFlags);
5613 }
5614
5615 return S_OK;
5616}
5617
5618/**
5619 * Query the VM that a guest property belongs to for the property.
5620 * @returns E_ACCESSDENIED if the VM process is not available or not
5621 * currently handling queries and the lookup should then be done in
5622 * VBoxSVC.
5623 */
5624HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5625 com::Utf8Str &aValue,
5626 LONG64 *aTimestamp,
5627 com::Utf8Str &aFlags) const
5628{
5629 HRESULT rc = S_OK;
5630 BSTR bValue = NULL;
5631 BSTR bFlags = NULL;
5632
5633 ComPtr<IInternalSessionControl> directControl;
5634 {
5635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5636 if (mData->mSession.mLockType == LockType_VM)
5637 directControl = mData->mSession.mDirectControl;
5638 }
5639
5640 /* ignore calls made after #OnSessionEnd() is called */
5641 if (!directControl)
5642 rc = E_ACCESSDENIED;
5643 else
5644 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5645 0 /* accessMode */,
5646 &bValue, aTimestamp, &bFlags);
5647
5648 aValue = bValue;
5649 aFlags = bFlags;
5650
5651 return rc;
5652}
5653#endif // VBOX_WITH_GUEST_PROPS
5654
5655HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5656 com::Utf8Str &aValue,
5657 LONG64 *aTimestamp,
5658 com::Utf8Str &aFlags)
5659{
5660#ifndef VBOX_WITH_GUEST_PROPS
5661 ReturnComNotImplemented();
5662#else // VBOX_WITH_GUEST_PROPS
5663
5664 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5665
5666 if (rc == E_ACCESSDENIED)
5667 /* The VM is not running or the service is not (yet) accessible */
5668 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5669 return rc;
5670#endif // VBOX_WITH_GUEST_PROPS
5671}
5672
5673HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5674{
5675 LONG64 dummyTimestamp;
5676 com::Utf8Str dummyFlags;
5677 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5678 return rc;
5679
5680}
5681HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5682{
5683 com::Utf8Str dummyFlags;
5684 com::Utf8Str dummyValue;
5685 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5686 return rc;
5687}
5688
5689#ifdef VBOX_WITH_GUEST_PROPS
5690/**
5691 * Set a guest property in VBoxSVC's internal structures.
5692 */
5693HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5694 const com::Utf8Str &aFlags, bool fDelete)
5695{
5696 using namespace guestProp;
5697
5698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5699 HRESULT rc = S_OK;
5700
5701 rc = i_checkStateDependency(MutableOrSavedStateDep);
5702 if (FAILED(rc)) return rc;
5703
5704 try
5705 {
5706 uint32_t fFlags = NILFLAG;
5707 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5708 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5709
5710 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5711 if (it == mHWData->mGuestProperties.end())
5712 {
5713 if (!fDelete)
5714 {
5715 i_setModified(IsModified_MachineData);
5716 mHWData.backupEx();
5717
5718 RTTIMESPEC time;
5719 HWData::GuestProperty prop;
5720 prop.strValue = Bstr(aValue).raw();
5721 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5722 prop.mFlags = fFlags;
5723 mHWData->mGuestProperties[aName] = prop;
5724 }
5725 }
5726 else
5727 {
5728 if (it->second.mFlags & (RDONLYHOST))
5729 {
5730 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5731 }
5732 else
5733 {
5734 i_setModified(IsModified_MachineData);
5735 mHWData.backupEx();
5736
5737 /* The backupEx() operation invalidates our iterator,
5738 * so get a new one. */
5739 it = mHWData->mGuestProperties.find(aName);
5740 Assert(it != mHWData->mGuestProperties.end());
5741
5742 if (!fDelete)
5743 {
5744 RTTIMESPEC time;
5745 it->second.strValue = aValue;
5746 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5747 it->second.mFlags = fFlags;
5748 }
5749 else
5750 mHWData->mGuestProperties.erase(it);
5751 }
5752 }
5753
5754 if ( SUCCEEDED(rc)
5755 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5756 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5757 RTSTR_MAX,
5758 aName.c_str(),
5759 RTSTR_MAX,
5760 NULL)
5761 )
5762 )
5763 {
5764 alock.release();
5765
5766 mParent->i_onGuestPropertyChange(mData->mUuid,
5767 Bstr(aName).raw(),
5768 Bstr(aValue).raw(),
5769 Bstr(aFlags).raw());
5770 }
5771 }
5772 catch (std::bad_alloc &)
5773 {
5774 rc = E_OUTOFMEMORY;
5775 }
5776
5777 return rc;
5778}
5779
5780/**
5781 * Set a property on the VM that that property belongs to.
5782 * @returns E_ACCESSDENIED if the VM process is not available or not
5783 * currently handling queries and the setting should then be done in
5784 * VBoxSVC.
5785 */
5786HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5787 const com::Utf8Str &aFlags, bool fDelete)
5788{
5789 HRESULT rc;
5790
5791 try
5792 {
5793 ComPtr<IInternalSessionControl> directControl;
5794 {
5795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5796 if (mData->mSession.mLockType == LockType_VM)
5797 directControl = mData->mSession.mDirectControl;
5798 }
5799
5800 BSTR dummy = NULL; /* will not be changed (setter) */
5801 LONG64 dummy64;
5802 if (!directControl)
5803 rc = E_ACCESSDENIED;
5804 else
5805 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5806 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5807 fDelete? 2: 1 /* accessMode */,
5808 &dummy, &dummy64, &dummy);
5809 }
5810 catch (std::bad_alloc &)
5811 {
5812 rc = E_OUTOFMEMORY;
5813 }
5814
5815 return rc;
5816}
5817#endif // VBOX_WITH_GUEST_PROPS
5818
5819HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5820 const com::Utf8Str &aFlags)
5821{
5822#ifndef VBOX_WITH_GUEST_PROPS
5823 ReturnComNotImplemented();
5824#else // VBOX_WITH_GUEST_PROPS
5825 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5826 if (rc == E_ACCESSDENIED)
5827 /* The VM is not running or the service is not (yet) accessible */
5828 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5829 return rc;
5830#endif // VBOX_WITH_GUEST_PROPS
5831}
5832
5833HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5834{
5835 return setGuestProperty(aProperty, aValue, "");
5836}
5837
5838HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5839{
5840#ifndef VBOX_WITH_GUEST_PROPS
5841 ReturnComNotImplemented();
5842#else // VBOX_WITH_GUEST_PROPS
5843 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5844 if (rc == E_ACCESSDENIED)
5845 /* The VM is not running or the service is not (yet) accessible */
5846 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5847 return rc;
5848#endif // VBOX_WITH_GUEST_PROPS
5849}
5850
5851#ifdef VBOX_WITH_GUEST_PROPS
5852/**
5853 * Enumerate the guest properties in VBoxSVC's internal structures.
5854 */
5855HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5856 std::vector<com::Utf8Str> &aNames,
5857 std::vector<com::Utf8Str> &aValues,
5858 std::vector<LONG64> &aTimestamps,
5859 std::vector<com::Utf8Str> &aFlags)
5860{
5861 using namespace guestProp;
5862
5863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5864 Utf8Str strPatterns(aPatterns);
5865
5866 HWData::GuestPropertyMap propMap;
5867
5868 /*
5869 * Look for matching patterns and build up a list.
5870 */
5871 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5872 while (it != mHWData->mGuestProperties.end())
5873 {
5874 if ( strPatterns.isEmpty()
5875 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5876 RTSTR_MAX,
5877 it->first.c_str(),
5878 RTSTR_MAX,
5879 NULL)
5880 )
5881 propMap.insert(*it);
5882 ++it;
5883 }
5884
5885 alock.release();
5886
5887 /*
5888 * And build up the arrays for returning the property information.
5889 */
5890 size_t cEntries = propMap.size();
5891
5892 aNames.resize(cEntries);
5893 aValues.resize(cEntries);
5894 aTimestamps.resize(cEntries);
5895 aFlags.resize(cEntries);
5896
5897 char szFlags[MAX_FLAGS_LEN + 1];
5898 size_t i= 0;
5899 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5900 {
5901 aNames[i] = it->first;
5902 aValues[i] = it->second.strValue;
5903 aTimestamps[i] = it->second.mTimestamp;
5904 writeFlags(it->second.mFlags, szFlags);
5905 aFlags[i] = Utf8Str(szFlags);
5906 }
5907
5908 return S_OK;
5909}
5910
5911/**
5912 * Enumerate the properties managed by a VM.
5913 * @returns E_ACCESSDENIED if the VM process is not available or not
5914 * currently handling queries and the setting should then be done in
5915 * VBoxSVC.
5916 */
5917HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5918 std::vector<com::Utf8Str> &aNames,
5919 std::vector<com::Utf8Str> &aValues,
5920 std::vector<LONG64> &aTimestamps,
5921 std::vector<com::Utf8Str> &aFlags)
5922{
5923 HRESULT rc;
5924 ComPtr<IInternalSessionControl> directControl;
5925 {
5926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5927 if (mData->mSession.mLockType == LockType_VM)
5928 directControl = mData->mSession.mDirectControl;
5929 }
5930
5931 com::SafeArray<BSTR> bNames;
5932 com::SafeArray<BSTR> bValues;
5933 com::SafeArray<LONG64> bTimestamps;
5934 com::SafeArray<BSTR> bFlags;
5935
5936 if (!directControl)
5937 rc = E_ACCESSDENIED;
5938 else
5939 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5940 ComSafeArrayAsOutParam(bNames),
5941 ComSafeArrayAsOutParam(bValues),
5942 ComSafeArrayAsOutParam(bTimestamps),
5943 ComSafeArrayAsOutParam(bFlags));
5944 size_t i;
5945 aNames.resize(bNames.size());
5946 for (i = 0; i < bNames.size(); ++i)
5947 aNames[i] = Utf8Str(bNames[i]);
5948 aValues.resize(bValues.size());
5949 for (i = 0; i < bValues.size(); ++i)
5950 aValues[i] = Utf8Str(bValues[i]);
5951 aTimestamps.resize(bTimestamps.size());
5952 for (i = 0; i < bTimestamps.size(); ++i)
5953 aTimestamps[i] = bTimestamps[i];
5954 aFlags.resize(bFlags.size());
5955 for (i = 0; i < bFlags.size(); ++i)
5956 aFlags[i] = Utf8Str(bFlags[i]);
5957
5958 return rc;
5959}
5960#endif // VBOX_WITH_GUEST_PROPS
5961HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5962 std::vector<com::Utf8Str> &aNames,
5963 std::vector<com::Utf8Str> &aValues,
5964 std::vector<LONG64> &aTimestamps,
5965 std::vector<com::Utf8Str> &aFlags)
5966{
5967#ifndef VBOX_WITH_GUEST_PROPS
5968 ReturnComNotImplemented();
5969#else // VBOX_WITH_GUEST_PROPS
5970
5971 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5972
5973 if (rc == E_ACCESSDENIED)
5974 /* The VM is not running or the service is not (yet) accessible */
5975 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5976 return rc;
5977#endif // VBOX_WITH_GUEST_PROPS
5978}
5979
5980HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5981 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5982{
5983 MediaData::AttachmentList atts;
5984
5985 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5986 if (FAILED(rc)) return rc;
5987
5988 size_t i = 0;
5989 aMediumAttachments.resize(atts.size());
5990 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5991 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5992
5993 return S_OK;
5994}
5995
5996HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5997 LONG aControllerPort,
5998 LONG aDevice,
5999 ComPtr<IMediumAttachment> &aAttachment)
6000{
6001 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6002 aName.c_str(), aControllerPort, aDevice));
6003
6004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6005
6006 aAttachment = NULL;
6007
6008 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6009 Bstr(aName).raw(),
6010 aControllerPort,
6011 aDevice);
6012 if (pAttach.isNull())
6013 return setError(VBOX_E_OBJECT_NOT_FOUND,
6014 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6015 aDevice, aControllerPort, aName.c_str());
6016
6017 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6018
6019 return S_OK;
6020}
6021
6022
6023HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6024 StorageBus_T aConnectionType,
6025 ComPtr<IStorageController> &aController)
6026{
6027 if ( (aConnectionType <= StorageBus_Null)
6028 || (aConnectionType > StorageBus_USB))
6029 return setError(E_INVALIDARG,
6030 tr("Invalid connection type: %d"),
6031 aConnectionType);
6032
6033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 HRESULT rc = i_checkStateDependency(MutableStateDep);
6036 if (FAILED(rc)) return rc;
6037
6038 /* try to find one with the name first. */
6039 ComObjPtr<StorageController> ctrl;
6040
6041 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6042 if (SUCCEEDED(rc))
6043 return setError(VBOX_E_OBJECT_IN_USE,
6044 tr("Storage controller named '%s' already exists"),
6045 aName.c_str());
6046
6047 ctrl.createObject();
6048
6049 /* get a new instance number for the storage controller */
6050 ULONG ulInstance = 0;
6051 bool fBootable = true;
6052 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6053 it != mStorageControllers->end();
6054 ++it)
6055 {
6056 if ((*it)->i_getStorageBus() == aConnectionType)
6057 {
6058 ULONG ulCurInst = (*it)->i_getInstance();
6059
6060 if (ulCurInst >= ulInstance)
6061 ulInstance = ulCurInst + 1;
6062
6063 /* Only one controller of each type can be marked as bootable. */
6064 if ((*it)->i_getBootable())
6065 fBootable = false;
6066 }
6067 }
6068
6069 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6070 if (FAILED(rc)) return rc;
6071
6072 i_setModified(IsModified_Storage);
6073 mStorageControllers.backup();
6074 mStorageControllers->push_back(ctrl);
6075
6076 ctrl.queryInterfaceTo(aController.asOutParam());
6077
6078 /* inform the direct session if any */
6079 alock.release();
6080 i_onStorageControllerChange();
6081
6082 return S_OK;
6083}
6084
6085HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6086 ComPtr<IStorageController> &aStorageController)
6087{
6088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6089
6090 ComObjPtr<StorageController> ctrl;
6091
6092 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6093 if (SUCCEEDED(rc))
6094 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6095
6096 return rc;
6097}
6098
6099HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6100 ComPtr<IStorageController> &aStorageController)
6101{
6102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6103
6104 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6105 it != mStorageControllers->end();
6106 ++it)
6107 {
6108 if ((*it)->i_getInstance() == aInstance)
6109 {
6110 (*it).queryInterfaceTo(aStorageController.asOutParam());
6111 return S_OK;
6112 }
6113 }
6114
6115 return setError(VBOX_E_OBJECT_NOT_FOUND,
6116 tr("Could not find a storage controller with instance number '%lu'"),
6117 aInstance);
6118}
6119
6120HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6121{
6122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6123
6124 HRESULT rc = i_checkStateDependency(MutableStateDep);
6125 if (FAILED(rc)) return rc;
6126
6127 ComObjPtr<StorageController> ctrl;
6128
6129 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6130 if (SUCCEEDED(rc))
6131 {
6132 /* Ensure that only one controller of each type is marked as bootable. */
6133 if (aBootable == TRUE)
6134 {
6135 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6136 it != mStorageControllers->end();
6137 ++it)
6138 {
6139 ComObjPtr<StorageController> aCtrl = (*it);
6140
6141 if ( (aCtrl->i_getName() != aName)
6142 && aCtrl->i_getBootable() == TRUE
6143 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6144 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6145 {
6146 aCtrl->i_setBootable(FALSE);
6147 break;
6148 }
6149 }
6150 }
6151
6152 if (SUCCEEDED(rc))
6153 {
6154 ctrl->i_setBootable(aBootable);
6155 i_setModified(IsModified_Storage);
6156 }
6157 }
6158
6159 if (SUCCEEDED(rc))
6160 {
6161 /* inform the direct session if any */
6162 alock.release();
6163 i_onStorageControllerChange();
6164 }
6165
6166 return rc;
6167}
6168
6169HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6170{
6171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6172
6173 HRESULT rc = i_checkStateDependency(MutableStateDep);
6174 if (FAILED(rc)) return rc;
6175
6176 ComObjPtr<StorageController> ctrl;
6177 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6178 if (FAILED(rc)) return rc;
6179
6180 {
6181 /* find all attached devices to the appropriate storage controller and detach them all */
6182 // make a temporary list because detachDevice invalidates iterators into
6183 // mMediaData->mAttachments
6184 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6185
6186 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6187 it != llAttachments2.end();
6188 ++it)
6189 {
6190 MediumAttachment *pAttachTemp = *it;
6191
6192 AutoCaller localAutoCaller(pAttachTemp);
6193 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6194
6195 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6196
6197 if (pAttachTemp->i_getControllerName() == aName)
6198 {
6199 rc = i_detachDevice(pAttachTemp, alock, NULL);
6200 if (FAILED(rc)) return rc;
6201 }
6202 }
6203 }
6204
6205 /* We can remove it now. */
6206 i_setModified(IsModified_Storage);
6207 mStorageControllers.backup();
6208
6209 ctrl->i_unshare();
6210
6211 mStorageControllers->remove(ctrl);
6212
6213 /* inform the direct session if any */
6214 alock.release();
6215 i_onStorageControllerChange();
6216
6217 return S_OK;
6218}
6219
6220HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6221 ComPtr<IUSBController> &aController)
6222{
6223 if ( (aType <= USBControllerType_Null)
6224 || (aType >= USBControllerType_Last))
6225 return setError(E_INVALIDARG,
6226 tr("Invalid USB controller type: %d"),
6227 aType);
6228
6229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6230
6231 HRESULT rc = i_checkStateDependency(MutableStateDep);
6232 if (FAILED(rc)) return rc;
6233
6234 /* try to find one with the same type first. */
6235 ComObjPtr<USBController> ctrl;
6236
6237 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6238 if (SUCCEEDED(rc))
6239 return setError(VBOX_E_OBJECT_IN_USE,
6240 tr("USB controller named '%s' already exists"),
6241 aName.c_str());
6242
6243 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6244 ULONG maxInstances;
6245 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6246 if (FAILED(rc))
6247 return rc;
6248
6249 ULONG cInstances = i_getUSBControllerCountByType(aType);
6250 if (cInstances >= maxInstances)
6251 return setError(E_INVALIDARG,
6252 tr("Too many USB controllers of this type"));
6253
6254 ctrl.createObject();
6255
6256 rc = ctrl->init(this, aName, aType);
6257 if (FAILED(rc)) return rc;
6258
6259 i_setModified(IsModified_USB);
6260 mUSBControllers.backup();
6261 mUSBControllers->push_back(ctrl);
6262
6263 ctrl.queryInterfaceTo(aController.asOutParam());
6264
6265 /* inform the direct session if any */
6266 alock.release();
6267 i_onUSBControllerChange();
6268
6269 return S_OK;
6270}
6271
6272HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6273{
6274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6275
6276 ComObjPtr<USBController> ctrl;
6277
6278 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6279 if (SUCCEEDED(rc))
6280 ctrl.queryInterfaceTo(aController.asOutParam());
6281
6282 return rc;
6283}
6284
6285HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6286 ULONG *aControllers)
6287{
6288 if ( (aType <= USBControllerType_Null)
6289 || (aType >= USBControllerType_Last))
6290 return setError(E_INVALIDARG,
6291 tr("Invalid USB controller type: %d"),
6292 aType);
6293
6294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6295
6296 ComObjPtr<USBController> ctrl;
6297
6298 *aControllers = i_getUSBControllerCountByType(aType);
6299
6300 return S_OK;
6301}
6302
6303HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6304{
6305
6306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6307
6308 HRESULT rc = i_checkStateDependency(MutableStateDep);
6309 if (FAILED(rc)) return rc;
6310
6311 ComObjPtr<USBController> ctrl;
6312 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6313 if (FAILED(rc)) return rc;
6314
6315 i_setModified(IsModified_USB);
6316 mUSBControllers.backup();
6317
6318 ctrl->i_unshare();
6319
6320 mUSBControllers->remove(ctrl);
6321
6322 /* inform the direct session if any */
6323 alock.release();
6324 i_onUSBControllerChange();
6325
6326 return S_OK;
6327}
6328
6329HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6330 ULONG *aOriginX,
6331 ULONG *aOriginY,
6332 ULONG *aWidth,
6333 ULONG *aHeight,
6334 BOOL *aEnabled)
6335{
6336 uint32_t u32OriginX= 0;
6337 uint32_t u32OriginY= 0;
6338 uint32_t u32Width = 0;
6339 uint32_t u32Height = 0;
6340 uint16_t u16Flags = 0;
6341
6342 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6343 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6344 if (RT_FAILURE(vrc))
6345 {
6346#ifdef RT_OS_WINDOWS
6347 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6348 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6349 * So just assign fEnable to TRUE again.
6350 * The right fix would be to change GUI API wrappers to make sure that parameters
6351 * are changed only if API succeeds.
6352 */
6353 *aEnabled = TRUE;
6354#endif
6355 return setError(VBOX_E_IPRT_ERROR,
6356 tr("Saved guest size is not available (%Rrc)"),
6357 vrc);
6358 }
6359
6360 *aOriginX = u32OriginX;
6361 *aOriginY = u32OriginY;
6362 *aWidth = u32Width;
6363 *aHeight = u32Height;
6364 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6365
6366 return S_OK;
6367}
6368
6369HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6370 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6371{
6372 if (aScreenId != 0)
6373 return E_NOTIMPL;
6374
6375 if ( aBitmapFormat != BitmapFormat_BGR0
6376 && aBitmapFormat != BitmapFormat_BGRA
6377 && aBitmapFormat != BitmapFormat_RGBA
6378 && aBitmapFormat != BitmapFormat_PNG)
6379 return setError(E_NOTIMPL,
6380 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 uint8_t *pu8Data = NULL;
6385 uint32_t cbData = 0;
6386 uint32_t u32Width = 0;
6387 uint32_t u32Height = 0;
6388
6389 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6390
6391 if (RT_FAILURE(vrc))
6392 return setError(VBOX_E_IPRT_ERROR,
6393 tr("Saved thumbnail data is not available (%Rrc)"),
6394 vrc);
6395
6396 HRESULT hr = S_OK;
6397
6398 *aWidth = u32Width;
6399 *aHeight = u32Height;
6400
6401 if (cbData > 0)
6402 {
6403 /* Convert pixels to the format expected by the API caller. */
6404 if (aBitmapFormat == BitmapFormat_BGR0)
6405 {
6406 /* [0] B, [1] G, [2] R, [3] 0. */
6407 aData.resize(cbData);
6408 memcpy(&aData.front(), pu8Data, cbData);
6409 }
6410 else if (aBitmapFormat == BitmapFormat_BGRA)
6411 {
6412 /* [0] B, [1] G, [2] R, [3] A. */
6413 aData.resize(cbData);
6414 for (uint32_t i = 0; i < cbData; i += 4)
6415 {
6416 aData[i] = pu8Data[i];
6417 aData[i + 1] = pu8Data[i + 1];
6418 aData[i + 2] = pu8Data[i + 2];
6419 aData[i + 3] = 0xff;
6420 }
6421 }
6422 else if (aBitmapFormat == BitmapFormat_RGBA)
6423 {
6424 /* [0] R, [1] G, [2] B, [3] A. */
6425 aData.resize(cbData);
6426 for (uint32_t i = 0; i < cbData; i += 4)
6427 {
6428 aData[i] = pu8Data[i + 2];
6429 aData[i + 1] = pu8Data[i + 1];
6430 aData[i + 2] = pu8Data[i];
6431 aData[i + 3] = 0xff;
6432 }
6433 }
6434 else if (aBitmapFormat == BitmapFormat_PNG)
6435 {
6436 uint8_t *pu8PNG = NULL;
6437 uint32_t cbPNG = 0;
6438 uint32_t cxPNG = 0;
6439 uint32_t cyPNG = 0;
6440
6441 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6442
6443 if (RT_SUCCESS(vrc))
6444 {
6445 aData.resize(cbPNG);
6446 if (cbPNG)
6447 memcpy(&aData.front(), pu8PNG, cbPNG);
6448 }
6449 else
6450 hr = setError(VBOX_E_IPRT_ERROR,
6451 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6452 vrc);
6453
6454 RTMemFree(pu8PNG);
6455 }
6456 }
6457
6458 freeSavedDisplayScreenshot(pu8Data);
6459
6460 return hr;
6461}
6462
6463HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6464 ULONG *aWidth,
6465 ULONG *aHeight,
6466 std::vector<BitmapFormat_T> &aBitmapFormats)
6467{
6468 if (aScreenId != 0)
6469 return E_NOTIMPL;
6470
6471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6472
6473 uint8_t *pu8Data = NULL;
6474 uint32_t cbData = 0;
6475 uint32_t u32Width = 0;
6476 uint32_t u32Height = 0;
6477
6478 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6479
6480 if (RT_FAILURE(vrc))
6481 return setError(VBOX_E_IPRT_ERROR,
6482 tr("Saved screenshot data is not available (%Rrc)"),
6483 vrc);
6484
6485 *aWidth = u32Width;
6486 *aHeight = u32Height;
6487 aBitmapFormats.resize(1);
6488 aBitmapFormats[0] = BitmapFormat_PNG;
6489
6490 freeSavedDisplayScreenshot(pu8Data);
6491
6492 return S_OK;
6493}
6494
6495HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6496 BitmapFormat_T aBitmapFormat,
6497 ULONG *aWidth,
6498 ULONG *aHeight,
6499 std::vector<BYTE> &aData)
6500{
6501 if (aScreenId != 0)
6502 return E_NOTIMPL;
6503
6504 if (aBitmapFormat != BitmapFormat_PNG)
6505 return E_NOTIMPL;
6506
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 uint8_t *pu8Data = NULL;
6510 uint32_t cbData = 0;
6511 uint32_t u32Width = 0;
6512 uint32_t u32Height = 0;
6513
6514 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6515
6516 if (RT_FAILURE(vrc))
6517 return setError(VBOX_E_IPRT_ERROR,
6518 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6519 vrc);
6520
6521 *aWidth = u32Width;
6522 *aHeight = u32Height;
6523
6524 aData.resize(cbData);
6525 if (cbData)
6526 memcpy(&aData.front(), pu8Data, cbData);
6527
6528 freeSavedDisplayScreenshot(pu8Data);
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::hotPlugCPU(ULONG aCpu)
6534{
6535 HRESULT rc = S_OK;
6536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6537
6538 if (!mHWData->mCPUHotPlugEnabled)
6539 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6540
6541 if (aCpu >= mHWData->mCPUCount)
6542 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6543
6544 if (mHWData->mCPUAttached[aCpu])
6545 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6546
6547 alock.release();
6548 rc = i_onCPUChange(aCpu, false);
6549 alock.acquire();
6550 if (FAILED(rc)) return rc;
6551
6552 i_setModified(IsModified_MachineData);
6553 mHWData.backup();
6554 mHWData->mCPUAttached[aCpu] = true;
6555
6556 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6557 if (Global::IsOnline(mData->mMachineState))
6558 i_saveSettings(NULL);
6559
6560 return S_OK;
6561}
6562
6563HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6564{
6565 HRESULT rc = S_OK;
6566
6567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6568
6569 if (!mHWData->mCPUHotPlugEnabled)
6570 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6571
6572 if (aCpu >= SchemaDefs::MaxCPUCount)
6573 return setError(E_INVALIDARG,
6574 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6575 SchemaDefs::MaxCPUCount);
6576
6577 if (!mHWData->mCPUAttached[aCpu])
6578 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6579
6580 /* CPU 0 can't be detached */
6581 if (aCpu == 0)
6582 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6583
6584 alock.release();
6585 rc = i_onCPUChange(aCpu, true);
6586 alock.acquire();
6587 if (FAILED(rc)) return rc;
6588
6589 i_setModified(IsModified_MachineData);
6590 mHWData.backup();
6591 mHWData->mCPUAttached[aCpu] = false;
6592
6593 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6594 if (Global::IsOnline(mData->mMachineState))
6595 i_saveSettings(NULL);
6596
6597 return S_OK;
6598}
6599
6600HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6601{
6602 *aAttached = false;
6603
6604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6605
6606 /* If hotplug is enabled the CPU is always enabled. */
6607 if (!mHWData->mCPUHotPlugEnabled)
6608 {
6609 if (aCpu < mHWData->mCPUCount)
6610 *aAttached = true;
6611 }
6612 else
6613 {
6614 if (aCpu < SchemaDefs::MaxCPUCount)
6615 *aAttached = mHWData->mCPUAttached[aCpu];
6616 }
6617
6618 return S_OK;
6619}
6620
6621HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6622{
6623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 Utf8Str log = i_queryLogFilename(aIdx);
6626 if (!RTFileExists(log.c_str()))
6627 log.setNull();
6628 aFilename = log;
6629
6630 return S_OK;
6631}
6632
6633HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6634{
6635 if (aSize < 0)
6636 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6637
6638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 HRESULT rc = S_OK;
6641 Utf8Str log = i_queryLogFilename(aIdx);
6642
6643 /* do not unnecessarily hold the lock while doing something which does
6644 * not need the lock and potentially takes a long time. */
6645 alock.release();
6646
6647 /* Limit the chunk size to 32K for now, as that gives better performance
6648 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6649 * One byte expands to approx. 25 bytes of breathtaking XML. */
6650 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6651 aData.resize(cbData);
6652
6653 RTFILE LogFile;
6654 int vrc = RTFileOpen(&LogFile, log.c_str(),
6655 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6656 if (RT_SUCCESS(vrc))
6657 {
6658 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6659 if (RT_SUCCESS(vrc))
6660 aData.resize(cbData);
6661 else
6662 rc = setError(VBOX_E_IPRT_ERROR,
6663 tr("Could not read log file '%s' (%Rrc)"),
6664 log.c_str(), vrc);
6665 RTFileClose(LogFile);
6666 }
6667 else
6668 rc = setError(VBOX_E_IPRT_ERROR,
6669 tr("Could not open log file '%s' (%Rrc)"),
6670 log.c_str(), vrc);
6671
6672 if (FAILED(rc))
6673 aData.resize(0);
6674
6675 return rc;
6676}
6677
6678
6679/**
6680 * Currently this method doesn't attach device to the running VM,
6681 * just makes sure it's plugged on next VM start.
6682 */
6683HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6684{
6685 // lock scope
6686 {
6687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 HRESULT rc = i_checkStateDependency(MutableStateDep);
6690 if (FAILED(rc)) return rc;
6691
6692 ChipsetType_T aChipset = ChipsetType_PIIX3;
6693 COMGETTER(ChipsetType)(&aChipset);
6694
6695 if (aChipset != ChipsetType_ICH9)
6696 {
6697 return setError(E_INVALIDARG,
6698 tr("Host PCI attachment only supported with ICH9 chipset"));
6699 }
6700
6701 // check if device with this host PCI address already attached
6702 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6703 it != mHWData->mPCIDeviceAssignments.end();
6704 ++it)
6705 {
6706 LONG iHostAddress = -1;
6707 ComPtr<PCIDeviceAttachment> pAttach;
6708 pAttach = *it;
6709 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6710 if (iHostAddress == aHostAddress)
6711 return setError(E_INVALIDARG,
6712 tr("Device with host PCI address already attached to this VM"));
6713 }
6714
6715 ComObjPtr<PCIDeviceAttachment> pda;
6716 char name[32];
6717
6718 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6719 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6720 Bstr bname(name);
6721 pda.createObject();
6722 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6723 i_setModified(IsModified_MachineData);
6724 mHWData.backup();
6725 mHWData->mPCIDeviceAssignments.push_back(pda);
6726 }
6727
6728 return S_OK;
6729}
6730
6731/**
6732 * Currently this method doesn't detach device from the running VM,
6733 * just makes sure it's not plugged on next VM start.
6734 */
6735HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6736{
6737 ComObjPtr<PCIDeviceAttachment> pAttach;
6738 bool fRemoved = false;
6739 HRESULT rc;
6740
6741 // lock scope
6742 {
6743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6744
6745 rc = i_checkStateDependency(MutableStateDep);
6746 if (FAILED(rc)) return rc;
6747
6748 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6749 it != mHWData->mPCIDeviceAssignments.end();
6750 ++it)
6751 {
6752 LONG iHostAddress = -1;
6753 pAttach = *it;
6754 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6755 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6756 {
6757 i_setModified(IsModified_MachineData);
6758 mHWData.backup();
6759 mHWData->mPCIDeviceAssignments.remove(pAttach);
6760 fRemoved = true;
6761 break;
6762 }
6763 }
6764 }
6765
6766
6767 /* Fire event outside of the lock */
6768 if (fRemoved)
6769 {
6770 Assert(!pAttach.isNull());
6771 ComPtr<IEventSource> es;
6772 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6773 Assert(SUCCEEDED(rc));
6774 Bstr mid;
6775 rc = this->COMGETTER(Id)(mid.asOutParam());
6776 Assert(SUCCEEDED(rc));
6777 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6778 }
6779
6780 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6781 tr("No host PCI device %08x attached"),
6782 aHostAddress
6783 );
6784}
6785
6786HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6787{
6788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6789
6790 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6791
6792 size_t i = 0;
6793 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6794 it != mHWData->mPCIDeviceAssignments.end();
6795 ++i, ++it)
6796 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6797
6798 return S_OK;
6799}
6800
6801HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6802{
6803 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6804
6805 return S_OK;
6806}
6807
6808HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6809{
6810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6811
6812 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6813
6814 return S_OK;
6815}
6816
6817HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6818{
6819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6820 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6821 if (SUCCEEDED(hrc))
6822 {
6823 hrc = mHWData.backupEx();
6824 if (SUCCEEDED(hrc))
6825 {
6826 i_setModified(IsModified_MachineData);
6827 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6828 }
6829 }
6830 return hrc;
6831}
6832
6833HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6834{
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6837 return S_OK;
6838}
6839
6840HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6841{
6842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6843 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6844 if (SUCCEEDED(hrc))
6845 {
6846 hrc = mHWData.backupEx();
6847 if (SUCCEEDED(hrc))
6848 {
6849 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6850 if (SUCCEEDED(hrc))
6851 i_setModified(IsModified_MachineData);
6852 }
6853 }
6854 return hrc;
6855}
6856
6857HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6858{
6859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6860
6861 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6862
6863 return S_OK;
6864}
6865
6866HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6867{
6868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6869 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6870 if (SUCCEEDED(hrc))
6871 {
6872 hrc = mHWData.backupEx();
6873 if (SUCCEEDED(hrc))
6874 {
6875 i_setModified(IsModified_MachineData);
6876 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6877 }
6878 }
6879 return hrc;
6880}
6881
6882HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6883{
6884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6885
6886 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6887
6888 return S_OK;
6889}
6890
6891HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6892{
6893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6894
6895 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6896 if ( SUCCEEDED(hrc)
6897 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6898 {
6899 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6900 int vrc;
6901
6902 if (aAutostartEnabled)
6903 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6904 else
6905 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6906
6907 if (RT_SUCCESS(vrc))
6908 {
6909 hrc = mHWData.backupEx();
6910 if (SUCCEEDED(hrc))
6911 {
6912 i_setModified(IsModified_MachineData);
6913 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6914 }
6915 }
6916 else if (vrc == VERR_NOT_SUPPORTED)
6917 hrc = setError(VBOX_E_NOT_SUPPORTED,
6918 tr("The VM autostart feature is not supported on this platform"));
6919 else if (vrc == VERR_PATH_NOT_FOUND)
6920 hrc = setError(E_FAIL,
6921 tr("The path to the autostart database is not set"));
6922 else
6923 hrc = setError(E_UNEXPECTED,
6924 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6925 aAutostartEnabled ? "Adding" : "Removing",
6926 mUserData->s.strName.c_str(), vrc);
6927 }
6928 return hrc;
6929}
6930
6931HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6932{
6933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6934
6935 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6936
6937 return S_OK;
6938}
6939
6940HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6941{
6942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6943 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6944 if (SUCCEEDED(hrc))
6945 {
6946 hrc = mHWData.backupEx();
6947 if (SUCCEEDED(hrc))
6948 {
6949 i_setModified(IsModified_MachineData);
6950 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6951 }
6952 }
6953 return hrc;
6954}
6955
6956HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6957{
6958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6959
6960 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6961
6962 return S_OK;
6963}
6964
6965HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6966{
6967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6968 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6969 if ( SUCCEEDED(hrc)
6970 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6971 {
6972 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6973 int vrc;
6974
6975 if (aAutostopType != AutostopType_Disabled)
6976 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6977 else
6978 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6979
6980 if (RT_SUCCESS(vrc))
6981 {
6982 hrc = mHWData.backupEx();
6983 if (SUCCEEDED(hrc))
6984 {
6985 i_setModified(IsModified_MachineData);
6986 mHWData->mAutostart.enmAutostopType = aAutostopType;
6987 }
6988 }
6989 else if (vrc == VERR_NOT_SUPPORTED)
6990 hrc = setError(VBOX_E_NOT_SUPPORTED,
6991 tr("The VM autostop feature is not supported on this platform"));
6992 else if (vrc == VERR_PATH_NOT_FOUND)
6993 hrc = setError(E_FAIL,
6994 tr("The path to the autostart database is not set"));
6995 else
6996 hrc = setError(E_UNEXPECTED,
6997 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6998 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6999 mUserData->s.strName.c_str(), vrc);
7000 }
7001 return hrc;
7002}
7003
7004HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7005{
7006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7007
7008 aDefaultFrontend = mHWData->mDefaultFrontend;
7009
7010 return S_OK;
7011}
7012
7013HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7014{
7015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7016 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7017 if (SUCCEEDED(hrc))
7018 {
7019 hrc = mHWData.backupEx();
7020 if (SUCCEEDED(hrc))
7021 {
7022 i_setModified(IsModified_MachineData);
7023 mHWData->mDefaultFrontend = aDefaultFrontend;
7024 }
7025 }
7026 return hrc;
7027}
7028
7029HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7030{
7031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7032 size_t cbIcon = mUserData->mIcon.size();
7033 aIcon.resize(cbIcon);
7034 if (cbIcon)
7035 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7040{
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7043 if (SUCCEEDED(hrc))
7044 {
7045 i_setModified(IsModified_MachineData);
7046 mUserData.backup();
7047 size_t cbIcon = aIcon.size();
7048 mUserData->mIcon.resize(cbIcon);
7049 if (cbIcon)
7050 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7051 }
7052 return hrc;
7053}
7054
7055HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7056{
7057#ifdef VBOX_WITH_USB
7058 *aUSBProxyAvailable = true;
7059#else
7060 *aUSBProxyAvailable = false;
7061#endif
7062 return S_OK;
7063}
7064
7065HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7066 ComPtr<IProgress> &aProgress)
7067{
7068 ComObjPtr<Progress> pP;
7069 Progress *ppP = pP;
7070 IProgress *iP = static_cast<IProgress *>(ppP);
7071 IProgress **pProgress = &iP;
7072
7073 IMachine *pTarget = aTarget;
7074
7075 /* Convert the options. */
7076 RTCList<CloneOptions_T> optList;
7077 if (aOptions.size())
7078 for (size_t i = 0; i < aOptions.size(); ++i)
7079 optList.append(aOptions[i]);
7080
7081 if (optList.contains(CloneOptions_Link))
7082 {
7083 if (!i_isSnapshotMachine())
7084 return setError(E_INVALIDARG,
7085 tr("Linked clone can only be created from a snapshot"));
7086 if (aMode != CloneMode_MachineState)
7087 return setError(E_INVALIDARG,
7088 tr("Linked clone can only be created for a single machine state"));
7089 }
7090 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7091
7092 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7093
7094 HRESULT rc = pWorker->start(pProgress);
7095
7096 pP = static_cast<Progress *>(*pProgress);
7097 pP.queryInterfaceTo(aProgress.asOutParam());
7098
7099 return rc;
7100
7101}
7102
7103HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7104{
7105 NOREF(aProgress);
7106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7107
7108 // This check should always fail.
7109 HRESULT rc = i_checkStateDependency(MutableStateDep);
7110 if (FAILED(rc)) return rc;
7111
7112 AssertFailedReturn(E_NOTIMPL);
7113}
7114
7115HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7116{
7117 NOREF(aSavedStateFile);
7118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7119
7120 // This check should always fail.
7121 HRESULT rc = i_checkStateDependency(MutableStateDep);
7122 if (FAILED(rc)) return rc;
7123
7124 AssertFailedReturn(E_NOTIMPL);
7125}
7126
7127HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7128{
7129 NOREF(aFRemoveFile);
7130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7131
7132 // This check should always fail.
7133 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7134 if (FAILED(rc)) return rc;
7135
7136 AssertFailedReturn(E_NOTIMPL);
7137}
7138
7139// public methods for internal purposes
7140/////////////////////////////////////////////////////////////////////////////
7141
7142/**
7143 * Adds the given IsModified_* flag to the dirty flags of the machine.
7144 * This must be called either during i_loadSettings or under the machine write lock.
7145 * @param fl
7146 */
7147void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7148{
7149 mData->flModifications |= fl;
7150 if (fAllowStateModification && i_isStateModificationAllowed())
7151 mData->mCurrentStateModified = true;
7152}
7153
7154/**
7155 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7156 * care of the write locking.
7157 *
7158 * @param fModifications The flag to add.
7159 */
7160void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7161{
7162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7163 i_setModified(fModification, fAllowStateModification);
7164}
7165
7166/**
7167 * Saves the registry entry of this machine to the given configuration node.
7168 *
7169 * @param aEntryNode Node to save the registry entry to.
7170 *
7171 * @note locks this object for reading.
7172 */
7173HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7174{
7175 AutoLimitedCaller autoCaller(this);
7176 AssertComRCReturnRC(autoCaller.rc());
7177
7178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7179
7180 data.uuid = mData->mUuid;
7181 data.strSettingsFile = mData->m_strConfigFile;
7182
7183 return S_OK;
7184}
7185
7186/**
7187 * Calculates the absolute path of the given path taking the directory of the
7188 * machine settings file as the current directory.
7189 *
7190 * @param aPath Path to calculate the absolute path for.
7191 * @param aResult Where to put the result (used only on success, can be the
7192 * same Utf8Str instance as passed in @a aPath).
7193 * @return IPRT result.
7194 *
7195 * @note Locks this object for reading.
7196 */
7197int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7198{
7199 AutoCaller autoCaller(this);
7200 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7201
7202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7203
7204 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7205
7206 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7207
7208 strSettingsDir.stripFilename();
7209 char folder[RTPATH_MAX];
7210 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7211 if (RT_SUCCESS(vrc))
7212 aResult = folder;
7213
7214 return vrc;
7215}
7216
7217/**
7218 * Copies strSource to strTarget, making it relative to the machine folder
7219 * if it is a subdirectory thereof, or simply copying it otherwise.
7220 *
7221 * @param strSource Path to evaluate and copy.
7222 * @param strTarget Buffer to receive target path.
7223 *
7224 * @note Locks this object for reading.
7225 */
7226void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7227 Utf8Str &strTarget)
7228{
7229 AutoCaller autoCaller(this);
7230 AssertComRCReturn(autoCaller.rc(), (void)0);
7231
7232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7233
7234 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7235 // use strTarget as a temporary buffer to hold the machine settings dir
7236 strTarget = mData->m_strConfigFileFull;
7237 strTarget.stripFilename();
7238 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7239 {
7240 // is relative: then append what's left
7241 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7242 // for empty paths (only possible for subdirs) use "." to avoid
7243 // triggering default settings for not present config attributes.
7244 if (strTarget.isEmpty())
7245 strTarget = ".";
7246 }
7247 else
7248 // is not relative: then overwrite
7249 strTarget = strSource;
7250}
7251
7252/**
7253 * Returns the full path to the machine's log folder in the
7254 * \a aLogFolder argument.
7255 */
7256void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7257{
7258 AutoCaller autoCaller(this);
7259 AssertComRCReturnVoid(autoCaller.rc());
7260
7261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7262
7263 char szTmp[RTPATH_MAX];
7264 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7265 if (RT_SUCCESS(vrc))
7266 {
7267 if (szTmp[0] && !mUserData.isNull())
7268 {
7269 char szTmp2[RTPATH_MAX];
7270 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7271 if (RT_SUCCESS(vrc))
7272 aLogFolder = BstrFmt("%s%c%s",
7273 szTmp2,
7274 RTPATH_DELIMITER,
7275 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7276 }
7277 else
7278 vrc = VERR_PATH_IS_RELATIVE;
7279 }
7280
7281 if (RT_FAILURE(vrc))
7282 {
7283 // fallback if VBOX_USER_LOGHOME is not set or invalid
7284 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7285 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7286 aLogFolder.append(RTPATH_DELIMITER);
7287 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7288 }
7289}
7290
7291/**
7292 * Returns the full path to the machine's log file for an given index.
7293 */
7294Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7295 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7296{
7297 Utf8Str logFolder;
7298 getLogFolder(logFolder);
7299 Assert(logFolder.length());
7300 Utf8Str log;
7301 if (idx == 0)
7302 log = Utf8StrFmt("%s%cVBox.log",
7303 logFolder.c_str(), RTPATH_DELIMITER);
7304 else
7305 log = Utf8StrFmt("%s%cVBox.log.%d",
7306 logFolder.c_str(), RTPATH_DELIMITER, idx);
7307 return log;
7308}
7309
7310/**
7311 * Returns the full path to the machine's (hardened) startup log file.
7312 */
7313Utf8Str Machine::i_getStartupLogFilename(void)
7314{
7315 Utf8Str strFilename;
7316 getLogFolder(strFilename);
7317 Assert(strFilename.length());
7318 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7319 return strFilename;
7320}
7321
7322
7323/**
7324 * Composes a unique saved state filename based on the current system time. The filename is
7325 * granular to the second so this will work so long as no more than one snapshot is taken on
7326 * a machine per second.
7327 *
7328 * Before version 4.1, we used this formula for saved state files:
7329 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7330 * which no longer works because saved state files can now be shared between the saved state of the
7331 * "saved" machine and an online snapshot, and the following would cause problems:
7332 * 1) save machine
7333 * 2) create online snapshot from that machine state --> reusing saved state file
7334 * 3) save machine again --> filename would be reused, breaking the online snapshot
7335 *
7336 * So instead we now use a timestamp.
7337 *
7338 * @param str
7339 */
7340
7341void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7342{
7343 AutoCaller autoCaller(this);
7344 AssertComRCReturnVoid(autoCaller.rc());
7345
7346 {
7347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7348 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7349 }
7350
7351 RTTIMESPEC ts;
7352 RTTimeNow(&ts);
7353 RTTIME time;
7354 RTTimeExplode(&time, &ts);
7355
7356 strStateFilePath += RTPATH_DELIMITER;
7357 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7358 time.i32Year, time.u8Month, time.u8MonthDay,
7359 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7360}
7361
7362/**
7363 * Returns the full path to the default video capture file.
7364 */
7365void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7366{
7367 AutoCaller autoCaller(this);
7368 AssertComRCReturnVoid(autoCaller.rc());
7369
7370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7371
7372 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7373 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7374 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7375}
7376
7377/**
7378 * Returns whether at least one USB controller is present for the VM.
7379 */
7380bool Machine::i_isUSBControllerPresent()
7381{
7382 AutoCaller autoCaller(this);
7383 AssertComRCReturn(autoCaller.rc(), false);
7384
7385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7386
7387 return (mUSBControllers->size() > 0);
7388}
7389
7390/**
7391 * @note Locks this object for writing, calls the client process
7392 * (inside the lock).
7393 */
7394HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7395 const Utf8Str &strFrontend,
7396 const Utf8Str &strEnvironment,
7397 ProgressProxy *aProgress)
7398{
7399 LogFlowThisFuncEnter();
7400
7401 AssertReturn(aControl, E_FAIL);
7402 AssertReturn(aProgress, E_FAIL);
7403 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7404
7405 AutoCaller autoCaller(this);
7406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7407
7408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7409
7410 if (!mData->mRegistered)
7411 return setError(E_UNEXPECTED,
7412 tr("The machine '%s' is not registered"),
7413 mUserData->s.strName.c_str());
7414
7415 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7416
7417 /* The process started when launching a VM with separate UI/VM processes is always
7418 * the UI process, i.e. needs special handling as it won't claim the session. */
7419 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7420
7421 if (fSeparate)
7422 {
7423 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName == "headless")
7424 return setError(VBOX_E_INVALID_OBJECT_STATE,
7425 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7426 mUserData->s.strName.c_str());
7427 }
7428 else
7429 {
7430 if ( mData->mSession.mState == SessionState_Locked
7431 || mData->mSession.mState == SessionState_Spawning
7432 || mData->mSession.mState == SessionState_Unlocking)
7433 return setError(VBOX_E_INVALID_OBJECT_STATE,
7434 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7435 mUserData->s.strName.c_str());
7436
7437 /* may not be busy */
7438 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7439 }
7440
7441 /* get the path to the executable */
7442 char szPath[RTPATH_MAX];
7443 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7444 size_t cchBufLeft = strlen(szPath);
7445 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7446 szPath[cchBufLeft] = 0;
7447 char *pszNamePart = szPath + cchBufLeft;
7448 cchBufLeft = sizeof(szPath) - cchBufLeft;
7449
7450 int vrc = VINF_SUCCESS;
7451 RTPROCESS pid = NIL_RTPROCESS;
7452
7453 RTENV env = RTENV_DEFAULT;
7454
7455 if (!strEnvironment.isEmpty())
7456 {
7457 char *newEnvStr = NULL;
7458
7459 do
7460 {
7461 /* clone the current environment */
7462 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7463 AssertRCBreakStmt(vrc2, vrc = vrc2);
7464
7465 newEnvStr = RTStrDup(strEnvironment.c_str());
7466 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7467
7468 /* put new variables to the environment
7469 * (ignore empty variable names here since RTEnv API
7470 * intentionally doesn't do that) */
7471 char *var = newEnvStr;
7472 for (char *p = newEnvStr; *p; ++p)
7473 {
7474 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7475 {
7476 *p = '\0';
7477 if (*var)
7478 {
7479 char *val = strchr(var, '=');
7480 if (val)
7481 {
7482 *val++ = '\0';
7483 vrc2 = RTEnvSetEx(env, var, val);
7484 }
7485 else
7486 vrc2 = RTEnvUnsetEx(env, var);
7487 if (RT_FAILURE(vrc2))
7488 break;
7489 }
7490 var = p + 1;
7491 }
7492 }
7493 if (RT_SUCCESS(vrc2) && *var)
7494 vrc2 = RTEnvPutEx(env, var);
7495
7496 AssertRCBreakStmt(vrc2, vrc = vrc2);
7497 }
7498 while (0);
7499
7500 if (newEnvStr != NULL)
7501 RTStrFree(newEnvStr);
7502 }
7503
7504 /* Hardened startup logging */
7505#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7506 Utf8Str strSupStartLogArg("--sup-startup-log=");
7507 {
7508 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7509 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7510 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7511 {
7512 Utf8Str strStartupLogDir = strStartupLogFile;
7513 strStartupLogDir.stripFilename();
7514 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7515 file without stripping the file. */
7516 }
7517 strSupStartLogArg.append(strStartupLogFile);
7518 }
7519 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7520#else
7521 const char *pszSupStartupLogArg = NULL;
7522#endif
7523
7524 Utf8Str strCanonicalName;
7525
7526#ifdef VBOX_WITH_QTGUI
7527 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7528 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7529 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7530 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7531 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7532 {
7533 strCanonicalName = "GUI/Qt";
7534# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7535 /* Modify the base path so that we don't need to use ".." below. */
7536 RTPathStripTrailingSlash(szPath);
7537 RTPathStripFilename(szPath);
7538 cchBufLeft = strlen(szPath);
7539 pszNamePart = szPath + cchBufLeft;
7540 cchBufLeft = sizeof(szPath) - cchBufLeft;
7541
7542# define OSX_APP_NAME "VirtualBoxVM"
7543# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7544
7545 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7546 if ( strAppOverride.contains(".")
7547 || strAppOverride.contains("/")
7548 || strAppOverride.contains("\\")
7549 || strAppOverride.contains(":"))
7550 strAppOverride.setNull();
7551 Utf8Str strAppPath;
7552 if (!strAppOverride.isEmpty())
7553 {
7554 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7555 Utf8Str strFullPath(szPath);
7556 strFullPath.append(strAppPath);
7557 /* there is a race, but people using this deserve the failure */
7558 if (!RTFileExists(strFullPath.c_str()))
7559 strAppOverride.setNull();
7560 }
7561 if (strAppOverride.isEmpty())
7562 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7563 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7564 strcpy(pszNamePart, strAppPath.c_str());
7565# else
7566 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7567 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7568 strcpy(pszNamePart, s_szVirtualBox_exe);
7569# endif
7570
7571 Utf8Str idStr = mData->mUuid.toString();
7572 const char *apszArgs[] =
7573 {
7574 szPath,
7575 "--comment", mUserData->s.strName.c_str(),
7576 "--startvm", idStr.c_str(),
7577 "--no-startvm-errormsgbox",
7578 NULL, /* For "--separate". */
7579 NULL, /* For "--sup-startup-log". */
7580 NULL
7581 };
7582 unsigned iArg = 6;
7583 if (fSeparate)
7584 apszArgs[iArg++] = "--separate";
7585 apszArgs[iArg++] = pszSupStartupLogArg;
7586
7587 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7588 }
7589#else /* !VBOX_WITH_QTGUI */
7590 if (0)
7591 ;
7592#endif /* VBOX_WITH_QTGUI */
7593
7594 else
7595
7596#ifdef VBOX_WITH_VBOXSDL
7597 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7598 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7599 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7600 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7601 {
7602 strCanonicalName = "GUI/SDL";
7603 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7604 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7605 strcpy(pszNamePart, s_szVBoxSDL_exe);
7606
7607 Utf8Str idStr = mData->mUuid.toString();
7608 const char *apszArgs[] =
7609 {
7610 szPath,
7611 "--comment", mUserData->s.strName.c_str(),
7612 "--startvm", idStr.c_str(),
7613 NULL, /* For "--separate". */
7614 NULL, /* For "--sup-startup-log". */
7615 NULL
7616 };
7617 unsigned iArg = 5;
7618 if (fSeparate)
7619 apszArgs[iArg++] = "--separate";
7620 apszArgs[iArg++] = pszSupStartupLogArg;
7621
7622 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7623 }
7624#else /* !VBOX_WITH_VBOXSDL */
7625 if (0)
7626 ;
7627#endif /* !VBOX_WITH_VBOXSDL */
7628
7629 else
7630
7631#ifdef VBOX_WITH_HEADLESS
7632 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7633 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7634 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7635 )
7636 {
7637 strCanonicalName = "headless";
7638 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7639 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7640 * and a VM works even if the server has not been installed.
7641 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7642 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7643 * differently in 4.0 and 3.x.
7644 */
7645 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7646 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7647 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7648
7649 Utf8Str idStr = mData->mUuid.toString();
7650 const char *apszArgs[] =
7651 {
7652 szPath,
7653 "--comment", mUserData->s.strName.c_str(),
7654 "--startvm", idStr.c_str(),
7655 "--vrde", "config",
7656 NULL, /* For "--capture". */
7657 NULL, /* For "--sup-startup-log". */
7658 NULL
7659 };
7660 unsigned iArg = 7;
7661 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7662 apszArgs[iArg++] = "--capture";
7663 apszArgs[iArg++] = pszSupStartupLogArg;
7664
7665# ifdef RT_OS_WINDOWS
7666 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7667# else
7668 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7669# endif
7670 }
7671#else /* !VBOX_WITH_HEADLESS */
7672 if (0)
7673 ;
7674#endif /* !VBOX_WITH_HEADLESS */
7675 else
7676 {
7677 RTEnvDestroy(env);
7678 return setError(E_INVALIDARG,
7679 tr("Invalid frontend name: '%s'"),
7680 strFrontend.c_str());
7681 }
7682
7683 RTEnvDestroy(env);
7684
7685 if (RT_FAILURE(vrc))
7686 return setError(VBOX_E_IPRT_ERROR,
7687 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7688 mUserData->s.strName.c_str(), vrc);
7689
7690 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7691
7692 if (!fSeparate)
7693 {
7694 /*
7695 * Note that we don't release the lock here before calling the client,
7696 * because it doesn't need to call us back if called with a NULL argument.
7697 * Releasing the lock here is dangerous because we didn't prepare the
7698 * launch data yet, but the client we've just started may happen to be
7699 * too fast and call LockMachine() that will fail (because of PID, etc.),
7700 * so that the Machine will never get out of the Spawning session state.
7701 */
7702
7703 /* inform the session that it will be a remote one */
7704 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7705#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7706 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7707#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7708 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7709#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7710 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7711
7712 if (FAILED(rc))
7713 {
7714 /* restore the session state */
7715 mData->mSession.mState = SessionState_Unlocked;
7716 alock.release();
7717 mParent->i_addProcessToReap(pid);
7718 /* The failure may occur w/o any error info (from RPC), so provide one */
7719 return setError(VBOX_E_VM_ERROR,
7720 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7721 }
7722
7723 /* attach launch data to the machine */
7724 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7725 mData->mSession.mRemoteControls.push_back(aControl);
7726 mData->mSession.mProgress = aProgress;
7727 mData->mSession.mPID = pid;
7728 mData->mSession.mState = SessionState_Spawning;
7729 Assert(strCanonicalName.isNotEmpty());
7730 mData->mSession.mName = strCanonicalName;
7731 }
7732 else
7733 {
7734 /* For separate UI process we declare the launch as completed instantly, as the
7735 * actual headless VM start may or may not come. No point in remembering anything
7736 * yet, as what matters for us is when the headless VM gets started. */
7737 aProgress->i_notifyComplete(S_OK);
7738 }
7739
7740 alock.release();
7741 mParent->i_addProcessToReap(pid);
7742
7743 LogFlowThisFuncLeave();
7744 return S_OK;
7745}
7746
7747/**
7748 * Returns @c true if the given session machine instance has an open direct
7749 * session (and optionally also for direct sessions which are closing) and
7750 * returns the session control machine instance if so.
7751 *
7752 * Note that when the method returns @c false, the arguments remain unchanged.
7753 *
7754 * @param aMachine Session machine object.
7755 * @param aControl Direct session control object (optional).
7756 * @param aAllowClosing If true then additionally a session which is currently
7757 * being closed will also be allowed.
7758 *
7759 * @note locks this object for reading.
7760 */
7761bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7762 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7763 bool aAllowClosing /*= false*/)
7764{
7765 AutoLimitedCaller autoCaller(this);
7766 AssertComRCReturn(autoCaller.rc(), false);
7767
7768 /* just return false for inaccessible machines */
7769 if (getObjectState().getState() != ObjectState::Ready)
7770 return false;
7771
7772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7773
7774 if ( ( mData->mSession.mState == SessionState_Locked
7775 && mData->mSession.mLockType == LockType_VM)
7776 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7777 )
7778 {
7779 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7780
7781 aMachine = mData->mSession.mMachine;
7782
7783 if (aControl != NULL)
7784 *aControl = mData->mSession.mDirectControl;
7785
7786 return true;
7787 }
7788
7789 return false;
7790}
7791
7792/**
7793 * Returns @c true if the given machine has an spawning direct session.
7794 *
7795 * @note locks this object for reading.
7796 */
7797bool Machine::i_isSessionSpawning()
7798{
7799 AutoLimitedCaller autoCaller(this);
7800 AssertComRCReturn(autoCaller.rc(), false);
7801
7802 /* just return false for inaccessible machines */
7803 if (getObjectState().getState() != ObjectState::Ready)
7804 return false;
7805
7806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7807
7808 if (mData->mSession.mState == SessionState_Spawning)
7809 return true;
7810
7811 return false;
7812}
7813
7814/**
7815 * Called from the client watcher thread to check for unexpected client process
7816 * death during Session_Spawning state (e.g. before it successfully opened a
7817 * direct session).
7818 *
7819 * On Win32 and on OS/2, this method is called only when we've got the
7820 * direct client's process termination notification, so it always returns @c
7821 * true.
7822 *
7823 * On other platforms, this method returns @c true if the client process is
7824 * terminated and @c false if it's still alive.
7825 *
7826 * @note Locks this object for writing.
7827 */
7828bool Machine::i_checkForSpawnFailure()
7829{
7830 AutoCaller autoCaller(this);
7831 if (!autoCaller.isOk())
7832 {
7833 /* nothing to do */
7834 LogFlowThisFunc(("Already uninitialized!\n"));
7835 return true;
7836 }
7837
7838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7839
7840 if (mData->mSession.mState != SessionState_Spawning)
7841 {
7842 /* nothing to do */
7843 LogFlowThisFunc(("Not spawning any more!\n"));
7844 return true;
7845 }
7846
7847 HRESULT rc = S_OK;
7848
7849 /* PID not yet initialized, skip check. */
7850 if (mData->mSession.mPID == NIL_RTPROCESS)
7851 return false;
7852
7853 RTPROCSTATUS status;
7854 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7855
7856 if (vrc != VERR_PROCESS_RUNNING)
7857 {
7858 Utf8Str strExtraInfo;
7859
7860#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7861 /* If the startup logfile exists and is of non-zero length, tell the
7862 user to look there for more details to encourage them to attach it
7863 when reporting startup issues. */
7864 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7865 uint64_t cbStartupLogFile = 0;
7866 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7867 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7868 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7869#endif
7870
7871 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7872 rc = setError(E_FAIL,
7873 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7874 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7875 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7876 rc = setError(E_FAIL,
7877 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7878 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7879 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7880 rc = setError(E_FAIL,
7881 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7882 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7883 else
7884 rc = setError(E_FAIL,
7885 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7886 i_getName().c_str(), vrc, strExtraInfo.c_str());
7887 }
7888
7889 if (FAILED(rc))
7890 {
7891 /* Close the remote session, remove the remote control from the list
7892 * and reset session state to Closed (@note keep the code in sync with
7893 * the relevant part in LockMachine()). */
7894
7895 Assert(mData->mSession.mRemoteControls.size() == 1);
7896 if (mData->mSession.mRemoteControls.size() == 1)
7897 {
7898 ErrorInfoKeeper eik;
7899 mData->mSession.mRemoteControls.front()->Uninitialize();
7900 }
7901
7902 mData->mSession.mRemoteControls.clear();
7903 mData->mSession.mState = SessionState_Unlocked;
7904
7905 /* finalize the progress after setting the state */
7906 if (!mData->mSession.mProgress.isNull())
7907 {
7908 mData->mSession.mProgress->notifyComplete(rc);
7909 mData->mSession.mProgress.setNull();
7910 }
7911
7912 mData->mSession.mPID = NIL_RTPROCESS;
7913
7914 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7915 return true;
7916 }
7917
7918 return false;
7919}
7920
7921/**
7922 * Checks whether the machine can be registered. If so, commits and saves
7923 * all settings.
7924 *
7925 * @note Must be called from mParent's write lock. Locks this object and
7926 * children for writing.
7927 */
7928HRESULT Machine::i_prepareRegister()
7929{
7930 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7931
7932 AutoLimitedCaller autoCaller(this);
7933 AssertComRCReturnRC(autoCaller.rc());
7934
7935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7936
7937 /* wait for state dependents to drop to zero */
7938 i_ensureNoStateDependencies();
7939
7940 if (!mData->mAccessible)
7941 return setError(VBOX_E_INVALID_OBJECT_STATE,
7942 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7943 mUserData->s.strName.c_str(),
7944 mData->mUuid.toString().c_str());
7945
7946 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7947
7948 if (mData->mRegistered)
7949 return setError(VBOX_E_INVALID_OBJECT_STATE,
7950 tr("The machine '%s' with UUID {%s} is already registered"),
7951 mUserData->s.strName.c_str(),
7952 mData->mUuid.toString().c_str());
7953
7954 HRESULT rc = S_OK;
7955
7956 // Ensure the settings are saved. If we are going to be registered and
7957 // no config file exists yet, create it by calling i_saveSettings() too.
7958 if ( (mData->flModifications)
7959 || (!mData->pMachineConfigFile->fileExists())
7960 )
7961 {
7962 rc = i_saveSettings(NULL);
7963 // no need to check whether VirtualBox.xml needs saving too since
7964 // we can't have a machine XML file rename pending
7965 if (FAILED(rc)) return rc;
7966 }
7967
7968 /* more config checking goes here */
7969
7970 if (SUCCEEDED(rc))
7971 {
7972 /* we may have had implicit modifications we want to fix on success */
7973 i_commit();
7974
7975 mData->mRegistered = true;
7976 }
7977 else
7978 {
7979 /* we may have had implicit modifications we want to cancel on failure*/
7980 i_rollback(false /* aNotify */);
7981 }
7982
7983 return rc;
7984}
7985
7986/**
7987 * Increases the number of objects dependent on the machine state or on the
7988 * registered state. Guarantees that these two states will not change at least
7989 * until #releaseStateDependency() is called.
7990 *
7991 * Depending on the @a aDepType value, additional state checks may be made.
7992 * These checks will set extended error info on failure. See
7993 * #checkStateDependency() for more info.
7994 *
7995 * If this method returns a failure, the dependency is not added and the caller
7996 * is not allowed to rely on any particular machine state or registration state
7997 * value and may return the failed result code to the upper level.
7998 *
7999 * @param aDepType Dependency type to add.
8000 * @param aState Current machine state (NULL if not interested).
8001 * @param aRegistered Current registered state (NULL if not interested).
8002 *
8003 * @note Locks this object for writing.
8004 */
8005HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8006 MachineState_T *aState /* = NULL */,
8007 BOOL *aRegistered /* = NULL */)
8008{
8009 AutoCaller autoCaller(this);
8010 AssertComRCReturnRC(autoCaller.rc());
8011
8012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8013
8014 HRESULT rc = i_checkStateDependency(aDepType);
8015 if (FAILED(rc)) return rc;
8016
8017 {
8018 if (mData->mMachineStateChangePending != 0)
8019 {
8020 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8021 * drop to zero so don't add more. It may make sense to wait a bit
8022 * and retry before reporting an error (since the pending state
8023 * transition should be really quick) but let's just assert for
8024 * now to see if it ever happens on practice. */
8025
8026 AssertFailed();
8027
8028 return setError(E_ACCESSDENIED,
8029 tr("Machine state change is in progress. Please retry the operation later."));
8030 }
8031
8032 ++mData->mMachineStateDeps;
8033 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8034 }
8035
8036 if (aState)
8037 *aState = mData->mMachineState;
8038 if (aRegistered)
8039 *aRegistered = mData->mRegistered;
8040
8041 return S_OK;
8042}
8043
8044/**
8045 * Decreases the number of objects dependent on the machine state.
8046 * Must always complete the #addStateDependency() call after the state
8047 * dependency is no more necessary.
8048 */
8049void Machine::i_releaseStateDependency()
8050{
8051 AutoCaller autoCaller(this);
8052 AssertComRCReturnVoid(autoCaller.rc());
8053
8054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8055
8056 /* releaseStateDependency() w/o addStateDependency()? */
8057 AssertReturnVoid(mData->mMachineStateDeps != 0);
8058 -- mData->mMachineStateDeps;
8059
8060 if (mData->mMachineStateDeps == 0)
8061 {
8062 /* inform i_ensureNoStateDependencies() that there are no more deps */
8063 if (mData->mMachineStateChangePending != 0)
8064 {
8065 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8066 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8067 }
8068 }
8069}
8070
8071Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8072{
8073 /* start with nothing found */
8074 Utf8Str strResult("");
8075
8076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8077
8078 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8079 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8080 // found:
8081 strResult = it->second; // source is a Utf8Str
8082
8083 return strResult;
8084}
8085
8086// protected methods
8087/////////////////////////////////////////////////////////////////////////////
8088
8089/**
8090 * Performs machine state checks based on the @a aDepType value. If a check
8091 * fails, this method will set extended error info, otherwise it will return
8092 * S_OK. It is supposed, that on failure, the caller will immediately return
8093 * the return value of this method to the upper level.
8094 *
8095 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8096 *
8097 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8098 * current state of this machine object allows to change settings of the
8099 * machine (i.e. the machine is not registered, or registered but not running
8100 * and not saved). It is useful to call this method from Machine setters
8101 * before performing any change.
8102 *
8103 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8104 * as for MutableStateDep except that if the machine is saved, S_OK is also
8105 * returned. This is useful in setters which allow changing machine
8106 * properties when it is in the saved state.
8107 *
8108 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8109 * if the current state of this machine object allows to change runtime
8110 * changeable settings of the machine (i.e. the machine is not registered, or
8111 * registered but either running or not running and not saved). It is useful
8112 * to call this method from Machine setters before performing any changes to
8113 * runtime changeable settings.
8114 *
8115 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8116 * the same as for MutableOrRunningStateDep except that if the machine is
8117 * saved, S_OK is also returned. This is useful in setters which allow
8118 * changing runtime and saved state changeable machine properties.
8119 *
8120 * @param aDepType Dependency type to check.
8121 *
8122 * @note Non Machine based classes should use #addStateDependency() and
8123 * #releaseStateDependency() methods or the smart AutoStateDependency
8124 * template.
8125 *
8126 * @note This method must be called from under this object's read or write
8127 * lock.
8128 */
8129HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8130{
8131 switch (aDepType)
8132 {
8133 case AnyStateDep:
8134 {
8135 break;
8136 }
8137 case MutableStateDep:
8138 {
8139 if ( mData->mRegistered
8140 && ( !i_isSessionMachine()
8141 || ( mData->mMachineState != MachineState_Aborted
8142 && mData->mMachineState != MachineState_Teleported
8143 && mData->mMachineState != MachineState_PoweredOff
8144 )
8145 )
8146 )
8147 return setError(VBOX_E_INVALID_VM_STATE,
8148 tr("The machine is not mutable (state is %s)"),
8149 Global::stringifyMachineState(mData->mMachineState));
8150 break;
8151 }
8152 case MutableOrSavedStateDep:
8153 {
8154 if ( mData->mRegistered
8155 && ( !i_isSessionMachine()
8156 || ( mData->mMachineState != MachineState_Aborted
8157 && mData->mMachineState != MachineState_Teleported
8158 && mData->mMachineState != MachineState_Saved
8159 && mData->mMachineState != MachineState_PoweredOff
8160 )
8161 )
8162 )
8163 return setError(VBOX_E_INVALID_VM_STATE,
8164 tr("The machine is not mutable (state is %s)"),
8165 Global::stringifyMachineState(mData->mMachineState));
8166 break;
8167 }
8168 case MutableOrRunningStateDep:
8169 {
8170 if ( mData->mRegistered
8171 && ( !i_isSessionMachine()
8172 || ( mData->mMachineState != MachineState_Aborted
8173 && mData->mMachineState != MachineState_Teleported
8174 && mData->mMachineState != MachineState_PoweredOff
8175 && !Global::IsOnline(mData->mMachineState)
8176 )
8177 )
8178 )
8179 return setError(VBOX_E_INVALID_VM_STATE,
8180 tr("The machine is not mutable (state is %s)"),
8181 Global::stringifyMachineState(mData->mMachineState));
8182 break;
8183 }
8184 case MutableOrSavedOrRunningStateDep:
8185 {
8186 if ( mData->mRegistered
8187 && ( !i_isSessionMachine()
8188 || ( mData->mMachineState != MachineState_Aborted
8189 && mData->mMachineState != MachineState_Teleported
8190 && mData->mMachineState != MachineState_Saved
8191 && mData->mMachineState != MachineState_PoweredOff
8192 && !Global::IsOnline(mData->mMachineState)
8193 )
8194 )
8195 )
8196 return setError(VBOX_E_INVALID_VM_STATE,
8197 tr("The machine is not mutable (state is %s)"),
8198 Global::stringifyMachineState(mData->mMachineState));
8199 break;
8200 }
8201 }
8202
8203 return S_OK;
8204}
8205
8206/**
8207 * Helper to initialize all associated child objects and allocate data
8208 * structures.
8209 *
8210 * This method must be called as a part of the object's initialization procedure
8211 * (usually done in the #init() method).
8212 *
8213 * @note Must be called only from #init() or from #registeredInit().
8214 */
8215HRESULT Machine::initDataAndChildObjects()
8216{
8217 AutoCaller autoCaller(this);
8218 AssertComRCReturnRC(autoCaller.rc());
8219 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8220 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8221
8222 AssertReturn(!mData->mAccessible, E_FAIL);
8223
8224 /* allocate data structures */
8225 mSSData.allocate();
8226 mUserData.allocate();
8227 mHWData.allocate();
8228 mMediaData.allocate();
8229 mStorageControllers.allocate();
8230 mUSBControllers.allocate();
8231
8232 /* initialize mOSTypeId */
8233 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8234
8235 /* create associated BIOS settings object */
8236 unconst(mBIOSSettings).createObject();
8237 mBIOSSettings->init(this);
8238
8239 /* create an associated VRDE object (default is disabled) */
8240 unconst(mVRDEServer).createObject();
8241 mVRDEServer->init(this);
8242
8243 /* create associated serial port objects */
8244 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8245 {
8246 unconst(mSerialPorts[slot]).createObject();
8247 mSerialPorts[slot]->init(this, slot);
8248 }
8249
8250 /* create associated parallel port objects */
8251 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8252 {
8253 unconst(mParallelPorts[slot]).createObject();
8254 mParallelPorts[slot]->init(this, slot);
8255 }
8256
8257 /* create the audio adapter object (always present, default is disabled) */
8258 unconst(mAudioAdapter).createObject();
8259 mAudioAdapter->init(this);
8260
8261 /* create the USB device filters object (always present) */
8262 unconst(mUSBDeviceFilters).createObject();
8263 mUSBDeviceFilters->init(this);
8264
8265 /* create associated network adapter objects */
8266 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8267 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8268 {
8269 unconst(mNetworkAdapters[slot]).createObject();
8270 mNetworkAdapters[slot]->init(this, slot);
8271 }
8272
8273 /* create the bandwidth control */
8274 unconst(mBandwidthControl).createObject();
8275 mBandwidthControl->init(this);
8276
8277 return S_OK;
8278}
8279
8280/**
8281 * Helper to uninitialize all associated child objects and to free all data
8282 * structures.
8283 *
8284 * This method must be called as a part of the object's uninitialization
8285 * procedure (usually done in the #uninit() method).
8286 *
8287 * @note Must be called only from #uninit() or from #registeredInit().
8288 */
8289void Machine::uninitDataAndChildObjects()
8290{
8291 AutoCaller autoCaller(this);
8292 AssertComRCReturnVoid(autoCaller.rc());
8293 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8294 || getObjectState().getState() == ObjectState::Limited);
8295
8296 /* tell all our other child objects we've been uninitialized */
8297 if (mBandwidthControl)
8298 {
8299 mBandwidthControl->uninit();
8300 unconst(mBandwidthControl).setNull();
8301 }
8302
8303 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8304 {
8305 if (mNetworkAdapters[slot])
8306 {
8307 mNetworkAdapters[slot]->uninit();
8308 unconst(mNetworkAdapters[slot]).setNull();
8309 }
8310 }
8311
8312 if (mUSBDeviceFilters)
8313 {
8314 mUSBDeviceFilters->uninit();
8315 unconst(mUSBDeviceFilters).setNull();
8316 }
8317
8318 if (mAudioAdapter)
8319 {
8320 mAudioAdapter->uninit();
8321 unconst(mAudioAdapter).setNull();
8322 }
8323
8324 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8325 {
8326 if (mParallelPorts[slot])
8327 {
8328 mParallelPorts[slot]->uninit();
8329 unconst(mParallelPorts[slot]).setNull();
8330 }
8331 }
8332
8333 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8334 {
8335 if (mSerialPorts[slot])
8336 {
8337 mSerialPorts[slot]->uninit();
8338 unconst(mSerialPorts[slot]).setNull();
8339 }
8340 }
8341
8342 if (mVRDEServer)
8343 {
8344 mVRDEServer->uninit();
8345 unconst(mVRDEServer).setNull();
8346 }
8347
8348 if (mBIOSSettings)
8349 {
8350 mBIOSSettings->uninit();
8351 unconst(mBIOSSettings).setNull();
8352 }
8353
8354 /* Deassociate media (only when a real Machine or a SnapshotMachine
8355 * instance is uninitialized; SessionMachine instances refer to real
8356 * Machine media). This is necessary for a clean re-initialization of
8357 * the VM after successfully re-checking the accessibility state. Note
8358 * that in case of normal Machine or SnapshotMachine uninitialization (as
8359 * a result of unregistering or deleting the snapshot), outdated media
8360 * attachments will already be uninitialized and deleted, so this
8361 * code will not affect them. */
8362 if ( !!mMediaData
8363 && (!i_isSessionMachine())
8364 )
8365 {
8366 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8367 it != mMediaData->mAttachments.end();
8368 ++it)
8369 {
8370 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8371 if (pMedium.isNull())
8372 continue;
8373 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8374 AssertComRC(rc);
8375 }
8376 }
8377
8378 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8379 {
8380 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8381 if (mData->mFirstSnapshot)
8382 {
8383 // snapshots tree is protected by machine write lock; strictly
8384 // this isn't necessary here since we're deleting the entire
8385 // machine, but otherwise we assert in Snapshot::uninit()
8386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8387 mData->mFirstSnapshot->uninit();
8388 mData->mFirstSnapshot.setNull();
8389 }
8390
8391 mData->mCurrentSnapshot.setNull();
8392 }
8393
8394 /* free data structures (the essential mData structure is not freed here
8395 * since it may be still in use) */
8396 mMediaData.free();
8397 mStorageControllers.free();
8398 mUSBControllers.free();
8399 mHWData.free();
8400 mUserData.free();
8401 mSSData.free();
8402}
8403
8404/**
8405 * Returns a pointer to the Machine object for this machine that acts like a
8406 * parent for complex machine data objects such as shared folders, etc.
8407 *
8408 * For primary Machine objects and for SnapshotMachine objects, returns this
8409 * object's pointer itself. For SessionMachine objects, returns the peer
8410 * (primary) machine pointer.
8411 */
8412Machine* Machine::i_getMachine()
8413{
8414 if (i_isSessionMachine())
8415 return (Machine*)mPeer;
8416 return this;
8417}
8418
8419/**
8420 * Makes sure that there are no machine state dependents. If necessary, waits
8421 * for the number of dependents to drop to zero.
8422 *
8423 * Make sure this method is called from under this object's write lock to
8424 * guarantee that no new dependents may be added when this method returns
8425 * control to the caller.
8426 *
8427 * @note Locks this object for writing. The lock will be released while waiting
8428 * (if necessary).
8429 *
8430 * @warning To be used only in methods that change the machine state!
8431 */
8432void Machine::i_ensureNoStateDependencies()
8433{
8434 AssertReturnVoid(isWriteLockOnCurrentThread());
8435
8436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8437
8438 /* Wait for all state dependents if necessary */
8439 if (mData->mMachineStateDeps != 0)
8440 {
8441 /* lazy semaphore creation */
8442 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8443 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8444
8445 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8446 mData->mMachineStateDeps));
8447
8448 ++mData->mMachineStateChangePending;
8449
8450 /* reset the semaphore before waiting, the last dependent will signal
8451 * it */
8452 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8453
8454 alock.release();
8455
8456 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8457
8458 alock.acquire();
8459
8460 -- mData->mMachineStateChangePending;
8461 }
8462}
8463
8464/**
8465 * Changes the machine state and informs callbacks.
8466 *
8467 * This method is not intended to fail so it either returns S_OK or asserts (and
8468 * returns a failure).
8469 *
8470 * @note Locks this object for writing.
8471 */
8472HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8473{
8474 LogFlowThisFuncEnter();
8475 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8476 Assert(aMachineState != MachineState_Null);
8477
8478 AutoCaller autoCaller(this);
8479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8480
8481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8482
8483 /* wait for state dependents to drop to zero */
8484 i_ensureNoStateDependencies();
8485
8486 MachineState_T const enmOldState = mData->mMachineState;
8487 if (enmOldState != aMachineState)
8488 {
8489 mData->mMachineState = aMachineState;
8490 RTTimeNow(&mData->mLastStateChange);
8491
8492#ifdef VBOX_WITH_DTRACE_R3_MAIN
8493 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8494#endif
8495 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8496 }
8497
8498 LogFlowThisFuncLeave();
8499 return S_OK;
8500}
8501
8502/**
8503 * Searches for a shared folder with the given logical name
8504 * in the collection of shared folders.
8505 *
8506 * @param aName logical name of the shared folder
8507 * @param aSharedFolder where to return the found object
8508 * @param aSetError whether to set the error info if the folder is
8509 * not found
8510 * @return
8511 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8512 *
8513 * @note
8514 * must be called from under the object's lock!
8515 */
8516HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8517 ComObjPtr<SharedFolder> &aSharedFolder,
8518 bool aSetError /* = false */)
8519{
8520 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8521 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8522 it != mHWData->mSharedFolders.end();
8523 ++it)
8524 {
8525 SharedFolder *pSF = *it;
8526 AutoCaller autoCaller(pSF);
8527 if (pSF->i_getName() == aName)
8528 {
8529 aSharedFolder = pSF;
8530 rc = S_OK;
8531 break;
8532 }
8533 }
8534
8535 if (aSetError && FAILED(rc))
8536 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8537
8538 return rc;
8539}
8540
8541/**
8542 * Initializes all machine instance data from the given settings structures
8543 * from XML. The exception is the machine UUID which needs special handling
8544 * depending on the caller's use case, so the caller needs to set that herself.
8545 *
8546 * This gets called in several contexts during machine initialization:
8547 *
8548 * -- When machine XML exists on disk already and needs to be loaded into memory,
8549 * for example, from registeredInit() to load all registered machines on
8550 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8551 * attached to the machine should be part of some media registry already.
8552 *
8553 * -- During OVF import, when a machine config has been constructed from an
8554 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8555 * ensure that the media listed as attachments in the config (which have
8556 * been imported from the OVF) receive the correct registry ID.
8557 *
8558 * -- During VM cloning.
8559 *
8560 * @param config Machine settings from XML.
8561 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8562 * for each attached medium in the config.
8563 * @return
8564 */
8565HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8566 const Guid *puuidRegistry)
8567{
8568 // copy name, description, OS type, teleporter, UTC etc.
8569 mUserData->s = config.machineUserData;
8570
8571 // Decode the Icon overide data from config userdata and set onto Machine.
8572 #define DECODE_STR_MAX _1M
8573 const char* pszStr = config.machineUserData.ovIcon.c_str();
8574 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8575 if (cbOut > DECODE_STR_MAX)
8576 return setError(E_FAIL,
8577 tr("Icon Data too long.'%d' > '%d'"),
8578 cbOut,
8579 DECODE_STR_MAX);
8580 mUserData->mIcon.resize(cbOut);
8581 int vrc = VINF_SUCCESS;
8582 if (cbOut)
8583 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8584 if (RT_FAILURE(vrc))
8585 {
8586 mUserData->mIcon.resize(0);
8587 return setError(E_FAIL,
8588 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8589 pszStr,
8590 vrc);
8591 }
8592
8593 // look up the object by Id to check it is valid
8594 ComPtr<IGuestOSType> guestOSType;
8595 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8596 guestOSType.asOutParam());
8597 if (FAILED(rc)) return rc;
8598
8599 // stateFile (optional)
8600 if (config.strStateFile.isEmpty())
8601 mSSData->strStateFilePath.setNull();
8602 else
8603 {
8604 Utf8Str stateFilePathFull(config.strStateFile);
8605 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8606 if (RT_FAILURE(vrc))
8607 return setError(E_FAIL,
8608 tr("Invalid saved state file path '%s' (%Rrc)"),
8609 config.strStateFile.c_str(),
8610 vrc);
8611 mSSData->strStateFilePath = stateFilePathFull;
8612 }
8613
8614 // snapshot folder needs special processing so set it again
8615 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8616 if (FAILED(rc)) return rc;
8617
8618 /* Copy the extra data items (Not in any case config is already the same as
8619 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8620 * make sure the extra data map is copied). */
8621 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8622
8623 /* currentStateModified (optional, default is true) */
8624 mData->mCurrentStateModified = config.fCurrentStateModified;
8625
8626 mData->mLastStateChange = config.timeLastStateChange;
8627
8628 /*
8629 * note: all mUserData members must be assigned prior this point because
8630 * we need to commit changes in order to let mUserData be shared by all
8631 * snapshot machine instances.
8632 */
8633 mUserData.commitCopy();
8634
8635 // machine registry, if present (must be loaded before snapshots)
8636 if (config.canHaveOwnMediaRegistry())
8637 {
8638 // determine machine folder
8639 Utf8Str strMachineFolder = i_getSettingsFileFull();
8640 strMachineFolder.stripFilename();
8641 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8642 config.mediaRegistry,
8643 strMachineFolder);
8644 if (FAILED(rc)) return rc;
8645 }
8646
8647 /* Snapshot node (optional) */
8648 size_t cRootSnapshots;
8649 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8650 {
8651 // there must be only one root snapshot
8652 Assert(cRootSnapshots == 1);
8653
8654 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8655
8656 rc = i_loadSnapshot(snap,
8657 config.uuidCurrentSnapshot,
8658 NULL); // no parent == first snapshot
8659 if (FAILED(rc)) return rc;
8660 }
8661
8662 // hardware data
8663 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8664 if (FAILED(rc)) return rc;
8665
8666 // load storage controllers
8667 rc = i_loadStorageControllers(config.storageMachine,
8668 puuidRegistry,
8669 NULL /* puuidSnapshot */);
8670 if (FAILED(rc)) return rc;
8671
8672 /*
8673 * NOTE: the assignment below must be the last thing to do,
8674 * otherwise it will be not possible to change the settings
8675 * somewhere in the code above because all setters will be
8676 * blocked by i_checkStateDependency(MutableStateDep).
8677 */
8678
8679 /* set the machine state to Aborted or Saved when appropriate */
8680 if (config.fAborted)
8681 {
8682 mSSData->strStateFilePath.setNull();
8683
8684 /* no need to use i_setMachineState() during init() */
8685 mData->mMachineState = MachineState_Aborted;
8686 }
8687 else if (!mSSData->strStateFilePath.isEmpty())
8688 {
8689 /* no need to use i_setMachineState() during init() */
8690 mData->mMachineState = MachineState_Saved;
8691 }
8692
8693 // after loading settings, we are no longer different from the XML on disk
8694 mData->flModifications = 0;
8695
8696 return S_OK;
8697}
8698
8699/**
8700 * Recursively loads all snapshots starting from the given.
8701 *
8702 * @param aNode <Snapshot> node.
8703 * @param aCurSnapshotId Current snapshot ID from the settings file.
8704 * @param aParentSnapshot Parent snapshot.
8705 */
8706HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8707 const Guid &aCurSnapshotId,
8708 Snapshot *aParentSnapshot)
8709{
8710 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8711 AssertReturn(!i_isSessionMachine(), E_FAIL);
8712
8713 HRESULT rc = S_OK;
8714
8715 Utf8Str strStateFile;
8716 if (!data.strStateFile.isEmpty())
8717 {
8718 /* optional */
8719 strStateFile = data.strStateFile;
8720 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8721 if (RT_FAILURE(vrc))
8722 return setError(E_FAIL,
8723 tr("Invalid saved state file path '%s' (%Rrc)"),
8724 strStateFile.c_str(),
8725 vrc);
8726 }
8727
8728 /* create a snapshot machine object */
8729 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8730 pSnapshotMachine.createObject();
8731 rc = pSnapshotMachine->initFromSettings(this,
8732 data.hardware,
8733 &data.debugging,
8734 &data.autostart,
8735 data.storage,
8736 data.uuid.ref(),
8737 strStateFile);
8738 if (FAILED(rc)) return rc;
8739
8740 /* create a snapshot object */
8741 ComObjPtr<Snapshot> pSnapshot;
8742 pSnapshot.createObject();
8743 /* initialize the snapshot */
8744 rc = pSnapshot->init(mParent, // VirtualBox object
8745 data.uuid,
8746 data.strName,
8747 data.strDescription,
8748 data.timestamp,
8749 pSnapshotMachine,
8750 aParentSnapshot);
8751 if (FAILED(rc)) return rc;
8752
8753 /* memorize the first snapshot if necessary */
8754 if (!mData->mFirstSnapshot)
8755 mData->mFirstSnapshot = pSnapshot;
8756
8757 /* memorize the current snapshot when appropriate */
8758 if ( !mData->mCurrentSnapshot
8759 && pSnapshot->i_getId() == aCurSnapshotId
8760 )
8761 mData->mCurrentSnapshot = pSnapshot;
8762
8763 // now create the children
8764 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8765 it != data.llChildSnapshots.end();
8766 ++it)
8767 {
8768 const settings::Snapshot &childData = *it;
8769 // recurse
8770 rc = i_loadSnapshot(childData,
8771 aCurSnapshotId,
8772 pSnapshot); // parent = the one we created above
8773 if (FAILED(rc)) return rc;
8774 }
8775
8776 return rc;
8777}
8778
8779/**
8780 * Loads settings into mHWData.
8781 *
8782 * @param data Reference to the hardware settings.
8783 * @param pDbg Pointer to the debugging settings.
8784 * @param pAutostart Pointer to the autostart settings.
8785 */
8786HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8787 const settings::Autostart *pAutostart)
8788{
8789 AssertReturn(!i_isSessionMachine(), E_FAIL);
8790
8791 HRESULT rc = S_OK;
8792
8793 try
8794 {
8795 /* The hardware version attribute (optional). */
8796 mHWData->mHWVersion = data.strVersion;
8797 mHWData->mHardwareUUID = data.uuid;
8798
8799 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8800 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8801 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8802 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8803 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8804 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8805 mHWData->mPAEEnabled = data.fPAE;
8806 mHWData->mLongMode = data.enmLongMode;
8807 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8808 mHWData->mCPUCount = data.cCPUs;
8809 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8810 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8811 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8812
8813 // cpu
8814 if (mHWData->mCPUHotPlugEnabled)
8815 {
8816 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8817 it != data.llCpus.end();
8818 ++it)
8819 {
8820 const settings::Cpu &cpu = *it;
8821
8822 mHWData->mCPUAttached[cpu.ulId] = true;
8823 }
8824 }
8825
8826 // cpuid leafs
8827 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8828 it != data.llCpuIdLeafs.end();
8829 ++it)
8830 {
8831 const settings::CpuIdLeaf &leaf = *it;
8832
8833 switch (leaf.ulId)
8834 {
8835 case 0x0:
8836 case 0x1:
8837 case 0x2:
8838 case 0x3:
8839 case 0x4:
8840 case 0x5:
8841 case 0x6:
8842 case 0x7:
8843 case 0x8:
8844 case 0x9:
8845 case 0xA:
8846 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8847 break;
8848
8849 case 0x80000000:
8850 case 0x80000001:
8851 case 0x80000002:
8852 case 0x80000003:
8853 case 0x80000004:
8854 case 0x80000005:
8855 case 0x80000006:
8856 case 0x80000007:
8857 case 0x80000008:
8858 case 0x80000009:
8859 case 0x8000000A:
8860 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8861 break;
8862
8863 default:
8864 /* just ignore */
8865 break;
8866 }
8867 }
8868
8869 mHWData->mMemorySize = data.ulMemorySizeMB;
8870 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8871
8872 // boot order
8873 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8874 {
8875 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8876 if (it == data.mapBootOrder.end())
8877 mHWData->mBootOrder[i] = DeviceType_Null;
8878 else
8879 mHWData->mBootOrder[i] = it->second;
8880 }
8881
8882 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8883 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8884 mHWData->mMonitorCount = data.cMonitors;
8885 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8886 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8887 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8888 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8889 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8890 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8891 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8892 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8893 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8894 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8895 if (!data.strVideoCaptureFile.isEmpty())
8896 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8897 else
8898 mHWData->mVideoCaptureFile.setNull();
8899 mHWData->mFirmwareType = data.firmwareType;
8900 mHWData->mPointingHIDType = data.pointingHIDType;
8901 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8902 mHWData->mChipsetType = data.chipsetType;
8903 mHWData->mParavirtProvider = data.paravirtProvider;
8904 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8905 mHWData->mHPETEnabled = data.fHPETEnabled;
8906
8907 /* VRDEServer */
8908 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8909 if (FAILED(rc)) return rc;
8910
8911 /* BIOS */
8912 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8913 if (FAILED(rc)) return rc;
8914
8915 // Bandwidth control (must come before network adapters)
8916 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8917 if (FAILED(rc)) return rc;
8918
8919 /* Shared folders */
8920 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8921 it != data.usbSettings.llUSBControllers.end();
8922 ++it)
8923 {
8924 const settings::USBController &settingsCtrl = *it;
8925 ComObjPtr<USBController> newCtrl;
8926
8927 newCtrl.createObject();
8928 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8929 mUSBControllers->push_back(newCtrl);
8930 }
8931
8932 /* USB device filters */
8933 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8934 if (FAILED(rc)) return rc;
8935
8936 // network adapters
8937 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8938 size_t oldCount = mNetworkAdapters.size();
8939 if (newCount > oldCount)
8940 {
8941 mNetworkAdapters.resize(newCount);
8942 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8943 {
8944 unconst(mNetworkAdapters[slot]).createObject();
8945 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8946 }
8947 }
8948 else if (newCount < oldCount)
8949 mNetworkAdapters.resize(newCount);
8950 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8951 it != data.llNetworkAdapters.end();
8952 ++it)
8953 {
8954 const settings::NetworkAdapter &nic = *it;
8955
8956 /* slot unicity is guaranteed by XML Schema */
8957 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8958 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8959 if (FAILED(rc)) return rc;
8960 }
8961
8962 // serial ports
8963 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8964 it != data.llSerialPorts.end();
8965 ++it)
8966 {
8967 const settings::SerialPort &s = *it;
8968
8969 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8970 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8971 if (FAILED(rc)) return rc;
8972 }
8973
8974 // parallel ports (optional)
8975 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8976 it != data.llParallelPorts.end();
8977 ++it)
8978 {
8979 const settings::ParallelPort &p = *it;
8980
8981 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8982 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8983 if (FAILED(rc)) return rc;
8984 }
8985
8986 /* AudioAdapter */
8987 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8988 if (FAILED(rc)) return rc;
8989
8990 /* Shared folders */
8991 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8992 it != data.llSharedFolders.end();
8993 ++it)
8994 {
8995 const settings::SharedFolder &sf = *it;
8996
8997 ComObjPtr<SharedFolder> sharedFolder;
8998 /* Check for double entries. Not allowed! */
8999 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9000 if (SUCCEEDED(rc))
9001 return setError(VBOX_E_OBJECT_IN_USE,
9002 tr("Shared folder named '%s' already exists"),
9003 sf.strName.c_str());
9004
9005 /* Create the new shared folder. Don't break on error. This will be
9006 * reported when the machine starts. */
9007 sharedFolder.createObject();
9008 rc = sharedFolder->init(i_getMachine(),
9009 sf.strName,
9010 sf.strHostPath,
9011 RT_BOOL(sf.fWritable),
9012 RT_BOOL(sf.fAutoMount),
9013 false /* fFailOnError */);
9014 if (FAILED(rc)) return rc;
9015 mHWData->mSharedFolders.push_back(sharedFolder);
9016 }
9017
9018 // Clipboard
9019 mHWData->mClipboardMode = data.clipboardMode;
9020
9021 // drag'n'drop
9022 mHWData->mDnDMode = data.dndMode;
9023
9024 // guest settings
9025 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9026
9027 // IO settings
9028 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9029 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9030
9031 // Host PCI devices
9032 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9033 it != data.pciAttachments.end();
9034 ++it)
9035 {
9036 const settings::HostPCIDeviceAttachment &hpda = *it;
9037 ComObjPtr<PCIDeviceAttachment> pda;
9038
9039 pda.createObject();
9040 pda->i_loadSettings(this, hpda);
9041 mHWData->mPCIDeviceAssignments.push_back(pda);
9042 }
9043
9044 /*
9045 * (The following isn't really real hardware, but it lives in HWData
9046 * for reasons of convenience.)
9047 */
9048
9049#ifdef VBOX_WITH_GUEST_PROPS
9050 /* Guest properties (optional) */
9051
9052 /* Only load transient guest properties for configs which have saved
9053 * state, because there shouldn't be any for powered off VMs. The same
9054 * logic applies for snapshots, as offline snapshots shouldn't have
9055 * any such properties. They confuse the code in various places.
9056 * Note: can't rely on the machine state, as it isn't set yet. */
9057 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9058 /* apologies for the hacky unconst() usage, but this needs hacking
9059 * actually inconsistent settings into consistency, otherwise there
9060 * will be some corner cases where the inconsistency survives
9061 * surprisingly long without getting fixed, especially for snapshots
9062 * as there are no config changes. */
9063 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9064 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9065 it != llGuestProperties.end();
9066 /*nothing*/)
9067 {
9068 const settings::GuestProperty &prop = *it;
9069 uint32_t fFlags = guestProp::NILFLAG;
9070 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9071 if ( fSkipTransientGuestProperties
9072 && ( fFlags & guestProp::TRANSIENT
9073 || fFlags & guestProp::TRANSRESET))
9074 {
9075 it = llGuestProperties.erase(it);
9076 continue;
9077 }
9078 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9079 mHWData->mGuestProperties[prop.strName] = property;
9080 ++it;
9081 }
9082
9083 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9084#endif /* VBOX_WITH_GUEST_PROPS defined */
9085
9086 rc = i_loadDebugging(pDbg);
9087 if (FAILED(rc))
9088 return rc;
9089
9090 mHWData->mAutostart = *pAutostart;
9091
9092 /* default frontend */
9093 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9094 }
9095 catch(std::bad_alloc &)
9096 {
9097 return E_OUTOFMEMORY;
9098 }
9099
9100 AssertComRC(rc);
9101 return rc;
9102}
9103
9104/**
9105 * Called from Machine::loadHardware() to load the debugging settings of the
9106 * machine.
9107 *
9108 * @param pDbg Pointer to the settings.
9109 */
9110HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9111{
9112 mHWData->mDebugging = *pDbg;
9113 /* no more processing currently required, this will probably change. */
9114 return S_OK;
9115}
9116
9117/**
9118 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9119 *
9120 * @param data
9121 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9122 * @param puuidSnapshot
9123 * @return
9124 */
9125HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9126 const Guid *puuidRegistry,
9127 const Guid *puuidSnapshot)
9128{
9129 AssertReturn(!i_isSessionMachine(), E_FAIL);
9130
9131 HRESULT rc = S_OK;
9132
9133 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9134 it != data.llStorageControllers.end();
9135 ++it)
9136 {
9137 const settings::StorageController &ctlData = *it;
9138
9139 ComObjPtr<StorageController> pCtl;
9140 /* Try to find one with the name first. */
9141 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9142 if (SUCCEEDED(rc))
9143 return setError(VBOX_E_OBJECT_IN_USE,
9144 tr("Storage controller named '%s' already exists"),
9145 ctlData.strName.c_str());
9146
9147 pCtl.createObject();
9148 rc = pCtl->init(this,
9149 ctlData.strName,
9150 ctlData.storageBus,
9151 ctlData.ulInstance,
9152 ctlData.fBootable);
9153 if (FAILED(rc)) return rc;
9154
9155 mStorageControllers->push_back(pCtl);
9156
9157 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9158 if (FAILED(rc)) return rc;
9159
9160 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9161 if (FAILED(rc)) return rc;
9162
9163 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9164 if (FAILED(rc)) return rc;
9165
9166 /* Set IDE emulation settings (only for AHCI controller). */
9167 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9168 {
9169 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9170 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9171 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9172 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9173 )
9174 return rc;
9175 }
9176
9177 /* Load the attached devices now. */
9178 rc = i_loadStorageDevices(pCtl,
9179 ctlData,
9180 puuidRegistry,
9181 puuidSnapshot);
9182 if (FAILED(rc)) return rc;
9183 }
9184
9185 return S_OK;
9186}
9187
9188/**
9189 * Called from i_loadStorageControllers for a controller's devices.
9190 *
9191 * @param aStorageController
9192 * @param data
9193 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9194 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9195 * @return
9196 */
9197HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9198 const settings::StorageController &data,
9199 const Guid *puuidRegistry,
9200 const Guid *puuidSnapshot)
9201{
9202 HRESULT rc = S_OK;
9203
9204 /* paranoia: detect duplicate attachments */
9205 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9206 it != data.llAttachedDevices.end();
9207 ++it)
9208 {
9209 const settings::AttachedDevice &ad = *it;
9210
9211 for (settings::AttachedDevicesList::const_iterator it2 = it;
9212 it2 != data.llAttachedDevices.end();
9213 ++it2)
9214 {
9215 if (it == it2)
9216 continue;
9217
9218 const settings::AttachedDevice &ad2 = *it2;
9219
9220 if ( ad.lPort == ad2.lPort
9221 && ad.lDevice == ad2.lDevice)
9222 {
9223 return setError(E_FAIL,
9224 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9225 aStorageController->i_getName().c_str(),
9226 ad.lPort,
9227 ad.lDevice,
9228 mUserData->s.strName.c_str());
9229 }
9230 }
9231 }
9232
9233 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9234 it != data.llAttachedDevices.end();
9235 ++it)
9236 {
9237 const settings::AttachedDevice &dev = *it;
9238 ComObjPtr<Medium> medium;
9239
9240 switch (dev.deviceType)
9241 {
9242 case DeviceType_Floppy:
9243 case DeviceType_DVD:
9244 if (dev.strHostDriveSrc.isNotEmpty())
9245 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9246 false /* fRefresh */, medium);
9247 else
9248 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9249 dev.uuid,
9250 false /* fRefresh */,
9251 false /* aSetError */,
9252 medium);
9253 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9254 // This is not an error. The host drive or UUID might have vanished, so just go
9255 // ahead without this removeable medium attachment
9256 rc = S_OK;
9257 break;
9258
9259 case DeviceType_HardDisk:
9260 {
9261 /* find a hard disk by UUID */
9262 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9263 if (FAILED(rc))
9264 {
9265 if (i_isSnapshotMachine())
9266 {
9267 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9268 // so the user knows that the bad disk is in a snapshot somewhere
9269 com::ErrorInfo info;
9270 return setError(E_FAIL,
9271 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9272 puuidSnapshot->raw(),
9273 info.getText().raw());
9274 }
9275 else
9276 return rc;
9277 }
9278
9279 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9280
9281 if (medium->i_getType() == MediumType_Immutable)
9282 {
9283 if (i_isSnapshotMachine())
9284 return setError(E_FAIL,
9285 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9286 "of the virtual machine '%s' ('%s')"),
9287 medium->i_getLocationFull().c_str(),
9288 dev.uuid.raw(),
9289 puuidSnapshot->raw(),
9290 mUserData->s.strName.c_str(),
9291 mData->m_strConfigFileFull.c_str());
9292
9293 return setError(E_FAIL,
9294 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9295 medium->i_getLocationFull().c_str(),
9296 dev.uuid.raw(),
9297 mUserData->s.strName.c_str(),
9298 mData->m_strConfigFileFull.c_str());
9299 }
9300
9301 if (medium->i_getType() == MediumType_MultiAttach)
9302 {
9303 if (i_isSnapshotMachine())
9304 return setError(E_FAIL,
9305 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9306 "of the virtual machine '%s' ('%s')"),
9307 medium->i_getLocationFull().c_str(),
9308 dev.uuid.raw(),
9309 puuidSnapshot->raw(),
9310 mUserData->s.strName.c_str(),
9311 mData->m_strConfigFileFull.c_str());
9312
9313 return setError(E_FAIL,
9314 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9315 medium->i_getLocationFull().c_str(),
9316 dev.uuid.raw(),
9317 mUserData->s.strName.c_str(),
9318 mData->m_strConfigFileFull.c_str());
9319 }
9320
9321 if ( !i_isSnapshotMachine()
9322 && medium->i_getChildren().size() != 0
9323 )
9324 return setError(E_FAIL,
9325 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9326 "because it has %d differencing child hard disks"),
9327 medium->i_getLocationFull().c_str(),
9328 dev.uuid.raw(),
9329 mUserData->s.strName.c_str(),
9330 mData->m_strConfigFileFull.c_str(),
9331 medium->i_getChildren().size());
9332
9333 if (i_findAttachment(mMediaData->mAttachments,
9334 medium))
9335 return setError(E_FAIL,
9336 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9337 medium->i_getLocationFull().c_str(),
9338 dev.uuid.raw(),
9339 mUserData->s.strName.c_str(),
9340 mData->m_strConfigFileFull.c_str());
9341
9342 break;
9343 }
9344
9345 default:
9346 return setError(E_FAIL,
9347 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9348 medium->i_getLocationFull().c_str(),
9349 mUserData->s.strName.c_str(),
9350 mData->m_strConfigFileFull.c_str());
9351 }
9352
9353 if (FAILED(rc))
9354 break;
9355
9356 /* Bandwidth groups are loaded at this point. */
9357 ComObjPtr<BandwidthGroup> pBwGroup;
9358
9359 if (!dev.strBwGroup.isEmpty())
9360 {
9361 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9362 if (FAILED(rc))
9363 return setError(E_FAIL,
9364 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9365 medium->i_getLocationFull().c_str(),
9366 dev.strBwGroup.c_str(),
9367 mUserData->s.strName.c_str(),
9368 mData->m_strConfigFileFull.c_str());
9369 pBwGroup->i_reference();
9370 }
9371
9372 const Bstr controllerName = aStorageController->i_getName();
9373 ComObjPtr<MediumAttachment> pAttachment;
9374 pAttachment.createObject();
9375 rc = pAttachment->init(this,
9376 medium,
9377 controllerName,
9378 dev.lPort,
9379 dev.lDevice,
9380 dev.deviceType,
9381 false,
9382 dev.fPassThrough,
9383 dev.fTempEject,
9384 dev.fNonRotational,
9385 dev.fDiscard,
9386 dev.fHotPluggable,
9387 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9388 if (FAILED(rc)) break;
9389
9390 /* associate the medium with this machine and snapshot */
9391 if (!medium.isNull())
9392 {
9393 AutoCaller medCaller(medium);
9394 if (FAILED(medCaller.rc())) return medCaller.rc();
9395 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9396
9397 if (i_isSnapshotMachine())
9398 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9399 else
9400 rc = medium->i_addBackReference(mData->mUuid);
9401 /* If the medium->addBackReference fails it sets an appropriate
9402 * error message, so no need to do any guesswork here. */
9403
9404 if (puuidRegistry)
9405 // caller wants registry ID to be set on all attached media (OVF import case)
9406 medium->i_addRegistry(*puuidRegistry);
9407 }
9408
9409 if (FAILED(rc))
9410 break;
9411
9412 /* back up mMediaData to let registeredInit() properly rollback on failure
9413 * (= limited accessibility) */
9414 i_setModified(IsModified_Storage);
9415 mMediaData.backup();
9416 mMediaData->mAttachments.push_back(pAttachment);
9417 }
9418
9419 return rc;
9420}
9421
9422/**
9423 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9424 *
9425 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9426 * @param aSnapshot where to return the found snapshot
9427 * @param aSetError true to set extended error info on failure
9428 */
9429HRESULT Machine::i_findSnapshotById(const Guid &aId,
9430 ComObjPtr<Snapshot> &aSnapshot,
9431 bool aSetError /* = false */)
9432{
9433 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9434
9435 if (!mData->mFirstSnapshot)
9436 {
9437 if (aSetError)
9438 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9439 return E_FAIL;
9440 }
9441
9442 if (aId.isZero())
9443 aSnapshot = mData->mFirstSnapshot;
9444 else
9445 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9446
9447 if (!aSnapshot)
9448 {
9449 if (aSetError)
9450 return setError(E_FAIL,
9451 tr("Could not find a snapshot with UUID {%s}"),
9452 aId.toString().c_str());
9453 return E_FAIL;
9454 }
9455
9456 return S_OK;
9457}
9458
9459/**
9460 * Returns the snapshot with the given name or fails of no such snapshot.
9461 *
9462 * @param aName snapshot name to find
9463 * @param aSnapshot where to return the found snapshot
9464 * @param aSetError true to set extended error info on failure
9465 */
9466HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9467 ComObjPtr<Snapshot> &aSnapshot,
9468 bool aSetError /* = false */)
9469{
9470 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9471
9472 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9473
9474 if (!mData->mFirstSnapshot)
9475 {
9476 if (aSetError)
9477 return setError(VBOX_E_OBJECT_NOT_FOUND,
9478 tr("This machine does not have any snapshots"));
9479 return VBOX_E_OBJECT_NOT_FOUND;
9480 }
9481
9482 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9483
9484 if (!aSnapshot)
9485 {
9486 if (aSetError)
9487 return setError(VBOX_E_OBJECT_NOT_FOUND,
9488 tr("Could not find a snapshot named '%s'"), strName.c_str());
9489 return VBOX_E_OBJECT_NOT_FOUND;
9490 }
9491
9492 return S_OK;
9493}
9494
9495/**
9496 * Returns a storage controller object with the given name.
9497 *
9498 * @param aName storage controller name to find
9499 * @param aStorageController where to return the found storage controller
9500 * @param aSetError true to set extended error info on failure
9501 */
9502HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9503 ComObjPtr<StorageController> &aStorageController,
9504 bool aSetError /* = false */)
9505{
9506 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9507
9508 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9509 it != mStorageControllers->end();
9510 ++it)
9511 {
9512 if ((*it)->i_getName() == aName)
9513 {
9514 aStorageController = (*it);
9515 return S_OK;
9516 }
9517 }
9518
9519 if (aSetError)
9520 return setError(VBOX_E_OBJECT_NOT_FOUND,
9521 tr("Could not find a storage controller named '%s'"),
9522 aName.c_str());
9523 return VBOX_E_OBJECT_NOT_FOUND;
9524}
9525
9526/**
9527 * Returns a USB controller object with the given name.
9528 *
9529 * @param aName USB controller name to find
9530 * @param aUSBController where to return the found USB controller
9531 * @param aSetError true to set extended error info on failure
9532 */
9533HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9534 ComObjPtr<USBController> &aUSBController,
9535 bool aSetError /* = false */)
9536{
9537 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9538
9539 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9540 it != mUSBControllers->end();
9541 ++it)
9542 {
9543 if ((*it)->i_getName() == aName)
9544 {
9545 aUSBController = (*it);
9546 return S_OK;
9547 }
9548 }
9549
9550 if (aSetError)
9551 return setError(VBOX_E_OBJECT_NOT_FOUND,
9552 tr("Could not find a storage controller named '%s'"),
9553 aName.c_str());
9554 return VBOX_E_OBJECT_NOT_FOUND;
9555}
9556
9557/**
9558 * Returns the number of USB controller instance of the given type.
9559 *
9560 * @param enmType USB controller type.
9561 */
9562ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9563{
9564 ULONG cCtrls = 0;
9565
9566 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9567 it != mUSBControllers->end();
9568 ++it)
9569 {
9570 if ((*it)->i_getControllerType() == enmType)
9571 cCtrls++;
9572 }
9573
9574 return cCtrls;
9575}
9576
9577HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9578 MediaData::AttachmentList &atts)
9579{
9580 AutoCaller autoCaller(this);
9581 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9582
9583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9584
9585 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9586 it != mMediaData->mAttachments.end();
9587 ++it)
9588 {
9589 const ComObjPtr<MediumAttachment> &pAtt = *it;
9590 // should never happen, but deal with NULL pointers in the list.
9591 AssertStmt(!pAtt.isNull(), continue);
9592
9593 // getControllerName() needs caller+read lock
9594 AutoCaller autoAttCaller(pAtt);
9595 if (FAILED(autoAttCaller.rc()))
9596 {
9597 atts.clear();
9598 return autoAttCaller.rc();
9599 }
9600 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9601
9602 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9603 atts.push_back(pAtt);
9604 }
9605
9606 return S_OK;
9607}
9608
9609
9610/**
9611 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9612 * file if the machine name was changed and about creating a new settings file
9613 * if this is a new machine.
9614 *
9615 * @note Must be never called directly but only from #saveSettings().
9616 */
9617HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9618{
9619 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9620
9621 HRESULT rc = S_OK;
9622
9623 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9624
9625 /// @todo need to handle primary group change, too
9626
9627 /* attempt to rename the settings file if machine name is changed */
9628 if ( mUserData->s.fNameSync
9629 && mUserData.isBackedUp()
9630 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9631 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9632 )
9633 {
9634 bool dirRenamed = false;
9635 bool fileRenamed = false;
9636
9637 Utf8Str configFile, newConfigFile;
9638 Utf8Str configFilePrev, newConfigFilePrev;
9639 Utf8Str configDir, newConfigDir;
9640
9641 do
9642 {
9643 int vrc = VINF_SUCCESS;
9644
9645 Utf8Str name = mUserData.backedUpData()->s.strName;
9646 Utf8Str newName = mUserData->s.strName;
9647 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9648 if (group == "/")
9649 group.setNull();
9650 Utf8Str newGroup = mUserData->s.llGroups.front();
9651 if (newGroup == "/")
9652 newGroup.setNull();
9653
9654 configFile = mData->m_strConfigFileFull;
9655
9656 /* first, rename the directory if it matches the group and machine name */
9657 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9658 group.c_str(), RTPATH_DELIMITER, name.c_str());
9659 /** @todo hack, make somehow use of ComposeMachineFilename */
9660 if (mUserData->s.fDirectoryIncludesUUID)
9661 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9662 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9663 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9664 /** @todo hack, make somehow use of ComposeMachineFilename */
9665 if (mUserData->s.fDirectoryIncludesUUID)
9666 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9667 configDir = configFile;
9668 configDir.stripFilename();
9669 newConfigDir = configDir;
9670 if ( configDir.length() >= groupPlusName.length()
9671 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9672 groupPlusName.c_str()))
9673 {
9674 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9675 Utf8Str newConfigBaseDir(newConfigDir);
9676 newConfigDir.append(newGroupPlusName);
9677 /* consistency: use \ if appropriate on the platform */
9678 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9679 /* new dir and old dir cannot be equal here because of 'if'
9680 * above and because name != newName */
9681 Assert(configDir != newConfigDir);
9682 if (!fSettingsFileIsNew)
9683 {
9684 /* perform real rename only if the machine is not new */
9685 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9686 if ( vrc == VERR_FILE_NOT_FOUND
9687 || vrc == VERR_PATH_NOT_FOUND)
9688 {
9689 /* create the parent directory, then retry renaming */
9690 Utf8Str parent(newConfigDir);
9691 parent.stripFilename();
9692 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9693 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9694 }
9695 if (RT_FAILURE(vrc))
9696 {
9697 rc = setError(E_FAIL,
9698 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9699 configDir.c_str(),
9700 newConfigDir.c_str(),
9701 vrc);
9702 break;
9703 }
9704 /* delete subdirectories which are no longer needed */
9705 Utf8Str dir(configDir);
9706 dir.stripFilename();
9707 while (dir != newConfigBaseDir && dir != ".")
9708 {
9709 vrc = RTDirRemove(dir.c_str());
9710 if (RT_FAILURE(vrc))
9711 break;
9712 dir.stripFilename();
9713 }
9714 dirRenamed = true;
9715 }
9716 }
9717
9718 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9719 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9720
9721 /* then try to rename the settings file itself */
9722 if (newConfigFile != configFile)
9723 {
9724 /* get the path to old settings file in renamed directory */
9725 configFile = Utf8StrFmt("%s%c%s",
9726 newConfigDir.c_str(),
9727 RTPATH_DELIMITER,
9728 RTPathFilename(configFile.c_str()));
9729 if (!fSettingsFileIsNew)
9730 {
9731 /* perform real rename only if the machine is not new */
9732 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9733 if (RT_FAILURE(vrc))
9734 {
9735 rc = setError(E_FAIL,
9736 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9737 configFile.c_str(),
9738 newConfigFile.c_str(),
9739 vrc);
9740 break;
9741 }
9742 fileRenamed = true;
9743 configFilePrev = configFile;
9744 configFilePrev += "-prev";
9745 newConfigFilePrev = newConfigFile;
9746 newConfigFilePrev += "-prev";
9747 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9748 }
9749 }
9750
9751 // update m_strConfigFileFull amd mConfigFile
9752 mData->m_strConfigFileFull = newConfigFile;
9753 // compute the relative path too
9754 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9755
9756 // store the old and new so that VirtualBox::i_saveSettings() can update
9757 // the media registry
9758 if ( mData->mRegistered
9759 && (configDir != newConfigDir || configFile != newConfigFile))
9760 {
9761 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9762
9763 if (pfNeedsGlobalSaveSettings)
9764 *pfNeedsGlobalSaveSettings = true;
9765 }
9766
9767 // in the saved state file path, replace the old directory with the new directory
9768 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9769 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9770
9771 // and do the same thing for the saved state file paths of all the online snapshots
9772 if (mData->mFirstSnapshot)
9773 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9774 newConfigDir.c_str());
9775 }
9776 while (0);
9777
9778 if (FAILED(rc))
9779 {
9780 /* silently try to rename everything back */
9781 if (fileRenamed)
9782 {
9783 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9784 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9785 }
9786 if (dirRenamed)
9787 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9788 }
9789
9790 if (FAILED(rc)) return rc;
9791 }
9792
9793 if (fSettingsFileIsNew)
9794 {
9795 /* create a virgin config file */
9796 int vrc = VINF_SUCCESS;
9797
9798 /* ensure the settings directory exists */
9799 Utf8Str path(mData->m_strConfigFileFull);
9800 path.stripFilename();
9801 if (!RTDirExists(path.c_str()))
9802 {
9803 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9804 if (RT_FAILURE(vrc))
9805 {
9806 return setError(E_FAIL,
9807 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9808 path.c_str(),
9809 vrc);
9810 }
9811 }
9812
9813 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9814 path = Utf8Str(mData->m_strConfigFileFull);
9815 RTFILE f = NIL_RTFILE;
9816 vrc = RTFileOpen(&f, path.c_str(),
9817 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9818 if (RT_FAILURE(vrc))
9819 return setError(E_FAIL,
9820 tr("Could not create the settings file '%s' (%Rrc)"),
9821 path.c_str(),
9822 vrc);
9823 RTFileClose(f);
9824 }
9825
9826 return rc;
9827}
9828
9829/**
9830 * Saves and commits machine data, user data and hardware data.
9831 *
9832 * Note that on failure, the data remains uncommitted.
9833 *
9834 * @a aFlags may combine the following flags:
9835 *
9836 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9837 * Used when saving settings after an operation that makes them 100%
9838 * correspond to the settings from the current snapshot.
9839 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9840 * #isReallyModified() returns false. This is necessary for cases when we
9841 * change machine data directly, not through the backup()/commit() mechanism.
9842 * - SaveS_Force: settings will be saved without doing a deep compare of the
9843 * settings structures. This is used when this is called because snapshots
9844 * have changed to avoid the overhead of the deep compare.
9845 *
9846 * @note Must be called from under this object's write lock. Locks children for
9847 * writing.
9848 *
9849 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9850 * initialized to false and that will be set to true by this function if
9851 * the caller must invoke VirtualBox::i_saveSettings() because the global
9852 * settings have changed. This will happen if a machine rename has been
9853 * saved and the global machine and media registries will therefore need
9854 * updating.
9855 */
9856HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9857 int aFlags /*= 0*/)
9858{
9859 LogFlowThisFuncEnter();
9860
9861 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9862
9863 /* make sure child objects are unable to modify the settings while we are
9864 * saving them */
9865 i_ensureNoStateDependencies();
9866
9867 AssertReturn(!i_isSnapshotMachine(),
9868 E_FAIL);
9869
9870 HRESULT rc = S_OK;
9871 bool fNeedsWrite = false;
9872
9873 /* First, prepare to save settings. It will care about renaming the
9874 * settings directory and file if the machine name was changed and about
9875 * creating a new settings file if this is a new machine. */
9876 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9877 if (FAILED(rc)) return rc;
9878
9879 // keep a pointer to the current settings structures
9880 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9881 settings::MachineConfigFile *pNewConfig = NULL;
9882
9883 try
9884 {
9885 // make a fresh one to have everyone write stuff into
9886 pNewConfig = new settings::MachineConfigFile(NULL);
9887 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9888
9889 // now go and copy all the settings data from COM to the settings structures
9890 // (this calles i_saveSettings() on all the COM objects in the machine)
9891 i_copyMachineDataToSettings(*pNewConfig);
9892
9893 if (aFlags & SaveS_ResetCurStateModified)
9894 {
9895 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9896 mData->mCurrentStateModified = FALSE;
9897 fNeedsWrite = true; // always, no need to compare
9898 }
9899 else if (aFlags & SaveS_Force)
9900 {
9901 fNeedsWrite = true; // always, no need to compare
9902 }
9903 else
9904 {
9905 if (!mData->mCurrentStateModified)
9906 {
9907 // do a deep compare of the settings that we just saved with the settings
9908 // previously stored in the config file; this invokes MachineConfigFile::operator==
9909 // which does a deep compare of all the settings, which is expensive but less expensive
9910 // than writing out XML in vain
9911 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9912
9913 // could still be modified if any settings changed
9914 mData->mCurrentStateModified = fAnySettingsChanged;
9915
9916 fNeedsWrite = fAnySettingsChanged;
9917 }
9918 else
9919 fNeedsWrite = true;
9920 }
9921
9922 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9923
9924 if (fNeedsWrite)
9925 // now spit it all out!
9926 pNewConfig->write(mData->m_strConfigFileFull);
9927
9928 mData->pMachineConfigFile = pNewConfig;
9929 delete pOldConfig;
9930 i_commit();
9931
9932 // after saving settings, we are no longer different from the XML on disk
9933 mData->flModifications = 0;
9934 }
9935 catch (HRESULT err)
9936 {
9937 // we assume that error info is set by the thrower
9938 rc = err;
9939
9940 // restore old config
9941 delete pNewConfig;
9942 mData->pMachineConfigFile = pOldConfig;
9943 }
9944 catch (...)
9945 {
9946 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9947 }
9948
9949 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9950 {
9951 /* Fire the data change event, even on failure (since we've already
9952 * committed all data). This is done only for SessionMachines because
9953 * mutable Machine instances are always not registered (i.e. private
9954 * to the client process that creates them) and thus don't need to
9955 * inform callbacks. */
9956 if (i_isSessionMachine())
9957 mParent->i_onMachineDataChange(mData->mUuid);
9958 }
9959
9960 LogFlowThisFunc(("rc=%08X\n", rc));
9961 LogFlowThisFuncLeave();
9962 return rc;
9963}
9964
9965/**
9966 * Implementation for saving the machine settings into the given
9967 * settings::MachineConfigFile instance. This copies machine extradata
9968 * from the previous machine config file in the instance data, if any.
9969 *
9970 * This gets called from two locations:
9971 *
9972 * -- Machine::i_saveSettings(), during the regular XML writing;
9973 *
9974 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9975 * exported to OVF and we write the VirtualBox proprietary XML
9976 * into a <vbox:Machine> tag.
9977 *
9978 * This routine fills all the fields in there, including snapshots, *except*
9979 * for the following:
9980 *
9981 * -- fCurrentStateModified. There is some special logic associated with that.
9982 *
9983 * The caller can then call MachineConfigFile::write() or do something else
9984 * with it.
9985 *
9986 * Caller must hold the machine lock!
9987 *
9988 * This throws XML errors and HRESULT, so the caller must have a catch block!
9989 */
9990void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9991{
9992 // deep copy extradata
9993 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9994
9995 config.uuid = mData->mUuid;
9996
9997 // copy name, description, OS type, teleport, UTC etc.
9998 config.machineUserData = mUserData->s;
9999
10000 // Encode the Icon Override data from Machine and store on config userdata.
10001 std::vector<BYTE> iconByte;
10002 getIcon(iconByte);
10003 ssize_t cbData = iconByte.size();
10004 if (cbData > 0)
10005 {
10006 ssize_t cchOut = RTBase64EncodedLength(cbData);
10007 Utf8Str strIconData;
10008 strIconData.reserve(cchOut+1);
10009 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10010 strIconData.mutableRaw(), strIconData.capacity(),
10011 NULL);
10012 if (RT_FAILURE(vrc))
10013 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10014 strIconData.jolt();
10015 config.machineUserData.ovIcon = strIconData;
10016 }
10017 else
10018 config.machineUserData.ovIcon.setNull();
10019
10020 if ( mData->mMachineState == MachineState_Saved
10021 || mData->mMachineState == MachineState_Restoring
10022 // when doing certain snapshot operations we may or may not have
10023 // a saved state in the current state, so keep everything as is
10024 || ( ( mData->mMachineState == MachineState_Snapshotting
10025 || mData->mMachineState == MachineState_DeletingSnapshot
10026 || mData->mMachineState == MachineState_RestoringSnapshot)
10027 && (!mSSData->strStateFilePath.isEmpty())
10028 )
10029 )
10030 {
10031 Assert(!mSSData->strStateFilePath.isEmpty());
10032 /* try to make the file name relative to the settings file dir */
10033 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10034 }
10035 else
10036 {
10037 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10038 config.strStateFile.setNull();
10039 }
10040
10041 if (mData->mCurrentSnapshot)
10042 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10043 else
10044 config.uuidCurrentSnapshot.clear();
10045
10046 config.timeLastStateChange = mData->mLastStateChange;
10047 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10048 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10049
10050 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10051 if (FAILED(rc)) throw rc;
10052
10053 rc = i_saveStorageControllers(config.storageMachine);
10054 if (FAILED(rc)) throw rc;
10055
10056 // save machine's media registry if this is VirtualBox 4.0 or later
10057 if (config.canHaveOwnMediaRegistry())
10058 {
10059 // determine machine folder
10060 Utf8Str strMachineFolder = i_getSettingsFileFull();
10061 strMachineFolder.stripFilename();
10062 mParent->i_saveMediaRegistry(config.mediaRegistry,
10063 i_getId(), // only media with registry ID == machine UUID
10064 strMachineFolder);
10065 // this throws HRESULT
10066 }
10067
10068 // save snapshots
10069 rc = i_saveAllSnapshots(config);
10070 if (FAILED(rc)) throw rc;
10071}
10072
10073/**
10074 * Saves all snapshots of the machine into the given machine config file. Called
10075 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10076 * @param config
10077 * @return
10078 */
10079HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10080{
10081 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10082
10083 HRESULT rc = S_OK;
10084
10085 try
10086 {
10087 config.llFirstSnapshot.clear();
10088
10089 if (mData->mFirstSnapshot)
10090 {
10091 // the settings use a list for "the first snapshot"
10092 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10093
10094 // get reference to the snapshot on the list and work on that
10095 // element straight in the list to avoid excessive copying later
10096 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10097 if (FAILED(rc)) throw rc;
10098 }
10099
10100// if (mType == IsSessionMachine)
10101// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10102
10103 }
10104 catch (HRESULT err)
10105 {
10106 /* we assume that error info is set by the thrower */
10107 rc = err;
10108 }
10109 catch (...)
10110 {
10111 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10112 }
10113
10114 return rc;
10115}
10116
10117/**
10118 * Saves the VM hardware configuration. It is assumed that the
10119 * given node is empty.
10120 *
10121 * @param data Reference to the settings object for the hardware config.
10122 * @param pDbg Pointer to the settings object for the debugging config
10123 * which happens to live in mHWData.
10124 * @param pAutostart Pointer to the settings object for the autostart config
10125 * which happens to live in mHWData.
10126 */
10127HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10128 settings::Autostart *pAutostart)
10129{
10130 HRESULT rc = S_OK;
10131
10132 try
10133 {
10134 /* The hardware version attribute (optional).
10135 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10136 if ( mHWData->mHWVersion == "1"
10137 && mSSData->strStateFilePath.isEmpty()
10138 )
10139 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10140 other point needs to be found where this can be done. */
10141
10142 data.strVersion = mHWData->mHWVersion;
10143 data.uuid = mHWData->mHardwareUUID;
10144
10145 // CPU
10146 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10147 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10148 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10149 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10150 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10151 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10152 data.fPAE = !!mHWData->mPAEEnabled;
10153 data.enmLongMode = mHWData->mLongMode;
10154 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10155 data.cCPUs = mHWData->mCPUCount;
10156 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10157 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10158 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10159
10160 data.llCpus.clear();
10161 if (data.fCpuHotPlug)
10162 {
10163 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10164 {
10165 if (mHWData->mCPUAttached[idx])
10166 {
10167 settings::Cpu cpu;
10168 cpu.ulId = idx;
10169 data.llCpus.push_back(cpu);
10170 }
10171 }
10172 }
10173
10174 /* Standard and Extended CPUID leafs. */
10175 data.llCpuIdLeafs.clear();
10176 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10177 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10178 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10179 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10180 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10181 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10182
10183 // memory
10184 data.ulMemorySizeMB = mHWData->mMemorySize;
10185 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10186
10187 // firmware
10188 data.firmwareType = mHWData->mFirmwareType;
10189
10190 // HID
10191 data.pointingHIDType = mHWData->mPointingHIDType;
10192 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10193
10194 // chipset
10195 data.chipsetType = mHWData->mChipsetType;
10196
10197 // paravirt
10198 data.paravirtProvider = mHWData->mParavirtProvider;
10199
10200
10201 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10202
10203 // HPET
10204 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10205
10206 // boot order
10207 data.mapBootOrder.clear();
10208 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10209 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10210
10211 // display
10212 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10213 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10214 data.cMonitors = mHWData->mMonitorCount;
10215 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10216 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10217 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10218 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10219 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10220 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10221 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10222 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10223 {
10224 if (mHWData->maVideoCaptureScreens[i])
10225 ASMBitSet(&data.u64VideoCaptureScreens, i);
10226 else
10227 ASMBitClear(&data.u64VideoCaptureScreens, i);
10228 }
10229 /* store relative video capture file if possible */
10230 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10231
10232 /* VRDEServer settings (optional) */
10233 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10234 if (FAILED(rc)) throw rc;
10235
10236 /* BIOS (required) */
10237 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10238 if (FAILED(rc)) throw rc;
10239
10240 /* USB Controller (required) */
10241 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10242 {
10243 ComObjPtr<USBController> ctrl = *it;
10244 settings::USBController settingsCtrl;
10245
10246 settingsCtrl.strName = ctrl->i_getName();
10247 settingsCtrl.enmType = ctrl->i_getControllerType();
10248
10249 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10250 }
10251
10252 /* USB device filters (required) */
10253 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10254 if (FAILED(rc)) throw rc;
10255
10256 /* Network adapters (required) */
10257 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10258 data.llNetworkAdapters.clear();
10259 /* Write out only the nominal number of network adapters for this
10260 * chipset type. Since Machine::commit() hasn't been called there
10261 * may be extra NIC settings in the vector. */
10262 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10263 {
10264 settings::NetworkAdapter nic;
10265 nic.ulSlot = (uint32_t)slot;
10266 /* paranoia check... must not be NULL, but must not crash either. */
10267 if (mNetworkAdapters[slot])
10268 {
10269 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10270 if (FAILED(rc)) throw rc;
10271
10272 data.llNetworkAdapters.push_back(nic);
10273 }
10274 }
10275
10276 /* Serial ports */
10277 data.llSerialPorts.clear();
10278 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10279 {
10280 settings::SerialPort s;
10281 s.ulSlot = slot;
10282 rc = mSerialPorts[slot]->i_saveSettings(s);
10283 if (FAILED(rc)) return rc;
10284
10285 data.llSerialPorts.push_back(s);
10286 }
10287
10288 /* Parallel ports */
10289 data.llParallelPorts.clear();
10290 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10291 {
10292 settings::ParallelPort p;
10293 p.ulSlot = slot;
10294 rc = mParallelPorts[slot]->i_saveSettings(p);
10295 if (FAILED(rc)) return rc;
10296
10297 data.llParallelPorts.push_back(p);
10298 }
10299
10300 /* Audio adapter */
10301 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10302 if (FAILED(rc)) return rc;
10303
10304 /* Shared folders */
10305 data.llSharedFolders.clear();
10306 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10307 it != mHWData->mSharedFolders.end();
10308 ++it)
10309 {
10310 SharedFolder *pSF = *it;
10311 AutoCaller sfCaller(pSF);
10312 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10313 settings::SharedFolder sf;
10314 sf.strName = pSF->i_getName();
10315 sf.strHostPath = pSF->i_getHostPath();
10316 sf.fWritable = !!pSF->i_isWritable();
10317 sf.fAutoMount = !!pSF->i_isAutoMounted();
10318
10319 data.llSharedFolders.push_back(sf);
10320 }
10321
10322 // clipboard
10323 data.clipboardMode = mHWData->mClipboardMode;
10324
10325 // drag'n'drop
10326 data.dndMode = mHWData->mDnDMode;
10327
10328 /* Guest */
10329 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10330
10331 // IO settings
10332 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10333 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10334
10335 /* BandwidthControl (required) */
10336 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10337 if (FAILED(rc)) throw rc;
10338
10339 /* Host PCI devices */
10340 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10341 it != mHWData->mPCIDeviceAssignments.end();
10342 ++it)
10343 {
10344 ComObjPtr<PCIDeviceAttachment> pda = *it;
10345 settings::HostPCIDeviceAttachment hpda;
10346
10347 rc = pda->i_saveSettings(hpda);
10348 if (FAILED(rc)) throw rc;
10349
10350 data.pciAttachments.push_back(hpda);
10351 }
10352
10353
10354 // guest properties
10355 data.llGuestProperties.clear();
10356#ifdef VBOX_WITH_GUEST_PROPS
10357 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10358 it != mHWData->mGuestProperties.end();
10359 ++it)
10360 {
10361 HWData::GuestProperty property = it->second;
10362
10363 /* Remove transient guest properties at shutdown unless we
10364 * are saving state. Note that restoring snapshot intentionally
10365 * keeps them, they will be removed if appropriate once the final
10366 * machine state is set (as crashes etc. need to work). */
10367 if ( ( mData->mMachineState == MachineState_PoweredOff
10368 || mData->mMachineState == MachineState_Aborted
10369 || mData->mMachineState == MachineState_Teleported)
10370 && ( property.mFlags & guestProp::TRANSIENT
10371 || property.mFlags & guestProp::TRANSRESET))
10372 continue;
10373 settings::GuestProperty prop;
10374 prop.strName = it->first;
10375 prop.strValue = property.strValue;
10376 prop.timestamp = property.mTimestamp;
10377 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10378 guestProp::writeFlags(property.mFlags, szFlags);
10379 prop.strFlags = szFlags;
10380
10381 data.llGuestProperties.push_back(prop);
10382 }
10383
10384 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10385 /* I presume this doesn't require a backup(). */
10386 mData->mGuestPropertiesModified = FALSE;
10387#endif /* VBOX_WITH_GUEST_PROPS defined */
10388
10389 *pDbg = mHWData->mDebugging;
10390 *pAutostart = mHWData->mAutostart;
10391
10392 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10393 }
10394 catch(std::bad_alloc &)
10395 {
10396 return E_OUTOFMEMORY;
10397 }
10398
10399 AssertComRC(rc);
10400 return rc;
10401}
10402
10403/**
10404 * Saves the storage controller configuration.
10405 *
10406 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10407 */
10408HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10409{
10410 data.llStorageControllers.clear();
10411
10412 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10413 it != mStorageControllers->end();
10414 ++it)
10415 {
10416 HRESULT rc;
10417 ComObjPtr<StorageController> pCtl = *it;
10418
10419 settings::StorageController ctl;
10420 ctl.strName = pCtl->i_getName();
10421 ctl.controllerType = pCtl->i_getControllerType();
10422 ctl.storageBus = pCtl->i_getStorageBus();
10423 ctl.ulInstance = pCtl->i_getInstance();
10424 ctl.fBootable = pCtl->i_getBootable();
10425
10426 /* Save the port count. */
10427 ULONG portCount;
10428 rc = pCtl->COMGETTER(PortCount)(&portCount);
10429 ComAssertComRCRet(rc, rc);
10430 ctl.ulPortCount = portCount;
10431
10432 /* Save fUseHostIOCache */
10433 BOOL fUseHostIOCache;
10434 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10435 ComAssertComRCRet(rc, rc);
10436 ctl.fUseHostIOCache = !!fUseHostIOCache;
10437
10438 /* Save IDE emulation settings. */
10439 if (ctl.controllerType == StorageControllerType_IntelAhci)
10440 {
10441 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10442 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10443 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10444 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10445 )
10446 ComAssertComRCRet(rc, rc);
10447 }
10448
10449 /* save the devices now. */
10450 rc = i_saveStorageDevices(pCtl, ctl);
10451 ComAssertComRCRet(rc, rc);
10452
10453 data.llStorageControllers.push_back(ctl);
10454 }
10455
10456 return S_OK;
10457}
10458
10459/**
10460 * Saves the hard disk configuration.
10461 */
10462HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10463 settings::StorageController &data)
10464{
10465 MediaData::AttachmentList atts;
10466
10467 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10468 if (FAILED(rc)) return rc;
10469
10470 data.llAttachedDevices.clear();
10471 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10472 it != atts.end();
10473 ++it)
10474 {
10475 settings::AttachedDevice dev;
10476 IMediumAttachment *iA = *it;
10477 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10478 Medium *pMedium = pAttach->i_getMedium();
10479
10480 dev.deviceType = pAttach->i_getType();
10481 dev.lPort = pAttach->i_getPort();
10482 dev.lDevice = pAttach->i_getDevice();
10483 dev.fPassThrough = pAttach->i_getPassthrough();
10484 dev.fHotPluggable = pAttach->i_getHotPluggable();
10485 if (pMedium)
10486 {
10487 if (pMedium->i_isHostDrive())
10488 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10489 else
10490 dev.uuid = pMedium->i_getId();
10491 dev.fTempEject = pAttach->i_getTempEject();
10492 dev.fNonRotational = pAttach->i_getNonRotational();
10493 dev.fDiscard = pAttach->i_getDiscard();
10494 }
10495
10496 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10497
10498 data.llAttachedDevices.push_back(dev);
10499 }
10500
10501 return S_OK;
10502}
10503
10504/**
10505 * Saves machine state settings as defined by aFlags
10506 * (SaveSTS_* values).
10507 *
10508 * @param aFlags Combination of SaveSTS_* flags.
10509 *
10510 * @note Locks objects for writing.
10511 */
10512HRESULT Machine::i_saveStateSettings(int aFlags)
10513{
10514 if (aFlags == 0)
10515 return S_OK;
10516
10517 AutoCaller autoCaller(this);
10518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10519
10520 /* This object's write lock is also necessary to serialize file access
10521 * (prevent concurrent reads and writes) */
10522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10523
10524 HRESULT rc = S_OK;
10525
10526 Assert(mData->pMachineConfigFile);
10527
10528 try
10529 {
10530 if (aFlags & SaveSTS_CurStateModified)
10531 mData->pMachineConfigFile->fCurrentStateModified = true;
10532
10533 if (aFlags & SaveSTS_StateFilePath)
10534 {
10535 if (!mSSData->strStateFilePath.isEmpty())
10536 /* try to make the file name relative to the settings file dir */
10537 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10538 else
10539 mData->pMachineConfigFile->strStateFile.setNull();
10540 }
10541
10542 if (aFlags & SaveSTS_StateTimeStamp)
10543 {
10544 Assert( mData->mMachineState != MachineState_Aborted
10545 || mSSData->strStateFilePath.isEmpty());
10546
10547 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10548
10549 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10550//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10551 }
10552
10553 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10554 }
10555 catch (...)
10556 {
10557 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10558 }
10559
10560 return rc;
10561}
10562
10563/**
10564 * Ensures that the given medium is added to a media registry. If this machine
10565 * was created with 4.0 or later, then the machine registry is used. Otherwise
10566 * the global VirtualBox media registry is used.
10567 *
10568 * Caller must NOT hold machine lock, media tree or any medium locks!
10569 *
10570 * @param pMedium
10571 */
10572void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10573{
10574 /* Paranoia checks: do not hold machine or media tree locks. */
10575 AssertReturnVoid(!isWriteLockOnCurrentThread());
10576 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10577
10578 ComObjPtr<Medium> pBase;
10579 {
10580 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10581 pBase = pMedium->i_getBase();
10582 }
10583
10584 /* Paranoia checks: do not hold medium locks. */
10585 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10586 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10587
10588 // decide which medium registry to use now that the medium is attached:
10589 Guid uuid;
10590 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10591 // machine XML is VirtualBox 4.0 or higher:
10592 uuid = i_getId(); // machine UUID
10593 else
10594 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10595
10596 if (pMedium->i_addRegistry(uuid))
10597 mParent->i_markRegistryModified(uuid);
10598
10599 /* For more complex hard disk structures it can happen that the base
10600 * medium isn't yet associated with any medium registry. Do that now. */
10601 if (pMedium != pBase)
10602 {
10603 /* Tree lock needed by Medium::addRegistry when recursing. */
10604 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10605 if (pBase->i_addRegistryRecursive(uuid))
10606 {
10607 treeLock.release();
10608 mParent->i_markRegistryModified(uuid);
10609 }
10610 }
10611}
10612
10613/**
10614 * Creates differencing hard disks for all normal hard disks attached to this
10615 * machine and a new set of attachments to refer to created disks.
10616 *
10617 * Used when taking a snapshot or when deleting the current state. Gets called
10618 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10619 *
10620 * This method assumes that mMediaData contains the original hard disk attachments
10621 * it needs to create diffs for. On success, these attachments will be replaced
10622 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10623 * called to delete created diffs which will also rollback mMediaData and restore
10624 * whatever was backed up before calling this method.
10625 *
10626 * Attachments with non-normal hard disks are left as is.
10627 *
10628 * If @a aOnline is @c false then the original hard disks that require implicit
10629 * diffs will be locked for reading. Otherwise it is assumed that they are
10630 * already locked for writing (when the VM was started). Note that in the latter
10631 * case it is responsibility of the caller to lock the newly created diffs for
10632 * writing if this method succeeds.
10633 *
10634 * @param aProgress Progress object to run (must contain at least as
10635 * many operations left as the number of hard disks
10636 * attached).
10637 * @param aOnline Whether the VM was online prior to this operation.
10638 *
10639 * @note The progress object is not marked as completed, neither on success nor
10640 * on failure. This is a responsibility of the caller.
10641 *
10642 * @note Locks this object and the media tree for writing.
10643 */
10644HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10645 ULONG aWeight,
10646 bool aOnline)
10647{
10648 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10649
10650 AutoCaller autoCaller(this);
10651 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10652
10653 AutoMultiWriteLock2 alock(this->lockHandle(),
10654 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10655
10656 /* must be in a protective state because we release the lock below */
10657 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10658 || mData->mMachineState == MachineState_OnlineSnapshotting
10659 || mData->mMachineState == MachineState_LiveSnapshotting
10660 || mData->mMachineState == MachineState_RestoringSnapshot
10661 || mData->mMachineState == MachineState_DeletingSnapshot
10662 , E_FAIL);
10663
10664 HRESULT rc = S_OK;
10665
10666 // use appropriate locked media map (online or offline)
10667 MediumLockListMap lockedMediaOffline;
10668 MediumLockListMap *lockedMediaMap;
10669 if (aOnline)
10670 lockedMediaMap = &mData->mSession.mLockedMedia;
10671 else
10672 lockedMediaMap = &lockedMediaOffline;
10673
10674 try
10675 {
10676 if (!aOnline)
10677 {
10678 /* lock all attached hard disks early to detect "in use"
10679 * situations before creating actual diffs */
10680 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10681 it != mMediaData->mAttachments.end();
10682 ++it)
10683 {
10684 MediumAttachment* pAtt = *it;
10685 if (pAtt->i_getType() == DeviceType_HardDisk)
10686 {
10687 Medium* pMedium = pAtt->i_getMedium();
10688 Assert(pMedium);
10689
10690 MediumLockList *pMediumLockList(new MediumLockList());
10691 alock.release();
10692 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10693 false /* fMediumLockWrite */,
10694 false /* fMediumLockWriteAll */,
10695 NULL,
10696 *pMediumLockList);
10697 alock.acquire();
10698 if (FAILED(rc))
10699 {
10700 delete pMediumLockList;
10701 throw rc;
10702 }
10703 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10704 if (FAILED(rc))
10705 {
10706 throw setError(rc,
10707 tr("Collecting locking information for all attached media failed"));
10708 }
10709 }
10710 }
10711
10712 /* Now lock all media. If this fails, nothing is locked. */
10713 alock.release();
10714 rc = lockedMediaMap->Lock();
10715 alock.acquire();
10716 if (FAILED(rc))
10717 {
10718 throw setError(rc,
10719 tr("Locking of attached media failed"));
10720 }
10721 }
10722
10723 /* remember the current list (note that we don't use backup() since
10724 * mMediaData may be already backed up) */
10725 MediaData::AttachmentList atts = mMediaData->mAttachments;
10726
10727 /* start from scratch */
10728 mMediaData->mAttachments.clear();
10729
10730 /* go through remembered attachments and create diffs for normal hard
10731 * disks and attach them */
10732 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10733 it != atts.end();
10734 ++it)
10735 {
10736 MediumAttachment* pAtt = *it;
10737
10738 DeviceType_T devType = pAtt->i_getType();
10739 Medium* pMedium = pAtt->i_getMedium();
10740
10741 if ( devType != DeviceType_HardDisk
10742 || pMedium == NULL
10743 || pMedium->i_getType() != MediumType_Normal)
10744 {
10745 /* copy the attachment as is */
10746
10747 /** @todo the progress object created in SessionMachine::TakeSnaphot
10748 * only expects operations for hard disks. Later other
10749 * device types need to show up in the progress as well. */
10750 if (devType == DeviceType_HardDisk)
10751 {
10752 if (pMedium == NULL)
10753 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10754 aWeight); // weight
10755 else
10756 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10757 pMedium->i_getBase()->i_getName().c_str()).raw(),
10758 aWeight); // weight
10759 }
10760
10761 mMediaData->mAttachments.push_back(pAtt);
10762 continue;
10763 }
10764
10765 /* need a diff */
10766 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10767 pMedium->i_getBase()->i_getName().c_str()).raw(),
10768 aWeight); // weight
10769
10770 Utf8Str strFullSnapshotFolder;
10771 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10772
10773 ComObjPtr<Medium> diff;
10774 diff.createObject();
10775 // store the diff in the same registry as the parent
10776 // (this cannot fail here because we can't create implicit diffs for
10777 // unregistered images)
10778 Guid uuidRegistryParent;
10779 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10780 Assert(fInRegistry); NOREF(fInRegistry);
10781 rc = diff->init(mParent,
10782 pMedium->i_getPreferredDiffFormat(),
10783 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10784 uuidRegistryParent,
10785 DeviceType_HardDisk);
10786 if (FAILED(rc)) throw rc;
10787
10788 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10789 * the push_back? Looks like we're going to release medium with the
10790 * wrong kind of lock (general issue with if we fail anywhere at all)
10791 * and an orphaned VDI in the snapshots folder. */
10792
10793 /* update the appropriate lock list */
10794 MediumLockList *pMediumLockList;
10795 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10796 AssertComRCThrowRC(rc);
10797 if (aOnline)
10798 {
10799 alock.release();
10800 /* The currently attached medium will be read-only, change
10801 * the lock type to read. */
10802 rc = pMediumLockList->Update(pMedium, false);
10803 alock.acquire();
10804 AssertComRCThrowRC(rc);
10805 }
10806
10807 /* release the locks before the potentially lengthy operation */
10808 alock.release();
10809 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10810 pMediumLockList,
10811 NULL /* aProgress */,
10812 true /* aWait */);
10813 alock.acquire();
10814 if (FAILED(rc)) throw rc;
10815
10816 /* actual lock list update is done in Medium::commitMedia */
10817
10818 rc = diff->i_addBackReference(mData->mUuid);
10819 AssertComRCThrowRC(rc);
10820
10821 /* add a new attachment */
10822 ComObjPtr<MediumAttachment> attachment;
10823 attachment.createObject();
10824 rc = attachment->init(this,
10825 diff,
10826 pAtt->i_getControllerName(),
10827 pAtt->i_getPort(),
10828 pAtt->i_getDevice(),
10829 DeviceType_HardDisk,
10830 true /* aImplicit */,
10831 false /* aPassthrough */,
10832 false /* aTempEject */,
10833 pAtt->i_getNonRotational(),
10834 pAtt->i_getDiscard(),
10835 pAtt->i_getHotPluggable(),
10836 pAtt->i_getBandwidthGroup());
10837 if (FAILED(rc)) throw rc;
10838
10839 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10840 AssertComRCThrowRC(rc);
10841 mMediaData->mAttachments.push_back(attachment);
10842 }
10843 }
10844 catch (HRESULT aRC) { rc = aRC; }
10845
10846 /* unlock all hard disks we locked when there is no VM */
10847 if (!aOnline)
10848 {
10849 ErrorInfoKeeper eik;
10850
10851 HRESULT rc1 = lockedMediaMap->Clear();
10852 AssertComRC(rc1);
10853 }
10854
10855 return rc;
10856}
10857
10858/**
10859 * Deletes implicit differencing hard disks created either by
10860 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10861 *
10862 * Note that to delete hard disks created by #AttachDevice() this method is
10863 * called from #fixupMedia() when the changes are rolled back.
10864 *
10865 * @note Locks this object and the media tree for writing.
10866 */
10867HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10868{
10869 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10870
10871 AutoCaller autoCaller(this);
10872 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10873
10874 AutoMultiWriteLock2 alock(this->lockHandle(),
10875 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10876
10877 /* We absolutely must have backed up state. */
10878 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10879
10880 /* Check if there are any implicitly created diff images. */
10881 bool fImplicitDiffs = false;
10882 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10883 it != mMediaData->mAttachments.end();
10884 ++it)
10885 {
10886 const ComObjPtr<MediumAttachment> &pAtt = *it;
10887 if (pAtt->i_isImplicit())
10888 {
10889 fImplicitDiffs = true;
10890 break;
10891 }
10892 }
10893 /* If there is nothing to do, leave early. This saves lots of image locking
10894 * effort. It also avoids a MachineStateChanged event without real reason.
10895 * This is important e.g. when loading a VM config, because there should be
10896 * no events. Otherwise API clients can become thoroughly confused for
10897 * inaccessible VMs (the code for loading VM configs uses this method for
10898 * cleanup if the config makes no sense), as they take such events as an
10899 * indication that the VM is alive, and they would force the VM config to
10900 * be reread, leading to an endless loop. */
10901 if (!fImplicitDiffs)
10902 return S_OK;
10903
10904 HRESULT rc = S_OK;
10905 MachineState_T oldState = mData->mMachineState;
10906
10907 /* will release the lock before the potentially lengthy operation,
10908 * so protect with the special state (unless already protected) */
10909 if ( oldState != MachineState_Snapshotting
10910 && oldState != MachineState_OnlineSnapshotting
10911 && oldState != MachineState_LiveSnapshotting
10912 && oldState != MachineState_RestoringSnapshot
10913 && oldState != MachineState_DeletingSnapshot
10914 && oldState != MachineState_DeletingSnapshotOnline
10915 && oldState != MachineState_DeletingSnapshotPaused
10916 )
10917 i_setMachineState(MachineState_SettingUp);
10918
10919 // use appropriate locked media map (online or offline)
10920 MediumLockListMap lockedMediaOffline;
10921 MediumLockListMap *lockedMediaMap;
10922 if (aOnline)
10923 lockedMediaMap = &mData->mSession.mLockedMedia;
10924 else
10925 lockedMediaMap = &lockedMediaOffline;
10926
10927 try
10928 {
10929 if (!aOnline)
10930 {
10931 /* lock all attached hard disks early to detect "in use"
10932 * situations before deleting actual diffs */
10933 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10934 it != mMediaData->mAttachments.end();
10935 ++it)
10936 {
10937 MediumAttachment* pAtt = *it;
10938 if (pAtt->i_getType() == DeviceType_HardDisk)
10939 {
10940 Medium* pMedium = pAtt->i_getMedium();
10941 Assert(pMedium);
10942
10943 MediumLockList *pMediumLockList(new MediumLockList());
10944 alock.release();
10945 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10946 false /* fMediumLockWrite */,
10947 false /* fMediumLockWriteAll */,
10948 NULL,
10949 *pMediumLockList);
10950 alock.acquire();
10951
10952 if (FAILED(rc))
10953 {
10954 delete pMediumLockList;
10955 throw rc;
10956 }
10957
10958 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10959 if (FAILED(rc))
10960 throw rc;
10961 }
10962 }
10963
10964 if (FAILED(rc))
10965 throw rc;
10966 } // end of offline
10967
10968 /* Lock lists are now up to date and include implicitly created media */
10969
10970 /* Go through remembered attachments and delete all implicitly created
10971 * diffs and fix up the attachment information */
10972 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10973 MediaData::AttachmentList implicitAtts;
10974 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10975 it != mMediaData->mAttachments.end();
10976 ++it)
10977 {
10978 ComObjPtr<MediumAttachment> pAtt = *it;
10979 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10980 if (pMedium.isNull())
10981 continue;
10982
10983 // Implicit attachments go on the list for deletion and back references are removed.
10984 if (pAtt->i_isImplicit())
10985 {
10986 /* Deassociate and mark for deletion */
10987 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10988 rc = pMedium->i_removeBackReference(mData->mUuid);
10989 if (FAILED(rc))
10990 throw rc;
10991 implicitAtts.push_back(pAtt);
10992 continue;
10993 }
10994
10995 /* Was this medium attached before? */
10996 if (!i_findAttachment(oldAtts, pMedium))
10997 {
10998 /* no: de-associate */
10999 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11000 rc = pMedium->i_removeBackReference(mData->mUuid);
11001 if (FAILED(rc))
11002 throw rc;
11003 continue;
11004 }
11005 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11006 }
11007
11008 /* If there are implicit attachments to delete, throw away the lock
11009 * map contents (which will unlock all media) since the medium
11010 * attachments will be rolled back. Below we need to completely
11011 * recreate the lock map anyway since it is infinitely complex to
11012 * do this incrementally (would need reconstructing each attachment
11013 * change, which would be extremely hairy). */
11014 if (implicitAtts.size() != 0)
11015 {
11016 ErrorInfoKeeper eik;
11017
11018 HRESULT rc1 = lockedMediaMap->Clear();
11019 AssertComRC(rc1);
11020 }
11021
11022 /* rollback hard disk changes */
11023 mMediaData.rollback();
11024
11025 MultiResult mrc(S_OK);
11026
11027 // Delete unused implicit diffs.
11028 if (implicitAtts.size() != 0)
11029 {
11030 alock.release();
11031
11032 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11033 {
11034 // Remove medium associated with this attachment.
11035 ComObjPtr<MediumAttachment> pAtt = *it;
11036 Assert(pAtt);
11037 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11038 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11039 Assert(pMedium);
11040
11041 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11042 // continue on delete failure, just collect error messages
11043 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11044 pMedium->i_getLocationFull().c_str() ));
11045 mrc = rc;
11046 }
11047 // Clear the list of deleted implicit attachments now, while not
11048 // holding the lock, as it will ultimately trigger Medium::uninit()
11049 // calls which assume that the media tree lock isn't held.
11050 implicitAtts.clear();
11051
11052 alock.acquire();
11053
11054 /* if there is a VM recreate media lock map as mentioned above,
11055 * otherwise it is a waste of time and we leave things unlocked */
11056 if (aOnline)
11057 {
11058 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11059 /* must never be NULL, but better safe than sorry */
11060 if (!pMachine.isNull())
11061 {
11062 alock.release();
11063 rc = mData->mSession.mMachine->i_lockMedia();
11064 alock.acquire();
11065 if (FAILED(rc))
11066 throw rc;
11067 }
11068 }
11069 }
11070 }
11071 catch (HRESULT aRC) {rc = aRC;}
11072
11073 if (mData->mMachineState == MachineState_SettingUp)
11074 i_setMachineState(oldState);
11075
11076 /* unlock all hard disks we locked when there is no VM */
11077 if (!aOnline)
11078 {
11079 ErrorInfoKeeper eik;
11080
11081 HRESULT rc1 = lockedMediaMap->Clear();
11082 AssertComRC(rc1);
11083 }
11084
11085 return rc;
11086}
11087
11088
11089/**
11090 * Looks through the given list of media attachments for one with the given parameters
11091 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11092 * can be searched as well if needed.
11093 *
11094 * @param list
11095 * @param aControllerName
11096 * @param aControllerPort
11097 * @param aDevice
11098 * @return
11099 */
11100MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11101 IN_BSTR aControllerName,
11102 LONG aControllerPort,
11103 LONG aDevice)
11104{
11105 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11106 {
11107 MediumAttachment *pAttach = *it;
11108 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11109 return pAttach;
11110 }
11111
11112 return NULL;
11113}
11114
11115/**
11116 * Looks through the given list of media attachments for one with the given parameters
11117 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11118 * can be searched as well if needed.
11119 *
11120 * @param list
11121 * @param aControllerName
11122 * @param aControllerPort
11123 * @param aDevice
11124 * @return
11125 */
11126MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11127 ComObjPtr<Medium> pMedium)
11128{
11129 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11130 {
11131 MediumAttachment *pAttach = *it;
11132 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11133 if (pMediumThis == pMedium)
11134 return pAttach;
11135 }
11136
11137 return NULL;
11138}
11139
11140/**
11141 * Looks through the given list of media attachments for one with the given parameters
11142 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11143 * can be searched as well if needed.
11144 *
11145 * @param list
11146 * @param aControllerName
11147 * @param aControllerPort
11148 * @param aDevice
11149 * @return
11150 */
11151MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11152 Guid &id)
11153{
11154 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11155 {
11156 MediumAttachment *pAttach = *it;
11157 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11158 if (pMediumThis->i_getId() == id)
11159 return pAttach;
11160 }
11161
11162 return NULL;
11163}
11164
11165/**
11166 * Main implementation for Machine::DetachDevice. This also gets called
11167 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11168 *
11169 * @param pAttach Medium attachment to detach.
11170 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11171 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11172 * SnapshotMachine, and this must be its snapshot.
11173 * @return
11174 */
11175HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11176 AutoWriteLock &writeLock,
11177 Snapshot *pSnapshot)
11178{
11179 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11180 DeviceType_T mediumType = pAttach->i_getType();
11181
11182 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11183
11184 if (pAttach->i_isImplicit())
11185 {
11186 /* attempt to implicitly delete the implicitly created diff */
11187
11188 /// @todo move the implicit flag from MediumAttachment to Medium
11189 /// and forbid any hard disk operation when it is implicit. Or maybe
11190 /// a special media state for it to make it even more simple.
11191
11192 Assert(mMediaData.isBackedUp());
11193
11194 /* will release the lock before the potentially lengthy operation, so
11195 * protect with the special state */
11196 MachineState_T oldState = mData->mMachineState;
11197 i_setMachineState(MachineState_SettingUp);
11198
11199 writeLock.release();
11200
11201 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11202 true /*aWait*/);
11203
11204 writeLock.acquire();
11205
11206 i_setMachineState(oldState);
11207
11208 if (FAILED(rc)) return rc;
11209 }
11210
11211 i_setModified(IsModified_Storage);
11212 mMediaData.backup();
11213 mMediaData->mAttachments.remove(pAttach);
11214
11215 if (!oldmedium.isNull())
11216 {
11217 // if this is from a snapshot, do not defer detachment to commitMedia()
11218 if (pSnapshot)
11219 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11220 // else if non-hard disk media, do not defer detachment to commitMedia() either
11221 else if (mediumType != DeviceType_HardDisk)
11222 oldmedium->i_removeBackReference(mData->mUuid);
11223 }
11224
11225 return S_OK;
11226}
11227
11228/**
11229 * Goes thru all media of the given list and
11230 *
11231 * 1) calls i_detachDevice() on each of them for this machine and
11232 * 2) adds all Medium objects found in the process to the given list,
11233 * depending on cleanupMode.
11234 *
11235 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11236 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11237 * media to the list.
11238 *
11239 * This gets called from Machine::Unregister, both for the actual Machine and
11240 * the SnapshotMachine objects that might be found in the snapshots.
11241 *
11242 * Requires caller and locking. The machine lock must be passed in because it
11243 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11244 *
11245 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11246 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11247 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11248 * Full, then all media get added;
11249 * otherwise no media get added.
11250 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11251 * @return
11252 */
11253HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11254 Snapshot *pSnapshot,
11255 CleanupMode_T cleanupMode,
11256 MediaList &llMedia)
11257{
11258 Assert(isWriteLockOnCurrentThread());
11259
11260 HRESULT rc;
11261
11262 // make a temporary list because i_detachDevice invalidates iterators into
11263 // mMediaData->mAttachments
11264 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11265
11266 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11267 {
11268 ComObjPtr<MediumAttachment> &pAttach = *it;
11269 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11270
11271 if (!pMedium.isNull())
11272 {
11273 AutoCaller mac(pMedium);
11274 if (FAILED(mac.rc())) return mac.rc();
11275 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11276 DeviceType_T devType = pMedium->i_getDeviceType();
11277 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11278 && devType == DeviceType_HardDisk)
11279 || (cleanupMode == CleanupMode_Full)
11280 )
11281 {
11282 llMedia.push_back(pMedium);
11283 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11284 /* Not allowed to keep this lock as below we need the parent
11285 * medium lock, and the lock order is parent to child. */
11286 lock.release();
11287 /*
11288 * Search for medias which are not attached to any machine, but
11289 * in the chain to an attached disk. Mediums are only consided
11290 * if they are:
11291 * - have only one child
11292 * - no references to any machines
11293 * - are of normal medium type
11294 */
11295 while (!pParent.isNull())
11296 {
11297 AutoCaller mac1(pParent);
11298 if (FAILED(mac1.rc())) return mac1.rc();
11299 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11300 if (pParent->i_getChildren().size() == 1)
11301 {
11302 if ( pParent->i_getMachineBackRefCount() == 0
11303 && pParent->i_getType() == MediumType_Normal
11304 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11305 llMedia.push_back(pParent);
11306 }
11307 else
11308 break;
11309 pParent = pParent->i_getParent();
11310 }
11311 }
11312 }
11313
11314 // real machine: then we need to use the proper method
11315 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11316
11317 if (FAILED(rc))
11318 return rc;
11319 }
11320
11321 return S_OK;
11322}
11323
11324/**
11325 * Perform deferred hard disk detachments.
11326 *
11327 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11328 * backed up).
11329 *
11330 * If @a aOnline is @c true then this method will also unlock the old hard disks
11331 * for which the new implicit diffs were created and will lock these new diffs for
11332 * writing.
11333 *
11334 * @param aOnline Whether the VM was online prior to this operation.
11335 *
11336 * @note Locks this object for writing!
11337 */
11338void Machine::i_commitMedia(bool aOnline /*= false*/)
11339{
11340 AutoCaller autoCaller(this);
11341 AssertComRCReturnVoid(autoCaller.rc());
11342
11343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11344
11345 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11346
11347 HRESULT rc = S_OK;
11348
11349 /* no attach/detach operations -- nothing to do */
11350 if (!mMediaData.isBackedUp())
11351 return;
11352
11353 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11354 bool fMediaNeedsLocking = false;
11355
11356 /* enumerate new attachments */
11357 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11358 it != mMediaData->mAttachments.end();
11359 ++it)
11360 {
11361 MediumAttachment *pAttach = *it;
11362
11363 pAttach->i_commit();
11364
11365 Medium* pMedium = pAttach->i_getMedium();
11366 bool fImplicit = pAttach->i_isImplicit();
11367
11368 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11369 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11370 fImplicit));
11371
11372 /** @todo convert all this Machine-based voodoo to MediumAttachment
11373 * based commit logic. */
11374 if (fImplicit)
11375 {
11376 /* convert implicit attachment to normal */
11377 pAttach->i_setImplicit(false);
11378
11379 if ( aOnline
11380 && pMedium
11381 && pAttach->i_getType() == DeviceType_HardDisk
11382 )
11383 {
11384 /* update the appropriate lock list */
11385 MediumLockList *pMediumLockList;
11386 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11387 AssertComRC(rc);
11388 if (pMediumLockList)
11389 {
11390 /* unlock if there's a need to change the locking */
11391 if (!fMediaNeedsLocking)
11392 {
11393 rc = mData->mSession.mLockedMedia.Unlock();
11394 AssertComRC(rc);
11395 fMediaNeedsLocking = true;
11396 }
11397 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11398 AssertComRC(rc);
11399 rc = pMediumLockList->Append(pMedium, true);
11400 AssertComRC(rc);
11401 }
11402 }
11403
11404 continue;
11405 }
11406
11407 if (pMedium)
11408 {
11409 /* was this medium attached before? */
11410 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11411 {
11412 MediumAttachment *pOldAttach = *oldIt;
11413 if (pOldAttach->i_getMedium() == pMedium)
11414 {
11415 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11416
11417 /* yes: remove from old to avoid de-association */
11418 oldAtts.erase(oldIt);
11419 break;
11420 }
11421 }
11422 }
11423 }
11424
11425 /* enumerate remaining old attachments and de-associate from the
11426 * current machine state */
11427 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11428 {
11429 MediumAttachment *pAttach = *it;
11430 Medium* pMedium = pAttach->i_getMedium();
11431
11432 /* Detach only hard disks, since DVD/floppy media is detached
11433 * instantly in MountMedium. */
11434 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11435 {
11436 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11437
11438 /* now de-associate from the current machine state */
11439 rc = pMedium->i_removeBackReference(mData->mUuid);
11440 AssertComRC(rc);
11441
11442 if (aOnline)
11443 {
11444 /* unlock since medium is not used anymore */
11445 MediumLockList *pMediumLockList;
11446 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11447 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11448 {
11449 /* this happens for online snapshots, there the attachment
11450 * is changing, but only to a diff image created under
11451 * the old one, so there is no separate lock list */
11452 Assert(!pMediumLockList);
11453 }
11454 else
11455 {
11456 AssertComRC(rc);
11457 if (pMediumLockList)
11458 {
11459 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11460 AssertComRC(rc);
11461 }
11462 }
11463 }
11464 }
11465 }
11466
11467 /* take media locks again so that the locking state is consistent */
11468 if (fMediaNeedsLocking)
11469 {
11470 Assert(aOnline);
11471 rc = mData->mSession.mLockedMedia.Lock();
11472 AssertComRC(rc);
11473 }
11474
11475 /* commit the hard disk changes */
11476 mMediaData.commit();
11477
11478 if (i_isSessionMachine())
11479 {
11480 /*
11481 * Update the parent machine to point to the new owner.
11482 * This is necessary because the stored parent will point to the
11483 * session machine otherwise and cause crashes or errors later
11484 * when the session machine gets invalid.
11485 */
11486 /** @todo Change the MediumAttachment class to behave like any other
11487 * class in this regard by creating peer MediumAttachment
11488 * objects for session machines and share the data with the peer
11489 * machine.
11490 */
11491 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11492 it != mMediaData->mAttachments.end();
11493 ++it)
11494 (*it)->i_updateParentMachine(mPeer);
11495
11496 /* attach new data to the primary machine and reshare it */
11497 mPeer->mMediaData.attach(mMediaData);
11498 }
11499
11500 return;
11501}
11502
11503/**
11504 * Perform deferred deletion of implicitly created diffs.
11505 *
11506 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11507 * backed up).
11508 *
11509 * @note Locks this object for writing!
11510 */
11511void Machine::i_rollbackMedia()
11512{
11513 AutoCaller autoCaller(this);
11514 AssertComRCReturnVoid(autoCaller.rc());
11515
11516 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11517 LogFlowThisFunc(("Entering rollbackMedia\n"));
11518
11519 HRESULT rc = S_OK;
11520
11521 /* no attach/detach operations -- nothing to do */
11522 if (!mMediaData.isBackedUp())
11523 return;
11524
11525 /* enumerate new attachments */
11526 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11527 it != mMediaData->mAttachments.end();
11528 ++it)
11529 {
11530 MediumAttachment *pAttach = *it;
11531 /* Fix up the backrefs for DVD/floppy media. */
11532 if (pAttach->i_getType() != DeviceType_HardDisk)
11533 {
11534 Medium* pMedium = pAttach->i_getMedium();
11535 if (pMedium)
11536 {
11537 rc = pMedium->i_removeBackReference(mData->mUuid);
11538 AssertComRC(rc);
11539 }
11540 }
11541
11542 (*it)->i_rollback();
11543
11544 pAttach = *it;
11545 /* Fix up the backrefs for DVD/floppy media. */
11546 if (pAttach->i_getType() != DeviceType_HardDisk)
11547 {
11548 Medium* pMedium = pAttach->i_getMedium();
11549 if (pMedium)
11550 {
11551 rc = pMedium->i_addBackReference(mData->mUuid);
11552 AssertComRC(rc);
11553 }
11554 }
11555 }
11556
11557 /** @todo convert all this Machine-based voodoo to MediumAttachment
11558 * based rollback logic. */
11559 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11560
11561 return;
11562}
11563
11564/**
11565 * Returns true if the settings file is located in the directory named exactly
11566 * as the machine; this means, among other things, that the machine directory
11567 * should be auto-renamed.
11568 *
11569 * @param aSettingsDir if not NULL, the full machine settings file directory
11570 * name will be assigned there.
11571 *
11572 * @note Doesn't lock anything.
11573 * @note Not thread safe (must be called from this object's lock).
11574 */
11575bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11576{
11577 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11578 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11579 if (aSettingsDir)
11580 *aSettingsDir = strMachineDirName;
11581 strMachineDirName.stripPath(); // vmname
11582 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11583 strConfigFileOnly.stripPath() // vmname.vbox
11584 .stripSuffix(); // vmname
11585 /** @todo hack, make somehow use of ComposeMachineFilename */
11586 if (mUserData->s.fDirectoryIncludesUUID)
11587 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11588
11589 AssertReturn(!strMachineDirName.isEmpty(), false);
11590 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11591
11592 return strMachineDirName == strConfigFileOnly;
11593}
11594
11595/**
11596 * Discards all changes to machine settings.
11597 *
11598 * @param aNotify Whether to notify the direct session about changes or not.
11599 *
11600 * @note Locks objects for writing!
11601 */
11602void Machine::i_rollback(bool aNotify)
11603{
11604 AutoCaller autoCaller(this);
11605 AssertComRCReturn(autoCaller.rc(), (void)0);
11606
11607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11608
11609 if (!mStorageControllers.isNull())
11610 {
11611 if (mStorageControllers.isBackedUp())
11612 {
11613 /* unitialize all new devices (absent in the backed up list). */
11614 StorageControllerList::const_iterator it = mStorageControllers->begin();
11615 StorageControllerList *backedList = mStorageControllers.backedUpData();
11616 while (it != mStorageControllers->end())
11617 {
11618 if ( std::find(backedList->begin(), backedList->end(), *it)
11619 == backedList->end()
11620 )
11621 {
11622 (*it)->uninit();
11623 }
11624 ++it;
11625 }
11626
11627 /* restore the list */
11628 mStorageControllers.rollback();
11629 }
11630
11631 /* rollback any changes to devices after restoring the list */
11632 if (mData->flModifications & IsModified_Storage)
11633 {
11634 StorageControllerList::const_iterator it = mStorageControllers->begin();
11635 while (it != mStorageControllers->end())
11636 {
11637 (*it)->i_rollback();
11638 ++it;
11639 }
11640 }
11641 }
11642
11643 if (!mUSBControllers.isNull())
11644 {
11645 if (mUSBControllers.isBackedUp())
11646 {
11647 /* unitialize all new devices (absent in the backed up list). */
11648 USBControllerList::const_iterator it = mUSBControllers->begin();
11649 USBControllerList *backedList = mUSBControllers.backedUpData();
11650 while (it != mUSBControllers->end())
11651 {
11652 if ( std::find(backedList->begin(), backedList->end(), *it)
11653 == backedList->end()
11654 )
11655 {
11656 (*it)->uninit();
11657 }
11658 ++it;
11659 }
11660
11661 /* restore the list */
11662 mUSBControllers.rollback();
11663 }
11664
11665 /* rollback any changes to devices after restoring the list */
11666 if (mData->flModifications & IsModified_USB)
11667 {
11668 USBControllerList::const_iterator it = mUSBControllers->begin();
11669 while (it != mUSBControllers->end())
11670 {
11671 (*it)->i_rollback();
11672 ++it;
11673 }
11674 }
11675 }
11676
11677 mUserData.rollback();
11678
11679 mHWData.rollback();
11680
11681 if (mData->flModifications & IsModified_Storage)
11682 i_rollbackMedia();
11683
11684 if (mBIOSSettings)
11685 mBIOSSettings->i_rollback();
11686
11687 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11688 mVRDEServer->i_rollback();
11689
11690 if (mAudioAdapter)
11691 mAudioAdapter->i_rollback();
11692
11693 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11694 mUSBDeviceFilters->i_rollback();
11695
11696 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11697 mBandwidthControl->i_rollback();
11698
11699 if (!mHWData.isNull())
11700 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11701 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11702 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11703 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11704
11705 if (mData->flModifications & IsModified_NetworkAdapters)
11706 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11707 if ( mNetworkAdapters[slot]
11708 && mNetworkAdapters[slot]->i_isModified())
11709 {
11710 mNetworkAdapters[slot]->i_rollback();
11711 networkAdapters[slot] = mNetworkAdapters[slot];
11712 }
11713
11714 if (mData->flModifications & IsModified_SerialPorts)
11715 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11716 if ( mSerialPorts[slot]
11717 && mSerialPorts[slot]->i_isModified())
11718 {
11719 mSerialPorts[slot]->i_rollback();
11720 serialPorts[slot] = mSerialPorts[slot];
11721 }
11722
11723 if (mData->flModifications & IsModified_ParallelPorts)
11724 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11725 if ( mParallelPorts[slot]
11726 && mParallelPorts[slot]->i_isModified())
11727 {
11728 mParallelPorts[slot]->i_rollback();
11729 parallelPorts[slot] = mParallelPorts[slot];
11730 }
11731
11732 if (aNotify)
11733 {
11734 /* inform the direct session about changes */
11735
11736 ComObjPtr<Machine> that = this;
11737 uint32_t flModifications = mData->flModifications;
11738 alock.release();
11739
11740 if (flModifications & IsModified_SharedFolders)
11741 that->i_onSharedFolderChange();
11742
11743 if (flModifications & IsModified_VRDEServer)
11744 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11745 if (flModifications & IsModified_USB)
11746 that->i_onUSBControllerChange();
11747
11748 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11749 if (networkAdapters[slot])
11750 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11751 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11752 if (serialPorts[slot])
11753 that->i_onSerialPortChange(serialPorts[slot]);
11754 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11755 if (parallelPorts[slot])
11756 that->i_onParallelPortChange(parallelPorts[slot]);
11757
11758 if (flModifications & IsModified_Storage)
11759 that->i_onStorageControllerChange();
11760
11761#if 0
11762 if (flModifications & IsModified_BandwidthControl)
11763 that->onBandwidthControlChange();
11764#endif
11765 }
11766}
11767
11768/**
11769 * Commits all the changes to machine settings.
11770 *
11771 * Note that this operation is supposed to never fail.
11772 *
11773 * @note Locks this object and children for writing.
11774 */
11775void Machine::i_commit()
11776{
11777 AutoCaller autoCaller(this);
11778 AssertComRCReturnVoid(autoCaller.rc());
11779
11780 AutoCaller peerCaller(mPeer);
11781 AssertComRCReturnVoid(peerCaller.rc());
11782
11783 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11784
11785 /*
11786 * use safe commit to ensure Snapshot machines (that share mUserData)
11787 * will still refer to a valid memory location
11788 */
11789 mUserData.commitCopy();
11790
11791 mHWData.commit();
11792
11793 if (mMediaData.isBackedUp())
11794 i_commitMedia(Global::IsOnline(mData->mMachineState));
11795
11796 mBIOSSettings->i_commit();
11797 mVRDEServer->i_commit();
11798 mAudioAdapter->i_commit();
11799 mUSBDeviceFilters->i_commit();
11800 mBandwidthControl->i_commit();
11801
11802 /* Since mNetworkAdapters is a list which might have been changed (resized)
11803 * without using the Backupable<> template we need to handle the copying
11804 * of the list entries manually, including the creation of peers for the
11805 * new objects. */
11806 bool commitNetworkAdapters = false;
11807 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11808 if (mPeer)
11809 {
11810 /* commit everything, even the ones which will go away */
11811 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11812 mNetworkAdapters[slot]->i_commit();
11813 /* copy over the new entries, creating a peer and uninit the original */
11814 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11815 for (size_t slot = 0; slot < newSize; slot++)
11816 {
11817 /* look if this adapter has a peer device */
11818 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11819 if (!peer)
11820 {
11821 /* no peer means the adapter is a newly created one;
11822 * create a peer owning data this data share it with */
11823 peer.createObject();
11824 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11825 }
11826 mPeer->mNetworkAdapters[slot] = peer;
11827 }
11828 /* uninit any no longer needed network adapters */
11829 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11830 mNetworkAdapters[slot]->uninit();
11831 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11832 {
11833 if (mPeer->mNetworkAdapters[slot])
11834 mPeer->mNetworkAdapters[slot]->uninit();
11835 }
11836 /* Keep the original network adapter count until this point, so that
11837 * discarding a chipset type change will not lose settings. */
11838 mNetworkAdapters.resize(newSize);
11839 mPeer->mNetworkAdapters.resize(newSize);
11840 }
11841 else
11842 {
11843 /* we have no peer (our parent is the newly created machine);
11844 * just commit changes to the network adapters */
11845 commitNetworkAdapters = true;
11846 }
11847 if (commitNetworkAdapters)
11848 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11849 mNetworkAdapters[slot]->i_commit();
11850
11851 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11852 mSerialPorts[slot]->i_commit();
11853 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11854 mParallelPorts[slot]->i_commit();
11855
11856 bool commitStorageControllers = false;
11857
11858 if (mStorageControllers.isBackedUp())
11859 {
11860 mStorageControllers.commit();
11861
11862 if (mPeer)
11863 {
11864 /* Commit all changes to new controllers (this will reshare data with
11865 * peers for those who have peers) */
11866 StorageControllerList *newList = new StorageControllerList();
11867 StorageControllerList::const_iterator it = mStorageControllers->begin();
11868 while (it != mStorageControllers->end())
11869 {
11870 (*it)->i_commit();
11871
11872 /* look if this controller has a peer device */
11873 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11874 if (!peer)
11875 {
11876 /* no peer means the device is a newly created one;
11877 * create a peer owning data this device share it with */
11878 peer.createObject();
11879 peer->init(mPeer, *it, true /* aReshare */);
11880 }
11881 else
11882 {
11883 /* remove peer from the old list */
11884 mPeer->mStorageControllers->remove(peer);
11885 }
11886 /* and add it to the new list */
11887 newList->push_back(peer);
11888
11889 ++it;
11890 }
11891
11892 /* uninit old peer's controllers that are left */
11893 it = mPeer->mStorageControllers->begin();
11894 while (it != mPeer->mStorageControllers->end())
11895 {
11896 (*it)->uninit();
11897 ++it;
11898 }
11899
11900 /* attach new list of controllers to our peer */
11901 mPeer->mStorageControllers.attach(newList);
11902 }
11903 else
11904 {
11905 /* we have no peer (our parent is the newly created machine);
11906 * just commit changes to devices */
11907 commitStorageControllers = true;
11908 }
11909 }
11910 else
11911 {
11912 /* the list of controllers itself is not changed,
11913 * just commit changes to controllers themselves */
11914 commitStorageControllers = true;
11915 }
11916
11917 if (commitStorageControllers)
11918 {
11919 StorageControllerList::const_iterator it = mStorageControllers->begin();
11920 while (it != mStorageControllers->end())
11921 {
11922 (*it)->i_commit();
11923 ++it;
11924 }
11925 }
11926
11927 bool commitUSBControllers = false;
11928
11929 if (mUSBControllers.isBackedUp())
11930 {
11931 mUSBControllers.commit();
11932
11933 if (mPeer)
11934 {
11935 /* Commit all changes to new controllers (this will reshare data with
11936 * peers for those who have peers) */
11937 USBControllerList *newList = new USBControllerList();
11938 USBControllerList::const_iterator it = mUSBControllers->begin();
11939 while (it != mUSBControllers->end())
11940 {
11941 (*it)->i_commit();
11942
11943 /* look if this controller has a peer device */
11944 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11945 if (!peer)
11946 {
11947 /* no peer means the device is a newly created one;
11948 * create a peer owning data this device share it with */
11949 peer.createObject();
11950 peer->init(mPeer, *it, true /* aReshare */);
11951 }
11952 else
11953 {
11954 /* remove peer from the old list */
11955 mPeer->mUSBControllers->remove(peer);
11956 }
11957 /* and add it to the new list */
11958 newList->push_back(peer);
11959
11960 ++it;
11961 }
11962
11963 /* uninit old peer's controllers that are left */
11964 it = mPeer->mUSBControllers->begin();
11965 while (it != mPeer->mUSBControllers->end())
11966 {
11967 (*it)->uninit();
11968 ++it;
11969 }
11970
11971 /* attach new list of controllers to our peer */
11972 mPeer->mUSBControllers.attach(newList);
11973 }
11974 else
11975 {
11976 /* we have no peer (our parent is the newly created machine);
11977 * just commit changes to devices */
11978 commitUSBControllers = true;
11979 }
11980 }
11981 else
11982 {
11983 /* the list of controllers itself is not changed,
11984 * just commit changes to controllers themselves */
11985 commitUSBControllers = true;
11986 }
11987
11988 if (commitUSBControllers)
11989 {
11990 USBControllerList::const_iterator it = mUSBControllers->begin();
11991 while (it != mUSBControllers->end())
11992 {
11993 (*it)->i_commit();
11994 ++it;
11995 }
11996 }
11997
11998 if (i_isSessionMachine())
11999 {
12000 /* attach new data to the primary machine and reshare it */
12001 mPeer->mUserData.attach(mUserData);
12002 mPeer->mHWData.attach(mHWData);
12003 /* mMediaData is reshared by fixupMedia */
12004 // mPeer->mMediaData.attach(mMediaData);
12005 Assert(mPeer->mMediaData.data() == mMediaData.data());
12006 }
12007}
12008
12009/**
12010 * Copies all the hardware data from the given machine.
12011 *
12012 * Currently, only called when the VM is being restored from a snapshot. In
12013 * particular, this implies that the VM is not running during this method's
12014 * call.
12015 *
12016 * @note This method must be called from under this object's lock.
12017 *
12018 * @note This method doesn't call #commit(), so all data remains backed up and
12019 * unsaved.
12020 */
12021void Machine::i_copyFrom(Machine *aThat)
12022{
12023 AssertReturnVoid(!i_isSnapshotMachine());
12024 AssertReturnVoid(aThat->i_isSnapshotMachine());
12025
12026 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12027
12028 mHWData.assignCopy(aThat->mHWData);
12029
12030 // create copies of all shared folders (mHWData after attaching a copy
12031 // contains just references to original objects)
12032 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12033 it != mHWData->mSharedFolders.end();
12034 ++it)
12035 {
12036 ComObjPtr<SharedFolder> folder;
12037 folder.createObject();
12038 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12039 AssertComRC(rc);
12040 *it = folder;
12041 }
12042
12043 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12044 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12045 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12046 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12047 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12048
12049 /* create private copies of all controllers */
12050 mStorageControllers.backup();
12051 mStorageControllers->clear();
12052 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12053 it != aThat->mStorageControllers->end();
12054 ++it)
12055 {
12056 ComObjPtr<StorageController> ctrl;
12057 ctrl.createObject();
12058 ctrl->initCopy(this, *it);
12059 mStorageControllers->push_back(ctrl);
12060 }
12061
12062 /* create private copies of all USB controllers */
12063 mUSBControllers.backup();
12064 mUSBControllers->clear();
12065 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12066 it != aThat->mUSBControllers->end();
12067 ++it)
12068 {
12069 ComObjPtr<USBController> ctrl;
12070 ctrl.createObject();
12071 ctrl->initCopy(this, *it);
12072 mUSBControllers->push_back(ctrl);
12073 }
12074
12075 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12076 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12077 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12078 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12079 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12080 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12081 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12082}
12083
12084/**
12085 * Returns whether the given storage controller is hotplug capable.
12086 *
12087 * @returns true if the controller supports hotplugging
12088 * false otherwise.
12089 * @param enmCtrlType The controller type to check for.
12090 */
12091bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12092{
12093 ComPtr<ISystemProperties> systemProperties;
12094 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12095 if (FAILED(rc))
12096 return false;
12097
12098 BOOL aHotplugCapable = FALSE;
12099 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12100
12101 return RT_BOOL(aHotplugCapable);
12102}
12103
12104#ifdef VBOX_WITH_RESOURCE_USAGE_API
12105
12106void Machine::i_getDiskList(MediaList &list)
12107{
12108 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12109 it != mMediaData->mAttachments.end();
12110 ++it)
12111 {
12112 MediumAttachment* pAttach = *it;
12113 /* just in case */
12114 AssertStmt(pAttach, continue);
12115
12116 AutoCaller localAutoCallerA(pAttach);
12117 if (FAILED(localAutoCallerA.rc())) continue;
12118
12119 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12120
12121 if (pAttach->i_getType() == DeviceType_HardDisk)
12122 list.push_back(pAttach->i_getMedium());
12123 }
12124}
12125
12126void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12127{
12128 AssertReturnVoid(isWriteLockOnCurrentThread());
12129 AssertPtrReturnVoid(aCollector);
12130
12131 pm::CollectorHAL *hal = aCollector->getHAL();
12132 /* Create sub metrics */
12133 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12134 "Percentage of processor time spent in user mode by the VM process.");
12135 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12136 "Percentage of processor time spent in kernel mode by the VM process.");
12137 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12138 "Size of resident portion of VM process in memory.");
12139 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12140 "Actual size of all VM disks combined.");
12141 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12142 "Network receive rate.");
12143 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12144 "Network transmit rate.");
12145 /* Create and register base metrics */
12146 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12147 cpuLoadUser, cpuLoadKernel);
12148 aCollector->registerBaseMetric(cpuLoad);
12149 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12150 ramUsageUsed);
12151 aCollector->registerBaseMetric(ramUsage);
12152 MediaList disks;
12153 i_getDiskList(disks);
12154 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12155 diskUsageUsed);
12156 aCollector->registerBaseMetric(diskUsage);
12157
12158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12159 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12160 new pm::AggregateAvg()));
12161 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12162 new pm::AggregateMin()));
12163 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12164 new pm::AggregateMax()));
12165 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12166 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12167 new pm::AggregateAvg()));
12168 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12169 new pm::AggregateMin()));
12170 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12171 new pm::AggregateMax()));
12172
12173 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12174 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12175 new pm::AggregateAvg()));
12176 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12177 new pm::AggregateMin()));
12178 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12179 new pm::AggregateMax()));
12180
12181 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12182 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12183 new pm::AggregateAvg()));
12184 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12185 new pm::AggregateMin()));
12186 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12187 new pm::AggregateMax()));
12188
12189
12190 /* Guest metrics collector */
12191 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12192 aCollector->registerGuest(mCollectorGuest);
12193 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12194
12195 /* Create sub metrics */
12196 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12197 "Percentage of processor time spent in user mode as seen by the guest.");
12198 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12199 "Percentage of processor time spent in kernel mode as seen by the guest.");
12200 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12201 "Percentage of processor time spent idling as seen by the guest.");
12202
12203 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12204 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12205 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12206 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12207 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12208 pm::SubMetric *guestMemCache = new pm::SubMetric(
12209 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12210
12211 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12212 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12213
12214 /* Create and register base metrics */
12215 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12216 machineNetRx, machineNetTx);
12217 aCollector->registerBaseMetric(machineNetRate);
12218
12219 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12220 guestLoadUser, guestLoadKernel, guestLoadIdle);
12221 aCollector->registerBaseMetric(guestCpuLoad);
12222
12223 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12224 guestMemTotal, guestMemFree,
12225 guestMemBalloon, guestMemShared,
12226 guestMemCache, guestPagedTotal);
12227 aCollector->registerBaseMetric(guestCpuMem);
12228
12229 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12230 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12231 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12233
12234 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12235 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12236 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12237 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12238
12239 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12240 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12245 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12248
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12253
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12258
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12263
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12268
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12273
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12278
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12283}
12284
12285void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12286{
12287 AssertReturnVoid(isWriteLockOnCurrentThread());
12288
12289 if (aCollector)
12290 {
12291 aCollector->unregisterMetricsFor(aMachine);
12292 aCollector->unregisterBaseMetricsFor(aMachine);
12293 }
12294}
12295
12296#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12297
12298
12299////////////////////////////////////////////////////////////////////////////////
12300
12301DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12302
12303HRESULT SessionMachine::FinalConstruct()
12304{
12305 LogFlowThisFunc(("\n"));
12306
12307 mClientToken = NULL;
12308
12309 return BaseFinalConstruct();
12310}
12311
12312void SessionMachine::FinalRelease()
12313{
12314 LogFlowThisFunc(("\n"));
12315
12316 Assert(!mClientToken);
12317 /* paranoia, should not hang around any more */
12318 if (mClientToken)
12319 {
12320 delete mClientToken;
12321 mClientToken = NULL;
12322 }
12323
12324 uninit(Uninit::Unexpected);
12325
12326 BaseFinalRelease();
12327}
12328
12329/**
12330 * @note Must be called only by Machine::LockMachine() from its own write lock.
12331 */
12332HRESULT SessionMachine::init(Machine *aMachine)
12333{
12334 LogFlowThisFuncEnter();
12335 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12336
12337 AssertReturn(aMachine, E_INVALIDARG);
12338
12339 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12340
12341 /* Enclose the state transition NotReady->InInit->Ready */
12342 AutoInitSpan autoInitSpan(this);
12343 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12344
12345 HRESULT rc = S_OK;
12346
12347 /* create the machine client token */
12348 try
12349 {
12350 mClientToken = new ClientToken(aMachine, this);
12351 if (!mClientToken->isReady())
12352 {
12353 delete mClientToken;
12354 mClientToken = NULL;
12355 rc = E_FAIL;
12356 }
12357 }
12358 catch (std::bad_alloc &)
12359 {
12360 rc = E_OUTOFMEMORY;
12361 }
12362 if (FAILED(rc))
12363 return rc;
12364
12365 /* memorize the peer Machine */
12366 unconst(mPeer) = aMachine;
12367 /* share the parent pointer */
12368 unconst(mParent) = aMachine->mParent;
12369
12370 /* take the pointers to data to share */
12371 mData.share(aMachine->mData);
12372 mSSData.share(aMachine->mSSData);
12373
12374 mUserData.share(aMachine->mUserData);
12375 mHWData.share(aMachine->mHWData);
12376 mMediaData.share(aMachine->mMediaData);
12377
12378 mStorageControllers.allocate();
12379 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12380 it != aMachine->mStorageControllers->end();
12381 ++it)
12382 {
12383 ComObjPtr<StorageController> ctl;
12384 ctl.createObject();
12385 ctl->init(this, *it);
12386 mStorageControllers->push_back(ctl);
12387 }
12388
12389 mUSBControllers.allocate();
12390 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12391 it != aMachine->mUSBControllers->end();
12392 ++it)
12393 {
12394 ComObjPtr<USBController> ctl;
12395 ctl.createObject();
12396 ctl->init(this, *it);
12397 mUSBControllers->push_back(ctl);
12398 }
12399
12400 unconst(mBIOSSettings).createObject();
12401 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12402 /* create another VRDEServer object that will be mutable */
12403 unconst(mVRDEServer).createObject();
12404 mVRDEServer->init(this, aMachine->mVRDEServer);
12405 /* create another audio adapter object that will be mutable */
12406 unconst(mAudioAdapter).createObject();
12407 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12408 /* create a list of serial ports that will be mutable */
12409 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12410 {
12411 unconst(mSerialPorts[slot]).createObject();
12412 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12413 }
12414 /* create a list of parallel ports that will be mutable */
12415 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12416 {
12417 unconst(mParallelPorts[slot]).createObject();
12418 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12419 }
12420
12421 /* create another USB device filters object that will be mutable */
12422 unconst(mUSBDeviceFilters).createObject();
12423 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12424
12425 /* create a list of network adapters that will be mutable */
12426 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12427 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12428 {
12429 unconst(mNetworkAdapters[slot]).createObject();
12430 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12431 }
12432
12433 /* create another bandwidth control object that will be mutable */
12434 unconst(mBandwidthControl).createObject();
12435 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12436
12437 /* default is to delete saved state on Saved -> PoweredOff transition */
12438 mRemoveSavedState = true;
12439
12440 /* Confirm a successful initialization when it's the case */
12441 autoInitSpan.setSucceeded();
12442
12443 miNATNetworksStarted = 0;
12444
12445 LogFlowThisFuncLeave();
12446 return rc;
12447}
12448
12449/**
12450 * Uninitializes this session object. If the reason is other than
12451 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12452 * or the client watcher code.
12453 *
12454 * @param aReason uninitialization reason
12455 *
12456 * @note Locks mParent + this object for writing.
12457 */
12458void SessionMachine::uninit(Uninit::Reason aReason)
12459{
12460 LogFlowThisFuncEnter();
12461 LogFlowThisFunc(("reason=%d\n", aReason));
12462
12463 /*
12464 * Strongly reference ourselves to prevent this object deletion after
12465 * mData->mSession.mMachine.setNull() below (which can release the last
12466 * reference and call the destructor). Important: this must be done before
12467 * accessing any members (and before AutoUninitSpan that does it as well).
12468 * This self reference will be released as the very last step on return.
12469 */
12470 ComObjPtr<SessionMachine> selfRef = this;
12471
12472 /* Enclose the state transition Ready->InUninit->NotReady */
12473 AutoUninitSpan autoUninitSpan(this);
12474 if (autoUninitSpan.uninitDone())
12475 {
12476 LogFlowThisFunc(("Already uninitialized\n"));
12477 LogFlowThisFuncLeave();
12478 return;
12479 }
12480
12481 if (autoUninitSpan.initFailed())
12482 {
12483 /* We've been called by init() because it's failed. It's not really
12484 * necessary (nor it's safe) to perform the regular uninit sequence
12485 * below, the following is enough.
12486 */
12487 LogFlowThisFunc(("Initialization failed.\n"));
12488 /* destroy the machine client token */
12489 if (mClientToken)
12490 {
12491 delete mClientToken;
12492 mClientToken = NULL;
12493 }
12494 uninitDataAndChildObjects();
12495 mData.free();
12496 unconst(mParent) = NULL;
12497 unconst(mPeer) = NULL;
12498 LogFlowThisFuncLeave();
12499 return;
12500 }
12501
12502 MachineState_T lastState;
12503 {
12504 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12505 lastState = mData->mMachineState;
12506 }
12507 NOREF(lastState);
12508
12509#ifdef VBOX_WITH_USB
12510 // release all captured USB devices, but do this before requesting the locks below
12511 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12512 {
12513 /* Console::captureUSBDevices() is called in the VM process only after
12514 * setting the machine state to Starting or Restoring.
12515 * Console::detachAllUSBDevices() will be called upon successful
12516 * termination. So, we need to release USB devices only if there was
12517 * an abnormal termination of a running VM.
12518 *
12519 * This is identical to SessionMachine::DetachAllUSBDevices except
12520 * for the aAbnormal argument. */
12521 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12522 AssertComRC(rc);
12523 NOREF(rc);
12524
12525 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12526 if (service)
12527 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12528 }
12529#endif /* VBOX_WITH_USB */
12530
12531 // we need to lock this object in uninit() because the lock is shared
12532 // with mPeer (as well as data we modify below). mParent lock is needed
12533 // by several calls to it, and USB needs host lock.
12534 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12535
12536#ifdef VBOX_WITH_RESOURCE_USAGE_API
12537 /*
12538 * It is safe to call Machine::i_unregisterMetrics() here because
12539 * PerformanceCollector::samplerCallback no longer accesses guest methods
12540 * holding the lock.
12541 */
12542 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12543 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12544 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12545 if (mCollectorGuest)
12546 {
12547 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12548 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12549 mCollectorGuest = NULL;
12550 }
12551#endif
12552
12553 if (aReason == Uninit::Abnormal)
12554 {
12555 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12556
12557 /* reset the state to Aborted */
12558 if (mData->mMachineState != MachineState_Aborted)
12559 i_setMachineState(MachineState_Aborted);
12560 }
12561
12562 // any machine settings modified?
12563 if (mData->flModifications)
12564 {
12565 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12566 i_rollback(false /* aNotify */);
12567 }
12568
12569 mData->mSession.mPID = NIL_RTPROCESS;
12570
12571 if (aReason == Uninit::Unexpected)
12572 {
12573 /* Uninitialization didn't come from #checkForDeath(), so tell the
12574 * client watcher thread to update the set of machines that have open
12575 * sessions. */
12576 mParent->i_updateClientWatcher();
12577 }
12578
12579 /* uninitialize all remote controls */
12580 if (mData->mSession.mRemoteControls.size())
12581 {
12582 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12583 mData->mSession.mRemoteControls.size()));
12584
12585 Data::Session::RemoteControlList::iterator it =
12586 mData->mSession.mRemoteControls.begin();
12587 while (it != mData->mSession.mRemoteControls.end())
12588 {
12589 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12590 HRESULT rc = (*it)->Uninitialize();
12591 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12592 if (FAILED(rc))
12593 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12594 ++it;
12595 }
12596 mData->mSession.mRemoteControls.clear();
12597 }
12598
12599 /* Remove all references to the NAT network service. The service will stop
12600 * if all references (also from other VMs) are removed. */
12601 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12602 {
12603 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12604 {
12605 NetworkAttachmentType_T type;
12606 HRESULT hrc;
12607
12608 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12609 if ( SUCCEEDED(hrc)
12610 && type == NetworkAttachmentType_NATNetwork)
12611 {
12612 Bstr name;
12613 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12614 if (SUCCEEDED(hrc))
12615 {
12616 multilock.release();
12617 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12618 mUserData->s.strName.c_str(), name.raw()));
12619 mParent->i_natNetworkRefDec(name.raw());
12620 multilock.acquire();
12621 }
12622 }
12623 }
12624 }
12625
12626 /*
12627 * An expected uninitialization can come only from #checkForDeath().
12628 * Otherwise it means that something's gone really wrong (for example,
12629 * the Session implementation has released the VirtualBox reference
12630 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12631 * etc). However, it's also possible, that the client releases the IPC
12632 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12633 * but the VirtualBox release event comes first to the server process.
12634 * This case is practically possible, so we should not assert on an
12635 * unexpected uninit, just log a warning.
12636 */
12637
12638 if ((aReason == Uninit::Unexpected))
12639 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12640
12641 if (aReason != Uninit::Normal)
12642 {
12643 mData->mSession.mDirectControl.setNull();
12644 }
12645 else
12646 {
12647 /* this must be null here (see #OnSessionEnd()) */
12648 Assert(mData->mSession.mDirectControl.isNull());
12649 Assert(mData->mSession.mState == SessionState_Unlocking);
12650 Assert(!mData->mSession.mProgress.isNull());
12651 }
12652 if (mData->mSession.mProgress)
12653 {
12654 if (aReason == Uninit::Normal)
12655 mData->mSession.mProgress->i_notifyComplete(S_OK);
12656 else
12657 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12658 COM_IIDOF(ISession),
12659 getComponentName(),
12660 tr("The VM session was aborted"));
12661 mData->mSession.mProgress.setNull();
12662 }
12663
12664 /* remove the association between the peer machine and this session machine */
12665 Assert( (SessionMachine*)mData->mSession.mMachine == this
12666 || aReason == Uninit::Unexpected);
12667
12668 /* reset the rest of session data */
12669 mData->mSession.mLockType = LockType_Null;
12670 mData->mSession.mMachine.setNull();
12671 mData->mSession.mState = SessionState_Unlocked;
12672 mData->mSession.mName.setNull();
12673
12674 /* destroy the machine client token before leaving the exclusive lock */
12675 if (mClientToken)
12676 {
12677 delete mClientToken;
12678 mClientToken = NULL;
12679 }
12680
12681 /* fire an event */
12682 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12683
12684 uninitDataAndChildObjects();
12685
12686 /* free the essential data structure last */
12687 mData.free();
12688
12689 /* release the exclusive lock before setting the below two to NULL */
12690 multilock.release();
12691
12692 unconst(mParent) = NULL;
12693 unconst(mPeer) = NULL;
12694
12695 LogFlowThisFuncLeave();
12696}
12697
12698// util::Lockable interface
12699////////////////////////////////////////////////////////////////////////////////
12700
12701/**
12702 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12703 * with the primary Machine instance (mPeer).
12704 */
12705RWLockHandle *SessionMachine::lockHandle() const
12706{
12707 AssertReturn(mPeer != NULL, NULL);
12708 return mPeer->lockHandle();
12709}
12710
12711// IInternalMachineControl methods
12712////////////////////////////////////////////////////////////////////////////////
12713
12714/**
12715 * Passes collected guest statistics to performance collector object
12716 */
12717HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12718 ULONG aCpuKernel, ULONG aCpuIdle,
12719 ULONG aMemTotal, ULONG aMemFree,
12720 ULONG aMemBalloon, ULONG aMemShared,
12721 ULONG aMemCache, ULONG aPageTotal,
12722 ULONG aAllocVMM, ULONG aFreeVMM,
12723 ULONG aBalloonedVMM, ULONG aSharedVMM,
12724 ULONG aVmNetRx, ULONG aVmNetTx)
12725{
12726#ifdef VBOX_WITH_RESOURCE_USAGE_API
12727 if (mCollectorGuest)
12728 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12729 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12730 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12731 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12732
12733 return S_OK;
12734#else
12735 NOREF(aValidStats);
12736 NOREF(aCpuUser);
12737 NOREF(aCpuKernel);
12738 NOREF(aCpuIdle);
12739 NOREF(aMemTotal);
12740 NOREF(aMemFree);
12741 NOREF(aMemBalloon);
12742 NOREF(aMemShared);
12743 NOREF(aMemCache);
12744 NOREF(aPageTotal);
12745 NOREF(aAllocVMM);
12746 NOREF(aFreeVMM);
12747 NOREF(aBalloonedVMM);
12748 NOREF(aSharedVMM);
12749 NOREF(aVmNetRx);
12750 NOREF(aVmNetTx);
12751 return E_NOTIMPL;
12752#endif
12753}
12754
12755////////////////////////////////////////////////////////////////////////////////
12756//
12757// SessionMachine task records
12758//
12759////////////////////////////////////////////////////////////////////////////////
12760
12761/**
12762 * Task record for saving the machine state.
12763 */
12764struct SessionMachine::SaveStateTask
12765 : public Machine::Task
12766{
12767 SaveStateTask(SessionMachine *m,
12768 Progress *p,
12769 const Utf8Str &t,
12770 Reason_T enmReason,
12771 const Utf8Str &strStateFilePath)
12772 : Task(m, p, t),
12773 m_enmReason(enmReason),
12774 m_strStateFilePath(strStateFilePath)
12775 {}
12776
12777 void handler()
12778 {
12779 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12780 }
12781
12782 Reason_T m_enmReason;
12783 Utf8Str m_strStateFilePath;
12784};
12785
12786/**
12787 * Task thread implementation for SessionMachine::SaveState(), called from
12788 * SessionMachine::taskHandler().
12789 *
12790 * @note Locks this object for writing.
12791 *
12792 * @param task
12793 * @return
12794 */
12795void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12796{
12797 LogFlowThisFuncEnter();
12798
12799 AutoCaller autoCaller(this);
12800 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12801 if (FAILED(autoCaller.rc()))
12802 {
12803 /* we might have been uninitialized because the session was accidentally
12804 * closed by the client, so don't assert */
12805 HRESULT rc = setError(E_FAIL,
12806 tr("The session has been accidentally closed"));
12807 task.m_pProgress->i_notifyComplete(rc);
12808 LogFlowThisFuncLeave();
12809 return;
12810 }
12811
12812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12813
12814 HRESULT rc = S_OK;
12815
12816 try
12817 {
12818 ComPtr<IInternalSessionControl> directControl;
12819 if (mData->mSession.mLockType == LockType_VM)
12820 directControl = mData->mSession.mDirectControl;
12821 if (directControl.isNull())
12822 throw setError(VBOX_E_INVALID_VM_STATE,
12823 tr("Trying to save state without a running VM"));
12824 alock.release();
12825 BOOL fSuspendedBySave;
12826 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12827 Assert(!fSuspendedBySave);
12828 alock.acquire();
12829
12830 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12831 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12832 throw E_FAIL);
12833
12834 if (SUCCEEDED(rc))
12835 {
12836 mSSData->strStateFilePath = task.m_strStateFilePath;
12837
12838 /* save all VM settings */
12839 rc = i_saveSettings(NULL);
12840 // no need to check whether VirtualBox.xml needs saving also since
12841 // we can't have a name change pending at this point
12842 }
12843 else
12844 {
12845 // On failure, set the state to the state we had at the beginning.
12846 i_setMachineState(task.m_machineStateBackup);
12847 i_updateMachineStateOnClient();
12848
12849 // Delete the saved state file (might have been already created).
12850 // No need to check whether this is shared with a snapshot here
12851 // because we certainly created a fresh saved state file here.
12852 RTFileDelete(task.m_strStateFilePath.c_str());
12853 }
12854 }
12855 catch (HRESULT aRC) { rc = aRC; }
12856
12857 task.m_pProgress->i_notifyComplete(rc);
12858
12859 LogFlowThisFuncLeave();
12860}
12861
12862/**
12863 * @note Locks this object for writing.
12864 */
12865HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12866{
12867 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12868}
12869
12870HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12871{
12872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12873
12874 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12875 if (FAILED(rc)) return rc;
12876
12877 if ( mData->mMachineState != MachineState_Running
12878 && mData->mMachineState != MachineState_Paused
12879 )
12880 return setError(VBOX_E_INVALID_VM_STATE,
12881 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12882 Global::stringifyMachineState(mData->mMachineState));
12883
12884 ComObjPtr<Progress> pProgress;
12885 pProgress.createObject();
12886 rc = pProgress->init(i_getVirtualBox(),
12887 static_cast<IMachine *>(this) /* aInitiator */,
12888 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12889 FALSE /* aCancelable */);
12890 if (FAILED(rc))
12891 return rc;
12892
12893 Utf8Str strStateFilePath;
12894 i_composeSavedStateFilename(strStateFilePath);
12895
12896 /* create and start the task on a separate thread (note that it will not
12897 * start working until we release alock) */
12898 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12899 rc = pTask->createThread();
12900 if (FAILED(rc))
12901 return rc;
12902
12903 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12904 i_setMachineState(MachineState_Saving);
12905 i_updateMachineStateOnClient();
12906
12907 pProgress.queryInterfaceTo(aProgress.asOutParam());
12908
12909 return S_OK;
12910}
12911
12912/**
12913 * @note Locks this object for writing.
12914 */
12915HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12916{
12917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12918
12919 HRESULT rc = i_checkStateDependency(MutableStateDep);
12920 if (FAILED(rc)) return rc;
12921
12922 if ( mData->mMachineState != MachineState_PoweredOff
12923 && mData->mMachineState != MachineState_Teleported
12924 && mData->mMachineState != MachineState_Aborted
12925 )
12926 return setError(VBOX_E_INVALID_VM_STATE,
12927 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12928 Global::stringifyMachineState(mData->mMachineState));
12929
12930 com::Utf8Str stateFilePathFull;
12931 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12932 if (RT_FAILURE(vrc))
12933 return setError(VBOX_E_FILE_ERROR,
12934 tr("Invalid saved state file path '%s' (%Rrc)"),
12935 aSavedStateFile.c_str(),
12936 vrc);
12937
12938 mSSData->strStateFilePath = stateFilePathFull;
12939
12940 /* The below i_setMachineState() will detect the state transition and will
12941 * update the settings file */
12942
12943 return i_setMachineState(MachineState_Saved);
12944}
12945
12946/**
12947 * @note Locks this object for writing.
12948 */
12949HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12950{
12951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12952
12953 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12954 if (FAILED(rc)) return rc;
12955
12956 if (mData->mMachineState != MachineState_Saved)
12957 return setError(VBOX_E_INVALID_VM_STATE,
12958 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12959 Global::stringifyMachineState(mData->mMachineState));
12960
12961 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12962
12963 /*
12964 * Saved -> PoweredOff transition will be detected in the SessionMachine
12965 * and properly handled.
12966 */
12967 rc = i_setMachineState(MachineState_PoweredOff);
12968 return rc;
12969}
12970
12971
12972/**
12973 * @note Locks the same as #i_setMachineState() does.
12974 */
12975HRESULT SessionMachine::updateState(MachineState_T aState)
12976{
12977 return i_setMachineState(aState);
12978}
12979
12980/**
12981 * @note Locks this object for writing.
12982 */
12983HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12984{
12985 IProgress* pProgress(aProgress);
12986
12987 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12988
12989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12990
12991 if (mData->mSession.mState != SessionState_Locked)
12992 return VBOX_E_INVALID_OBJECT_STATE;
12993
12994 if (!mData->mSession.mProgress.isNull())
12995 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12996
12997 /* If we didn't reference the NAT network service yet, add a reference to
12998 * force a start */
12999 if (miNATNetworksStarted < 1)
13000 {
13001 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13002 {
13003 NetworkAttachmentType_T type;
13004 HRESULT hrc;
13005 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13006 if ( SUCCEEDED(hrc)
13007 && type == NetworkAttachmentType_NATNetwork)
13008 {
13009 Bstr name;
13010 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13011 if (SUCCEEDED(hrc))
13012 {
13013 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13014 mUserData->s.strName.c_str(), name.raw()));
13015 mPeer->lockHandle()->unlockWrite();
13016 mParent->i_natNetworkRefInc(name.raw());
13017#ifdef RT_LOCK_STRICT
13018 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13019#else
13020 mPeer->lockHandle()->lockWrite();
13021#endif
13022 }
13023 }
13024 }
13025 miNATNetworksStarted++;
13026 }
13027
13028 LogFlowThisFunc(("returns S_OK.\n"));
13029 return S_OK;
13030}
13031
13032/**
13033 * @note Locks this object for writing.
13034 */
13035HRESULT SessionMachine::endPowerUp(LONG aResult)
13036{
13037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13038
13039 if (mData->mSession.mState != SessionState_Locked)
13040 return VBOX_E_INVALID_OBJECT_STATE;
13041
13042 /* Finalize the LaunchVMProcess progress object. */
13043 if (mData->mSession.mProgress)
13044 {
13045 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13046 mData->mSession.mProgress.setNull();
13047 }
13048
13049 if (SUCCEEDED((HRESULT)aResult))
13050 {
13051#ifdef VBOX_WITH_RESOURCE_USAGE_API
13052 /* The VM has been powered up successfully, so it makes sense
13053 * now to offer the performance metrics for a running machine
13054 * object. Doing it earlier wouldn't be safe. */
13055 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13056 mData->mSession.mPID);
13057#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13058 }
13059
13060 return S_OK;
13061}
13062
13063/**
13064 * @note Locks this object for writing.
13065 */
13066HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13067{
13068 LogFlowThisFuncEnter();
13069
13070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13071
13072 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13073 E_FAIL);
13074
13075 /* create a progress object to track operation completion */
13076 ComObjPtr<Progress> pProgress;
13077 pProgress.createObject();
13078 pProgress->init(i_getVirtualBox(),
13079 static_cast<IMachine *>(this) /* aInitiator */,
13080 Bstr(tr("Stopping the virtual machine")).raw(),
13081 FALSE /* aCancelable */);
13082
13083 /* fill in the console task data */
13084 mConsoleTaskData.mLastState = mData->mMachineState;
13085 mConsoleTaskData.mProgress = pProgress;
13086
13087 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13088 i_setMachineState(MachineState_Stopping);
13089
13090 pProgress.queryInterfaceTo(aProgress.asOutParam());
13091
13092 return S_OK;
13093}
13094
13095/**
13096 * @note Locks this object for writing.
13097 */
13098HRESULT SessionMachine::endPoweringDown(LONG aResult,
13099 const com::Utf8Str &aErrMsg)
13100{
13101 LogFlowThisFuncEnter();
13102
13103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13104
13105 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13106 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13107 && mConsoleTaskData.mLastState != MachineState_Null,
13108 E_FAIL);
13109
13110 /*
13111 * On failure, set the state to the state we had when BeginPoweringDown()
13112 * was called (this is expected by Console::PowerDown() and the associated
13113 * task). On success the VM process already changed the state to
13114 * MachineState_PoweredOff, so no need to do anything.
13115 */
13116 if (FAILED(aResult))
13117 i_setMachineState(mConsoleTaskData.mLastState);
13118
13119 /* notify the progress object about operation completion */
13120 Assert(mConsoleTaskData.mProgress);
13121 if (SUCCEEDED(aResult))
13122 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13123 else
13124 {
13125 if (aErrMsg.length())
13126 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13127 COM_IIDOF(ISession),
13128 getComponentName(),
13129 aErrMsg.c_str());
13130 else
13131 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13132 }
13133
13134 /* clear out the temporary saved state data */
13135 mConsoleTaskData.mLastState = MachineState_Null;
13136 mConsoleTaskData.mProgress.setNull();
13137
13138 LogFlowThisFuncLeave();
13139 return S_OK;
13140}
13141
13142
13143/**
13144 * Goes through the USB filters of the given machine to see if the given
13145 * device matches any filter or not.
13146 *
13147 * @note Locks the same as USBController::hasMatchingFilter() does.
13148 */
13149HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13150 BOOL *aMatched,
13151 ULONG *aMaskedInterfaces)
13152{
13153 LogFlowThisFunc(("\n"));
13154
13155#ifdef VBOX_WITH_USB
13156 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13157#else
13158 NOREF(aDevice);
13159 NOREF(aMaskedInterfaces);
13160 *aMatched = FALSE;
13161#endif
13162
13163 return S_OK;
13164}
13165
13166/**
13167 * @note Locks the same as Host::captureUSBDevice() does.
13168 */
13169HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13170{
13171 LogFlowThisFunc(("\n"));
13172
13173#ifdef VBOX_WITH_USB
13174 /* if captureDeviceForVM() fails, it must have set extended error info */
13175 clearError();
13176 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13177 if (FAILED(rc)) return rc;
13178
13179 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13180 AssertReturn(service, E_FAIL);
13181 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13182#else
13183 NOREF(aId);
13184 return E_NOTIMPL;
13185#endif
13186}
13187
13188/**
13189 * @note Locks the same as Host::detachUSBDevice() does.
13190 */
13191HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13192 BOOL aDone)
13193{
13194 LogFlowThisFunc(("\n"));
13195
13196#ifdef VBOX_WITH_USB
13197 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13198 AssertReturn(service, E_FAIL);
13199 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13200#else
13201 NOREF(aId);
13202 NOREF(aDone);
13203 return E_NOTIMPL;
13204#endif
13205}
13206
13207/**
13208 * Inserts all machine filters to the USB proxy service and then calls
13209 * Host::autoCaptureUSBDevices().
13210 *
13211 * Called by Console from the VM process upon VM startup.
13212 *
13213 * @note Locks what called methods lock.
13214 */
13215HRESULT SessionMachine::autoCaptureUSBDevices()
13216{
13217 LogFlowThisFunc(("\n"));
13218
13219#ifdef VBOX_WITH_USB
13220 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13221 AssertComRC(rc);
13222 NOREF(rc);
13223
13224 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13225 AssertReturn(service, E_FAIL);
13226 return service->autoCaptureDevicesForVM(this);
13227#else
13228 return S_OK;
13229#endif
13230}
13231
13232/**
13233 * Removes all machine filters from the USB proxy service and then calls
13234 * Host::detachAllUSBDevices().
13235 *
13236 * Called by Console from the VM process upon normal VM termination or by
13237 * SessionMachine::uninit() upon abnormal VM termination (from under the
13238 * Machine/SessionMachine lock).
13239 *
13240 * @note Locks what called methods lock.
13241 */
13242HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13243{
13244 LogFlowThisFunc(("\n"));
13245
13246#ifdef VBOX_WITH_USB
13247 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13248 AssertComRC(rc);
13249 NOREF(rc);
13250
13251 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13252 AssertReturn(service, E_FAIL);
13253 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13254#else
13255 NOREF(aDone);
13256 return S_OK;
13257#endif
13258}
13259
13260/**
13261 * @note Locks this object for writing.
13262 */
13263HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13264 ComPtr<IProgress> &aProgress)
13265{
13266 LogFlowThisFuncEnter();
13267
13268 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13269 /*
13270 * We don't assert below because it might happen that a non-direct session
13271 * informs us it is closed right after we've been uninitialized -- it's ok.
13272 */
13273
13274 /* get IInternalSessionControl interface */
13275 ComPtr<IInternalSessionControl> control(aSession);
13276
13277 ComAssertRet(!control.isNull(), E_INVALIDARG);
13278
13279 /* Creating a Progress object requires the VirtualBox lock, and
13280 * thus locking it here is required by the lock order rules. */
13281 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13282
13283 if (control == mData->mSession.mDirectControl)
13284 {
13285 /* The direct session is being normally closed by the client process
13286 * ----------------------------------------------------------------- */
13287
13288 /* go to the closing state (essential for all open*Session() calls and
13289 * for #checkForDeath()) */
13290 Assert(mData->mSession.mState == SessionState_Locked);
13291 mData->mSession.mState = SessionState_Unlocking;
13292
13293 /* set direct control to NULL to release the remote instance */
13294 mData->mSession.mDirectControl.setNull();
13295 LogFlowThisFunc(("Direct control is set to NULL\n"));
13296
13297 if (mData->mSession.mProgress)
13298 {
13299 /* finalize the progress, someone might wait if a frontend
13300 * closes the session before powering on the VM. */
13301 mData->mSession.mProgress->notifyComplete(E_FAIL,
13302 COM_IIDOF(ISession),
13303 getComponentName(),
13304 tr("The VM session was closed before any attempt to power it on"));
13305 mData->mSession.mProgress.setNull();
13306 }
13307
13308 /* Create the progress object the client will use to wait until
13309 * #checkForDeath() is called to uninitialize this session object after
13310 * it releases the IPC semaphore.
13311 * Note! Because we're "reusing" mProgress here, this must be a proxy
13312 * object just like for LaunchVMProcess. */
13313 Assert(mData->mSession.mProgress.isNull());
13314 ComObjPtr<ProgressProxy> progress;
13315 progress.createObject();
13316 ComPtr<IUnknown> pPeer(mPeer);
13317 progress->init(mParent, pPeer,
13318 Bstr(tr("Closing session")).raw(),
13319 FALSE /* aCancelable */);
13320 progress.queryInterfaceTo(aProgress.asOutParam());
13321 mData->mSession.mProgress = progress;
13322 }
13323 else
13324 {
13325 /* the remote session is being normally closed */
13326 Data::Session::RemoteControlList::iterator it =
13327 mData->mSession.mRemoteControls.begin();
13328 while (it != mData->mSession.mRemoteControls.end())
13329 {
13330 if (control == *it)
13331 break;
13332 ++it;
13333 }
13334 BOOL found = it != mData->mSession.mRemoteControls.end();
13335 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13336 E_INVALIDARG);
13337 // This MUST be erase(it), not remove(*it) as the latter triggers a
13338 // very nasty use after free due to the place where the value "lives".
13339 mData->mSession.mRemoteControls.erase(it);
13340 }
13341
13342 /* signal the client watcher thread, because the client is going away */
13343 mParent->i_updateClientWatcher();
13344
13345 LogFlowThisFuncLeave();
13346 return S_OK;
13347}
13348
13349HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13350 std::vector<com::Utf8Str> &aValues,
13351 std::vector<LONG64> &aTimestamps,
13352 std::vector<com::Utf8Str> &aFlags)
13353{
13354 LogFlowThisFunc(("\n"));
13355
13356#ifdef VBOX_WITH_GUEST_PROPS
13357 using namespace guestProp;
13358
13359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13360
13361 size_t cEntries = mHWData->mGuestProperties.size();
13362 aNames.resize(cEntries);
13363 aValues.resize(cEntries);
13364 aTimestamps.resize(cEntries);
13365 aFlags.resize(cEntries);
13366
13367 size_t i = 0;
13368 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13369 it != mHWData->mGuestProperties.end();
13370 ++it, ++i)
13371 {
13372 char szFlags[MAX_FLAGS_LEN + 1];
13373 aNames[i] = it->first;
13374 aValues[i] = it->second.strValue;
13375 aTimestamps[i] = it->second.mTimestamp;
13376
13377 /* If it is NULL, keep it NULL. */
13378 if (it->second.mFlags)
13379 {
13380 writeFlags(it->second.mFlags, szFlags);
13381 aFlags[i] = szFlags;
13382 }
13383 else
13384 aFlags[i] = "";
13385 }
13386 return S_OK;
13387#else
13388 ReturnComNotImplemented();
13389#endif
13390}
13391
13392HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13393 const com::Utf8Str &aValue,
13394 LONG64 aTimestamp,
13395 const com::Utf8Str &aFlags,
13396 BOOL *aNotify)
13397{
13398 LogFlowThisFunc(("\n"));
13399
13400#ifdef VBOX_WITH_GUEST_PROPS
13401 using namespace guestProp;
13402
13403 *aNotify = FALSE;
13404
13405 try
13406 {
13407 /*
13408 * Convert input up front.
13409 */
13410 uint32_t fFlags = NILFLAG;
13411 if (aFlags.length())
13412 {
13413 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13414 AssertRCReturn(vrc, E_INVALIDARG);
13415 }
13416
13417 /*
13418 * Now grab the object lock, validate the state and do the update.
13419 */
13420
13421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13422
13423 switch (mData->mMachineState)
13424 {
13425 case MachineState_Paused:
13426 case MachineState_Running:
13427 case MachineState_Teleporting:
13428 case MachineState_TeleportingPausedVM:
13429 case MachineState_OnlineSnapshotting:
13430 case MachineState_LiveSnapshotting:
13431 case MachineState_DeletingSnapshotOnline:
13432 case MachineState_DeletingSnapshotPaused:
13433 case MachineState_Saving:
13434 case MachineState_Stopping:
13435 break;
13436
13437 default:
13438 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13439 VBOX_E_INVALID_VM_STATE);
13440 }
13441
13442 i_setModified(IsModified_MachineData);
13443 mHWData.backup();
13444
13445 bool fDelete = !aValue.length();
13446 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13447 if (it != mHWData->mGuestProperties.end())
13448 {
13449 if (!fDelete)
13450 {
13451 it->second.strValue = aValue;
13452 it->second.mTimestamp = aTimestamp;
13453 it->second.mFlags = fFlags;
13454 }
13455 else
13456 mHWData->mGuestProperties.erase(it);
13457
13458 mData->mGuestPropertiesModified = TRUE;
13459 }
13460 else if (!fDelete)
13461 {
13462 HWData::GuestProperty prop;
13463 prop.strValue = aValue;
13464 prop.mTimestamp = aTimestamp;
13465 prop.mFlags = fFlags;
13466
13467 mHWData->mGuestProperties[aName] = prop;
13468 mData->mGuestPropertiesModified = TRUE;
13469 }
13470
13471 /*
13472 * Send a callback notification if appropriate
13473 */
13474 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13475 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13476 RTSTR_MAX,
13477 aName.c_str(),
13478 RTSTR_MAX, NULL)
13479 )
13480 {
13481 alock.release();
13482
13483 mParent->i_onGuestPropertyChange(mData->mUuid,
13484 Bstr(aName).raw(),
13485 Bstr(aValue).raw(),
13486 Bstr(aFlags).raw());
13487 *aNotify = TRUE;
13488 }
13489 }
13490 catch (...)
13491 {
13492 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13493 }
13494 return S_OK;
13495#else
13496 ReturnComNotImplemented();
13497#endif
13498}
13499
13500
13501HRESULT SessionMachine::lockMedia()
13502{
13503 AutoMultiWriteLock2 alock(this->lockHandle(),
13504 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13505
13506 AssertReturn( mData->mMachineState == MachineState_Starting
13507 || mData->mMachineState == MachineState_Restoring
13508 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13509
13510 clearError();
13511 alock.release();
13512 return i_lockMedia();
13513}
13514
13515HRESULT SessionMachine::unlockMedia()
13516{
13517 HRESULT hrc = i_unlockMedia();
13518 return hrc;
13519}
13520
13521HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13522 ComPtr<IMediumAttachment> &aNewAttachment)
13523{
13524 // request the host lock first, since might be calling Host methods for getting host drives;
13525 // next, protect the media tree all the while we're in here, as well as our member variables
13526 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13527 this->lockHandle(),
13528 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13529
13530 IMediumAttachment *iAttach = aAttachment;
13531 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13532
13533 Bstr ctrlName;
13534 LONG lPort;
13535 LONG lDevice;
13536 bool fTempEject;
13537 {
13538 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13539
13540 /* Need to query the details first, as the IMediumAttachment reference
13541 * might be to the original settings, which we are going to change. */
13542 ctrlName = pAttach->i_getControllerName();
13543 lPort = pAttach->i_getPort();
13544 lDevice = pAttach->i_getDevice();
13545 fTempEject = pAttach->i_getTempEject();
13546 }
13547
13548 if (!fTempEject)
13549 {
13550 /* Remember previously mounted medium. The medium before taking the
13551 * backup is not necessarily the same thing. */
13552 ComObjPtr<Medium> oldmedium;
13553 oldmedium = pAttach->i_getMedium();
13554
13555 i_setModified(IsModified_Storage);
13556 mMediaData.backup();
13557
13558 // The backup operation makes the pAttach reference point to the
13559 // old settings. Re-get the correct reference.
13560 pAttach = i_findAttachment(mMediaData->mAttachments,
13561 ctrlName.raw(),
13562 lPort,
13563 lDevice);
13564
13565 {
13566 AutoCaller autoAttachCaller(this);
13567 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13568
13569 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13570 if (!oldmedium.isNull())
13571 oldmedium->i_removeBackReference(mData->mUuid);
13572
13573 pAttach->i_updateMedium(NULL);
13574 pAttach->i_updateEjected();
13575 }
13576
13577 i_setModified(IsModified_Storage);
13578 }
13579 else
13580 {
13581 {
13582 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13583 pAttach->i_updateEjected();
13584 }
13585 }
13586
13587 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13588
13589 return S_OK;
13590}
13591
13592// public methods only for internal purposes
13593/////////////////////////////////////////////////////////////////////////////
13594
13595#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13596/**
13597 * Called from the client watcher thread to check for expected or unexpected
13598 * death of the client process that has a direct session to this machine.
13599 *
13600 * On Win32 and on OS/2, this method is called only when we've got the
13601 * mutex (i.e. the client has either died or terminated normally) so it always
13602 * returns @c true (the client is terminated, the session machine is
13603 * uninitialized).
13604 *
13605 * On other platforms, the method returns @c true if the client process has
13606 * terminated normally or abnormally and the session machine was uninitialized,
13607 * and @c false if the client process is still alive.
13608 *
13609 * @note Locks this object for writing.
13610 */
13611bool SessionMachine::i_checkForDeath()
13612{
13613 Uninit::Reason reason;
13614 bool terminated = false;
13615
13616 /* Enclose autoCaller with a block because calling uninit() from under it
13617 * will deadlock. */
13618 {
13619 AutoCaller autoCaller(this);
13620 if (!autoCaller.isOk())
13621 {
13622 /* return true if not ready, to cause the client watcher to exclude
13623 * the corresponding session from watching */
13624 LogFlowThisFunc(("Already uninitialized!\n"));
13625 return true;
13626 }
13627
13628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13629
13630 /* Determine the reason of death: if the session state is Closing here,
13631 * everything is fine. Otherwise it means that the client did not call
13632 * OnSessionEnd() before it released the IPC semaphore. This may happen
13633 * either because the client process has abnormally terminated, or
13634 * because it simply forgot to call ISession::Close() before exiting. We
13635 * threat the latter also as an abnormal termination (see
13636 * Session::uninit() for details). */
13637 reason = mData->mSession.mState == SessionState_Unlocking ?
13638 Uninit::Normal :
13639 Uninit::Abnormal;
13640
13641 if (mClientToken)
13642 terminated = mClientToken->release();
13643 } /* AutoCaller block */
13644
13645 if (terminated)
13646 uninit(reason);
13647
13648 return terminated;
13649}
13650
13651void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13652{
13653 LogFlowThisFunc(("\n"));
13654
13655 strTokenId.setNull();
13656
13657 AutoCaller autoCaller(this);
13658 AssertComRCReturnVoid(autoCaller.rc());
13659
13660 Assert(mClientToken);
13661 if (mClientToken)
13662 mClientToken->getId(strTokenId);
13663}
13664#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13665IToken *SessionMachine::i_getToken()
13666{
13667 LogFlowThisFunc(("\n"));
13668
13669 AutoCaller autoCaller(this);
13670 AssertComRCReturn(autoCaller.rc(), NULL);
13671
13672 Assert(mClientToken);
13673 if (mClientToken)
13674 return mClientToken->getToken();
13675 else
13676 return NULL;
13677}
13678#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13679
13680Machine::ClientToken *SessionMachine::i_getClientToken()
13681{
13682 LogFlowThisFunc(("\n"));
13683
13684 AutoCaller autoCaller(this);
13685 AssertComRCReturn(autoCaller.rc(), NULL);
13686
13687 return mClientToken;
13688}
13689
13690
13691/**
13692 * @note Locks this object for reading.
13693 */
13694HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13695{
13696 LogFlowThisFunc(("\n"));
13697
13698 AutoCaller autoCaller(this);
13699 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13700
13701 ComPtr<IInternalSessionControl> directControl;
13702 {
13703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13704 if (mData->mSession.mLockType == LockType_VM)
13705 directControl = mData->mSession.mDirectControl;
13706 }
13707
13708 /* ignore notifications sent after #OnSessionEnd() is called */
13709 if (!directControl)
13710 return S_OK;
13711
13712 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13713}
13714
13715/**
13716 * @note Locks this object for reading.
13717 */
13718HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13719 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13720 IN_BSTR aGuestIp, LONG aGuestPort)
13721{
13722 LogFlowThisFunc(("\n"));
13723
13724 AutoCaller autoCaller(this);
13725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13726
13727 ComPtr<IInternalSessionControl> directControl;
13728 {
13729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13730 if (mData->mSession.mLockType == LockType_VM)
13731 directControl = mData->mSession.mDirectControl;
13732 }
13733
13734 /* ignore notifications sent after #OnSessionEnd() is called */
13735 if (!directControl)
13736 return S_OK;
13737 /*
13738 * instead acting like callback we ask IVirtualBox deliver corresponding event
13739 */
13740
13741 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13742 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13743 return S_OK;
13744}
13745
13746/**
13747 * @note Locks this object for reading.
13748 */
13749HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13750{
13751 LogFlowThisFunc(("\n"));
13752
13753 AutoCaller autoCaller(this);
13754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13755
13756 ComPtr<IInternalSessionControl> directControl;
13757 {
13758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13759 if (mData->mSession.mLockType == LockType_VM)
13760 directControl = mData->mSession.mDirectControl;
13761 }
13762
13763 /* ignore notifications sent after #OnSessionEnd() is called */
13764 if (!directControl)
13765 return S_OK;
13766
13767 return directControl->OnSerialPortChange(serialPort);
13768}
13769
13770/**
13771 * @note Locks this object for reading.
13772 */
13773HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13774{
13775 LogFlowThisFunc(("\n"));
13776
13777 AutoCaller autoCaller(this);
13778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13779
13780 ComPtr<IInternalSessionControl> directControl;
13781 {
13782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13783 if (mData->mSession.mLockType == LockType_VM)
13784 directControl = mData->mSession.mDirectControl;
13785 }
13786
13787 /* ignore notifications sent after #OnSessionEnd() is called */
13788 if (!directControl)
13789 return S_OK;
13790
13791 return directControl->OnParallelPortChange(parallelPort);
13792}
13793
13794/**
13795 * @note Locks this object for reading.
13796 */
13797HRESULT SessionMachine::i_onStorageControllerChange()
13798{
13799 LogFlowThisFunc(("\n"));
13800
13801 AutoCaller autoCaller(this);
13802 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13803
13804 ComPtr<IInternalSessionControl> directControl;
13805 {
13806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13807 if (mData->mSession.mLockType == LockType_VM)
13808 directControl = mData->mSession.mDirectControl;
13809 }
13810
13811 /* ignore notifications sent after #OnSessionEnd() is called */
13812 if (!directControl)
13813 return S_OK;
13814
13815 return directControl->OnStorageControllerChange();
13816}
13817
13818/**
13819 * @note Locks this object for reading.
13820 */
13821HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13822{
13823 LogFlowThisFunc(("\n"));
13824
13825 AutoCaller autoCaller(this);
13826 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13827
13828 ComPtr<IInternalSessionControl> directControl;
13829 {
13830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13831 if (mData->mSession.mLockType == LockType_VM)
13832 directControl = mData->mSession.mDirectControl;
13833 }
13834
13835 /* ignore notifications sent after #OnSessionEnd() is called */
13836 if (!directControl)
13837 return S_OK;
13838
13839 return directControl->OnMediumChange(aAttachment, aForce);
13840}
13841
13842/**
13843 * @note Locks this object for reading.
13844 */
13845HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13846{
13847 LogFlowThisFunc(("\n"));
13848
13849 AutoCaller autoCaller(this);
13850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13851
13852 ComPtr<IInternalSessionControl> directControl;
13853 {
13854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13855 if (mData->mSession.mLockType == LockType_VM)
13856 directControl = mData->mSession.mDirectControl;
13857 }
13858
13859 /* ignore notifications sent after #OnSessionEnd() is called */
13860 if (!directControl)
13861 return S_OK;
13862
13863 return directControl->OnCPUChange(aCPU, aRemove);
13864}
13865
13866HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13872
13873 ComPtr<IInternalSessionControl> directControl;
13874 {
13875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13876 if (mData->mSession.mLockType == LockType_VM)
13877 directControl = mData->mSession.mDirectControl;
13878 }
13879
13880 /* ignore notifications sent after #OnSessionEnd() is called */
13881 if (!directControl)
13882 return S_OK;
13883
13884 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13885}
13886
13887/**
13888 * @note Locks this object for reading.
13889 */
13890HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13891{
13892 LogFlowThisFunc(("\n"));
13893
13894 AutoCaller autoCaller(this);
13895 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13896
13897 ComPtr<IInternalSessionControl> directControl;
13898 {
13899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13900 if (mData->mSession.mLockType == LockType_VM)
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* ignore notifications sent after #OnSessionEnd() is called */
13905 if (!directControl)
13906 return S_OK;
13907
13908 return directControl->OnVRDEServerChange(aRestart);
13909}
13910
13911/**
13912 * @note Locks this object for reading.
13913 */
13914HRESULT SessionMachine::i_onVideoCaptureChange()
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918 AutoCaller autoCaller(this);
13919 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13920
13921 ComPtr<IInternalSessionControl> directControl;
13922 {
13923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13924 if (mData->mSession.mLockType == LockType_VM)
13925 directControl = mData->mSession.mDirectControl;
13926 }
13927
13928 /* ignore notifications sent after #OnSessionEnd() is called */
13929 if (!directControl)
13930 return S_OK;
13931
13932 return directControl->OnVideoCaptureChange();
13933}
13934
13935/**
13936 * @note Locks this object for reading.
13937 */
13938HRESULT SessionMachine::i_onUSBControllerChange()
13939{
13940 LogFlowThisFunc(("\n"));
13941
13942 AutoCaller autoCaller(this);
13943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13944
13945 ComPtr<IInternalSessionControl> directControl;
13946 {
13947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13948 if (mData->mSession.mLockType == LockType_VM)
13949 directControl = mData->mSession.mDirectControl;
13950 }
13951
13952 /* ignore notifications sent after #OnSessionEnd() is called */
13953 if (!directControl)
13954 return S_OK;
13955
13956 return directControl->OnUSBControllerChange();
13957}
13958
13959/**
13960 * @note Locks this object for reading.
13961 */
13962HRESULT SessionMachine::i_onSharedFolderChange()
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturnRC(autoCaller.rc());
13968
13969 ComPtr<IInternalSessionControl> directControl;
13970 {
13971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13972 if (mData->mSession.mLockType == LockType_VM)
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 /* ignore notifications sent after #OnSessionEnd() is called */
13977 if (!directControl)
13978 return S_OK;
13979
13980 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13981}
13982
13983/**
13984 * @note Locks this object for reading.
13985 */
13986HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13987{
13988 LogFlowThisFunc(("\n"));
13989
13990 AutoCaller autoCaller(this);
13991 AssertComRCReturnRC(autoCaller.rc());
13992
13993 ComPtr<IInternalSessionControl> directControl;
13994 {
13995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13996 if (mData->mSession.mLockType == LockType_VM)
13997 directControl = mData->mSession.mDirectControl;
13998 }
13999
14000 /* ignore notifications sent after #OnSessionEnd() is called */
14001 if (!directControl)
14002 return S_OK;
14003
14004 return directControl->OnClipboardModeChange(aClipboardMode);
14005}
14006
14007/**
14008 * @note Locks this object for reading.
14009 */
14010HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14011{
14012 LogFlowThisFunc(("\n"));
14013
14014 AutoCaller autoCaller(this);
14015 AssertComRCReturnRC(autoCaller.rc());
14016
14017 ComPtr<IInternalSessionControl> directControl;
14018 {
14019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14020 if (mData->mSession.mLockType == LockType_VM)
14021 directControl = mData->mSession.mDirectControl;
14022 }
14023
14024 /* ignore notifications sent after #OnSessionEnd() is called */
14025 if (!directControl)
14026 return S_OK;
14027
14028 return directControl->OnDnDModeChange(aDnDMode);
14029}
14030
14031/**
14032 * @note Locks this object for reading.
14033 */
14034HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 AutoCaller autoCaller(this);
14039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14040
14041 ComPtr<IInternalSessionControl> directControl;
14042 {
14043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14044 if (mData->mSession.mLockType == LockType_VM)
14045 directControl = mData->mSession.mDirectControl;
14046 }
14047
14048 /* ignore notifications sent after #OnSessionEnd() is called */
14049 if (!directControl)
14050 return S_OK;
14051
14052 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14053}
14054
14055/**
14056 * @note Locks this object for reading.
14057 */
14058HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14059{
14060 LogFlowThisFunc(("\n"));
14061
14062 AutoCaller autoCaller(this);
14063 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14064
14065 ComPtr<IInternalSessionControl> directControl;
14066 {
14067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14068 if (mData->mSession.mLockType == LockType_VM)
14069 directControl = mData->mSession.mDirectControl;
14070 }
14071
14072 /* ignore notifications sent after #OnSessionEnd() is called */
14073 if (!directControl)
14074 return S_OK;
14075
14076 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14077}
14078
14079/**
14080 * Returns @c true if this machine's USB controller reports it has a matching
14081 * filter for the given USB device and @c false otherwise.
14082 *
14083 * @note locks this object for reading.
14084 */
14085bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14086{
14087 AutoCaller autoCaller(this);
14088 /* silently return if not ready -- this method may be called after the
14089 * direct machine session has been called */
14090 if (!autoCaller.isOk())
14091 return false;
14092
14093#ifdef VBOX_WITH_USB
14094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14095
14096 switch (mData->mMachineState)
14097 {
14098 case MachineState_Starting:
14099 case MachineState_Restoring:
14100 case MachineState_TeleportingIn:
14101 case MachineState_Paused:
14102 case MachineState_Running:
14103 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14104 * elsewhere... */
14105 alock.release();
14106 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14107 default: break;
14108 }
14109#else
14110 NOREF(aDevice);
14111 NOREF(aMaskedIfs);
14112#endif
14113 return false;
14114}
14115
14116/**
14117 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14118 */
14119HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14120 IVirtualBoxErrorInfo *aError,
14121 ULONG aMaskedIfs,
14122 const com::Utf8Str &aCaptureFilename)
14123{
14124 LogFlowThisFunc(("\n"));
14125
14126 AutoCaller autoCaller(this);
14127
14128 /* This notification may happen after the machine object has been
14129 * uninitialized (the session was closed), so don't assert. */
14130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14131
14132 ComPtr<IInternalSessionControl> directControl;
14133 {
14134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14135 if (mData->mSession.mLockType == LockType_VM)
14136 directControl = mData->mSession.mDirectControl;
14137 }
14138
14139 /* fail on notifications sent after #OnSessionEnd() is called, it is
14140 * expected by the caller */
14141 if (!directControl)
14142 return E_FAIL;
14143
14144 /* No locks should be held at this point. */
14145 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14146 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14147
14148 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14149}
14150
14151/**
14152 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14153 */
14154HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14155 IVirtualBoxErrorInfo *aError)
14156{
14157 LogFlowThisFunc(("\n"));
14158
14159 AutoCaller autoCaller(this);
14160
14161 /* This notification may happen after the machine object has been
14162 * uninitialized (the session was closed), so don't assert. */
14163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* fail on notifications sent after #OnSessionEnd() is called, it is
14173 * expected by the caller */
14174 if (!directControl)
14175 return E_FAIL;
14176
14177 /* No locks should be held at this point. */
14178 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14179 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14180
14181 return directControl->OnUSBDeviceDetach(aId, aError);
14182}
14183
14184// protected methods
14185/////////////////////////////////////////////////////////////////////////////
14186
14187/**
14188 * Deletes the given file if it is no longer in use by either the current machine state
14189 * (if the machine is "saved") or any of the machine's snapshots.
14190 *
14191 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14192 * but is different for each SnapshotMachine. When calling this, the order of calling this
14193 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14194 * is therefore critical. I know, it's all rather messy.
14195 *
14196 * @param strStateFile
14197 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14198 * the test for whether the saved state file is in use.
14199 */
14200void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14201 Snapshot *pSnapshotToIgnore)
14202{
14203 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14204 if ( (strStateFile.isNotEmpty())
14205 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14206 )
14207 // ... and it must also not be shared with other snapshots
14208 if ( !mData->mFirstSnapshot
14209 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14210 // this checks the SnapshotMachine's state file paths
14211 )
14212 RTFileDelete(strStateFile.c_str());
14213}
14214
14215/**
14216 * Locks the attached media.
14217 *
14218 * All attached hard disks are locked for writing and DVD/floppy are locked for
14219 * reading. Parents of attached hard disks (if any) are locked for reading.
14220 *
14221 * This method also performs accessibility check of all media it locks: if some
14222 * media is inaccessible, the method will return a failure and a bunch of
14223 * extended error info objects per each inaccessible medium.
14224 *
14225 * Note that this method is atomic: if it returns a success, all media are
14226 * locked as described above; on failure no media is locked at all (all
14227 * succeeded individual locks will be undone).
14228 *
14229 * The caller is responsible for doing the necessary state sanity checks.
14230 *
14231 * The locks made by this method must be undone by calling #unlockMedia() when
14232 * no more needed.
14233 */
14234HRESULT SessionMachine::i_lockMedia()
14235{
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14238
14239 AutoMultiWriteLock2 alock(this->lockHandle(),
14240 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14241
14242 /* bail out if trying to lock things with already set up locking */
14243 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14244
14245 MultiResult mrc(S_OK);
14246
14247 /* Collect locking information for all medium objects attached to the VM. */
14248 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14249 it != mMediaData->mAttachments.end();
14250 ++it)
14251 {
14252 MediumAttachment* pAtt = *it;
14253 DeviceType_T devType = pAtt->i_getType();
14254 Medium *pMedium = pAtt->i_getMedium();
14255
14256 MediumLockList *pMediumLockList(new MediumLockList());
14257 // There can be attachments without a medium (floppy/dvd), and thus
14258 // it's impossible to create a medium lock list. It still makes sense
14259 // to have the empty medium lock list in the map in case a medium is
14260 // attached later.
14261 if (pMedium != NULL)
14262 {
14263 MediumType_T mediumType = pMedium->i_getType();
14264 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14265 || mediumType == MediumType_Shareable;
14266 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14267
14268 alock.release();
14269 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14270 !fIsReadOnlyLock /* fMediumLockWrite */,
14271 false /* fMediumLockWriteAll */,
14272 NULL,
14273 *pMediumLockList);
14274 alock.acquire();
14275 if (FAILED(mrc))
14276 {
14277 delete pMediumLockList;
14278 mData->mSession.mLockedMedia.Clear();
14279 break;
14280 }
14281 }
14282
14283 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14284 if (FAILED(rc))
14285 {
14286 mData->mSession.mLockedMedia.Clear();
14287 mrc = setError(rc,
14288 tr("Collecting locking information for all attached media failed"));
14289 break;
14290 }
14291 }
14292
14293 if (SUCCEEDED(mrc))
14294 {
14295 /* Now lock all media. If this fails, nothing is locked. */
14296 alock.release();
14297 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14298 alock.acquire();
14299 if (FAILED(rc))
14300 {
14301 mrc = setError(rc,
14302 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14303 }
14304 }
14305
14306 return mrc;
14307}
14308
14309/**
14310 * Undoes the locks made by by #lockMedia().
14311 */
14312HRESULT SessionMachine::i_unlockMedia()
14313{
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14316
14317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14318
14319 /* we may be holding important error info on the current thread;
14320 * preserve it */
14321 ErrorInfoKeeper eik;
14322
14323 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14324 AssertComRC(rc);
14325 return rc;
14326}
14327
14328/**
14329 * Helper to change the machine state (reimplementation).
14330 *
14331 * @note Locks this object for writing.
14332 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14333 * it can cause crashes in random places due to unexpectedly committing
14334 * the current settings. The caller is responsible for that. The call
14335 * to saveStateSettings is fine, because this method does not commit.
14336 */
14337HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14338{
14339 LogFlowThisFuncEnter();
14340 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14341
14342 AutoCaller autoCaller(this);
14343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14344
14345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14346
14347 MachineState_T oldMachineState = mData->mMachineState;
14348
14349 AssertMsgReturn(oldMachineState != aMachineState,
14350 ("oldMachineState=%s, aMachineState=%s\n",
14351 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14352 E_FAIL);
14353
14354 HRESULT rc = S_OK;
14355
14356 int stsFlags = 0;
14357 bool deleteSavedState = false;
14358
14359 /* detect some state transitions */
14360
14361 if ( ( oldMachineState == MachineState_Saved
14362 && aMachineState == MachineState_Restoring)
14363 || ( ( oldMachineState == MachineState_PoweredOff
14364 || oldMachineState == MachineState_Teleported
14365 || oldMachineState == MachineState_Aborted
14366 )
14367 && ( aMachineState == MachineState_TeleportingIn
14368 || aMachineState == MachineState_Starting
14369 )
14370 )
14371 )
14372 {
14373 /* The EMT thread is about to start */
14374
14375 /* Nothing to do here for now... */
14376
14377 /// @todo NEWMEDIA don't let mDVDDrive and other children
14378 /// change anything when in the Starting/Restoring state
14379 }
14380 else if ( ( oldMachineState == MachineState_Running
14381 || oldMachineState == MachineState_Paused
14382 || oldMachineState == MachineState_Teleporting
14383 || oldMachineState == MachineState_OnlineSnapshotting
14384 || oldMachineState == MachineState_LiveSnapshotting
14385 || oldMachineState == MachineState_Stuck
14386 || oldMachineState == MachineState_Starting
14387 || oldMachineState == MachineState_Stopping
14388 || oldMachineState == MachineState_Saving
14389 || oldMachineState == MachineState_Restoring
14390 || oldMachineState == MachineState_TeleportingPausedVM
14391 || oldMachineState == MachineState_TeleportingIn
14392 )
14393 && ( aMachineState == MachineState_PoweredOff
14394 || aMachineState == MachineState_Saved
14395 || aMachineState == MachineState_Teleported
14396 || aMachineState == MachineState_Aborted
14397 )
14398 )
14399 {
14400 /* The EMT thread has just stopped, unlock attached media. Note that as
14401 * opposed to locking that is done from Console, we do unlocking here
14402 * because the VM process may have aborted before having a chance to
14403 * properly unlock all media it locked. */
14404
14405 unlockMedia();
14406 }
14407
14408 if (oldMachineState == MachineState_Restoring)
14409 {
14410 if (aMachineState != MachineState_Saved)
14411 {
14412 /*
14413 * delete the saved state file once the machine has finished
14414 * restoring from it (note that Console sets the state from
14415 * Restoring to Saved if the VM couldn't restore successfully,
14416 * to give the user an ability to fix an error and retry --
14417 * we keep the saved state file in this case)
14418 */
14419 deleteSavedState = true;
14420 }
14421 }
14422 else if ( oldMachineState == MachineState_Saved
14423 && ( aMachineState == MachineState_PoweredOff
14424 || aMachineState == MachineState_Aborted
14425 || aMachineState == MachineState_Teleported
14426 )
14427 )
14428 {
14429 /*
14430 * delete the saved state after SessionMachine::ForgetSavedState() is called
14431 * or if the VM process (owning a direct VM session) crashed while the
14432 * VM was Saved
14433 */
14434
14435 /// @todo (dmik)
14436 // Not sure that deleting the saved state file just because of the
14437 // client death before it attempted to restore the VM is a good
14438 // thing. But when it crashes we need to go to the Aborted state
14439 // which cannot have the saved state file associated... The only
14440 // way to fix this is to make the Aborted condition not a VM state
14441 // but a bool flag: i.e., when a crash occurs, set it to true and
14442 // change the state to PoweredOff or Saved depending on the
14443 // saved state presence.
14444
14445 deleteSavedState = true;
14446 mData->mCurrentStateModified = TRUE;
14447 stsFlags |= SaveSTS_CurStateModified;
14448 }
14449
14450 if ( aMachineState == MachineState_Starting
14451 || aMachineState == MachineState_Restoring
14452 || aMachineState == MachineState_TeleportingIn
14453 )
14454 {
14455 /* set the current state modified flag to indicate that the current
14456 * state is no more identical to the state in the
14457 * current snapshot */
14458 if (!mData->mCurrentSnapshot.isNull())
14459 {
14460 mData->mCurrentStateModified = TRUE;
14461 stsFlags |= SaveSTS_CurStateModified;
14462 }
14463 }
14464
14465 if (deleteSavedState)
14466 {
14467 if (mRemoveSavedState)
14468 {
14469 Assert(!mSSData->strStateFilePath.isEmpty());
14470
14471 // it is safe to delete the saved state file if ...
14472 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14473 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14474 // ... none of the snapshots share the saved state file
14475 )
14476 RTFileDelete(mSSData->strStateFilePath.c_str());
14477 }
14478
14479 mSSData->strStateFilePath.setNull();
14480 stsFlags |= SaveSTS_StateFilePath;
14481 }
14482
14483 /* redirect to the underlying peer machine */
14484 mPeer->i_setMachineState(aMachineState);
14485
14486 if ( oldMachineState != MachineState_RestoringSnapshot
14487 && ( aMachineState == MachineState_PoweredOff
14488 || aMachineState == MachineState_Teleported
14489 || aMachineState == MachineState_Aborted
14490 || aMachineState == MachineState_Saved))
14491 {
14492 /* the machine has stopped execution
14493 * (or the saved state file was adopted) */
14494 stsFlags |= SaveSTS_StateTimeStamp;
14495 }
14496
14497 if ( ( oldMachineState == MachineState_PoweredOff
14498 || oldMachineState == MachineState_Aborted
14499 || oldMachineState == MachineState_Teleported
14500 )
14501 && aMachineState == MachineState_Saved)
14502 {
14503 /* the saved state file was adopted */
14504 Assert(!mSSData->strStateFilePath.isEmpty());
14505 stsFlags |= SaveSTS_StateFilePath;
14506 }
14507
14508#ifdef VBOX_WITH_GUEST_PROPS
14509 if ( aMachineState == MachineState_PoweredOff
14510 || aMachineState == MachineState_Aborted
14511 || aMachineState == MachineState_Teleported)
14512 {
14513 /* Make sure any transient guest properties get removed from the
14514 * property store on shutdown. */
14515 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14516
14517 /* remove it from the settings representation */
14518 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14519 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14520 it != llGuestProperties.end();
14521 /*nothing*/)
14522 {
14523 const settings::GuestProperty &prop = *it;
14524 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14525 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14526 {
14527 it = llGuestProperties.erase(it);
14528 fNeedsSaving = true;
14529 }
14530 else
14531 {
14532 ++it;
14533 }
14534 }
14535
14536 /* Additionally remove it from the HWData representation. Required to
14537 * keep everything in sync, as this is what the API keeps using. */
14538 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14539 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14540 it != llHWGuestProperties.end();
14541 /*nothing*/)
14542 {
14543 uint32_t fFlags = it->second.mFlags;
14544 if ( fFlags & guestProp::TRANSIENT
14545 || fFlags & guestProp::TRANSRESET)
14546 {
14547 /* iterator where we need to continue after the erase call
14548 * (C++03 is a fact still, and it doesn't return the iterator
14549 * which would allow continuing) */
14550 HWData::GuestPropertyMap::iterator it2 = it;
14551 ++it2;
14552 llHWGuestProperties.erase(it);
14553 it = it2;
14554 fNeedsSaving = true;
14555 }
14556 else
14557 {
14558 ++it;
14559 }
14560 }
14561
14562 if (fNeedsSaving)
14563 {
14564 mData->mCurrentStateModified = TRUE;
14565 stsFlags |= SaveSTS_CurStateModified;
14566 }
14567 }
14568#endif /* VBOX_WITH_GUEST_PROPS */
14569
14570 rc = i_saveStateSettings(stsFlags);
14571
14572 if ( ( oldMachineState != MachineState_PoweredOff
14573 && oldMachineState != MachineState_Aborted
14574 && oldMachineState != MachineState_Teleported
14575 )
14576 && ( aMachineState == MachineState_PoweredOff
14577 || aMachineState == MachineState_Aborted
14578 || aMachineState == MachineState_Teleported
14579 )
14580 )
14581 {
14582 /* we've been shut down for any reason */
14583 /* no special action so far */
14584 }
14585
14586 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14587 LogFlowThisFuncLeave();
14588 return rc;
14589}
14590
14591/**
14592 * Sends the current machine state value to the VM process.
14593 *
14594 * @note Locks this object for reading, then calls a client process.
14595 */
14596HRESULT SessionMachine::i_updateMachineStateOnClient()
14597{
14598 AutoCaller autoCaller(this);
14599 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14600
14601 ComPtr<IInternalSessionControl> directControl;
14602 {
14603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14604 AssertReturn(!!mData, E_FAIL);
14605 if (mData->mSession.mLockType == LockType_VM)
14606 directControl = mData->mSession.mDirectControl;
14607
14608 /* directControl may be already set to NULL here in #OnSessionEnd()
14609 * called too early by the direct session process while there is still
14610 * some operation (like deleting the snapshot) in progress. The client
14611 * process in this case is waiting inside Session::close() for the
14612 * "end session" process object to complete, while #uninit() called by
14613 * #checkForDeath() on the Watcher thread is waiting for the pending
14614 * operation to complete. For now, we accept this inconsistent behavior
14615 * and simply do nothing here. */
14616
14617 if (mData->mSession.mState == SessionState_Unlocking)
14618 return S_OK;
14619 }
14620
14621 /* ignore notifications sent after #OnSessionEnd() is called */
14622 if (!directControl)
14623 return S_OK;
14624
14625 return directControl->UpdateMachineState(mData->mMachineState);
14626}
14627
14628
14629/**
14630 * Static Machine method that can get passed to RTThreadCreate to
14631 * have a thread started for a Task. See Machine::Task.
14632 */
14633/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14634{
14635 AssertReturn(pvUser, VERR_INVALID_POINTER);
14636
14637 Task *pTask = static_cast<Task *>(pvUser);
14638 pTask->handler();
14639 /** @todo r=klaus it would be safer to update the progress object here,
14640 * as it avoids possible races due to scoping issues/tricks in the handler */
14641 // it's our responsibility to delete the task
14642 delete pTask;
14643
14644 return 0;
14645}
14646
14647/*static*/
14648HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14649{
14650 va_list args;
14651 va_start(args, pcszMsg);
14652 HRESULT rc = setErrorInternal(aResultCode,
14653 getStaticClassIID(),
14654 getStaticComponentName(),
14655 Utf8Str(pcszMsg, args),
14656 false /* aWarning */,
14657 true /* aLogIt */);
14658 va_end(args);
14659 return rc;
14660}
14661
14662
14663HRESULT Machine::updateState(MachineState_T aState)
14664{
14665 NOREF(aState);
14666 ReturnComNotImplemented();
14667}
14668
14669HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14670{
14671 NOREF(aProgress);
14672 ReturnComNotImplemented();
14673}
14674
14675HRESULT Machine::endPowerUp(LONG aResult)
14676{
14677 NOREF(aResult);
14678 ReturnComNotImplemented();
14679}
14680
14681HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14682{
14683 NOREF(aProgress);
14684 ReturnComNotImplemented();
14685}
14686
14687HRESULT Machine::endPoweringDown(LONG aResult,
14688 const com::Utf8Str &aErrMsg)
14689{
14690 NOREF(aResult);
14691 NOREF(aErrMsg);
14692 ReturnComNotImplemented();
14693}
14694
14695HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14696 BOOL *aMatched,
14697 ULONG *aMaskedInterfaces)
14698{
14699 NOREF(aDevice);
14700 NOREF(aMatched);
14701 NOREF(aMaskedInterfaces);
14702 ReturnComNotImplemented();
14703
14704}
14705
14706HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14707{
14708 NOREF(aId); NOREF(aCaptureFilename);
14709 ReturnComNotImplemented();
14710}
14711
14712HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14713 BOOL aDone)
14714{
14715 NOREF(aId);
14716 NOREF(aDone);
14717 ReturnComNotImplemented();
14718}
14719
14720HRESULT Machine::autoCaptureUSBDevices()
14721{
14722 ReturnComNotImplemented();
14723}
14724
14725HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14726{
14727 NOREF(aDone);
14728 ReturnComNotImplemented();
14729}
14730
14731HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14732 ComPtr<IProgress> &aProgress)
14733{
14734 NOREF(aSession);
14735 NOREF(aProgress);
14736 ReturnComNotImplemented();
14737}
14738
14739HRESULT Machine::finishOnlineMergeMedium()
14740{
14741 ReturnComNotImplemented();
14742}
14743
14744HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14745 std::vector<com::Utf8Str> &aValues,
14746 std::vector<LONG64> &aTimestamps,
14747 std::vector<com::Utf8Str> &aFlags)
14748{
14749 NOREF(aNames);
14750 NOREF(aValues);
14751 NOREF(aTimestamps);
14752 NOREF(aFlags);
14753 ReturnComNotImplemented();
14754}
14755
14756HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14757 const com::Utf8Str &aValue,
14758 LONG64 aTimestamp,
14759 const com::Utf8Str &aFlags,
14760 BOOL *aNotify)
14761{
14762 NOREF(aName);
14763 NOREF(aValue);
14764 NOREF(aTimestamp);
14765 NOREF(aFlags);
14766 NOREF(aNotify);
14767 ReturnComNotImplemented();
14768}
14769
14770HRESULT Machine::lockMedia()
14771{
14772 ReturnComNotImplemented();
14773}
14774
14775HRESULT Machine::unlockMedia()
14776{
14777 ReturnComNotImplemented();
14778}
14779
14780HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14781 ComPtr<IMediumAttachment> &aNewAttachment)
14782{
14783 NOREF(aAttachment);
14784 NOREF(aNewAttachment);
14785 ReturnComNotImplemented();
14786}
14787
14788HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14789 ULONG aCpuUser,
14790 ULONG aCpuKernel,
14791 ULONG aCpuIdle,
14792 ULONG aMemTotal,
14793 ULONG aMemFree,
14794 ULONG aMemBalloon,
14795 ULONG aMemShared,
14796 ULONG aMemCache,
14797 ULONG aPagedTotal,
14798 ULONG aMemAllocTotal,
14799 ULONG aMemFreeTotal,
14800 ULONG aMemBalloonTotal,
14801 ULONG aMemSharedTotal,
14802 ULONG aVmNetRx,
14803 ULONG aVmNetTx)
14804{
14805 NOREF(aValidStats);
14806 NOREF(aCpuUser);
14807 NOREF(aCpuKernel);
14808 NOREF(aCpuIdle);
14809 NOREF(aMemTotal);
14810 NOREF(aMemFree);
14811 NOREF(aMemBalloon);
14812 NOREF(aMemShared);
14813 NOREF(aMemCache);
14814 NOREF(aPagedTotal);
14815 NOREF(aMemAllocTotal);
14816 NOREF(aMemFreeTotal);
14817 NOREF(aMemBalloonTotal);
14818 NOREF(aMemSharedTotal);
14819 NOREF(aVmNetRx);
14820 NOREF(aVmNetTx);
14821 ReturnComNotImplemented();
14822}
Note: See TracBrowser for help on using the repository browser.

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