VirtualBox

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

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

Main/Machine: repair VM configs a bit more thoroughly, as otherwise it can lead to inconsistencies (repair gets lost) when the config was repaired and then immediately a new offline snapshot is taken

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 512.0 KB
Line 
1/* $Id: MachineImpl.cpp 55835 2015-05-12 18:37:34Z 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.215389.xyz. 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 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 i_rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->i_unregisterMachineMedia(i_getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 i_saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!i_isSnapshotMachine());
818 Assert(!i_isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(i_getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->i_unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 i_rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// Wrapped IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
892{
893 /* mParent is constant during life time, no need to lock */
894 ComObjPtr<VirtualBox> pVirtualBox(mParent);
895 aParent = pVirtualBox;
896
897 return S_OK;
898}
899
900
901HRESULT Machine::getAccessible(BOOL *aAccessible)
902{
903 /* In some cases (medium registry related), it is necessary to be able to
904 * go through the list of all machines. Happens when an inaccessible VM
905 * has a sensible medium registry. */
906 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
908
909 HRESULT rc = S_OK;
910
911 if (!mData->mAccessible)
912 {
913 /* try to initialize the VM once more if not accessible */
914
915 AutoReinitSpan autoReinitSpan(this);
916 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
917
918#ifdef DEBUG
919 LogFlowThisFunc(("Dumping media backreferences\n"));
920 mParent->i_dumpAllBackRefs();
921#endif
922
923 if (mData->pMachineConfigFile)
924 {
925 // reset the XML file to force loadSettings() (called from registeredInit())
926 // to parse it again; the file might have changed
927 delete mData->pMachineConfigFile;
928 mData->pMachineConfigFile = NULL;
929 }
930
931 rc = i_registeredInit();
932
933 if (SUCCEEDED(rc) && mData->mAccessible)
934 {
935 autoReinitSpan.setSucceeded();
936
937 /* make sure interesting parties will notice the accessibility
938 * state change */
939 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
940 mParent->i_onMachineDataChange(mData->mUuid);
941 }
942 }
943
944 if (SUCCEEDED(rc))
945 *aAccessible = mData->mAccessible;
946
947 LogFlowThisFuncLeave();
948
949 return rc;
950}
951
952HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
953{
954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
955
956 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
957 {
958 /* return shortly */
959 aAccessError = NULL;
960 return S_OK;
961 }
962
963 HRESULT rc = S_OK;
964
965 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
966 rc = errorInfo.createObject();
967 if (SUCCEEDED(rc))
968 {
969 errorInfo->init(mData->mAccessError.getResultCode(),
970 mData->mAccessError.getInterfaceID().ref(),
971 Utf8Str(mData->mAccessError.getComponent()).c_str(),
972 Utf8Str(mData->mAccessError.getText()));
973 aAccessError = errorInfo;
974 }
975
976 return rc;
977}
978
979HRESULT Machine::getName(com::Utf8Str &aName)
980{
981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
982
983 aName = mUserData->s.strName;
984
985 return S_OK;
986}
987
988HRESULT Machine::setName(const com::Utf8Str &aName)
989{
990 // prohibit setting a UUID only as the machine name, or else it can
991 // never be found by findMachine()
992 Guid test(aName);
993
994 if (test.isValid())
995 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
996
997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 HRESULT rc = i_checkStateDependency(MutableStateDep);
1000 if (FAILED(rc)) return rc;
1001
1002 i_setModified(IsModified_MachineData);
1003 mUserData.backup();
1004 mUserData->s.strName = aName;
1005
1006 return S_OK;
1007}
1008
1009HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1010{
1011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1012
1013 aDescription = mUserData->s.strDescription;
1014
1015 return S_OK;
1016}
1017
1018HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1019{
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 // this can be done in principle in any state as it doesn't affect the VM
1023 // significantly, but play safe by not messing around while complex
1024 // activities are going on
1025 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1026 if (FAILED(rc)) return rc;
1027
1028 i_setModified(IsModified_MachineData);
1029 mUserData.backup();
1030 mUserData->s.strDescription = aDescription;
1031
1032 return S_OK;
1033}
1034
1035HRESULT Machine::getId(com::Guid &aId)
1036{
1037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 aId = mData->mUuid;
1040
1041 return S_OK;
1042}
1043
1044HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1045{
1046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1047 aGroups.resize(mUserData->s.llGroups.size());
1048 size_t i = 0;
1049 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1050 it != mUserData->s.llGroups.end(); ++it, ++i)
1051 aGroups[i] = (*it);
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1057{
1058 StringsList llGroups;
1059 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1060 if (FAILED(rc))
1061 return rc;
1062
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 rc = i_checkStateDependency(MutableOrSavedStateDep);
1066 if (FAILED(rc)) return rc;
1067
1068 i_setModified(IsModified_MachineData);
1069 mUserData.backup();
1070 mUserData->s.llGroups = llGroups;
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1076{
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 aOSTypeId = mUserData->s.strOsType;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1085{
1086 /* look up the object by Id to check it is valid */
1087 ComPtr<IGuestOSType> guestOSType;
1088 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1089 if (FAILED(rc)) return rc;
1090
1091 /* when setting, always use the "etalon" value for consistency -- lookup
1092 * by ID is case-insensitive and the input value may have different case */
1093 Bstr osTypeId;
1094 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1095 if (FAILED(rc)) return rc;
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 rc = i_checkStateDependency(MutableStateDep);
1100 if (FAILED(rc)) return rc;
1101
1102 i_setModified(IsModified_MachineData);
1103 mUserData.backup();
1104 mUserData->s.strOsType = osTypeId;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1110{
1111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 *aFirmwareType = mHWData->mFirmwareType;
1114
1115 return S_OK;
1116}
1117
1118HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1119{
1120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 HRESULT rc = i_checkStateDependency(MutableStateDep);
1123 if (FAILED(rc)) return rc;
1124
1125 i_setModified(IsModified_MachineData);
1126 mHWData.backup();
1127 mHWData->mFirmwareType = aFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1133{
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1137
1138 return S_OK;
1139}
1140
1141HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1142{
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 HRESULT rc = i_checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 i_setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1151
1152 return S_OK;
1153}
1154
1155HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1156{
1157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 *aPointingHIDType = mHWData->mPointingHIDType;
1160
1161 return S_OK;
1162}
1163
1164HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1165{
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = i_checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 i_setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mPointingHIDType = aPointingHIDType;
1174
1175 return S_OK;
1176}
1177
1178HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1179{
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aChipsetType = mHWData->mChipsetType;
1183
1184 return S_OK;
1185}
1186
1187HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1188{
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = i_checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 if (aChipsetType != mHWData->mChipsetType)
1195 {
1196 i_setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mChipsetType = aChipsetType;
1199
1200 // Resize network adapter array, to be finalized on commit/rollback.
1201 // We must not throw away entries yet, otherwise settings are lost
1202 // without a way to roll back.
1203 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1204 size_t oldCount = mNetworkAdapters.size();
1205 if (newCount > oldCount)
1206 {
1207 mNetworkAdapters.resize(newCount);
1208 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1209 {
1210 unconst(mNetworkAdapters[slot]).createObject();
1211 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1212 }
1213 }
1214 }
1215
1216 return S_OK;
1217}
1218
1219HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1220{
1221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 *aParavirtProvider = mHWData->mParavirtProvider;
1224
1225 return S_OK;
1226}
1227
1228HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1229{
1230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 HRESULT rc = i_checkStateDependency(MutableStateDep);
1233 if (FAILED(rc)) return rc;
1234
1235 if (aParavirtProvider != mHWData->mParavirtProvider)
1236 {
1237 i_setModified(IsModified_MachineData);
1238 mHWData.backup();
1239 mHWData->mParavirtProvider = aParavirtProvider;
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aParavirtProvider = mHWData->mParavirtProvider;
1250 switch (mHWData->mParavirtProvider)
1251 {
1252 case ParavirtProvider_None:
1253 case ParavirtProvider_HyperV:
1254 case ParavirtProvider_KVM:
1255 case ParavirtProvider_Minimal:
1256 break;
1257
1258 /* Resolve dynamic provider types to the effective types. */
1259 default:
1260 {
1261 ComPtr<IGuestOSType> ptrGuestOSType;
1262 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1263 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1264
1265 Bstr guestTypeFamilyId;
1266 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1267 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1268 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1269
1270 switch (mHWData->mParavirtProvider)
1271 {
1272 case ParavirtProvider_Legacy:
1273 {
1274 if (fOsXGuest)
1275 *aParavirtProvider = ParavirtProvider_Minimal;
1276 else
1277 *aParavirtProvider = ParavirtProvider_None;
1278 break;
1279 }
1280
1281 case ParavirtProvider_Default:
1282 {
1283 if (fOsXGuest)
1284 *aParavirtProvider = ParavirtProvider_Minimal;
1285 else if ( mUserData->s.strOsType == "Windows10"
1286 || mUserData->s.strOsType == "Windows10_64"
1287 || mUserData->s.strOsType == "Windows81"
1288 || mUserData->s.strOsType == "Windows81_64"
1289 || mUserData->s.strOsType == "Windows8"
1290 || mUserData->s.strOsType == "Windows8_64"
1291 || mUserData->s.strOsType == "Windows7"
1292 || mUserData->s.strOsType == "Windows7_64"
1293 || mUserData->s.strOsType == "WindowsVista"
1294 || mUserData->s.strOsType == "WindowsVista_64"
1295 || mUserData->s.strOsType == "Windows2012"
1296 || mUserData->s.strOsType == "Windows2012_64"
1297 || mUserData->s.strOsType == "Windows2008"
1298 || mUserData->s.strOsType == "Windows2008_64")
1299 {
1300 *aParavirtProvider = ParavirtProvider_HyperV;
1301 }
1302 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1303 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1304 || mUserData->s.strOsType == "Linux"
1305 || mUserData->s.strOsType == "Linux_64"
1306 || mUserData->s.strOsType == "ArchLinux"
1307 || mUserData->s.strOsType == "ArchLinux_64"
1308 || mUserData->s.strOsType == "Debian"
1309 || mUserData->s.strOsType == "Debian_64"
1310 || mUserData->s.strOsType == "Fedora"
1311 || mUserData->s.strOsType == "Fedora_64"
1312 || mUserData->s.strOsType == "Gentoo"
1313 || mUserData->s.strOsType == "Gentoo_64"
1314 || mUserData->s.strOsType == "Mandriva"
1315 || mUserData->s.strOsType == "Mandriva_64"
1316 || mUserData->s.strOsType == "OpenSUSE"
1317 || mUserData->s.strOsType == "OpenSUSE_64"
1318 || mUserData->s.strOsType == "Oracle"
1319 || mUserData->s.strOsType == "Oracle_64"
1320 || mUserData->s.strOsType == "RedHat"
1321 || mUserData->s.strOsType == "RedHat_64"
1322 || mUserData->s.strOsType == "Turbolinux"
1323 || mUserData->s.strOsType == "Turbolinux_64"
1324 || mUserData->s.strOsType == "Ubuntu"
1325 || mUserData->s.strOsType == "Ubuntu_64"
1326 || mUserData->s.strOsType == "Xandros"
1327 || mUserData->s.strOsType == "Xandros_64")
1328 {
1329 *aParavirtProvider = ParavirtProvider_KVM;
1330 }
1331 else
1332 *aParavirtProvider = ParavirtProvider_None;
1333 break;
1334 }
1335 }
1336 break;
1337 }
1338 }
1339
1340 Assert( *aParavirtProvider == ParavirtProvider_None
1341 || *aParavirtProvider == ParavirtProvider_Minimal
1342 || *aParavirtProvider == ParavirtProvider_HyperV
1343 || *aParavirtProvider == ParavirtProvider_KVM);
1344 return S_OK;
1345}
1346
1347HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1348{
1349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1350
1351 aHardwareVersion = mHWData->mHWVersion;
1352
1353 return S_OK;
1354}
1355
1356HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1357{
1358 /* check known version */
1359 Utf8Str hwVersion = aHardwareVersion;
1360 if ( hwVersion.compare("1") != 0
1361 && hwVersion.compare("2") != 0)
1362 return setError(E_INVALIDARG,
1363 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1364
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 HRESULT rc = i_checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 i_setModified(IsModified_MachineData);
1371 mHWData.backup();
1372 mHWData->mHWVersion = aHardwareVersion;
1373
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 if (!mHWData->mHardwareUUID.isZero())
1382 aHardwareUUID = mHWData->mHardwareUUID;
1383 else
1384 aHardwareUUID = mData->mUuid;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1390{
1391 if (!aHardwareUUID.isValid())
1392 return E_INVALIDARG;
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 if (aHardwareUUID == mData->mUuid)
1402 mHWData->mHardwareUUID.clear();
1403 else
1404 mHWData->mHardwareUUID = aHardwareUUID;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1410{
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 *aMemorySize = mHWData->mMemorySize;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setMemorySize(ULONG aMemorySize)
1419{
1420 /* check RAM limits */
1421 if ( aMemorySize < MM_RAM_MIN_IN_MB
1422 || aMemorySize > MM_RAM_MAX_IN_MB
1423 )
1424 return setError(E_INVALIDARG,
1425 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1426 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1427
1428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 HRESULT rc = i_checkStateDependency(MutableStateDep);
1431 if (FAILED(rc)) return rc;
1432
1433 i_setModified(IsModified_MachineData);
1434 mHWData.backup();
1435 mHWData->mMemorySize = aMemorySize;
1436
1437 return S_OK;
1438}
1439
1440HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1441{
1442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1443
1444 *aCPUCount = mHWData->mCPUCount;
1445
1446 return S_OK;
1447}
1448
1449HRESULT Machine::setCPUCount(ULONG aCPUCount)
1450{
1451 /* check CPU limits */
1452 if ( aCPUCount < SchemaDefs::MinCPUCount
1453 || aCPUCount > SchemaDefs::MaxCPUCount
1454 )
1455 return setError(E_INVALIDARG,
1456 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1457 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1458
1459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1462 if (mHWData->mCPUHotPlugEnabled)
1463 {
1464 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1465 {
1466 if (mHWData->mCPUAttached[idx])
1467 return setError(E_INVALIDARG,
1468 tr("There is still a CPU attached to socket %lu."
1469 "Detach the CPU before removing the socket"),
1470 aCPUCount, idx+1);
1471 }
1472 }
1473
1474 HRESULT rc = i_checkStateDependency(MutableStateDep);
1475 if (FAILED(rc)) return rc;
1476
1477 i_setModified(IsModified_MachineData);
1478 mHWData.backup();
1479 mHWData->mCPUCount = aCPUCount;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1485{
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1489
1490 return S_OK;
1491}
1492
1493HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1494{
1495 HRESULT rc = S_OK;
1496
1497 /* check throttle limits */
1498 if ( aCPUExecutionCap < 1
1499 || aCPUExecutionCap > 100
1500 )
1501 return setError(E_INVALIDARG,
1502 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1503 aCPUExecutionCap, 1, 100);
1504
1505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1506
1507 alock.release();
1508 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1509 alock.acquire();
1510 if (FAILED(rc)) return rc;
1511
1512 i_setModified(IsModified_MachineData);
1513 mHWData.backup();
1514 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1515
1516 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1517 if (Global::IsOnline(mData->mMachineState))
1518 i_saveSettings(NULL);
1519
1520 return S_OK;
1521}
1522
1523HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1524{
1525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1528
1529 return S_OK;
1530}
1531
1532HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1533{
1534 HRESULT rc = S_OK;
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 rc = i_checkStateDependency(MutableStateDep);
1539 if (FAILED(rc)) return rc;
1540
1541 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1542 {
1543 if (aCPUHotPlugEnabled)
1544 {
1545 i_setModified(IsModified_MachineData);
1546 mHWData.backup();
1547
1548 /* Add the amount of CPUs currently attached */
1549 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1550 mHWData->mCPUAttached[i] = true;
1551 }
1552 else
1553 {
1554 /*
1555 * We can disable hotplug only if the amount of maximum CPUs is equal
1556 * to the amount of attached CPUs
1557 */
1558 unsigned cCpusAttached = 0;
1559 unsigned iHighestId = 0;
1560
1561 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1562 {
1563 if (mHWData->mCPUAttached[i])
1564 {
1565 cCpusAttached++;
1566 iHighestId = i;
1567 }
1568 }
1569
1570 if ( (cCpusAttached != mHWData->mCPUCount)
1571 || (iHighestId >= mHWData->mCPUCount))
1572 return setError(E_INVALIDARG,
1573 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1574
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 }
1578 }
1579
1580 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1581
1582 return rc;
1583}
1584
1585HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1586{
1587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1588
1589 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1595{
1596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1599 if (SUCCEEDED(hrc))
1600 {
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1604 }
1605 return hrc;
1606}
1607
1608HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1609{
1610#ifdef VBOX_WITH_USB_CARDREADER
1611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1612
1613 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1614
1615 return S_OK;
1616#else
1617 NOREF(aEmulatedUSBCardReaderEnabled);
1618 return E_NOTIMPL;
1619#endif
1620}
1621
1622HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1623{
1624#ifdef VBOX_WITH_USB_CARDREADER
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1628 if (FAILED(rc)) return rc;
1629
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1633
1634 return S_OK;
1635#else
1636 NOREF(aEmulatedUSBCardReaderEnabled);
1637 return E_NOTIMPL;
1638#endif
1639}
1640
1641HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1642{
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *aHPETEnabled = mHWData->mHPETEnabled;
1646
1647 return S_OK;
1648}
1649
1650HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1651{
1652 HRESULT rc = S_OK;
1653
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 rc = i_checkStateDependency(MutableStateDep);
1657 if (FAILED(rc)) return rc;
1658
1659 i_setModified(IsModified_MachineData);
1660 mHWData.backup();
1661
1662 mHWData->mHPETEnabled = aHPETEnabled;
1663
1664 return rc;
1665}
1666
1667HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1668{
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 i_setModified(IsModified_MachineData);
1682 mHWData.backup();
1683 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1684
1685 alock.release();
1686 rc = i_onVideoCaptureChange();
1687 alock.acquire();
1688 if (FAILED(rc))
1689 {
1690 /*
1691 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1692 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1693 * determine if it should start or stop capturing. Therefore we need to manually
1694 * undo change.
1695 */
1696 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1697 return rc;
1698 }
1699
1700 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1701 if (Global::IsOnline(mData->mMachineState))
1702 i_saveSettings(NULL);
1703
1704 return rc;
1705}
1706
1707HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1708{
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1711 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1712 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1713 return S_OK;
1714}
1715
1716HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1717{
1718 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1719 bool fChanged = false;
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1724 {
1725 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1726 {
1727 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1728 fChanged = true;
1729 }
1730 }
1731 if (fChanged)
1732 {
1733 alock.release();
1734 HRESULT rc = i_onVideoCaptureChange();
1735 alock.acquire();
1736 if (FAILED(rc)) return rc;
1737 i_setModified(IsModified_MachineData);
1738
1739 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1740 if (Global::IsOnline(mData->mMachineState))
1741 i_saveSettings(NULL);
1742 }
1743
1744 return S_OK;
1745}
1746
1747HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1748{
1749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1750 if (mHWData->mVideoCaptureFile.isEmpty())
1751 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1752 else
1753 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1758{
1759 Utf8Str strFile(aVideoCaptureFile);
1760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 if ( Global::IsOnline(mData->mMachineState)
1763 && mHWData->mVideoCaptureEnabled)
1764 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1765
1766 if (!RTPathStartsWithRoot(strFile.c_str()))
1767 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1768
1769 if (!strFile.isEmpty())
1770 {
1771 Utf8Str defaultFile;
1772 i_getDefaultVideoCaptureFile(defaultFile);
1773 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1774 strFile.setNull();
1775 }
1776
1777 i_setModified(IsModified_MachineData);
1778 mHWData.backup();
1779 mHWData->mVideoCaptureFile = strFile;
1780
1781 return S_OK;
1782}
1783
1784HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1785{
1786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1787 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1788 return S_OK;
1789}
1790
1791HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1792{
1793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1794
1795 if ( Global::IsOnline(mData->mMachineState)
1796 && mHWData->mVideoCaptureEnabled)
1797 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1798
1799 i_setModified(IsModified_MachineData);
1800 mHWData.backup();
1801 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1802
1803 return S_OK;
1804}
1805
1806HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1807{
1808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1809 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1810 return S_OK;
1811}
1812
1813HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1814{
1815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1816
1817 if ( Global::IsOnline(mData->mMachineState)
1818 && mHWData->mVideoCaptureEnabled)
1819 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1820
1821 i_setModified(IsModified_MachineData);
1822 mHWData.backup();
1823 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1824
1825 return S_OK;
1826}
1827
1828HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1829{
1830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1832 return S_OK;
1833}
1834
1835HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1836{
1837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1838
1839 if ( Global::IsOnline(mData->mMachineState)
1840 && mHWData->mVideoCaptureEnabled)
1841 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1842
1843 i_setModified(IsModified_MachineData);
1844 mHWData.backup();
1845 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1851{
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1858{
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1873{
1874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1875 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1876 return S_OK;
1877}
1878
1879HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1880{
1881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 if ( Global::IsOnline(mData->mMachineState)
1884 && mHWData->mVideoCaptureEnabled)
1885 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1886
1887 i_setModified(IsModified_MachineData);
1888 mHWData.backup();
1889 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1890
1891 return S_OK;
1892}
1893
1894HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1895{
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1898 return S_OK;
1899}
1900
1901HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1902{
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1912
1913 return S_OK;
1914}
1915
1916HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1917{
1918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1919
1920 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1921 return S_OK;
1922}
1923
1924HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1925{
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 if ( Global::IsOnline(mData->mMachineState)
1929 && mHWData->mVideoCaptureEnabled)
1930 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1940{
1941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1944
1945 return S_OK;
1946}
1947
1948HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1949{
1950 switch (aGraphicsControllerType)
1951 {
1952 case GraphicsControllerType_Null:
1953 case GraphicsControllerType_VBoxVGA:
1954#ifdef VBOX_WITH_VMSVGA
1955 case GraphicsControllerType_VMSVGA:
1956#endif
1957 break;
1958 default:
1959 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1960 }
1961
1962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 HRESULT rc = i_checkStateDependency(MutableStateDep);
1965 if (FAILED(rc)) return rc;
1966
1967 i_setModified(IsModified_MachineData);
1968 mHWData.backup();
1969 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1970
1971 return S_OK;
1972}
1973
1974HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1975{
1976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 *aVRAMSize = mHWData->mVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1984{
1985 /* check VRAM limits */
1986 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1987 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1988 return setError(E_INVALIDARG,
1989 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1990 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1991
1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 HRESULT rc = i_checkStateDependency(MutableStateDep);
1995 if (FAILED(rc)) return rc;
1996
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mVRAMSize = aVRAMSize;
2000
2001 return S_OK;
2002}
2003
2004/** @todo this method should not be public */
2005HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2006{
2007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2008
2009 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2010
2011 return S_OK;
2012}
2013
2014/**
2015 * Set the memory balloon size.
2016 *
2017 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2018 * we have to make sure that we never call IGuest from here.
2019 */
2020HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2021{
2022 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2023#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2024 /* check limits */
2025 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2026 return setError(E_INVALIDARG,
2027 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2028 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2035
2036 return S_OK;
2037#else
2038 NOREF(aMemoryBalloonSize);
2039 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2040#endif
2041}
2042
2043HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2044{
2045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2048 return S_OK;
2049}
2050
2051HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2052{
2053#ifdef VBOX_WITH_PAGE_SHARING
2054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2055
2056 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2057 i_setModified(IsModified_MachineData);
2058 mHWData.backup();
2059 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2060 return S_OK;
2061#else
2062 NOREF(aPageFusionEnabled);
2063 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2064#endif
2065}
2066
2067HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2068{
2069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2072
2073 return S_OK;
2074}
2075
2076HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2077{
2078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2079
2080 HRESULT rc = i_checkStateDependency(MutableStateDep);
2081 if (FAILED(rc)) return rc;
2082
2083 /** @todo check validity! */
2084
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2088
2089 return S_OK;
2090}
2091
2092
2093HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2094{
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2098
2099 return S_OK;
2100}
2101
2102HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2103{
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 HRESULT rc = i_checkStateDependency(MutableStateDep);
2107 if (FAILED(rc)) return rc;
2108
2109 /** @todo check validity! */
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2113
2114 return S_OK;
2115}
2116
2117HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 *aMonitorCount = mHWData->mMonitorCount;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2127{
2128 /* make sure monitor count is a sensible number */
2129 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2130 return setError(E_INVALIDARG,
2131 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2132 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2133
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 i_setModified(IsModified_MachineData);
2140 mHWData.backup();
2141 mHWData->mMonitorCount = aMonitorCount;
2142
2143 return S_OK;
2144}
2145
2146HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2147{
2148 /* mBIOSSettings is constant during life time, no need to lock */
2149 aBIOSSettings = mBIOSSettings;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2155{
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 switch (aProperty)
2159 {
2160 case CPUPropertyType_PAE:
2161 *aValue = mHWData->mPAEEnabled;
2162 break;
2163
2164 case CPUPropertyType_LongMode:
2165 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2166 *aValue = TRUE;
2167 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2168 *aValue = FALSE;
2169#if HC_ARCH_BITS == 64
2170 else
2171 *aValue = TRUE;
2172#else
2173 else
2174 {
2175 *aValue = FALSE;
2176
2177 ComPtr<IGuestOSType> ptrGuestOSType;
2178 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2179 if (SUCCEEDED(hrc2))
2180 {
2181 BOOL fIs64Bit = FALSE;
2182 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2183 if (SUCCEEDED(hrc2) && fIs64Bit)
2184 {
2185 ComObjPtr<Host> ptrHost = mParent->i_host();
2186 alock.release();
2187
2188 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2189 if (FAILED(hrc2))
2190 *aValue = FALSE;
2191 }
2192 }
2193 }
2194#endif
2195 break;
2196
2197 case CPUPropertyType_TripleFaultReset:
2198 *aValue = mHWData->mTripleFaultReset;
2199 break;
2200
2201 default:
2202 return E_INVALIDARG;
2203 }
2204 return S_OK;
2205}
2206
2207HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2208{
2209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 HRESULT rc = i_checkStateDependency(MutableStateDep);
2212 if (FAILED(rc)) return rc;
2213
2214 switch (aProperty)
2215 {
2216 case CPUPropertyType_PAE:
2217 i_setModified(IsModified_MachineData);
2218 mHWData.backup();
2219 mHWData->mPAEEnabled = !!aValue;
2220 break;
2221
2222 case CPUPropertyType_LongMode:
2223 i_setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2226 break;
2227
2228 case CPUPropertyType_TripleFaultReset:
2229 i_setModified(IsModified_MachineData);
2230 mHWData.backup();
2231 mHWData->mTripleFaultReset = !!aValue;
2232 break;
2233
2234 default:
2235 return E_INVALIDARG;
2236 }
2237 return S_OK;
2238}
2239
2240HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2241{
2242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 switch(aId)
2245 {
2246 case 0x0:
2247 case 0x1:
2248 case 0x2:
2249 case 0x3:
2250 case 0x4:
2251 case 0x5:
2252 case 0x6:
2253 case 0x7:
2254 case 0x8:
2255 case 0x9:
2256 case 0xA:
2257 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2258 return E_INVALIDARG;
2259
2260 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2261 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2262 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2263 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2264 break;
2265
2266 case 0x80000000:
2267 case 0x80000001:
2268 case 0x80000002:
2269 case 0x80000003:
2270 case 0x80000004:
2271 case 0x80000005:
2272 case 0x80000006:
2273 case 0x80000007:
2274 case 0x80000008:
2275 case 0x80000009:
2276 case 0x8000000A:
2277 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2278 return E_INVALIDARG;
2279
2280 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2281 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2282 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2283 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2284 break;
2285
2286 default:
2287 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2288 }
2289 return S_OK;
2290}
2291
2292
2293HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2294{
2295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 HRESULT rc = i_checkStateDependency(MutableStateDep);
2298 if (FAILED(rc)) return rc;
2299
2300 switch(aId)
2301 {
2302 case 0x0:
2303 case 0x1:
2304 case 0x2:
2305 case 0x3:
2306 case 0x4:
2307 case 0x5:
2308 case 0x6:
2309 case 0x7:
2310 case 0x8:
2311 case 0x9:
2312 case 0xA:
2313 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2314 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2318 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2319 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2320 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2321 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2322 break;
2323
2324 case 0x80000000:
2325 case 0x80000001:
2326 case 0x80000002:
2327 case 0x80000003:
2328 case 0x80000004:
2329 case 0x80000005:
2330 case 0x80000006:
2331 case 0x80000007:
2332 case 0x80000008:
2333 case 0x80000009:
2334 case 0x8000000A:
2335 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2336 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2337 i_setModified(IsModified_MachineData);
2338 mHWData.backup();
2339 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2340 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2341 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2342 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2343 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2344 break;
2345
2346 default:
2347 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2348 }
2349 return S_OK;
2350}
2351
2352HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2353{
2354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2355
2356 HRESULT rc = i_checkStateDependency(MutableStateDep);
2357 if (FAILED(rc)) return rc;
2358
2359 switch(aId)
2360 {
2361 case 0x0:
2362 case 0x1:
2363 case 0x2:
2364 case 0x3:
2365 case 0x4:
2366 case 0x5:
2367 case 0x6:
2368 case 0x7:
2369 case 0x8:
2370 case 0x9:
2371 case 0xA:
2372 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2373 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2374 i_setModified(IsModified_MachineData);
2375 mHWData.backup();
2376 /* Invalidate leaf. */
2377 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2378 break;
2379
2380 case 0x80000000:
2381 case 0x80000001:
2382 case 0x80000002:
2383 case 0x80000003:
2384 case 0x80000004:
2385 case 0x80000005:
2386 case 0x80000006:
2387 case 0x80000007:
2388 case 0x80000008:
2389 case 0x80000009:
2390 case 0x8000000A:
2391 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2392 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 /* Invalidate leaf. */
2396 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2397 break;
2398
2399 default:
2400 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2401 }
2402 return S_OK;
2403}
2404
2405HRESULT Machine::removeAllCPUIDLeaves()
2406{
2407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 HRESULT rc = i_checkStateDependency(MutableStateDep);
2410 if (FAILED(rc)) return rc;
2411
2412 i_setModified(IsModified_MachineData);
2413 mHWData.backup();
2414
2415 /* Invalidate all standard leafs. */
2416 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2417 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2418
2419 /* Invalidate all extended leafs. */
2420 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2421 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2422
2423 return S_OK;
2424}
2425HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 switch(aProperty)
2430 {
2431 case HWVirtExPropertyType_Enabled:
2432 *aValue = mHWData->mHWVirtExEnabled;
2433 break;
2434
2435 case HWVirtExPropertyType_VPID:
2436 *aValue = mHWData->mHWVirtExVPIDEnabled;
2437 break;
2438
2439 case HWVirtExPropertyType_NestedPaging:
2440 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2441 break;
2442
2443 case HWVirtExPropertyType_UnrestrictedExecution:
2444 *aValue = mHWData->mHWVirtExUXEnabled;
2445 break;
2446
2447 case HWVirtExPropertyType_LargePages:
2448 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2449#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2450 *aValue = FALSE;
2451#endif
2452 break;
2453
2454 case HWVirtExPropertyType_Force:
2455 *aValue = mHWData->mHWVirtExForceEnabled;
2456 break;
2457
2458 default:
2459 return E_INVALIDARG;
2460 }
2461 return S_OK;
2462}
2463
2464HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2465{
2466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 HRESULT rc = i_checkStateDependency(MutableStateDep);
2469 if (FAILED(rc)) return rc;
2470
2471 switch(aProperty)
2472 {
2473 case HWVirtExPropertyType_Enabled:
2474 i_setModified(IsModified_MachineData);
2475 mHWData.backup();
2476 mHWData->mHWVirtExEnabled = !!aValue;
2477 break;
2478
2479 case HWVirtExPropertyType_VPID:
2480 i_setModified(IsModified_MachineData);
2481 mHWData.backup();
2482 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2483 break;
2484
2485 case HWVirtExPropertyType_NestedPaging:
2486 i_setModified(IsModified_MachineData);
2487 mHWData.backup();
2488 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2489 break;
2490
2491 case HWVirtExPropertyType_UnrestrictedExecution:
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494 mHWData->mHWVirtExUXEnabled = !!aValue;
2495 break;
2496
2497 case HWVirtExPropertyType_LargePages:
2498 i_setModified(IsModified_MachineData);
2499 mHWData.backup();
2500 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2501 break;
2502
2503 case HWVirtExPropertyType_Force:
2504 i_setModified(IsModified_MachineData);
2505 mHWData.backup();
2506 mHWData->mHWVirtExForceEnabled = !!aValue;
2507 break;
2508
2509 default:
2510 return E_INVALIDARG;
2511 }
2512
2513 return S_OK;
2514}
2515
2516HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2521
2522 return S_OK;
2523}
2524
2525HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2526{
2527 /* @todo (r=dmik):
2528 * 1. Allow to change the name of the snapshot folder containing snapshots
2529 * 2. Rename the folder on disk instead of just changing the property
2530 * value (to be smart and not to leave garbage). Note that it cannot be
2531 * done here because the change may be rolled back. Thus, the right
2532 * place is #saveSettings().
2533 */
2534
2535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 HRESULT rc = i_checkStateDependency(MutableStateDep);
2538 if (FAILED(rc)) return rc;
2539
2540 if (!mData->mCurrentSnapshot.isNull())
2541 return setError(E_FAIL,
2542 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2543
2544 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2545
2546 if (strSnapshotFolder.isEmpty())
2547 strSnapshotFolder = "Snapshots";
2548 int vrc = i_calculateFullPath(strSnapshotFolder,
2549 strSnapshotFolder);
2550 if (RT_FAILURE(vrc))
2551 return setError(E_FAIL,
2552 tr("Invalid snapshot folder '%s' (%Rrc)"),
2553 strSnapshotFolder.c_str(), vrc);
2554
2555 i_setModified(IsModified_MachineData);
2556 mUserData.backup();
2557
2558 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2564{
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 aMediumAttachments.resize(mMediaData->mAttachments.size());
2568 size_t i = 0;
2569 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2570 it != mMediaData->mAttachments.end(); ++it, ++i)
2571 aMediumAttachments[i] = *it;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 Assert(!!mVRDEServer);
2581
2582 aVRDEServer = mVRDEServer;
2583
2584 return S_OK;
2585}
2586
2587HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2588{
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 aAudioAdapter = mAudioAdapter;
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2597{
2598#ifdef VBOX_WITH_VUSB
2599 clearError();
2600 MultiResult rc(S_OK);
2601
2602# ifdef VBOX_WITH_USB
2603 rc = mParent->i_host()->i_checkUSBProxyService();
2604 if (FAILED(rc)) return rc;
2605# endif
2606
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 USBControllerList data = *mUSBControllers.data();
2610 aUSBControllers.resize(data.size());
2611 size_t i = 0;
2612 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2613 aUSBControllers[i] = *it;
2614
2615 return S_OK;
2616#else
2617 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2618 * extended error info to indicate that USB is simply not available
2619 * (w/o treating it as a failure), for example, as in OSE */
2620 NOREF(aUSBControllers);
2621 ReturnComNotImplemented();
2622#endif /* VBOX_WITH_VUSB */
2623}
2624
2625HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2626{
2627#ifdef VBOX_WITH_VUSB
2628 clearError();
2629 MultiResult rc(S_OK);
2630
2631# ifdef VBOX_WITH_USB
2632 rc = mParent->i_host()->i_checkUSBProxyService();
2633 if (FAILED(rc)) return rc;
2634# endif
2635
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 aUSBDeviceFilters = mUSBDeviceFilters;
2639 return rc;
2640#else
2641 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2642 * extended error info to indicate that USB is simply not available
2643 * (w/o treating it as a failure), for example, as in OSE */
2644 NOREF(aUSBDeviceFilters);
2645 ReturnComNotImplemented();
2646#endif /* VBOX_WITH_VUSB */
2647}
2648
2649HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aSettingsFilePath = mData->m_strConfigFileFull;
2654
2655 return S_OK;
2656}
2657
2658HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2659{
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2663 if (FAILED(rc)) return rc;
2664
2665 if (!mData->pMachineConfigFile->fileExists())
2666 // this is a new machine, and no config file exists yet:
2667 *aSettingsModified = TRUE;
2668 else
2669 *aSettingsModified = (mData->flModifications != 0);
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 *aSessionState = mData->mSession.mState;
2679
2680 return S_OK;
2681}
2682
2683HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2684{
2685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 aSessionName = mData->mSession.mName;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 *aSessionPID = mData->mSession.mPID;
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getState(MachineState_T *aState)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aState = mData->mMachineState;
2706 Assert(mData->mMachineState != MachineState_Null);
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2716
2717 return S_OK;
2718}
2719
2720HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2721{
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 aStateFilePath = mSSData->strStateFilePath;
2725
2726 return S_OK;
2727}
2728
2729HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2730{
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 i_getLogFolder(aLogFolder);
2734
2735 return S_OK;
2736}
2737
2738HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 aCurrentSnapshot = mData->mCurrentSnapshot;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2752 ? 0
2753 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 /* Note: for machines with no snapshots, we always return FALSE
2763 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2764 * reasons :) */
2765
2766 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2767 ? FALSE
2768 : mData->mCurrentStateModified;
2769
2770 return S_OK;
2771}
2772
2773HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2774{
2775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2776
2777 aSharedFolders.resize(mHWData->mSharedFolders.size());
2778 size_t i = 0;
2779 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2780 it != mHWData->mSharedFolders.end(); ++i, ++it)
2781 aSharedFolders[i] = *it;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 *aClipboardMode = mHWData->mClipboardMode;
2791
2792 return S_OK;
2793}
2794
2795HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2796{
2797 HRESULT rc = S_OK;
2798
2799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 alock.release();
2802 rc = i_onClipboardModeChange(aClipboardMode);
2803 alock.acquire();
2804 if (FAILED(rc)) return rc;
2805
2806 i_setModified(IsModified_MachineData);
2807 mHWData.backup();
2808 mHWData->mClipboardMode = aClipboardMode;
2809
2810 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2811 if (Global::IsOnline(mData->mMachineState))
2812 i_saveSettings(NULL);
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aDnDMode = mHWData->mDnDMode;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2827{
2828 HRESULT rc = S_OK;
2829
2830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 alock.release();
2833 rc = i_onDnDModeChange(aDnDMode);
2834
2835 alock.acquire();
2836 if (FAILED(rc)) return rc;
2837
2838 i_setModified(IsModified_MachineData);
2839 mHWData.backup();
2840 mHWData->mDnDMode = aDnDMode;
2841
2842 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2843 if (Global::IsOnline(mData->mMachineState))
2844 i_saveSettings(NULL);
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 try
2854 {
2855 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2856 }
2857 catch (...)
2858 {
2859 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2860 }
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2866{
2867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2870 if (FAILED(rc)) return rc;
2871
2872 i_setModified(IsModified_MachineData);
2873 mHWData.backup();
2874 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2875 return rc;
2876}
2877
2878HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881 StorageControllerList data = *mStorageControllers.data();
2882 size_t i = 0;
2883 aStorageControllers.resize(data.size());
2884 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2885 aStorageControllers[i] = *it;
2886 return S_OK;
2887}
2888
2889HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2890{
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aEnabled = mUserData->s.fTeleporterEnabled;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2899{
2900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 /* Only allow it to be set to true when PoweredOff or Aborted.
2903 (Clearing it is always permitted.) */
2904 if ( aTeleporterEnabled
2905 && mData->mRegistered
2906 && ( !i_isSessionMachine()
2907 || ( mData->mMachineState != MachineState_PoweredOff
2908 && mData->mMachineState != MachineState_Teleported
2909 && mData->mMachineState != MachineState_Aborted
2910 )
2911 )
2912 )
2913 return setError(VBOX_E_INVALID_VM_STATE,
2914 tr("The machine is not powered off (state is %s)"),
2915 Global::stringifyMachineState(mData->mMachineState));
2916
2917 i_setModified(IsModified_MachineData);
2918 mUserData.backup();
2919 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2920
2921 return S_OK;
2922}
2923
2924HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2925{
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2929
2930 return S_OK;
2931}
2932
2933HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2934{
2935 if (aTeleporterPort >= _64K)
2936 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2937
2938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2941 if (FAILED(rc)) return rc;
2942
2943 i_setModified(IsModified_MachineData);
2944 mUserData.backup();
2945 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2964 if (FAILED(rc)) return rc;
2965
2966 i_setModified(IsModified_MachineData);
2967 mUserData.backup();
2968 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2969
2970 return S_OK;
2971}
2972
2973HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2974{
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2982{
2983 /*
2984 * Hash the password first.
2985 */
2986 com::Utf8Str aT = aTeleporterPassword;
2987
2988 if (!aT.isEmpty())
2989 {
2990 if (VBoxIsPasswordHashed(&aT))
2991 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2992 VBoxHashPassword(&aT);
2993 }
2994
2995 /*
2996 * Do the update.
2997 */
2998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2999 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3000 if (SUCCEEDED(hrc))
3001 {
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.strTeleporterPassword = aT;
3005 }
3006
3007 return hrc;
3008}
3009
3010HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3011{
3012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3015 return S_OK;
3016}
3017
3018HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3019{
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 /* @todo deal with running state change. */
3023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3024 if (FAILED(rc)) return rc;
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3029 return S_OK;
3030}
3031
3032HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3033{
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3041{
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3059 return S_OK;
3060}
3061
3062HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3063{
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 /* @todo deal with running state change. */
3067 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3068 if (FAILED(rc)) return rc;
3069
3070 i_setModified(IsModified_MachineData);
3071 mUserData.backup();
3072 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3073 return S_OK;
3074}
3075
3076HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3077{
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3081
3082 return S_OK;
3083}
3084
3085HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3086{
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 /* @todo deal with running state change. */
3090 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3091 if (FAILED(rc)) return rc;
3092
3093 i_setModified(IsModified_MachineData);
3094 mUserData.backup();
3095 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3096
3097 return S_OK;
3098}
3099
3100HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3101{
3102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3105 return S_OK;
3106}
3107
3108HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3109{
3110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3111
3112 /* @todo deal with running state change. */
3113 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3114 if (FAILED(rc)) return rc;
3115
3116 i_setModified(IsModified_MachineData);
3117 mUserData.backup();
3118 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3119 return S_OK;
3120}
3121
3122HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3123{
3124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3125
3126 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3127
3128 return S_OK;
3129}
3130
3131HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3132{
3133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 /* Only allow it to be set to true when PoweredOff or Aborted.
3136 (Clearing it is always permitted.) */
3137 if ( aRTCUseUTC
3138 && mData->mRegistered
3139 && ( !i_isSessionMachine()
3140 || ( mData->mMachineState != MachineState_PoweredOff
3141 && mData->mMachineState != MachineState_Teleported
3142 && mData->mMachineState != MachineState_Aborted
3143 )
3144 )
3145 )
3146 return setError(VBOX_E_INVALID_VM_STATE,
3147 tr("The machine is not powered off (state is %s)"),
3148 Global::stringifyMachineState(mData->mMachineState));
3149
3150 i_setModified(IsModified_MachineData);
3151 mUserData.backup();
3152 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3153
3154 return S_OK;
3155}
3156
3157HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3158{
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3162
3163 return S_OK;
3164}
3165
3166HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3167{
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 HRESULT rc = i_checkStateDependency(MutableStateDep);
3171 if (FAILED(rc)) return rc;
3172
3173 i_setModified(IsModified_MachineData);
3174 mHWData.backup();
3175 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3176
3177 return S_OK;
3178}
3179
3180HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3181{
3182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 *aIOCacheSize = mHWData->mIOCacheSize;
3185
3186 return S_OK;
3187}
3188
3189HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3190{
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 HRESULT rc = i_checkStateDependency(MutableStateDep);
3194 if (FAILED(rc)) return rc;
3195
3196 i_setModified(IsModified_MachineData);
3197 mHWData.backup();
3198 mHWData->mIOCacheSize = aIOCacheSize;
3199
3200 return S_OK;
3201}
3202
3203
3204/**
3205 * @note Locks objects!
3206 */
3207HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3208 LockType_T aLockType)
3209{
3210 /* check the session state */
3211 SessionState_T state;
3212 HRESULT rc = aSession->COMGETTER(State)(&state);
3213 if (FAILED(rc)) return rc;
3214
3215 if (state != SessionState_Unlocked)
3216 return setError(VBOX_E_INVALID_OBJECT_STATE,
3217 tr("The given session is busy"));
3218
3219 // get the client's IInternalSessionControl interface
3220 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3221 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3222 E_INVALIDARG);
3223
3224 // session name (only used in some code paths)
3225 Utf8Str strSessionName;
3226
3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 if (!mData->mRegistered)
3230 return setError(E_UNEXPECTED,
3231 tr("The machine '%s' is not registered"),
3232 mUserData->s.strName.c_str());
3233
3234 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235
3236 SessionState_T oldState = mData->mSession.mState;
3237 /* Hack: in case the session is closing and there is a progress object
3238 * which allows waiting for the session to be closed, take the opportunity
3239 * and do a limited wait (max. 1 second). This helps a lot when the system
3240 * is busy and thus session closing can take a little while. */
3241 if ( mData->mSession.mState == SessionState_Unlocking
3242 && mData->mSession.mProgress)
3243 {
3244 alock.release();
3245 mData->mSession.mProgress->WaitForCompletion(1000);
3246 alock.acquire();
3247 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3248 }
3249
3250 // try again now
3251 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3252 // (i.e. session machine exists)
3253 && (aLockType == LockType_Shared) // caller wants a shared link to the
3254 // existing session that holds the write lock:
3255 )
3256 {
3257 // OK, share the session... we are now dealing with three processes:
3258 // 1) VBoxSVC (where this code runs);
3259 // 2) process C: the caller's client process (who wants a shared session);
3260 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3261
3262 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3263 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3264 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3265 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3266 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3267
3268 /*
3269 * Release the lock before calling the client process. It's safe here
3270 * since the only thing to do after we get the lock again is to add
3271 * the remote control to the list (which doesn't directly influence
3272 * anything).
3273 */
3274 alock.release();
3275
3276 // get the console of the session holding the write lock (this is a remote call)
3277 ComPtr<IConsole> pConsoleW;
3278 if (mData->mSession.mLockType == LockType_VM)
3279 {
3280 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3281 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3282 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3283 if (FAILED(rc))
3284 // the failure may occur w/o any error info (from RPC), so provide one
3285 return setError(VBOX_E_VM_ERROR,
3286 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3287 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3288 }
3289
3290 // share the session machine and W's console with the caller's session
3291 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3292 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3293 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3294
3295 if (FAILED(rc))
3296 // the failure may occur w/o any error info (from RPC), so provide one
3297 return setError(VBOX_E_VM_ERROR,
3298 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3299 alock.acquire();
3300
3301 // need to revalidate the state after acquiring the lock again
3302 if (mData->mSession.mState != SessionState_Locked)
3303 {
3304 pSessionControl->Uninitialize();
3305 return setError(VBOX_E_INVALID_SESSION_STATE,
3306 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3307 mUserData->s.strName.c_str());
3308 }
3309
3310 // add the caller's session to the list
3311 mData->mSession.mRemoteControls.push_back(pSessionControl);
3312 }
3313 else if ( mData->mSession.mState == SessionState_Locked
3314 || mData->mSession.mState == SessionState_Unlocking
3315 )
3316 {
3317 // sharing not permitted, or machine still unlocking:
3318 return setError(VBOX_E_INVALID_OBJECT_STATE,
3319 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3320 mUserData->s.strName.c_str());
3321 }
3322 else
3323 {
3324 // machine is not locked: then write-lock the machine (create the session machine)
3325
3326 // must not be busy
3327 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3328
3329 // get the caller's session PID
3330 RTPROCESS pid = NIL_RTPROCESS;
3331 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3332 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3333 Assert(pid != NIL_RTPROCESS);
3334
3335 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3336
3337 if (fLaunchingVMProcess)
3338 {
3339 if (mData->mSession.mPID == NIL_RTPROCESS)
3340 {
3341 // two or more clients racing for a lock, the one which set the
3342 // session state to Spawning will win, the others will get an
3343 // error as we can't decide here if waiting a little would help
3344 // (only for shared locks this would avoid an error)
3345 return setError(VBOX_E_INVALID_OBJECT_STATE,
3346 tr("The machine '%s' already has a lock request pending"),
3347 mUserData->s.strName.c_str());
3348 }
3349
3350 // this machine is awaiting for a spawning session to be opened:
3351 // then the calling process must be the one that got started by
3352 // LaunchVMProcess()
3353
3354 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3355 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3356
3357#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3358 /* Hardened windows builds spawns three processes when a VM is
3359 launched, the 3rd one is the one that will end up here. */
3360 RTPROCESS ppid;
3361 int rc = RTProcQueryParent(pid, &ppid);
3362 if (RT_SUCCESS(rc))
3363 rc = RTProcQueryParent(ppid, &ppid);
3364 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3365 || rc == VERR_ACCESS_DENIED)
3366 {
3367 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3368 mData->mSession.mPID = pid;
3369 }
3370#endif
3371
3372 if (mData->mSession.mPID != pid)
3373 return setError(E_ACCESSDENIED,
3374 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3375 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3376 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3377 }
3378
3379 // create the mutable SessionMachine from the current machine
3380 ComObjPtr<SessionMachine> sessionMachine;
3381 sessionMachine.createObject();
3382 rc = sessionMachine->init(this);
3383 AssertComRC(rc);
3384
3385 /* NOTE: doing return from this function after this point but
3386 * before the end is forbidden since it may call SessionMachine::uninit()
3387 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3388 * lock while still holding the Machine lock in alock so that a deadlock
3389 * is possible due to the wrong lock order. */
3390
3391 if (SUCCEEDED(rc))
3392 {
3393 /*
3394 * Set the session state to Spawning to protect against subsequent
3395 * attempts to open a session and to unregister the machine after
3396 * we release the lock.
3397 */
3398 SessionState_T origState = mData->mSession.mState;
3399 mData->mSession.mState = SessionState_Spawning;
3400
3401#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3402 /* Get the client token ID to be passed to the client process */
3403 Utf8Str strTokenId;
3404 sessionMachine->i_getTokenId(strTokenId);
3405 Assert(!strTokenId.isEmpty());
3406#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3407 /* Get the client token to be passed to the client process */
3408 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3409 /* The token is now "owned" by pToken, fix refcount */
3410 if (!pToken.isNull())
3411 pToken->Release();
3412#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3413
3414 /*
3415 * Release the lock before calling the client process -- it will call
3416 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3417 * because the state is Spawning, so that LaunchVMProcess() and
3418 * LockMachine() calls will fail. This method, called before we
3419 * acquire the lock again, will fail because of the wrong PID.
3420 *
3421 * Note that mData->mSession.mRemoteControls accessed outside
3422 * the lock may not be modified when state is Spawning, so it's safe.
3423 */
3424 alock.release();
3425
3426 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3427#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3428 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3429#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3430 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3431 /* Now the token is owned by the client process. */
3432 pToken.setNull();
3433#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3434 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3435
3436 /* The failure may occur w/o any error info (from RPC), so provide one */
3437 if (FAILED(rc))
3438 setError(VBOX_E_VM_ERROR,
3439 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3440
3441 // get session name, either to remember or to compare against
3442 // the already known session name.
3443 {
3444 Bstr bstrSessionName;
3445 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3446 if (SUCCEEDED(rc2))
3447 strSessionName = bstrSessionName;
3448 }
3449
3450 if ( SUCCEEDED(rc)
3451 && fLaunchingVMProcess
3452 )
3453 {
3454 /* complete the remote session initialization */
3455
3456 /* get the console from the direct session */
3457 ComPtr<IConsole> console;
3458 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3459 ComAssertComRC(rc);
3460
3461 if (SUCCEEDED(rc) && !console)
3462 {
3463 ComAssert(!!console);
3464 rc = E_FAIL;
3465 }
3466
3467 /* assign machine & console to the remote session */
3468 if (SUCCEEDED(rc))
3469 {
3470 /*
3471 * after LaunchVMProcess(), the first and the only
3472 * entry in remoteControls is that remote session
3473 */
3474 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3475 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3476 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3477
3478 /* The failure may occur w/o any error info (from RPC), so provide one */
3479 if (FAILED(rc))
3480 setError(VBOX_E_VM_ERROR,
3481 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3482 }
3483
3484 if (FAILED(rc))
3485 pSessionControl->Uninitialize();
3486 }
3487
3488 /* acquire the lock again */
3489 alock.acquire();
3490
3491 /* Restore the session state */
3492 mData->mSession.mState = origState;
3493 }
3494
3495 // finalize spawning anyway (this is why we don't return on errors above)
3496 if (fLaunchingVMProcess)
3497 {
3498 Assert(mData->mSession.mName == strSessionName);
3499 /* Note that the progress object is finalized later */
3500 /** @todo Consider checking mData->mSession.mProgress for cancellation
3501 * around here. */
3502
3503 /* We don't reset mSession.mPID here because it is necessary for
3504 * SessionMachine::uninit() to reap the child process later. */
3505
3506 if (FAILED(rc))
3507 {
3508 /* Close the remote session, remove the remote control from the list
3509 * and reset session state to Closed (@note keep the code in sync
3510 * with the relevant part in checkForSpawnFailure()). */
3511
3512 Assert(mData->mSession.mRemoteControls.size() == 1);
3513 if (mData->mSession.mRemoteControls.size() == 1)
3514 {
3515 ErrorInfoKeeper eik;
3516 mData->mSession.mRemoteControls.front()->Uninitialize();
3517 }
3518
3519 mData->mSession.mRemoteControls.clear();
3520 mData->mSession.mState = SessionState_Unlocked;
3521 }
3522 }
3523 else
3524 {
3525 /* memorize PID of the directly opened session */
3526 if (SUCCEEDED(rc))
3527 mData->mSession.mPID = pid;
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 mData->mSession.mLockType = aLockType;
3533 /* memorize the direct session control and cache IUnknown for it */
3534 mData->mSession.mDirectControl = pSessionControl;
3535 mData->mSession.mState = SessionState_Locked;
3536 if (!fLaunchingVMProcess)
3537 mData->mSession.mName = strSessionName;
3538 /* associate the SessionMachine with this Machine */
3539 mData->mSession.mMachine = sessionMachine;
3540
3541 /* request an IUnknown pointer early from the remote party for later
3542 * identity checks (it will be internally cached within mDirectControl
3543 * at least on XPCOM) */
3544 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3545 NOREF(unk);
3546 }
3547
3548 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3549 * would break the lock order */
3550 alock.release();
3551
3552 /* uninitialize the created session machine on failure */
3553 if (FAILED(rc))
3554 sessionMachine->uninit();
3555 }
3556
3557 if (SUCCEEDED(rc))
3558 {
3559 /*
3560 * tell the client watcher thread to update the set of
3561 * machines that have open sessions
3562 */
3563 mParent->i_updateClientWatcher();
3564
3565 if (oldState != SessionState_Locked)
3566 /* fire an event */
3567 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3568 }
3569
3570 return rc;
3571}
3572
3573/**
3574 * @note Locks objects!
3575 */
3576HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3577 const com::Utf8Str &aName,
3578 const com::Utf8Str &aEnvironment,
3579 ComPtr<IProgress> &aProgress)
3580{
3581 Utf8Str strFrontend(aName);
3582 /* "emergencystop" doesn't need the session, so skip the checks/interface
3583 * retrieval. This code doesn't quite fit in here, but introducing a
3584 * special API method would be even more effort, and would require explicit
3585 * support by every API client. It's better to hide the feature a bit. */
3586 if (strFrontend != "emergencystop")
3587 CheckComArgNotNull(aSession);
3588
3589 HRESULT rc = S_OK;
3590 if (strFrontend.isEmpty())
3591 {
3592 Bstr bstrFrontend;
3593 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3594 if (FAILED(rc))
3595 return rc;
3596 strFrontend = bstrFrontend;
3597 if (strFrontend.isEmpty())
3598 {
3599 ComPtr<ISystemProperties> systemProperties;
3600 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3601 if (FAILED(rc))
3602 return rc;
3603 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3604 if (FAILED(rc))
3605 return rc;
3606 strFrontend = bstrFrontend;
3607 }
3608 /* paranoia - emergencystop is not a valid default */
3609 if (strFrontend == "emergencystop")
3610 strFrontend = Utf8Str::Empty;
3611 }
3612 /* default frontend: Qt GUI */
3613 if (strFrontend.isEmpty())
3614 strFrontend = "GUI/Qt";
3615
3616 if (strFrontend != "emergencystop")
3617 {
3618 /* check the session state */
3619 SessionState_T state;
3620 rc = aSession->COMGETTER(State)(&state);
3621 if (FAILED(rc))
3622 return rc;
3623
3624 if (state != SessionState_Unlocked)
3625 return setError(VBOX_E_INVALID_OBJECT_STATE,
3626 tr("The given session is busy"));
3627
3628 /* get the IInternalSessionControl interface */
3629 ComPtr<IInternalSessionControl> control(aSession);
3630 ComAssertMsgRet(!control.isNull(),
3631 ("No IInternalSessionControl interface"),
3632 E_INVALIDARG);
3633
3634 /* get the teleporter enable state for the progress object init. */
3635 BOOL fTeleporterEnabled;
3636 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3637 if (FAILED(rc))
3638 return rc;
3639
3640 /* create a progress object */
3641 ComObjPtr<ProgressProxy> progress;
3642 progress.createObject();
3643 rc = progress->init(mParent,
3644 static_cast<IMachine*>(this),
3645 Bstr(tr("Starting VM")).raw(),
3646 TRUE /* aCancelable */,
3647 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3648 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3649 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3650 2 /* uFirstOperationWeight */,
3651 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3652
3653 if (SUCCEEDED(rc))
3654 {
3655 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3656 if (SUCCEEDED(rc))
3657 {
3658 aProgress = progress;
3659
3660 /* signal the client watcher thread */
3661 mParent->i_updateClientWatcher();
3662
3663 /* fire an event */
3664 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3665 }
3666 }
3667 }
3668 else
3669 {
3670 /* no progress object - either instant success or failure */
3671 aProgress = NULL;
3672
3673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3674
3675 if (mData->mSession.mState != SessionState_Locked)
3676 return setError(VBOX_E_INVALID_OBJECT_STATE,
3677 tr("The machine '%s' is not locked by a session"),
3678 mUserData->s.strName.c_str());
3679
3680 /* must have a VM process associated - do not kill normal API clients
3681 * with an open session */
3682 if (!Global::IsOnline(mData->mMachineState))
3683 return setError(VBOX_E_INVALID_OBJECT_STATE,
3684 tr("The machine '%s' does not have a VM process"),
3685 mUserData->s.strName.c_str());
3686
3687 /* forcibly terminate the VM process */
3688 if (mData->mSession.mPID != NIL_RTPROCESS)
3689 RTProcTerminate(mData->mSession.mPID);
3690
3691 /* signal the client watcher thread, as most likely the client has
3692 * been terminated */
3693 mParent->i_updateClientWatcher();
3694 }
3695
3696 return rc;
3697}
3698
3699HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3700{
3701 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3702 return setError(E_INVALIDARG,
3703 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3704 aPosition, SchemaDefs::MaxBootPosition);
3705
3706 if (aDevice == DeviceType_USB)
3707 return setError(E_NOTIMPL,
3708 tr("Booting from USB device is currently not supported"));
3709
3710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3711
3712 HRESULT rc = i_checkStateDependency(MutableStateDep);
3713 if (FAILED(rc)) return rc;
3714
3715 i_setModified(IsModified_MachineData);
3716 mHWData.backup();
3717 mHWData->mBootOrder[aPosition - 1] = aDevice;
3718
3719 return S_OK;
3720}
3721
3722HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3723{
3724 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3725 return setError(E_INVALIDARG,
3726 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3727 aPosition, SchemaDefs::MaxBootPosition);
3728
3729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3730
3731 *aDevice = mHWData->mBootOrder[aPosition - 1];
3732
3733 return S_OK;
3734}
3735
3736HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3737 LONG aControllerPort,
3738 LONG aDevice,
3739 DeviceType_T aType,
3740 const ComPtr<IMedium> &aMedium)
3741{
3742 IMedium *aM = aMedium;
3743 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3744 aName.c_str(), aControllerPort, aDevice, aType, aM));
3745
3746 // request the host lock first, since might be calling Host methods for getting host drives;
3747 // next, protect the media tree all the while we're in here, as well as our member variables
3748 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3749 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3750
3751 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3752 if (FAILED(rc)) return rc;
3753
3754 /// @todo NEWMEDIA implicit machine registration
3755 if (!mData->mRegistered)
3756 return setError(VBOX_E_INVALID_OBJECT_STATE,
3757 tr("Cannot attach storage devices to an unregistered machine"));
3758
3759 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3760
3761 /* Check for an existing controller. */
3762 ComObjPtr<StorageController> ctl;
3763 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3764 if (FAILED(rc)) return rc;
3765
3766 StorageControllerType_T ctrlType;
3767 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3768 if (FAILED(rc))
3769 return setError(E_FAIL,
3770 tr("Could not get type of controller '%s'"),
3771 aName.c_str());
3772
3773 bool fSilent = false;
3774 Utf8Str strReconfig;
3775
3776 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3777 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3778 if ( mData->mMachineState == MachineState_Paused
3779 && strReconfig == "1")
3780 fSilent = true;
3781
3782 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3783 bool fHotplug = false;
3784 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3785 fHotplug = true;
3786
3787 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3788 return setError(VBOX_E_INVALID_VM_STATE,
3789 tr("Controller '%s' does not support hotplugging"),
3790 aName.c_str());
3791
3792 // check that the port and device are not out of range
3793 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3794 if (FAILED(rc)) return rc;
3795
3796 /* check if the device slot is already busy */
3797 MediumAttachment *pAttachTemp;
3798 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3799 Bstr(aName).raw(),
3800 aControllerPort,
3801 aDevice)))
3802 {
3803 Medium *pMedium = pAttachTemp->i_getMedium();
3804 if (pMedium)
3805 {
3806 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3807 return setError(VBOX_E_OBJECT_IN_USE,
3808 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3809 pMedium->i_getLocationFull().c_str(),
3810 aControllerPort,
3811 aDevice,
3812 aName.c_str());
3813 }
3814 else
3815 return setError(VBOX_E_OBJECT_IN_USE,
3816 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3817 aControllerPort, aDevice, aName.c_str());
3818 }
3819
3820 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3821 if (aMedium && medium.isNull())
3822 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3823
3824 AutoCaller mediumCaller(medium);
3825 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3826
3827 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3828
3829 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3830 && !medium.isNull()
3831 )
3832 return setError(VBOX_E_OBJECT_IN_USE,
3833 tr("Medium '%s' is already attached to this virtual machine"),
3834 medium->i_getLocationFull().c_str());
3835
3836 if (!medium.isNull())
3837 {
3838 MediumType_T mtype = medium->i_getType();
3839 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3840 // For DVDs it's not written to the config file, so needs no global config
3841 // version bump. For floppies it's a new attribute "type", which is ignored
3842 // by older VirtualBox version, so needs no global config version bump either.
3843 // For hard disks this type is not accepted.
3844 if (mtype == MediumType_MultiAttach)
3845 {
3846 // This type is new with VirtualBox 4.0 and therefore requires settings
3847 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3848 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3849 // two reasons: The medium type is a property of the media registry tree, which
3850 // can reside in the global config file (for pre-4.0 media); we would therefore
3851 // possibly need to bump the global config version. We don't want to do that though
3852 // because that might make downgrading to pre-4.0 impossible.
3853 // As a result, we can only use these two new types if the medium is NOT in the
3854 // global registry:
3855 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3856 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3857 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3858 )
3859 return setError(VBOX_E_INVALID_OBJECT_STATE,
3860 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3861 "to machines that were created with VirtualBox 4.0 or later"),
3862 medium->i_getLocationFull().c_str());
3863 }
3864 }
3865
3866 bool fIndirect = false;
3867 if (!medium.isNull())
3868 fIndirect = medium->i_isReadOnly();
3869 bool associate = true;
3870
3871 do
3872 {
3873 if ( aType == DeviceType_HardDisk
3874 && mMediaData.isBackedUp())
3875 {
3876 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3877
3878 /* check if the medium was attached to the VM before we started
3879 * changing attachments in which case the attachment just needs to
3880 * be restored */
3881 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3882 {
3883 AssertReturn(!fIndirect, E_FAIL);
3884
3885 /* see if it's the same bus/channel/device */
3886 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3887 {
3888 /* the simplest case: restore the whole attachment
3889 * and return, nothing else to do */
3890 mMediaData->mAttachments.push_back(pAttachTemp);
3891
3892 /* Reattach the medium to the VM. */
3893 if (fHotplug || fSilent)
3894 {
3895 mediumLock.release();
3896 treeLock.release();
3897 alock.release();
3898
3899 MediumLockList *pMediumLockList(new MediumLockList());
3900
3901 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3902 true /* fMediumLockWrite */,
3903 false /* fMediumLockWriteAll */,
3904 NULL,
3905 *pMediumLockList);
3906 alock.acquire();
3907 if (FAILED(rc))
3908 delete pMediumLockList;
3909 else
3910 {
3911 mData->mSession.mLockedMedia.Unlock();
3912 alock.release();
3913 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3914 mData->mSession.mLockedMedia.Lock();
3915 alock.acquire();
3916 }
3917 alock.release();
3918
3919 if (SUCCEEDED(rc))
3920 {
3921 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3922 /* Remove lock list in case of error. */
3923 if (FAILED(rc))
3924 {
3925 mData->mSession.mLockedMedia.Unlock();
3926 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3927 mData->mSession.mLockedMedia.Lock();
3928 }
3929 }
3930 }
3931
3932 return S_OK;
3933 }
3934
3935 /* bus/channel/device differ; we need a new attachment object,
3936 * but don't try to associate it again */
3937 associate = false;
3938 break;
3939 }
3940 }
3941
3942 /* go further only if the attachment is to be indirect */
3943 if (!fIndirect)
3944 break;
3945
3946 /* perform the so called smart attachment logic for indirect
3947 * attachments. Note that smart attachment is only applicable to base
3948 * hard disks. */
3949
3950 if (medium->i_getParent().isNull())
3951 {
3952 /* first, investigate the backup copy of the current hard disk
3953 * attachments to make it possible to re-attach existing diffs to
3954 * another device slot w/o losing their contents */
3955 if (mMediaData.isBackedUp())
3956 {
3957 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3958
3959 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3960 uint32_t foundLevel = 0;
3961
3962 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3963 {
3964 uint32_t level = 0;
3965 MediumAttachment *pAttach = *it;
3966 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3967 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3968 if (pMedium.isNull())
3969 continue;
3970
3971 if (pMedium->i_getBase(&level) == medium)
3972 {
3973 /* skip the hard disk if its currently attached (we
3974 * cannot attach the same hard disk twice) */
3975 if (i_findAttachment(mMediaData->mAttachments,
3976 pMedium))
3977 continue;
3978
3979 /* matched device, channel and bus (i.e. attached to the
3980 * same place) will win and immediately stop the search;
3981 * otherwise the attachment that has the youngest
3982 * descendant of medium will be used
3983 */
3984 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3985 {
3986 /* the simplest case: restore the whole attachment
3987 * and return, nothing else to do */
3988 mMediaData->mAttachments.push_back(*it);
3989
3990 /* Reattach the medium to the VM. */
3991 if (fHotplug || fSilent)
3992 {
3993 mediumLock.release();
3994 treeLock.release();
3995 alock.release();
3996
3997 MediumLockList *pMediumLockList(new MediumLockList());
3998
3999 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4000 true /* fMediumLockWrite */,
4001 false /* fMediumLockWriteAll */,
4002 NULL,
4003 *pMediumLockList);
4004 alock.acquire();
4005 if (FAILED(rc))
4006 delete pMediumLockList;
4007 else
4008 {
4009 mData->mSession.mLockedMedia.Unlock();
4010 alock.release();
4011 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4012 mData->mSession.mLockedMedia.Lock();
4013 alock.acquire();
4014 }
4015 alock.release();
4016
4017 if (SUCCEEDED(rc))
4018 {
4019 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4020 /* Remove lock list in case of error. */
4021 if (FAILED(rc))
4022 {
4023 mData->mSession.mLockedMedia.Unlock();
4024 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4025 mData->mSession.mLockedMedia.Lock();
4026 }
4027 }
4028 }
4029
4030 return S_OK;
4031 }
4032 else if ( foundIt == oldAtts.end()
4033 || level > foundLevel /* prefer younger */
4034 )
4035 {
4036 foundIt = it;
4037 foundLevel = level;
4038 }
4039 }
4040 }
4041
4042 if (foundIt != oldAtts.end())
4043 {
4044 /* use the previously attached hard disk */
4045 medium = (*foundIt)->i_getMedium();
4046 mediumCaller.attach(medium);
4047 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4048 mediumLock.attach(medium);
4049 /* not implicit, doesn't require association with this VM */
4050 fIndirect = false;
4051 associate = false;
4052 /* go right to the MediumAttachment creation */
4053 break;
4054 }
4055 }
4056
4057 /* must give up the medium lock and medium tree lock as below we
4058 * go over snapshots, which needs a lock with higher lock order. */
4059 mediumLock.release();
4060 treeLock.release();
4061
4062 /* then, search through snapshots for the best diff in the given
4063 * hard disk's chain to base the new diff on */
4064
4065 ComObjPtr<Medium> base;
4066 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4067 while (snap)
4068 {
4069 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4070
4071 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4072
4073 MediumAttachment *pAttachFound = NULL;
4074 uint32_t foundLevel = 0;
4075
4076 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4077 {
4078 MediumAttachment *pAttach = *it;
4079 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4080 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4081 if (pMedium.isNull())
4082 continue;
4083
4084 uint32_t level = 0;
4085 if (pMedium->i_getBase(&level) == medium)
4086 {
4087 /* matched device, channel and bus (i.e. attached to the
4088 * same place) will win and immediately stop the search;
4089 * otherwise the attachment that has the youngest
4090 * descendant of medium will be used
4091 */
4092 if ( pAttach->i_getDevice() == aDevice
4093 && pAttach->i_getPort() == aControllerPort
4094 && pAttach->i_getControllerName() == aName
4095 )
4096 {
4097 pAttachFound = pAttach;
4098 break;
4099 }
4100 else if ( !pAttachFound
4101 || level > foundLevel /* prefer younger */
4102 )
4103 {
4104 pAttachFound = pAttach;
4105 foundLevel = level;
4106 }
4107 }
4108 }
4109
4110 if (pAttachFound)
4111 {
4112 base = pAttachFound->i_getMedium();
4113 break;
4114 }
4115
4116 snap = snap->i_getParent();
4117 }
4118
4119 /* re-lock medium tree and the medium, as we need it below */
4120 treeLock.acquire();
4121 mediumLock.acquire();
4122
4123 /* found a suitable diff, use it as a base */
4124 if (!base.isNull())
4125 {
4126 medium = base;
4127 mediumCaller.attach(medium);
4128 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4129 mediumLock.attach(medium);
4130 }
4131 }
4132
4133 Utf8Str strFullSnapshotFolder;
4134 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4135
4136 ComObjPtr<Medium> diff;
4137 diff.createObject();
4138 // store this diff in the same registry as the parent
4139 Guid uuidRegistryParent;
4140 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4141 {
4142 // parent image has no registry: this can happen if we're attaching a new immutable
4143 // image that has not yet been attached (medium then points to the base and we're
4144 // creating the diff image for the immutable, and the parent is not yet registered);
4145 // put the parent in the machine registry then
4146 mediumLock.release();
4147 treeLock.release();
4148 alock.release();
4149 i_addMediumToRegistry(medium);
4150 alock.acquire();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4154 }
4155 rc = diff->init(mParent,
4156 medium->i_getPreferredDiffFormat(),
4157 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4158 uuidRegistryParent,
4159 DeviceType_HardDisk);
4160 if (FAILED(rc)) return rc;
4161
4162 /* Apply the normal locking logic to the entire chain. */
4163 MediumLockList *pMediumLockList(new MediumLockList());
4164 mediumLock.release();
4165 treeLock.release();
4166 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4167 true /* fMediumLockWrite */,
4168 false /* fMediumLockWriteAll */,
4169 medium,
4170 *pMediumLockList);
4171 treeLock.acquire();
4172 mediumLock.acquire();
4173 if (SUCCEEDED(rc))
4174 {
4175 mediumLock.release();
4176 treeLock.release();
4177 rc = pMediumLockList->Lock();
4178 treeLock.acquire();
4179 mediumLock.acquire();
4180 if (FAILED(rc))
4181 setError(rc,
4182 tr("Could not lock medium when creating diff '%s'"),
4183 diff->i_getLocationFull().c_str());
4184 else
4185 {
4186 /* will release the lock before the potentially lengthy
4187 * operation, so protect with the special state */
4188 MachineState_T oldState = mData->mMachineState;
4189 i_setMachineState(MachineState_SettingUp);
4190
4191 mediumLock.release();
4192 treeLock.release();
4193 alock.release();
4194
4195 rc = medium->i_createDiffStorage(diff,
4196 MediumVariant_Standard,
4197 pMediumLockList,
4198 NULL /* aProgress */,
4199 true /* aWait */);
4200
4201 alock.acquire();
4202 treeLock.acquire();
4203 mediumLock.acquire();
4204
4205 i_setMachineState(oldState);
4206 }
4207 }
4208
4209 /* Unlock the media and free the associated memory. */
4210 delete pMediumLockList;
4211
4212 if (FAILED(rc)) return rc;
4213
4214 /* use the created diff for the actual attachment */
4215 medium = diff;
4216 mediumCaller.attach(medium);
4217 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4218 mediumLock.attach(medium);
4219 }
4220 while (0);
4221
4222 ComObjPtr<MediumAttachment> attachment;
4223 attachment.createObject();
4224 rc = attachment->init(this,
4225 medium,
4226 aName,
4227 aControllerPort,
4228 aDevice,
4229 aType,
4230 fIndirect,
4231 false /* fPassthrough */,
4232 false /* fTempEject */,
4233 false /* fNonRotational */,
4234 false /* fDiscard */,
4235 fHotplug /* fHotPluggable */,
4236 Utf8Str::Empty);
4237 if (FAILED(rc)) return rc;
4238
4239 if (associate && !medium.isNull())
4240 {
4241 // as the last step, associate the medium to the VM
4242 rc = medium->i_addBackReference(mData->mUuid);
4243 // here we can fail because of Deleting, or being in process of creating a Diff
4244 if (FAILED(rc)) return rc;
4245
4246 mediumLock.release();
4247 treeLock.release();
4248 alock.release();
4249 i_addMediumToRegistry(medium);
4250 alock.acquire();
4251 treeLock.acquire();
4252 mediumLock.acquire();
4253 }
4254
4255 /* success: finally remember the attachment */
4256 i_setModified(IsModified_Storage);
4257 mMediaData.backup();
4258 mMediaData->mAttachments.push_back(attachment);
4259
4260 mediumLock.release();
4261 treeLock.release();
4262 alock.release();
4263
4264 if (fHotplug || fSilent)
4265 {
4266 if (!medium.isNull())
4267 {
4268 MediumLockList *pMediumLockList(new MediumLockList());
4269
4270 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4271 true /* fMediumLockWrite */,
4272 false /* fMediumLockWriteAll */,
4273 NULL,
4274 *pMediumLockList);
4275 alock.acquire();
4276 if (FAILED(rc))
4277 delete pMediumLockList;
4278 else
4279 {
4280 mData->mSession.mLockedMedia.Unlock();
4281 alock.release();
4282 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4283 mData->mSession.mLockedMedia.Lock();
4284 alock.acquire();
4285 }
4286 alock.release();
4287 }
4288
4289 if (SUCCEEDED(rc))
4290 {
4291 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4292 /* Remove lock list in case of error. */
4293 if (FAILED(rc))
4294 {
4295 mData->mSession.mLockedMedia.Unlock();
4296 mData->mSession.mLockedMedia.Remove(attachment);
4297 mData->mSession.mLockedMedia.Lock();
4298 }
4299 }
4300 }
4301
4302 /* Save modified registries, but skip this machine as it's the caller's
4303 * job to save its settings like all other settings changes. */
4304 mParent->i_unmarkRegistryModified(i_getId());
4305 mParent->i_saveModifiedRegistries();
4306
4307 return rc;
4308}
4309
4310HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4311 LONG aDevice)
4312{
4313 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4314 aName.c_str(), aControllerPort, aDevice));
4315
4316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4317
4318 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4319 if (FAILED(rc)) return rc;
4320
4321 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4322
4323 /* Check for an existing controller. */
4324 ComObjPtr<StorageController> ctl;
4325 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4326 if (FAILED(rc)) return rc;
4327
4328 StorageControllerType_T ctrlType;
4329 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4330 if (FAILED(rc))
4331 return setError(E_FAIL,
4332 tr("Could not get type of controller '%s'"),
4333 aName.c_str());
4334
4335 bool fSilent = false;
4336 Utf8Str strReconfig;
4337
4338 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4339 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4340 if ( mData->mMachineState == MachineState_Paused
4341 && strReconfig == "1")
4342 fSilent = true;
4343
4344 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4345 bool fHotplug = false;
4346 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4347 fHotplug = true;
4348
4349 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4350 return setError(VBOX_E_INVALID_VM_STATE,
4351 tr("Controller '%s' does not support hotplugging"),
4352 aName.c_str());
4353
4354 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4355 Bstr(aName).raw(),
4356 aControllerPort,
4357 aDevice);
4358 if (!pAttach)
4359 return setError(VBOX_E_OBJECT_NOT_FOUND,
4360 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4361 aDevice, aControllerPort, aName.c_str());
4362
4363 if (fHotplug && !pAttach->i_getHotPluggable())
4364 return setError(VBOX_E_NOT_SUPPORTED,
4365 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4366 aDevice, aControllerPort, aName.c_str());
4367
4368 /*
4369 * The VM has to detach the device before we delete any implicit diffs.
4370 * If this fails we can roll back without loosing data.
4371 */
4372 if (fHotplug || fSilent)
4373 {
4374 alock.release();
4375 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4376 alock.acquire();
4377 }
4378 if (FAILED(rc)) return rc;
4379
4380 /* If we are here everything went well and we can delete the implicit now. */
4381 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4382
4383 alock.release();
4384
4385 /* Save modified registries, but skip this machine as it's the caller's
4386 * job to save its settings like all other settings changes. */
4387 mParent->i_unmarkRegistryModified(i_getId());
4388 mParent->i_saveModifiedRegistries();
4389
4390 return rc;
4391}
4392
4393HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4394 LONG aDevice, BOOL aPassthrough)
4395{
4396 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4397 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4398
4399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4400
4401 HRESULT rc = i_checkStateDependency(MutableStateDep);
4402 if (FAILED(rc)) return rc;
4403
4404 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4405
4406 if (Global::IsOnlineOrTransient(mData->mMachineState))
4407 return setError(VBOX_E_INVALID_VM_STATE,
4408 tr("Invalid machine state: %s"),
4409 Global::stringifyMachineState(mData->mMachineState));
4410
4411 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4412 Bstr(aName).raw(),
4413 aControllerPort,
4414 aDevice);
4415 if (!pAttach)
4416 return setError(VBOX_E_OBJECT_NOT_FOUND,
4417 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4418 aDevice, aControllerPort, aName.c_str());
4419
4420
4421 i_setModified(IsModified_Storage);
4422 mMediaData.backup();
4423
4424 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4425
4426 if (pAttach->i_getType() != DeviceType_DVD)
4427 return setError(E_INVALIDARG,
4428 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4429 aDevice, aControllerPort, aName.c_str());
4430 pAttach->i_updatePassthrough(!!aPassthrough);
4431
4432 return S_OK;
4433}
4434
4435HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4436 LONG aDevice, BOOL aTemporaryEject)
4437{
4438
4439 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4440 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4441
4442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4443
4444 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4445 if (FAILED(rc)) return rc;
4446
4447 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4448 Bstr(aName).raw(),
4449 aControllerPort,
4450 aDevice);
4451 if (!pAttach)
4452 return setError(VBOX_E_OBJECT_NOT_FOUND,
4453 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4454 aDevice, aControllerPort, aName.c_str());
4455
4456
4457 i_setModified(IsModified_Storage);
4458 mMediaData.backup();
4459
4460 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4461
4462 if (pAttach->i_getType() != DeviceType_DVD)
4463 return setError(E_INVALIDARG,
4464 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4465 aDevice, aControllerPort, aName.c_str());
4466 pAttach->i_updateTempEject(!!aTemporaryEject);
4467
4468 return S_OK;
4469}
4470
4471HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4472 LONG aDevice, BOOL aNonRotational)
4473{
4474
4475 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4476 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4477
4478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4479
4480 HRESULT rc = i_checkStateDependency(MutableStateDep);
4481 if (FAILED(rc)) return rc;
4482
4483 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4484
4485 if (Global::IsOnlineOrTransient(mData->mMachineState))
4486 return setError(VBOX_E_INVALID_VM_STATE,
4487 tr("Invalid machine state: %s"),
4488 Global::stringifyMachineState(mData->mMachineState));
4489
4490 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4491 Bstr(aName).raw(),
4492 aControllerPort,
4493 aDevice);
4494 if (!pAttach)
4495 return setError(VBOX_E_OBJECT_NOT_FOUND,
4496 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4497 aDevice, aControllerPort, aName.c_str());
4498
4499
4500 i_setModified(IsModified_Storage);
4501 mMediaData.backup();
4502
4503 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4504
4505 if (pAttach->i_getType() != DeviceType_HardDisk)
4506 return setError(E_INVALIDARG,
4507 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"),
4508 aDevice, aControllerPort, aName.c_str());
4509 pAttach->i_updateNonRotational(!!aNonRotational);
4510
4511 return S_OK;
4512}
4513
4514HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4515 LONG aDevice, BOOL aDiscard)
4516{
4517
4518 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4519 aName.c_str(), aControllerPort, aDevice, aDiscard));
4520
4521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4522
4523 HRESULT rc = i_checkStateDependency(MutableStateDep);
4524 if (FAILED(rc)) return rc;
4525
4526 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4527
4528 if (Global::IsOnlineOrTransient(mData->mMachineState))
4529 return setError(VBOX_E_INVALID_VM_STATE,
4530 tr("Invalid machine state: %s"),
4531 Global::stringifyMachineState(mData->mMachineState));
4532
4533 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4534 Bstr(aName).raw(),
4535 aControllerPort,
4536 aDevice);
4537 if (!pAttach)
4538 return setError(VBOX_E_OBJECT_NOT_FOUND,
4539 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4540 aDevice, aControllerPort, aName.c_str());
4541
4542
4543 i_setModified(IsModified_Storage);
4544 mMediaData.backup();
4545
4546 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4547
4548 if (pAttach->i_getType() != DeviceType_HardDisk)
4549 return setError(E_INVALIDARG,
4550 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"),
4551 aDevice, aControllerPort, aName.c_str());
4552 pAttach->i_updateDiscard(!!aDiscard);
4553
4554 return S_OK;
4555}
4556
4557HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4558 LONG aDevice, BOOL aHotPluggable)
4559{
4560 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4561 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4562
4563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4564
4565 HRESULT rc = i_checkStateDependency(MutableStateDep);
4566 if (FAILED(rc)) return rc;
4567
4568 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4569
4570 if (Global::IsOnlineOrTransient(mData->mMachineState))
4571 return setError(VBOX_E_INVALID_VM_STATE,
4572 tr("Invalid machine state: %s"),
4573 Global::stringifyMachineState(mData->mMachineState));
4574
4575 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4576 Bstr(aName).raw(),
4577 aControllerPort,
4578 aDevice);
4579 if (!pAttach)
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584 /* Check for an existing controller. */
4585 ComObjPtr<StorageController> ctl;
4586 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4587 if (FAILED(rc)) return rc;
4588
4589 StorageControllerType_T ctrlType;
4590 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4591 if (FAILED(rc))
4592 return setError(E_FAIL,
4593 tr("Could not get type of controller '%s'"),
4594 aName.c_str());
4595
4596 if (!i_isControllerHotplugCapable(ctrlType))
4597 return setError(VBOX_E_NOT_SUPPORTED,
4598 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4599 aName.c_str());
4600
4601 i_setModified(IsModified_Storage);
4602 mMediaData.backup();
4603
4604 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4605
4606 if (pAttach->i_getType() == DeviceType_Floppy)
4607 return setError(E_INVALIDARG,
4608 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"),
4609 aDevice, aControllerPort, aName.c_str());
4610 pAttach->i_updateHotPluggable(!!aHotPluggable);
4611
4612 return S_OK;
4613}
4614
4615HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4616 LONG aDevice)
4617{
4618 int rc = S_OK;
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4620 aName.c_str(), aControllerPort, aDevice));
4621
4622 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4623
4624 return rc;
4625}
4626
4627HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4628 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4629{
4630 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4631 aName.c_str(), aControllerPort, aDevice));
4632
4633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4634
4635 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4636 if (FAILED(rc)) return rc;
4637
4638 if (Global::IsOnlineOrTransient(mData->mMachineState))
4639 return setError(VBOX_E_INVALID_VM_STATE,
4640 tr("Invalid machine state: %s"),
4641 Global::stringifyMachineState(mData->mMachineState));
4642
4643 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4644 Bstr(aName).raw(),
4645 aControllerPort,
4646 aDevice);
4647 if (!pAttach)
4648 return setError(VBOX_E_OBJECT_NOT_FOUND,
4649 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4650 aDevice, aControllerPort, aName.c_str());
4651
4652
4653 i_setModified(IsModified_Storage);
4654 mMediaData.backup();
4655
4656 IBandwidthGroup *iB = aBandwidthGroup;
4657 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4658 if (aBandwidthGroup && group.isNull())
4659 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4660
4661 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4662
4663 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4664 if (strBandwidthGroupOld.isNotEmpty())
4665 {
4666 /* Get the bandwidth group object and release it - this must not fail. */
4667 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4668 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4669 Assert(SUCCEEDED(rc));
4670
4671 pBandwidthGroupOld->i_release();
4672 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4673 }
4674
4675 if (!group.isNull())
4676 {
4677 group->i_reference();
4678 pAttach->i_updateBandwidthGroup(group->i_getName());
4679 }
4680
4681 return S_OK;
4682}
4683
4684HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4685 LONG aControllerPort,
4686 LONG aDevice,
4687 DeviceType_T aType)
4688{
4689 HRESULT rc = S_OK;
4690
4691 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4692 aName.c_str(), aControllerPort, aDevice, aType));
4693
4694 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4695
4696 return rc;
4697}
4698
4699
4700HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4701 LONG aControllerPort,
4702 LONG aDevice,
4703 BOOL aForce)
4704{
4705 int rc = S_OK;
4706 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4707 aName.c_str(), aControllerPort, aForce));
4708
4709 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4710
4711 return rc;
4712}
4713
4714HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4715 LONG aControllerPort,
4716 LONG aDevice,
4717 const ComPtr<IMedium> &aMedium,
4718 BOOL aForce)
4719{
4720 int rc = S_OK;
4721 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4722 aName.c_str(), aControllerPort, aDevice, aForce));
4723
4724 // request the host lock first, since might be calling Host methods for getting host drives;
4725 // next, protect the media tree all the while we're in here, as well as our member variables
4726 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4727 this->lockHandle(),
4728 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4729
4730 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4731 Bstr(aName).raw(),
4732 aControllerPort,
4733 aDevice);
4734 if (pAttach.isNull())
4735 return setError(VBOX_E_OBJECT_NOT_FOUND,
4736 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4737 aDevice, aControllerPort, aName.c_str());
4738
4739 /* Remember previously mounted medium. The medium before taking the
4740 * backup is not necessarily the same thing. */
4741 ComObjPtr<Medium> oldmedium;
4742 oldmedium = pAttach->i_getMedium();
4743
4744 IMedium *iM = aMedium;
4745 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4746 if (aMedium && pMedium.isNull())
4747 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4748
4749 AutoCaller mediumCaller(pMedium);
4750 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4751
4752 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4753 if (pMedium)
4754 {
4755 DeviceType_T mediumType = pAttach->i_getType();
4756 switch (mediumType)
4757 {
4758 case DeviceType_DVD:
4759 case DeviceType_Floppy:
4760 break;
4761
4762 default:
4763 return setError(VBOX_E_INVALID_OBJECT_STATE,
4764 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4765 aControllerPort,
4766 aDevice,
4767 aName.c_str());
4768 }
4769 }
4770
4771 i_setModified(IsModified_Storage);
4772 mMediaData.backup();
4773
4774 {
4775 // The backup operation makes the pAttach reference point to the
4776 // old settings. Re-get the correct reference.
4777 pAttach = i_findAttachment(mMediaData->mAttachments,
4778 Bstr(aName).raw(),
4779 aControllerPort,
4780 aDevice);
4781 if (!oldmedium.isNull())
4782 oldmedium->i_removeBackReference(mData->mUuid);
4783 if (!pMedium.isNull())
4784 {
4785 pMedium->i_addBackReference(mData->mUuid);
4786
4787 mediumLock.release();
4788 multiLock.release();
4789 i_addMediumToRegistry(pMedium);
4790 multiLock.acquire();
4791 mediumLock.acquire();
4792 }
4793
4794 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4795 pAttach->i_updateMedium(pMedium);
4796 }
4797
4798 i_setModified(IsModified_Storage);
4799
4800 mediumLock.release();
4801 multiLock.release();
4802 rc = i_onMediumChange(pAttach, aForce);
4803 multiLock.acquire();
4804 mediumLock.acquire();
4805
4806 /* On error roll back this change only. */
4807 if (FAILED(rc))
4808 {
4809 if (!pMedium.isNull())
4810 pMedium->i_removeBackReference(mData->mUuid);
4811 pAttach = i_findAttachment(mMediaData->mAttachments,
4812 Bstr(aName).raw(),
4813 aControllerPort,
4814 aDevice);
4815 /* If the attachment is gone in the meantime, bail out. */
4816 if (pAttach.isNull())
4817 return rc;
4818 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4819 if (!oldmedium.isNull())
4820 oldmedium->i_addBackReference(mData->mUuid);
4821 pAttach->i_updateMedium(oldmedium);
4822 }
4823
4824 mediumLock.release();
4825 multiLock.release();
4826
4827 /* Save modified registries, but skip this machine as it's the caller's
4828 * job to save its settings like all other settings changes. */
4829 mParent->i_unmarkRegistryModified(i_getId());
4830 mParent->i_saveModifiedRegistries();
4831
4832 return rc;
4833}
4834HRESULT Machine::getMedium(const com::Utf8Str &aName,
4835 LONG aControllerPort,
4836 LONG aDevice,
4837 ComPtr<IMedium> &aMedium)
4838{
4839 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4840 aName.c_str(), aControllerPort, aDevice));
4841
4842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4843
4844 aMedium = NULL;
4845
4846 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4847 Bstr(aName).raw(),
4848 aControllerPort,
4849 aDevice);
4850 if (pAttach.isNull())
4851 return setError(VBOX_E_OBJECT_NOT_FOUND,
4852 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4853 aDevice, aControllerPort, aName.c_str());
4854
4855 aMedium = pAttach->i_getMedium();
4856
4857 return S_OK;
4858}
4859
4860HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4861{
4862
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4866
4867 return S_OK;
4868}
4869
4870HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4871{
4872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4873
4874 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4875
4876 return S_OK;
4877}
4878
4879HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4880{
4881 /* Do not assert if slot is out of range, just return the advertised
4882 status. testdriver/vbox.py triggers this in logVmInfo. */
4883 if (aSlot >= mNetworkAdapters.size())
4884 return setError(E_INVALIDARG,
4885 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4886 aSlot, mNetworkAdapters.size());
4887
4888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4889
4890 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4891
4892 return S_OK;
4893}
4894
4895HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4896{
4897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4898
4899 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4900 size_t i = 0;
4901 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4902 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4903 ++it, ++i)
4904 aKeys[i] = it->first;
4905
4906 return S_OK;
4907}
4908
4909 /**
4910 * @note Locks this object for reading.
4911 */
4912HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4913 com::Utf8Str &aValue)
4914{
4915 /* start with nothing found */
4916 aValue = "";
4917
4918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4919
4920 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4921 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4922 // found:
4923 aValue = it->second; // source is a Utf8Str
4924
4925 /* return the result to caller (may be empty) */
4926 return S_OK;
4927}
4928
4929 /**
4930 * @note Locks mParent for writing + this object for writing.
4931 */
4932HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4933{
4934 Utf8Str strOldValue; // empty
4935
4936 // locking note: we only hold the read lock briefly to look up the old value,
4937 // then release it and call the onExtraCanChange callbacks. There is a small
4938 // chance of a race insofar as the callback might be called twice if two callers
4939 // change the same key at the same time, but that's a much better solution
4940 // than the deadlock we had here before. The actual changing of the extradata
4941 // is then performed under the write lock and race-free.
4942
4943 // look up the old value first; if nothing has changed then we need not do anything
4944 {
4945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4946
4947 // For snapshots don't even think about allowing changes, extradata
4948 // is global for a machine, so there is nothing snapshot specific.
4949 if (i_isSnapshotMachine())
4950 return setError(VBOX_E_INVALID_VM_STATE,
4951 tr("Cannot set extradata for a snapshot"));
4952
4953 // check if the right IMachine instance is used
4954 if (mData->mRegistered && !i_isSessionMachine())
4955 return setError(VBOX_E_INVALID_VM_STATE,
4956 tr("Cannot set extradata for an immutable machine"));
4957
4958 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4959 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4960 strOldValue = it->second;
4961 }
4962
4963 bool fChanged;
4964 if ((fChanged = (strOldValue != aValue)))
4965 {
4966 // ask for permission from all listeners outside the locks;
4967 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4968 // lock to copy the list of callbacks to invoke
4969 Bstr error;
4970 Bstr bstrValue(aValue);
4971
4972 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4973 {
4974 const char *sep = error.isEmpty() ? "" : ": ";
4975 CBSTR err = error.raw();
4976 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4977 sep, err));
4978 return setError(E_ACCESSDENIED,
4979 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4980 aKey.c_str(),
4981 aValue.c_str(),
4982 sep,
4983 err);
4984 }
4985
4986 // data is changing and change not vetoed: then write it out under the lock
4987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 if (aValue.isEmpty())
4990 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4991 else
4992 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4993 // creates a new key if needed
4994
4995 bool fNeedsGlobalSaveSettings = false;
4996 // This saving of settings is tricky: there is no "old state" for the
4997 // extradata items at all (unlike all other settings), so the old/new
4998 // settings comparison would give a wrong result!
4999 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5000
5001 if (fNeedsGlobalSaveSettings)
5002 {
5003 // save the global settings; for that we should hold only the VirtualBox lock
5004 alock.release();
5005 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5006 mParent->i_saveSettings();
5007 }
5008 }
5009
5010 // fire notification outside the lock
5011 if (fChanged)
5012 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5013
5014 return S_OK;
5015}
5016
5017HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5018{
5019 aProgress = NULL;
5020 NOREF(aSettingsFilePath);
5021 ReturnComNotImplemented();
5022}
5023
5024HRESULT Machine::saveSettings()
5025{
5026 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5027
5028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5029 if (FAILED(rc)) return rc;
5030
5031 /* the settings file path may never be null */
5032 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5033
5034 /* save all VM data excluding snapshots */
5035 bool fNeedsGlobalSaveSettings = false;
5036 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5037 mlock.release();
5038
5039 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5040 {
5041 // save the global settings; for that we should hold only the VirtualBox lock
5042 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5043 rc = mParent->i_saveSettings();
5044 }
5045
5046 return rc;
5047}
5048
5049
5050HRESULT Machine::discardSettings()
5051{
5052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5053
5054 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5055 if (FAILED(rc)) return rc;
5056
5057 /*
5058 * during this rollback, the session will be notified if data has
5059 * been actually changed
5060 */
5061 i_rollback(true /* aNotify */);
5062
5063 return S_OK;
5064}
5065
5066/** @note Locks objects! */
5067HRESULT Machine::unregister(AutoCaller &autoCaller,
5068 CleanupMode_T aCleanupMode,
5069 std::vector<ComPtr<IMedium> > &aMedia)
5070{
5071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5072
5073 Guid id(i_getId());
5074
5075 if (mData->mSession.mState != SessionState_Unlocked)
5076 return setError(VBOX_E_INVALID_OBJECT_STATE,
5077 tr("Cannot unregister the machine '%s' while it is locked"),
5078 mUserData->s.strName.c_str());
5079
5080 // wait for state dependents to drop to zero
5081 i_ensureNoStateDependencies();
5082
5083 if (!mData->mAccessible)
5084 {
5085 // inaccessible maschines can only be unregistered; uninitialize ourselves
5086 // here because currently there may be no unregistered that are inaccessible
5087 // (this state combination is not supported). Note releasing the caller and
5088 // leaving the lock before calling uninit()
5089 alock.release();
5090 autoCaller.release();
5091
5092 uninit();
5093
5094 mParent->i_unregisterMachine(this, id);
5095 // calls VirtualBox::i_saveSettings()
5096
5097 return S_OK;
5098 }
5099
5100 HRESULT rc = S_OK;
5101
5102 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5103 // discard saved state
5104 if (mData->mMachineState == MachineState_Saved)
5105 {
5106 // add the saved state file to the list of files the caller should delete
5107 Assert(!mSSData->strStateFilePath.isEmpty());
5108 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5109
5110 mSSData->strStateFilePath.setNull();
5111
5112 // unconditionally set the machine state to powered off, we now
5113 // know no session has locked the machine
5114 mData->mMachineState = MachineState_PoweredOff;
5115 }
5116
5117 size_t cSnapshots = 0;
5118 if (mData->mFirstSnapshot)
5119 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5120 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5121 // fail now before we start detaching media
5122 return setError(VBOX_E_INVALID_OBJECT_STATE,
5123 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5124 mUserData->s.strName.c_str(), cSnapshots);
5125
5126 // This list collects the medium objects from all medium attachments
5127 // which we will detach from the machine and its snapshots, in a specific
5128 // order which allows for closing all media without getting "media in use"
5129 // errors, simply by going through the list from the front to the back:
5130 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5131 // and must be closed before the parent media from the snapshots, or closing the parents
5132 // will fail because they still have children);
5133 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5134 // the root ("first") snapshot of the machine.
5135 MediaList llMedia;
5136
5137 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5138 && mMediaData->mAttachments.size()
5139 )
5140 {
5141 // we have media attachments: detach them all and add the Medium objects to our list
5142 if (aCleanupMode != CleanupMode_UnregisterOnly)
5143 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5144 else
5145 return setError(VBOX_E_INVALID_OBJECT_STATE,
5146 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5147 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5148 }
5149
5150 if (cSnapshots)
5151 {
5152 // add the media from the medium attachments of the snapshots to llMedia
5153 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5154 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5155 // into the children first
5156
5157 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5158 MachineState_T oldState = mData->mMachineState;
5159 mData->mMachineState = MachineState_DeletingSnapshot;
5160
5161 // make a copy of the first snapshot so the refcount does not drop to 0
5162 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5163 // because of the AutoCaller voodoo)
5164 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5165
5166 // GO!
5167 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5168
5169 mData->mMachineState = oldState;
5170 }
5171
5172 if (FAILED(rc))
5173 {
5174 i_rollbackMedia();
5175 return rc;
5176 }
5177
5178 // commit all the media changes made above
5179 i_commitMedia();
5180
5181 mData->mRegistered = false;
5182
5183 // machine lock no longer needed
5184 alock.release();
5185
5186 // return media to caller
5187 size_t i = 0;
5188 aMedia.resize(llMedia.size());
5189 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5190 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5191
5192 mParent->i_unregisterMachine(this, id);
5193 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5194
5195 return S_OK;
5196}
5197
5198/**
5199 * Task record for deleting a machine config.
5200 */
5201struct Machine::DeleteConfigTask
5202 : public Machine::Task
5203{
5204 DeleteConfigTask(Machine *m,
5205 Progress *p,
5206 const Utf8Str &t,
5207 const RTCList<ComPtr<IMedium> > &llMediums,
5208 const StringsList &llFilesToDelete)
5209 : Task(m, p, t),
5210 m_llMediums(llMediums),
5211 m_llFilesToDelete(llFilesToDelete)
5212 {}
5213
5214 void handler()
5215 {
5216 m_pMachine->i_deleteConfigHandler(*this);
5217 }
5218
5219 RTCList<ComPtr<IMedium> > m_llMediums;
5220 StringsList m_llFilesToDelete;
5221};
5222
5223/**
5224 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5225 * SessionMachine::taskHandler().
5226 *
5227 * @note Locks this object for writing.
5228 *
5229 * @param task
5230 * @return
5231 */
5232void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5233{
5234 LogFlowThisFuncEnter();
5235
5236 AutoCaller autoCaller(this);
5237 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5238 if (FAILED(autoCaller.rc()))
5239 {
5240 /* we might have been uninitialized because the session was accidentally
5241 * closed by the client, so don't assert */
5242 HRESULT rc = setError(E_FAIL,
5243 tr("The session has been accidentally closed"));
5244 task.m_pProgress->i_notifyComplete(rc);
5245 LogFlowThisFuncLeave();
5246 return;
5247 }
5248
5249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5250
5251 HRESULT rc = S_OK;
5252
5253 try
5254 {
5255 ULONG uLogHistoryCount = 3;
5256 ComPtr<ISystemProperties> systemProperties;
5257 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5258 if (FAILED(rc)) throw rc;
5259
5260 if (!systemProperties.isNull())
5261 {
5262 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5263 if (FAILED(rc)) throw rc;
5264 }
5265
5266 MachineState_T oldState = mData->mMachineState;
5267 i_setMachineState(MachineState_SettingUp);
5268 alock.release();
5269 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5270 {
5271 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5272 {
5273 AutoCaller mac(pMedium);
5274 if (FAILED(mac.rc())) throw mac.rc();
5275 Utf8Str strLocation = pMedium->i_getLocationFull();
5276 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5277 if (FAILED(rc)) throw rc;
5278 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5279 }
5280 if (pMedium->i_isMediumFormatFile())
5281 {
5282 ComPtr<IProgress> pProgress2;
5283 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5284 if (FAILED(rc)) throw rc;
5285 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5286 if (FAILED(rc)) throw rc;
5287 /* Check the result of the asynchronous process. */
5288 LONG iRc;
5289 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5290 if (FAILED(rc)) throw rc;
5291 /* If the thread of the progress object has an error, then
5292 * retrieve the error info from there, or it'll be lost. */
5293 if (FAILED(iRc))
5294 throw setError(ProgressErrorInfo(pProgress2));
5295 }
5296
5297 /* Close the medium, deliberately without checking the return
5298 * code, and without leaving any trace in the error info, as
5299 * a failure here is a very minor issue, which shouldn't happen
5300 * as above we even managed to delete the medium. */
5301 {
5302 ErrorInfoKeeper eik;
5303 pMedium->Close();
5304 }
5305 }
5306 i_setMachineState(oldState);
5307 alock.acquire();
5308
5309 // delete the files pushed on the task list by Machine::Delete()
5310 // (this includes saved states of the machine and snapshots and
5311 // medium storage files from the IMedium list passed in, and the
5312 // machine XML file)
5313 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5314 while (it != task.m_llFilesToDelete.end())
5315 {
5316 const Utf8Str &strFile = *it;
5317 LogFunc(("Deleting file %s\n", strFile.c_str()));
5318 int vrc = RTFileDelete(strFile.c_str());
5319 if (RT_FAILURE(vrc))
5320 throw setError(VBOX_E_IPRT_ERROR,
5321 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5322
5323 ++it;
5324 if (it == task.m_llFilesToDelete.end())
5325 {
5326 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5327 if (FAILED(rc)) throw rc;
5328 break;
5329 }
5330
5331 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5332 if (FAILED(rc)) throw rc;
5333 }
5334
5335 /* delete the settings only when the file actually exists */
5336 if (mData->pMachineConfigFile->fileExists())
5337 {
5338 /* Delete any backup or uncommitted XML files. Ignore failures.
5339 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5340 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5341 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5342 RTFileDelete(otherXml.c_str());
5343 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5344 RTFileDelete(otherXml.c_str());
5345
5346 /* delete the Logs folder, nothing important should be left
5347 * there (we don't check for errors because the user might have
5348 * some private files there that we don't want to delete) */
5349 Utf8Str logFolder;
5350 getLogFolder(logFolder);
5351 Assert(logFolder.length());
5352 if (RTDirExists(logFolder.c_str()))
5353 {
5354 /* Delete all VBox.log[.N] files from the Logs folder
5355 * (this must be in sync with the rotation logic in
5356 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5357 * files that may have been created by the GUI. */
5358 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5359 logFolder.c_str(), RTPATH_DELIMITER);
5360 RTFileDelete(log.c_str());
5361 log = Utf8StrFmt("%s%cVBox.png",
5362 logFolder.c_str(), RTPATH_DELIMITER);
5363 RTFileDelete(log.c_str());
5364 for (int i = uLogHistoryCount; i > 0; i--)
5365 {
5366 log = Utf8StrFmt("%s%cVBox.log.%d",
5367 logFolder.c_str(), RTPATH_DELIMITER, i);
5368 RTFileDelete(log.c_str());
5369 log = Utf8StrFmt("%s%cVBox.png.%d",
5370 logFolder.c_str(), RTPATH_DELIMITER, i);
5371 RTFileDelete(log.c_str());
5372 }
5373#if defined(RT_OS_WINDOWS)
5374 log = Utf8StrFmt("%s%cVBoxStartup.log",
5375 logFolder.c_str(), RTPATH_DELIMITER);
5376 RTFileDelete(log.c_str());
5377#endif
5378
5379 RTDirRemove(logFolder.c_str());
5380 }
5381
5382 /* delete the Snapshots folder, nothing important should be left
5383 * there (we don't check for errors because the user might have
5384 * some private files there that we don't want to delete) */
5385 Utf8Str strFullSnapshotFolder;
5386 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5387 Assert(!strFullSnapshotFolder.isEmpty());
5388 if (RTDirExists(strFullSnapshotFolder.c_str()))
5389 RTDirRemove(strFullSnapshotFolder.c_str());
5390
5391 // delete the directory that contains the settings file, but only
5392 // if it matches the VM name
5393 Utf8Str settingsDir;
5394 if (i_isInOwnDir(&settingsDir))
5395 RTDirRemove(settingsDir.c_str());
5396 }
5397
5398 alock.release();
5399
5400 mParent->i_saveModifiedRegistries();
5401 }
5402 catch (HRESULT aRC) { rc = aRC; }
5403
5404 task.m_pProgress->i_notifyComplete(rc);
5405
5406 LogFlowThisFuncLeave();
5407}
5408
5409HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5410{
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = i_checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 if (mData->mRegistered)
5417 return setError(VBOX_E_INVALID_VM_STATE,
5418 tr("Cannot delete settings of a registered machine"));
5419
5420 // collect files to delete
5421 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5422 if (mData->pMachineConfigFile->fileExists())
5423 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5424
5425 RTCList<ComPtr<IMedium> > llMediums;
5426 for (size_t i = 0; i < aMedia.size(); ++i)
5427 {
5428 IMedium *pIMedium(aMedia[i]);
5429 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5430 if (pMedium.isNull())
5431 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5432 SafeArray<BSTR> ids;
5433 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5434 if (FAILED(rc)) return rc;
5435 /* At this point the medium should not have any back references
5436 * anymore. If it has it is attached to another VM and *must* not
5437 * deleted. */
5438 if (ids.size() < 1)
5439 llMediums.append(pMedium);
5440 }
5441
5442 ComObjPtr<Progress> pProgress;
5443 pProgress.createObject();
5444 rc = pProgress->init(i_getVirtualBox(),
5445 static_cast<IMachine*>(this) /* aInitiator */,
5446 Bstr(tr("Deleting files")).raw(),
5447 true /* fCancellable */,
5448 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5449 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5450 if (FAILED(rc))
5451 return rc;
5452
5453 /* create and start the task on a separate thread (note that it will not
5454 * start working until we release alock) */
5455 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5456 rc = pTask->createThread();
5457 if (FAILED(rc))
5458 return rc;
5459
5460 pProgress.queryInterfaceTo(aProgress.asOutParam());
5461
5462 LogFlowFuncLeave();
5463
5464 return S_OK;
5465}
5466
5467HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5468{
5469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5470
5471 ComObjPtr<Snapshot> pSnapshot;
5472 HRESULT rc;
5473
5474 if (aNameOrId.isEmpty())
5475 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5476 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5477 else
5478 {
5479 Guid uuid(aNameOrId);
5480 if (uuid.isValid())
5481 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5482 else
5483 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5484 }
5485 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5486
5487 return rc;
5488}
5489
5490HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5491{
5492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5495 if (FAILED(rc)) return rc;
5496
5497 ComObjPtr<SharedFolder> sharedFolder;
5498 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5499 if (SUCCEEDED(rc))
5500 return setError(VBOX_E_OBJECT_IN_USE,
5501 tr("Shared folder named '%s' already exists"),
5502 aName.c_str());
5503
5504 sharedFolder.createObject();
5505 rc = sharedFolder->init(i_getMachine(),
5506 aName,
5507 aHostPath,
5508 !!aWritable,
5509 !!aAutomount,
5510 true /* fFailOnError */);
5511 if (FAILED(rc)) return rc;
5512
5513 i_setModified(IsModified_SharedFolders);
5514 mHWData.backup();
5515 mHWData->mSharedFolders.push_back(sharedFolder);
5516
5517 /* inform the direct session if any */
5518 alock.release();
5519 i_onSharedFolderChange();
5520
5521 return S_OK;
5522}
5523
5524HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5525{
5526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5527
5528 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5529 if (FAILED(rc)) return rc;
5530
5531 ComObjPtr<SharedFolder> sharedFolder;
5532 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5533 if (FAILED(rc)) return rc;
5534
5535 i_setModified(IsModified_SharedFolders);
5536 mHWData.backup();
5537 mHWData->mSharedFolders.remove(sharedFolder);
5538
5539 /* inform the direct session if any */
5540 alock.release();
5541 i_onSharedFolderChange();
5542
5543 return S_OK;
5544}
5545
5546HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5547{
5548 /* start with No */
5549 *aCanShow = FALSE;
5550
5551 ComPtr<IInternalSessionControl> directControl;
5552 {
5553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5554
5555 if (mData->mSession.mState != SessionState_Locked)
5556 return setError(VBOX_E_INVALID_VM_STATE,
5557 tr("Machine is not locked for session (session state: %s)"),
5558 Global::stringifySessionState(mData->mSession.mState));
5559
5560 if (mData->mSession.mLockType == LockType_VM)
5561 directControl = mData->mSession.mDirectControl;
5562 }
5563
5564 /* ignore calls made after #OnSessionEnd() is called */
5565 if (!directControl)
5566 return S_OK;
5567
5568 LONG64 dummy;
5569 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5570}
5571
5572HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5573{
5574 ComPtr<IInternalSessionControl> directControl;
5575 {
5576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5577
5578 if (mData->mSession.mState != SessionState_Locked)
5579 return setError(E_FAIL,
5580 tr("Machine is not locked for session (session state: %s)"),
5581 Global::stringifySessionState(mData->mSession.mState));
5582
5583 if (mData->mSession.mLockType == LockType_VM)
5584 directControl = mData->mSession.mDirectControl;
5585 }
5586
5587 /* ignore calls made after #OnSessionEnd() is called */
5588 if (!directControl)
5589 return S_OK;
5590
5591 BOOL dummy;
5592 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5593}
5594
5595#ifdef VBOX_WITH_GUEST_PROPS
5596/**
5597 * Look up a guest property in VBoxSVC's internal structures.
5598 */
5599HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5600 com::Utf8Str &aValue,
5601 LONG64 *aTimestamp,
5602 com::Utf8Str &aFlags) const
5603{
5604 using namespace guestProp;
5605
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5608
5609 if (it != mHWData->mGuestProperties.end())
5610 {
5611 char szFlags[MAX_FLAGS_LEN + 1];
5612 aValue = it->second.strValue;
5613 *aTimestamp = it->second.mTimestamp;
5614 writeFlags(it->second.mFlags, szFlags);
5615 aFlags = Utf8Str(szFlags);
5616 }
5617
5618 return S_OK;
5619}
5620
5621/**
5622 * Query the VM that a guest property belongs to for the property.
5623 * @returns E_ACCESSDENIED if the VM process is not available or not
5624 * currently handling queries and the lookup should then be done in
5625 * VBoxSVC.
5626 */
5627HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5628 com::Utf8Str &aValue,
5629 LONG64 *aTimestamp,
5630 com::Utf8Str &aFlags) const
5631{
5632 HRESULT rc = S_OK;
5633 BSTR bValue = NULL;
5634 BSTR bFlags = NULL;
5635
5636 ComPtr<IInternalSessionControl> directControl;
5637 {
5638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5639 if (mData->mSession.mLockType == LockType_VM)
5640 directControl = mData->mSession.mDirectControl;
5641 }
5642
5643 /* ignore calls made after #OnSessionEnd() is called */
5644 if (!directControl)
5645 rc = E_ACCESSDENIED;
5646 else
5647 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5648 0 /* accessMode */,
5649 &bValue, aTimestamp, &bFlags);
5650
5651 aValue = bValue;
5652 aFlags = bFlags;
5653
5654 return rc;
5655}
5656#endif // VBOX_WITH_GUEST_PROPS
5657
5658HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5659 com::Utf8Str &aValue,
5660 LONG64 *aTimestamp,
5661 com::Utf8Str &aFlags)
5662{
5663#ifndef VBOX_WITH_GUEST_PROPS
5664 ReturnComNotImplemented();
5665#else // VBOX_WITH_GUEST_PROPS
5666
5667 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5668
5669 if (rc == E_ACCESSDENIED)
5670 /* The VM is not running or the service is not (yet) accessible */
5671 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5672 return rc;
5673#endif // VBOX_WITH_GUEST_PROPS
5674}
5675
5676HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5677{
5678 LONG64 dummyTimestamp;
5679 com::Utf8Str dummyFlags;
5680 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5681 return rc;
5682
5683}
5684HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5685{
5686 com::Utf8Str dummyFlags;
5687 com::Utf8Str dummyValue;
5688 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5689 return rc;
5690}
5691
5692#ifdef VBOX_WITH_GUEST_PROPS
5693/**
5694 * Set a guest property in VBoxSVC's internal structures.
5695 */
5696HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5697 const com::Utf8Str &aFlags, bool fDelete)
5698{
5699 using namespace guestProp;
5700
5701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5702 HRESULT rc = S_OK;
5703
5704 rc = i_checkStateDependency(MutableOrSavedStateDep);
5705 if (FAILED(rc)) return rc;
5706
5707 try
5708 {
5709 uint32_t fFlags = NILFLAG;
5710 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5711 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5712
5713 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5714 if (it == mHWData->mGuestProperties.end())
5715 {
5716 if (!fDelete)
5717 {
5718 i_setModified(IsModified_MachineData);
5719 mHWData.backupEx();
5720
5721 RTTIMESPEC time;
5722 HWData::GuestProperty prop;
5723 prop.strValue = Bstr(aValue).raw();
5724 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5725 prop.mFlags = fFlags;
5726 mHWData->mGuestProperties[aName] = prop;
5727 }
5728 }
5729 else
5730 {
5731 if (it->second.mFlags & (RDONLYHOST))
5732 {
5733 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5734 }
5735 else
5736 {
5737 i_setModified(IsModified_MachineData);
5738 mHWData.backupEx();
5739
5740 /* The backupEx() operation invalidates our iterator,
5741 * so get a new one. */
5742 it = mHWData->mGuestProperties.find(aName);
5743 Assert(it != mHWData->mGuestProperties.end());
5744
5745 if (!fDelete)
5746 {
5747 RTTIMESPEC time;
5748 it->second.strValue = aValue;
5749 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5750 it->second.mFlags = fFlags;
5751 }
5752 else
5753 mHWData->mGuestProperties.erase(it);
5754 }
5755 }
5756
5757 if ( SUCCEEDED(rc)
5758 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5759 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5760 RTSTR_MAX,
5761 aName.c_str(),
5762 RTSTR_MAX,
5763 NULL)
5764 )
5765 )
5766 {
5767 alock.release();
5768
5769 mParent->i_onGuestPropertyChange(mData->mUuid,
5770 Bstr(aName).raw(),
5771 Bstr(aValue).raw(),
5772 Bstr(aFlags).raw());
5773 }
5774 }
5775 catch (std::bad_alloc &)
5776 {
5777 rc = E_OUTOFMEMORY;
5778 }
5779
5780 return rc;
5781}
5782
5783/**
5784 * Set a property on the VM that that property belongs to.
5785 * @returns E_ACCESSDENIED if the VM process is not available or not
5786 * currently handling queries and the setting should then be done in
5787 * VBoxSVC.
5788 */
5789HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5790 const com::Utf8Str &aFlags, bool fDelete)
5791{
5792 HRESULT rc;
5793
5794 try
5795 {
5796 ComPtr<IInternalSessionControl> directControl;
5797 {
5798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5799 if (mData->mSession.mLockType == LockType_VM)
5800 directControl = mData->mSession.mDirectControl;
5801 }
5802
5803 BSTR dummy = NULL; /* will not be changed (setter) */
5804 LONG64 dummy64;
5805 if (!directControl)
5806 rc = E_ACCESSDENIED;
5807 else
5808 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5809 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5810 fDelete? 2: 1 /* accessMode */,
5811 &dummy, &dummy64, &dummy);
5812 }
5813 catch (std::bad_alloc &)
5814 {
5815 rc = E_OUTOFMEMORY;
5816 }
5817
5818 return rc;
5819}
5820#endif // VBOX_WITH_GUEST_PROPS
5821
5822HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5823 const com::Utf8Str &aFlags)
5824{
5825#ifndef VBOX_WITH_GUEST_PROPS
5826 ReturnComNotImplemented();
5827#else // VBOX_WITH_GUEST_PROPS
5828 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5829 if (rc == E_ACCESSDENIED)
5830 /* The VM is not running or the service is not (yet) accessible */
5831 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5832 return rc;
5833#endif // VBOX_WITH_GUEST_PROPS
5834}
5835
5836HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5837{
5838 return setGuestProperty(aProperty, aValue, "");
5839}
5840
5841HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5842{
5843#ifndef VBOX_WITH_GUEST_PROPS
5844 ReturnComNotImplemented();
5845#else // VBOX_WITH_GUEST_PROPS
5846 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5847 if (rc == E_ACCESSDENIED)
5848 /* The VM is not running or the service is not (yet) accessible */
5849 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5850 return rc;
5851#endif // VBOX_WITH_GUEST_PROPS
5852}
5853
5854#ifdef VBOX_WITH_GUEST_PROPS
5855/**
5856 * Enumerate the guest properties in VBoxSVC's internal structures.
5857 */
5858HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5859 std::vector<com::Utf8Str> &aNames,
5860 std::vector<com::Utf8Str> &aValues,
5861 std::vector<LONG64> &aTimestamps,
5862 std::vector<com::Utf8Str> &aFlags)
5863{
5864 using namespace guestProp;
5865
5866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5867 Utf8Str strPatterns(aPatterns);
5868
5869 HWData::GuestPropertyMap propMap;
5870
5871 /*
5872 * Look for matching patterns and build up a list.
5873 */
5874 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5875 while (it != mHWData->mGuestProperties.end())
5876 {
5877 if ( strPatterns.isEmpty()
5878 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5879 RTSTR_MAX,
5880 it->first.c_str(),
5881 RTSTR_MAX,
5882 NULL)
5883 )
5884 propMap.insert(*it);
5885 ++it;
5886 }
5887
5888 alock.release();
5889
5890 /*
5891 * And build up the arrays for returning the property information.
5892 */
5893 size_t cEntries = propMap.size();
5894
5895 aNames.resize(cEntries);
5896 aValues.resize(cEntries);
5897 aTimestamps.resize(cEntries);
5898 aFlags.resize(cEntries);
5899
5900 char szFlags[MAX_FLAGS_LEN + 1];
5901 size_t i= 0;
5902 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5903 {
5904 aNames[i] = it->first;
5905 aValues[i] = it->second.strValue;
5906 aTimestamps[i] = it->second.mTimestamp;
5907 writeFlags(it->second.mFlags, szFlags);
5908 aFlags[i] = Utf8Str(szFlags);
5909 }
5910
5911 return S_OK;
5912}
5913
5914/**
5915 * Enumerate the properties managed by a VM.
5916 * @returns E_ACCESSDENIED if the VM process is not available or not
5917 * currently handling queries and the setting should then be done in
5918 * VBoxSVC.
5919 */
5920HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5921 std::vector<com::Utf8Str> &aNames,
5922 std::vector<com::Utf8Str> &aValues,
5923 std::vector<LONG64> &aTimestamps,
5924 std::vector<com::Utf8Str> &aFlags)
5925{
5926 HRESULT rc;
5927 ComPtr<IInternalSessionControl> directControl;
5928 {
5929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5930 if (mData->mSession.mLockType == LockType_VM)
5931 directControl = mData->mSession.mDirectControl;
5932 }
5933
5934 com::SafeArray<BSTR> bNames;
5935 com::SafeArray<BSTR> bValues;
5936 com::SafeArray<LONG64> bTimestamps;
5937 com::SafeArray<BSTR> bFlags;
5938
5939 if (!directControl)
5940 rc = E_ACCESSDENIED;
5941 else
5942 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5943 ComSafeArrayAsOutParam(bNames),
5944 ComSafeArrayAsOutParam(bValues),
5945 ComSafeArrayAsOutParam(bTimestamps),
5946 ComSafeArrayAsOutParam(bFlags));
5947 size_t i;
5948 aNames.resize(bNames.size());
5949 for (i = 0; i < bNames.size(); ++i)
5950 aNames[i] = Utf8Str(bNames[i]);
5951 aValues.resize(bValues.size());
5952 for (i = 0; i < bValues.size(); ++i)
5953 aValues[i] = Utf8Str(bValues[i]);
5954 aTimestamps.resize(bTimestamps.size());
5955 for (i = 0; i < bTimestamps.size(); ++i)
5956 aTimestamps[i] = bTimestamps[i];
5957 aFlags.resize(bFlags.size());
5958 for (i = 0; i < bFlags.size(); ++i)
5959 aFlags[i] = Utf8Str(bFlags[i]);
5960
5961 return rc;
5962}
5963#endif // VBOX_WITH_GUEST_PROPS
5964HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5965 std::vector<com::Utf8Str> &aNames,
5966 std::vector<com::Utf8Str> &aValues,
5967 std::vector<LONG64> &aTimestamps,
5968 std::vector<com::Utf8Str> &aFlags)
5969{
5970#ifndef VBOX_WITH_GUEST_PROPS
5971 ReturnComNotImplemented();
5972#else // VBOX_WITH_GUEST_PROPS
5973
5974 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5975
5976 if (rc == E_ACCESSDENIED)
5977 /* The VM is not running or the service is not (yet) accessible */
5978 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5979 return rc;
5980#endif // VBOX_WITH_GUEST_PROPS
5981}
5982
5983HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5984 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5985{
5986 MediaData::AttachmentList atts;
5987
5988 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5989 if (FAILED(rc)) return rc;
5990
5991 size_t i = 0;
5992 aMediumAttachments.resize(atts.size());
5993 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5994 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5995
5996 return S_OK;
5997}
5998
5999HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6000 LONG aControllerPort,
6001 LONG aDevice,
6002 ComPtr<IMediumAttachment> &aAttachment)
6003{
6004 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6005 aName.c_str(), aControllerPort, aDevice));
6006
6007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6008
6009 aAttachment = NULL;
6010
6011 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6012 Bstr(aName).raw(),
6013 aControllerPort,
6014 aDevice);
6015 if (pAttach.isNull())
6016 return setError(VBOX_E_OBJECT_NOT_FOUND,
6017 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6018 aDevice, aControllerPort, aName.c_str());
6019
6020 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6021
6022 return S_OK;
6023}
6024
6025
6026HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6027 StorageBus_T aConnectionType,
6028 ComPtr<IStorageController> &aController)
6029{
6030 if ( (aConnectionType <= StorageBus_Null)
6031 || (aConnectionType > StorageBus_USB))
6032 return setError(E_INVALIDARG,
6033 tr("Invalid connection type: %d"),
6034 aConnectionType);
6035
6036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6037
6038 HRESULT rc = i_checkStateDependency(MutableStateDep);
6039 if (FAILED(rc)) return rc;
6040
6041 /* try to find one with the name first. */
6042 ComObjPtr<StorageController> ctrl;
6043
6044 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6045 if (SUCCEEDED(rc))
6046 return setError(VBOX_E_OBJECT_IN_USE,
6047 tr("Storage controller named '%s' already exists"),
6048 aName.c_str());
6049
6050 ctrl.createObject();
6051
6052 /* get a new instance number for the storage controller */
6053 ULONG ulInstance = 0;
6054 bool fBootable = true;
6055 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6056 it != mStorageControllers->end();
6057 ++it)
6058 {
6059 if ((*it)->i_getStorageBus() == aConnectionType)
6060 {
6061 ULONG ulCurInst = (*it)->i_getInstance();
6062
6063 if (ulCurInst >= ulInstance)
6064 ulInstance = ulCurInst + 1;
6065
6066 /* Only one controller of each type can be marked as bootable. */
6067 if ((*it)->i_getBootable())
6068 fBootable = false;
6069 }
6070 }
6071
6072 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6073 if (FAILED(rc)) return rc;
6074
6075 i_setModified(IsModified_Storage);
6076 mStorageControllers.backup();
6077 mStorageControllers->push_back(ctrl);
6078
6079 ctrl.queryInterfaceTo(aController.asOutParam());
6080
6081 /* inform the direct session if any */
6082 alock.release();
6083 i_onStorageControllerChange();
6084
6085 return S_OK;
6086}
6087
6088HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6089 ComPtr<IStorageController> &aStorageController)
6090{
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 ComObjPtr<StorageController> ctrl;
6094
6095 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6096 if (SUCCEEDED(rc))
6097 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6098
6099 return rc;
6100}
6101
6102HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6103 ComPtr<IStorageController> &aStorageController)
6104{
6105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6106
6107 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6108 it != mStorageControllers->end();
6109 ++it)
6110 {
6111 if ((*it)->i_getInstance() == aInstance)
6112 {
6113 (*it).queryInterfaceTo(aStorageController.asOutParam());
6114 return S_OK;
6115 }
6116 }
6117
6118 return setError(VBOX_E_OBJECT_NOT_FOUND,
6119 tr("Could not find a storage controller with instance number '%lu'"),
6120 aInstance);
6121}
6122
6123HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6124{
6125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6126
6127 HRESULT rc = i_checkStateDependency(MutableStateDep);
6128 if (FAILED(rc)) return rc;
6129
6130 ComObjPtr<StorageController> ctrl;
6131
6132 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6133 if (SUCCEEDED(rc))
6134 {
6135 /* Ensure that only one controller of each type is marked as bootable. */
6136 if (aBootable == TRUE)
6137 {
6138 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6139 it != mStorageControllers->end();
6140 ++it)
6141 {
6142 ComObjPtr<StorageController> aCtrl = (*it);
6143
6144 if ( (aCtrl->i_getName() != aName)
6145 && aCtrl->i_getBootable() == TRUE
6146 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6147 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6148 {
6149 aCtrl->i_setBootable(FALSE);
6150 break;
6151 }
6152 }
6153 }
6154
6155 if (SUCCEEDED(rc))
6156 {
6157 ctrl->i_setBootable(aBootable);
6158 i_setModified(IsModified_Storage);
6159 }
6160 }
6161
6162 if (SUCCEEDED(rc))
6163 {
6164 /* inform the direct session if any */
6165 alock.release();
6166 i_onStorageControllerChange();
6167 }
6168
6169 return rc;
6170}
6171
6172HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6173{
6174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6175
6176 HRESULT rc = i_checkStateDependency(MutableStateDep);
6177 if (FAILED(rc)) return rc;
6178
6179 ComObjPtr<StorageController> ctrl;
6180 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6181 if (FAILED(rc)) return rc;
6182
6183 {
6184 /* find all attached devices to the appropriate storage controller and detach them all */
6185 // make a temporary list because detachDevice invalidates iterators into
6186 // mMediaData->mAttachments
6187 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6188
6189 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6190 it != llAttachments2.end();
6191 ++it)
6192 {
6193 MediumAttachment *pAttachTemp = *it;
6194
6195 AutoCaller localAutoCaller(pAttachTemp);
6196 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6197
6198 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6199
6200 if (pAttachTemp->i_getControllerName() == aName)
6201 {
6202 rc = i_detachDevice(pAttachTemp, alock, NULL);
6203 if (FAILED(rc)) return rc;
6204 }
6205 }
6206 }
6207
6208 /* We can remove it now. */
6209 i_setModified(IsModified_Storage);
6210 mStorageControllers.backup();
6211
6212 ctrl->i_unshare();
6213
6214 mStorageControllers->remove(ctrl);
6215
6216 /* inform the direct session if any */
6217 alock.release();
6218 i_onStorageControllerChange();
6219
6220 return S_OK;
6221}
6222
6223HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6224 ComPtr<IUSBController> &aController)
6225{
6226 if ( (aType <= USBControllerType_Null)
6227 || (aType >= USBControllerType_Last))
6228 return setError(E_INVALIDARG,
6229 tr("Invalid USB controller type: %d"),
6230 aType);
6231
6232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6233
6234 HRESULT rc = i_checkStateDependency(MutableStateDep);
6235 if (FAILED(rc)) return rc;
6236
6237 /* try to find one with the same type first. */
6238 ComObjPtr<USBController> ctrl;
6239
6240 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6241 if (SUCCEEDED(rc))
6242 return setError(VBOX_E_OBJECT_IN_USE,
6243 tr("USB controller named '%s' already exists"),
6244 aName.c_str());
6245
6246 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6247 ULONG maxInstances;
6248 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6249 if (FAILED(rc))
6250 return rc;
6251
6252 ULONG cInstances = i_getUSBControllerCountByType(aType);
6253 if (cInstances >= maxInstances)
6254 return setError(E_INVALIDARG,
6255 tr("Too many USB controllers of this type"));
6256
6257 ctrl.createObject();
6258
6259 rc = ctrl->init(this, aName, aType);
6260 if (FAILED(rc)) return rc;
6261
6262 i_setModified(IsModified_USB);
6263 mUSBControllers.backup();
6264 mUSBControllers->push_back(ctrl);
6265
6266 ctrl.queryInterfaceTo(aController.asOutParam());
6267
6268 /* inform the direct session if any */
6269 alock.release();
6270 i_onUSBControllerChange();
6271
6272 return S_OK;
6273}
6274
6275HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6276{
6277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6278
6279 ComObjPtr<USBController> ctrl;
6280
6281 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6282 if (SUCCEEDED(rc))
6283 ctrl.queryInterfaceTo(aController.asOutParam());
6284
6285 return rc;
6286}
6287
6288HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6289 ULONG *aControllers)
6290{
6291 if ( (aType <= USBControllerType_Null)
6292 || (aType >= USBControllerType_Last))
6293 return setError(E_INVALIDARG,
6294 tr("Invalid USB controller type: %d"),
6295 aType);
6296
6297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6298
6299 ComObjPtr<USBController> ctrl;
6300
6301 *aControllers = i_getUSBControllerCountByType(aType);
6302
6303 return S_OK;
6304}
6305
6306HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6307{
6308
6309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 HRESULT rc = i_checkStateDependency(MutableStateDep);
6312 if (FAILED(rc)) return rc;
6313
6314 ComObjPtr<USBController> ctrl;
6315 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6316 if (FAILED(rc)) return rc;
6317
6318 i_setModified(IsModified_USB);
6319 mUSBControllers.backup();
6320
6321 ctrl->i_unshare();
6322
6323 mUSBControllers->remove(ctrl);
6324
6325 /* inform the direct session if any */
6326 alock.release();
6327 i_onUSBControllerChange();
6328
6329 return S_OK;
6330}
6331
6332HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6333 ULONG *aOriginX,
6334 ULONG *aOriginY,
6335 ULONG *aWidth,
6336 ULONG *aHeight,
6337 BOOL *aEnabled)
6338{
6339 uint32_t u32OriginX= 0;
6340 uint32_t u32OriginY= 0;
6341 uint32_t u32Width = 0;
6342 uint32_t u32Height = 0;
6343 uint16_t u16Flags = 0;
6344
6345 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6346 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6347 if (RT_FAILURE(vrc))
6348 {
6349#ifdef RT_OS_WINDOWS
6350 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6351 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6352 * So just assign fEnable to TRUE again.
6353 * The right fix would be to change GUI API wrappers to make sure that parameters
6354 * are changed only if API succeeds.
6355 */
6356 *aEnabled = TRUE;
6357#endif
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Saved guest size is not available (%Rrc)"),
6360 vrc);
6361 }
6362
6363 *aOriginX = u32OriginX;
6364 *aOriginY = u32OriginY;
6365 *aWidth = u32Width;
6366 *aHeight = u32Height;
6367 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6368
6369 return S_OK;
6370}
6371
6372HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6373{
6374 if (aScreenId != 0)
6375 return E_NOTIMPL;
6376
6377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 uint8_t *pu8Data = NULL;
6380 uint32_t cbData = 0;
6381 uint32_t u32Width = 0;
6382 uint32_t u32Height = 0;
6383
6384 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6385
6386 if (RT_FAILURE(vrc))
6387 return setError(VBOX_E_IPRT_ERROR,
6388 tr("Saved screenshot data is not available (%Rrc)"),
6389 vrc);
6390
6391 *aSize = cbData;
6392 *aWidth = u32Width;
6393 *aHeight = u32Height;
6394
6395 freeSavedDisplayScreenshot(pu8Data);
6396
6397 return S_OK;
6398}
6399
6400HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6401 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6402{
6403 if (aScreenId != 0)
6404 return E_NOTIMPL;
6405
6406 if ( aBitmapFormat != BitmapFormat_BGR0
6407 && aBitmapFormat != BitmapFormat_BGRA
6408 && aBitmapFormat != BitmapFormat_RGBA
6409 && aBitmapFormat != BitmapFormat_PNG)
6410 return setError(E_NOTIMPL,
6411 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6412
6413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 uint8_t *pu8Data = NULL;
6416 uint32_t cbData = 0;
6417 uint32_t u32Width = 0;
6418 uint32_t u32Height = 0;
6419
6420 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6421
6422 if (RT_FAILURE(vrc))
6423 return setError(VBOX_E_IPRT_ERROR,
6424 tr("Saved thumbnail data is not available (%Rrc)"),
6425 vrc);
6426
6427 HRESULT hr = S_OK;
6428
6429 *aWidth = u32Width;
6430 *aHeight = u32Height;
6431
6432 if (cbData > 0)
6433 {
6434 /* Convert pixels to the format expected by the API caller. */
6435 if (aBitmapFormat == BitmapFormat_BGR0)
6436 {
6437 /* [0] B, [1] G, [2] R, [3] 0. */
6438 aData.resize(cbData);
6439 memcpy(&aData.front(), pu8Data, cbData);
6440 }
6441 else if (aBitmapFormat == BitmapFormat_BGRA)
6442 {
6443 /* [0] B, [1] G, [2] R, [3] A. */
6444 aData.resize(cbData);
6445 for (uint32_t i = 0; i < cbData; i += 4)
6446 {
6447 aData[i] = pu8Data[i];
6448 aData[i + 1] = pu8Data[i + 1];
6449 aData[i + 2] = pu8Data[i + 2];
6450 aData[i + 3] = 0xff;
6451 }
6452 }
6453 else if (aBitmapFormat == BitmapFormat_RGBA)
6454 {
6455 /* [0] R, [1] G, [2] B, [3] A. */
6456 aData.resize(cbData);
6457 for (uint32_t i = 0; i < cbData; i += 4)
6458 {
6459 aData[i] = pu8Data[i + 2];
6460 aData[i + 1] = pu8Data[i + 1];
6461 aData[i + 2] = pu8Data[i];
6462 aData[i + 3] = 0xff;
6463 }
6464 }
6465 else if (aBitmapFormat == BitmapFormat_PNG)
6466 {
6467 uint8_t *pu8PNG = NULL;
6468 uint32_t cbPNG = 0;
6469 uint32_t cxPNG = 0;
6470 uint32_t cyPNG = 0;
6471
6472 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6473
6474 if (RT_SUCCESS(vrc))
6475 {
6476 aData.resize(cbPNG);
6477 if (cbPNG)
6478 memcpy(&aData.front(), pu8PNG, cbPNG);
6479 }
6480 else
6481 hr = setError(VBOX_E_IPRT_ERROR,
6482 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6483 vrc);
6484
6485 RTMemFree(pu8PNG);
6486 }
6487 }
6488
6489 freeSavedDisplayScreenshot(pu8Data);
6490
6491 return hr;
6492}
6493
6494HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6495{
6496 if (aScreenId != 0)
6497 return E_NOTIMPL;
6498
6499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6500
6501 uint8_t *pu8Data = NULL;
6502 uint32_t cbData = 0;
6503 uint32_t u32Width = 0;
6504 uint32_t u32Height = 0;
6505
6506 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6507
6508 if (RT_FAILURE(vrc))
6509 return setError(VBOX_E_IPRT_ERROR,
6510 tr("Saved screenshot data is not available (%Rrc)"),
6511 vrc);
6512
6513 *aSize = cbData;
6514 *aWidth = u32Width;
6515 *aHeight = u32Height;
6516
6517 freeSavedDisplayScreenshot(pu8Data);
6518
6519 return S_OK;
6520}
6521
6522HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6523{
6524 if (aScreenId != 0)
6525 return E_NOTIMPL;
6526
6527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6528
6529 uint8_t *pu8Data = NULL;
6530 uint32_t cbData = 0;
6531 uint32_t u32Width = 0;
6532 uint32_t u32Height = 0;
6533
6534 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6535
6536 if (RT_FAILURE(vrc))
6537 return setError(VBOX_E_IPRT_ERROR,
6538 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6539 vrc);
6540
6541 *aWidth = u32Width;
6542 *aHeight = u32Height;
6543
6544 aData.resize(cbData);
6545 if (cbData)
6546 memcpy(&aData.front(), pu8Data, cbData);
6547
6548 freeSavedDisplayScreenshot(pu8Data);
6549
6550 return S_OK;
6551}
6552
6553HRESULT Machine::hotPlugCPU(ULONG aCpu)
6554{
6555 HRESULT rc = S_OK;
6556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 if (!mHWData->mCPUHotPlugEnabled)
6559 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6560
6561 if (aCpu >= mHWData->mCPUCount)
6562 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6563
6564 if (mHWData->mCPUAttached[aCpu])
6565 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6566
6567 alock.release();
6568 rc = i_onCPUChange(aCpu, false);
6569 alock.acquire();
6570 if (FAILED(rc)) return rc;
6571
6572 i_setModified(IsModified_MachineData);
6573 mHWData.backup();
6574 mHWData->mCPUAttached[aCpu] = true;
6575
6576 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6577 if (Global::IsOnline(mData->mMachineState))
6578 i_saveSettings(NULL);
6579
6580 return S_OK;
6581}
6582
6583HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6584{
6585 HRESULT rc = S_OK;
6586
6587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6588
6589 if (!mHWData->mCPUHotPlugEnabled)
6590 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6591
6592 if (aCpu >= SchemaDefs::MaxCPUCount)
6593 return setError(E_INVALIDARG,
6594 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6595 SchemaDefs::MaxCPUCount);
6596
6597 if (!mHWData->mCPUAttached[aCpu])
6598 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6599
6600 /* CPU 0 can't be detached */
6601 if (aCpu == 0)
6602 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6603
6604 alock.release();
6605 rc = i_onCPUChange(aCpu, true);
6606 alock.acquire();
6607 if (FAILED(rc)) return rc;
6608
6609 i_setModified(IsModified_MachineData);
6610 mHWData.backup();
6611 mHWData->mCPUAttached[aCpu] = false;
6612
6613 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6614 if (Global::IsOnline(mData->mMachineState))
6615 i_saveSettings(NULL);
6616
6617 return S_OK;
6618}
6619
6620HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6621{
6622 *aAttached = false;
6623
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625
6626 /* If hotplug is enabled the CPU is always enabled. */
6627 if (!mHWData->mCPUHotPlugEnabled)
6628 {
6629 if (aCpu < mHWData->mCPUCount)
6630 *aAttached = true;
6631 }
6632 else
6633 {
6634 if (aCpu < SchemaDefs::MaxCPUCount)
6635 *aAttached = mHWData->mCPUAttached[aCpu];
6636 }
6637
6638 return S_OK;
6639}
6640
6641HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6642{
6643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 Utf8Str log = i_queryLogFilename(aIdx);
6646 if (!RTFileExists(log.c_str()))
6647 log.setNull();
6648 aFilename = log;
6649
6650 return S_OK;
6651}
6652
6653HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6654{
6655 if (aSize < 0)
6656 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6657
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 HRESULT rc = S_OK;
6661 Utf8Str log = i_queryLogFilename(aIdx);
6662
6663 /* do not unnecessarily hold the lock while doing something which does
6664 * not need the lock and potentially takes a long time. */
6665 alock.release();
6666
6667 /* Limit the chunk size to 32K for now, as that gives better performance
6668 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6669 * One byte expands to approx. 25 bytes of breathtaking XML. */
6670 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6671 aData.resize(cbData);
6672
6673 RTFILE LogFile;
6674 int vrc = RTFileOpen(&LogFile, log.c_str(),
6675 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6676 if (RT_SUCCESS(vrc))
6677 {
6678 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6679 if (RT_SUCCESS(vrc))
6680 aData.resize(cbData);
6681 else
6682 rc = setError(VBOX_E_IPRT_ERROR,
6683 tr("Could not read log file '%s' (%Rrc)"),
6684 log.c_str(), vrc);
6685 RTFileClose(LogFile);
6686 }
6687 else
6688 rc = setError(VBOX_E_IPRT_ERROR,
6689 tr("Could not open log file '%s' (%Rrc)"),
6690 log.c_str(), vrc);
6691
6692 if (FAILED(rc))
6693 aData.resize(0);
6694
6695 return rc;
6696}
6697
6698
6699/**
6700 * Currently this method doesn't attach device to the running VM,
6701 * just makes sure it's plugged on next VM start.
6702 */
6703HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6704{
6705 // lock scope
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708
6709 HRESULT rc = i_checkStateDependency(MutableStateDep);
6710 if (FAILED(rc)) return rc;
6711
6712 ChipsetType_T aChipset = ChipsetType_PIIX3;
6713 COMGETTER(ChipsetType)(&aChipset);
6714
6715 if (aChipset != ChipsetType_ICH9)
6716 {
6717 return setError(E_INVALIDARG,
6718 tr("Host PCI attachment only supported with ICH9 chipset"));
6719 }
6720
6721 // check if device with this host PCI address already attached
6722 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6723 it != mHWData->mPCIDeviceAssignments.end();
6724 ++it)
6725 {
6726 LONG iHostAddress = -1;
6727 ComPtr<PCIDeviceAttachment> pAttach;
6728 pAttach = *it;
6729 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6730 if (iHostAddress == aHostAddress)
6731 return setError(E_INVALIDARG,
6732 tr("Device with host PCI address already attached to this VM"));
6733 }
6734
6735 ComObjPtr<PCIDeviceAttachment> pda;
6736 char name[32];
6737
6738 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6739 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6740 Bstr bname(name);
6741 pda.createObject();
6742 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6743 i_setModified(IsModified_MachineData);
6744 mHWData.backup();
6745 mHWData->mPCIDeviceAssignments.push_back(pda);
6746 }
6747
6748 return S_OK;
6749}
6750
6751/**
6752 * Currently this method doesn't detach device from the running VM,
6753 * just makes sure it's not plugged on next VM start.
6754 */
6755HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6756{
6757 ComObjPtr<PCIDeviceAttachment> pAttach;
6758 bool fRemoved = false;
6759 HRESULT rc;
6760
6761 // lock scope
6762 {
6763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 rc = i_checkStateDependency(MutableStateDep);
6766 if (FAILED(rc)) return rc;
6767
6768 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6769 it != mHWData->mPCIDeviceAssignments.end();
6770 ++it)
6771 {
6772 LONG iHostAddress = -1;
6773 pAttach = *it;
6774 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6775 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6776 {
6777 i_setModified(IsModified_MachineData);
6778 mHWData.backup();
6779 mHWData->mPCIDeviceAssignments.remove(pAttach);
6780 fRemoved = true;
6781 break;
6782 }
6783 }
6784 }
6785
6786
6787 /* Fire event outside of the lock */
6788 if (fRemoved)
6789 {
6790 Assert(!pAttach.isNull());
6791 ComPtr<IEventSource> es;
6792 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6793 Assert(SUCCEEDED(rc));
6794 Bstr mid;
6795 rc = this->COMGETTER(Id)(mid.asOutParam());
6796 Assert(SUCCEEDED(rc));
6797 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6798 }
6799
6800 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6801 tr("No host PCI device %08x attached"),
6802 aHostAddress
6803 );
6804}
6805
6806HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6807{
6808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6811
6812 size_t i = 0;
6813 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6814 it != mHWData->mPCIDeviceAssignments.end();
6815 ++i, ++it)
6816 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6817
6818 return S_OK;
6819}
6820
6821HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6822{
6823 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6824
6825 return S_OK;
6826}
6827
6828HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6829{
6830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6831
6832 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6833
6834 return S_OK;
6835}
6836
6837HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6838{
6839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6840 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6841 if (SUCCEEDED(hrc))
6842 {
6843 hrc = mHWData.backupEx();
6844 if (SUCCEEDED(hrc))
6845 {
6846 i_setModified(IsModified_MachineData);
6847 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6848 }
6849 }
6850 return hrc;
6851}
6852
6853HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6854{
6855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6857 return S_OK;
6858}
6859
6860HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6861{
6862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6863 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6864 if (SUCCEEDED(hrc))
6865 {
6866 hrc = mHWData.backupEx();
6867 if (SUCCEEDED(hrc))
6868 {
6869 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6870 if (SUCCEEDED(hrc))
6871 i_setModified(IsModified_MachineData);
6872 }
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6882
6883 return S_OK;
6884}
6885
6886HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6887{
6888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6890 if (SUCCEEDED(hrc))
6891 {
6892 hrc = mHWData.backupEx();
6893 if (SUCCEEDED(hrc))
6894 {
6895 i_setModified(IsModified_MachineData);
6896 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6897 }
6898 }
6899 return hrc;
6900}
6901
6902HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6903{
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6907
6908 return S_OK;
6909}
6910
6911HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6912{
6913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6914
6915 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6916 if ( SUCCEEDED(hrc)
6917 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6918 {
6919 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6920 int vrc;
6921
6922 if (aAutostartEnabled)
6923 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6924 else
6925 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6926
6927 if (RT_SUCCESS(vrc))
6928 {
6929 hrc = mHWData.backupEx();
6930 if (SUCCEEDED(hrc))
6931 {
6932 i_setModified(IsModified_MachineData);
6933 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6934 }
6935 }
6936 else if (vrc == VERR_NOT_SUPPORTED)
6937 hrc = setError(VBOX_E_NOT_SUPPORTED,
6938 tr("The VM autostart feature is not supported on this platform"));
6939 else if (vrc == VERR_PATH_NOT_FOUND)
6940 hrc = setError(E_FAIL,
6941 tr("The path to the autostart database is not set"));
6942 else
6943 hrc = setError(E_UNEXPECTED,
6944 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6945 aAutostartEnabled ? "Adding" : "Removing",
6946 mUserData->s.strName.c_str(), vrc);
6947 }
6948 return hrc;
6949}
6950
6951HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6952{
6953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6956
6957 return S_OK;
6958}
6959
6960HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6961{
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6964 if (SUCCEEDED(hrc))
6965 {
6966 hrc = mHWData.backupEx();
6967 if (SUCCEEDED(hrc))
6968 {
6969 i_setModified(IsModified_MachineData);
6970 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6971 }
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6981
6982 return S_OK;
6983}
6984
6985HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6989 if ( SUCCEEDED(hrc)
6990 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6991 {
6992 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6993 int vrc;
6994
6995 if (aAutostopType != AutostopType_Disabled)
6996 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6997 else
6998 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6999
7000 if (RT_SUCCESS(vrc))
7001 {
7002 hrc = mHWData.backupEx();
7003 if (SUCCEEDED(hrc))
7004 {
7005 i_setModified(IsModified_MachineData);
7006 mHWData->mAutostart.enmAutostopType = aAutostopType;
7007 }
7008 }
7009 else if (vrc == VERR_NOT_SUPPORTED)
7010 hrc = setError(VBOX_E_NOT_SUPPORTED,
7011 tr("The VM autostop feature is not supported on this platform"));
7012 else if (vrc == VERR_PATH_NOT_FOUND)
7013 hrc = setError(E_FAIL,
7014 tr("The path to the autostart database is not set"));
7015 else
7016 hrc = setError(E_UNEXPECTED,
7017 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7018 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7019 mUserData->s.strName.c_str(), vrc);
7020 }
7021 return hrc;
7022}
7023
7024HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7025{
7026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7027
7028 aDefaultFrontend = mHWData->mDefaultFrontend;
7029
7030 return S_OK;
7031}
7032
7033HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7034{
7035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7036 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7037 if (SUCCEEDED(hrc))
7038 {
7039 hrc = mHWData.backupEx();
7040 if (SUCCEEDED(hrc))
7041 {
7042 i_setModified(IsModified_MachineData);
7043 mHWData->mDefaultFrontend = aDefaultFrontend;
7044 }
7045 }
7046 return hrc;
7047}
7048
7049HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7050{
7051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7052 size_t cbIcon = mUserData->mIcon.size();
7053 aIcon.resize(cbIcon);
7054 if (cbIcon)
7055 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7056 return S_OK;
7057}
7058
7059HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7060{
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7063 if (SUCCEEDED(hrc))
7064 {
7065 i_setModified(IsModified_MachineData);
7066 mUserData.backup();
7067 size_t cbIcon = aIcon.size();
7068 mUserData->mIcon.resize(cbIcon);
7069 if (cbIcon)
7070 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7071 }
7072 return hrc;
7073}
7074
7075HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7076{
7077#ifdef VBOX_WITH_USB
7078 *aUSBProxyAvailable = true;
7079#else
7080 *aUSBProxyAvailable = false;
7081#endif
7082 return S_OK;
7083}
7084
7085HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7086 ComPtr<IProgress> &aProgress)
7087{
7088 ComObjPtr<Progress> pP;
7089 Progress *ppP = pP;
7090 IProgress *iP = static_cast<IProgress *>(ppP);
7091 IProgress **pProgress = &iP;
7092
7093 IMachine *pTarget = aTarget;
7094
7095 /* Convert the options. */
7096 RTCList<CloneOptions_T> optList;
7097 if (aOptions.size())
7098 for (size_t i = 0; i < aOptions.size(); ++i)
7099 optList.append(aOptions[i]);
7100
7101 if (optList.contains(CloneOptions_Link))
7102 {
7103 if (!i_isSnapshotMachine())
7104 return setError(E_INVALIDARG,
7105 tr("Linked clone can only be created from a snapshot"));
7106 if (aMode != CloneMode_MachineState)
7107 return setError(E_INVALIDARG,
7108 tr("Linked clone can only be created for a single machine state"));
7109 }
7110 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7111
7112 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7113
7114 HRESULT rc = pWorker->start(pProgress);
7115
7116 pP = static_cast<Progress *>(*pProgress);
7117 pP.queryInterfaceTo(aProgress.asOutParam());
7118
7119 return rc;
7120
7121}
7122
7123HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7124{
7125 NOREF(aProgress);
7126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7127
7128 // This check should always fail.
7129 HRESULT rc = i_checkStateDependency(MutableStateDep);
7130 if (FAILED(rc)) return rc;
7131
7132 AssertFailedReturn(E_NOTIMPL);
7133}
7134
7135HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7136{
7137 NOREF(aSavedStateFile);
7138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7139
7140 // This check should always fail.
7141 HRESULT rc = i_checkStateDependency(MutableStateDep);
7142 if (FAILED(rc)) return rc;
7143
7144 AssertFailedReturn(E_NOTIMPL);
7145}
7146
7147HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7148{
7149 NOREF(aFRemoveFile);
7150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 // This check should always fail.
7153 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7154 if (FAILED(rc)) return rc;
7155
7156 AssertFailedReturn(E_NOTIMPL);
7157}
7158
7159// public methods for internal purposes
7160/////////////////////////////////////////////////////////////////////////////
7161
7162/**
7163 * Adds the given IsModified_* flag to the dirty flags of the machine.
7164 * This must be called either during i_loadSettings or under the machine write lock.
7165 * @param fl
7166 */
7167void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7168{
7169 mData->flModifications |= fl;
7170 if (fAllowStateModification && i_isStateModificationAllowed())
7171 mData->mCurrentStateModified = true;
7172}
7173
7174/**
7175 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7176 * care of the write locking.
7177 *
7178 * @param fModifications The flag to add.
7179 */
7180void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7181{
7182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7183 i_setModified(fModification, fAllowStateModification);
7184}
7185
7186/**
7187 * Saves the registry entry of this machine to the given configuration node.
7188 *
7189 * @param aEntryNode Node to save the registry entry to.
7190 *
7191 * @note locks this object for reading.
7192 */
7193HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7194{
7195 AutoLimitedCaller autoCaller(this);
7196 AssertComRCReturnRC(autoCaller.rc());
7197
7198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7199
7200 data.uuid = mData->mUuid;
7201 data.strSettingsFile = mData->m_strConfigFile;
7202
7203 return S_OK;
7204}
7205
7206/**
7207 * Calculates the absolute path of the given path taking the directory of the
7208 * machine settings file as the current directory.
7209 *
7210 * @param aPath Path to calculate the absolute path for.
7211 * @param aResult Where to put the result (used only on success, can be the
7212 * same Utf8Str instance as passed in @a aPath).
7213 * @return IPRT result.
7214 *
7215 * @note Locks this object for reading.
7216 */
7217int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7218{
7219 AutoCaller autoCaller(this);
7220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7221
7222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7223
7224 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7225
7226 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7227
7228 strSettingsDir.stripFilename();
7229 char folder[RTPATH_MAX];
7230 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7231 if (RT_SUCCESS(vrc))
7232 aResult = folder;
7233
7234 return vrc;
7235}
7236
7237/**
7238 * Copies strSource to strTarget, making it relative to the machine folder
7239 * if it is a subdirectory thereof, or simply copying it otherwise.
7240 *
7241 * @param strSource Path to evaluate and copy.
7242 * @param strTarget Buffer to receive target path.
7243 *
7244 * @note Locks this object for reading.
7245 */
7246void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7247 Utf8Str &strTarget)
7248{
7249 AutoCaller autoCaller(this);
7250 AssertComRCReturn(autoCaller.rc(), (void)0);
7251
7252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7253
7254 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7255 // use strTarget as a temporary buffer to hold the machine settings dir
7256 strTarget = mData->m_strConfigFileFull;
7257 strTarget.stripFilename();
7258 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7259 {
7260 // is relative: then append what's left
7261 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7262 // for empty paths (only possible for subdirs) use "." to avoid
7263 // triggering default settings for not present config attributes.
7264 if (strTarget.isEmpty())
7265 strTarget = ".";
7266 }
7267 else
7268 // is not relative: then overwrite
7269 strTarget = strSource;
7270}
7271
7272/**
7273 * Returns the full path to the machine's log folder in the
7274 * \a aLogFolder argument.
7275 */
7276void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7277{
7278 AutoCaller autoCaller(this);
7279 AssertComRCReturnVoid(autoCaller.rc());
7280
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 char szTmp[RTPATH_MAX];
7284 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7285 if (RT_SUCCESS(vrc))
7286 {
7287 if (szTmp[0] && !mUserData.isNull())
7288 {
7289 char szTmp2[RTPATH_MAX];
7290 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7291 if (RT_SUCCESS(vrc))
7292 aLogFolder = BstrFmt("%s%c%s",
7293 szTmp2,
7294 RTPATH_DELIMITER,
7295 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7296 }
7297 else
7298 vrc = VERR_PATH_IS_RELATIVE;
7299 }
7300
7301 if (RT_FAILURE(vrc))
7302 {
7303 // fallback if VBOX_USER_LOGHOME is not set or invalid
7304 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7305 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7306 aLogFolder.append(RTPATH_DELIMITER);
7307 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7308 }
7309}
7310
7311/**
7312 * Returns the full path to the machine's log file for an given index.
7313 */
7314Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7315 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7316{
7317 Utf8Str logFolder;
7318 getLogFolder(logFolder);
7319 Assert(logFolder.length());
7320 Utf8Str log;
7321 if (idx == 0)
7322 log = Utf8StrFmt("%s%cVBox.log",
7323 logFolder.c_str(), RTPATH_DELIMITER);
7324 else
7325 log = Utf8StrFmt("%s%cVBox.log.%d",
7326 logFolder.c_str(), RTPATH_DELIMITER, idx);
7327 return log;
7328}
7329
7330/**
7331 * Returns the full path to the machine's (hardened) startup log file.
7332 */
7333Utf8Str Machine::i_getStartupLogFilename(void)
7334{
7335 Utf8Str strFilename;
7336 getLogFolder(strFilename);
7337 Assert(strFilename.length());
7338 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7339 return strFilename;
7340}
7341
7342
7343/**
7344 * Composes a unique saved state filename based on the current system time. The filename is
7345 * granular to the second so this will work so long as no more than one snapshot is taken on
7346 * a machine per second.
7347 *
7348 * Before version 4.1, we used this formula for saved state files:
7349 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7350 * which no longer works because saved state files can now be shared between the saved state of the
7351 * "saved" machine and an online snapshot, and the following would cause problems:
7352 * 1) save machine
7353 * 2) create online snapshot from that machine state --> reusing saved state file
7354 * 3) save machine again --> filename would be reused, breaking the online snapshot
7355 *
7356 * So instead we now use a timestamp.
7357 *
7358 * @param str
7359 */
7360
7361void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7362{
7363 AutoCaller autoCaller(this);
7364 AssertComRCReturnVoid(autoCaller.rc());
7365
7366 {
7367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7368 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7369 }
7370
7371 RTTIMESPEC ts;
7372 RTTimeNow(&ts);
7373 RTTIME time;
7374 RTTimeExplode(&time, &ts);
7375
7376 strStateFilePath += RTPATH_DELIMITER;
7377 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7378 time.i32Year, time.u8Month, time.u8MonthDay,
7379 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7380}
7381
7382/**
7383 * Returns the full path to the default video capture file.
7384 */
7385void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7386{
7387 AutoCaller autoCaller(this);
7388 AssertComRCReturnVoid(autoCaller.rc());
7389
7390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7391
7392 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7393 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7394 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7395}
7396
7397/**
7398 * Returns whether at least one USB controller is present for the VM.
7399 */
7400bool Machine::i_isUSBControllerPresent()
7401{
7402 AutoCaller autoCaller(this);
7403 AssertComRCReturn(autoCaller.rc(), false);
7404
7405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7406
7407 return (mUSBControllers->size() > 0);
7408}
7409
7410/**
7411 * @note Locks this object for writing, calls the client process
7412 * (inside the lock).
7413 */
7414HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7415 const Utf8Str &strFrontend,
7416 const Utf8Str &strEnvironment,
7417 ProgressProxy *aProgress)
7418{
7419 LogFlowThisFuncEnter();
7420
7421 AssertReturn(aControl, E_FAIL);
7422 AssertReturn(aProgress, E_FAIL);
7423 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7424
7425 AutoCaller autoCaller(this);
7426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7427
7428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7429
7430 if (!mData->mRegistered)
7431 return setError(E_UNEXPECTED,
7432 tr("The machine '%s' is not registered"),
7433 mUserData->s.strName.c_str());
7434
7435 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7436
7437 /* The process started when launching a VM with separate UI/VM processes is always
7438 * the UI process, i.e. needs special handling as it won't claim the session. */
7439 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7440
7441 if (fSeparate)
7442 {
7443 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName == "headless")
7444 return setError(VBOX_E_INVALID_OBJECT_STATE,
7445 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7446 mUserData->s.strName.c_str());
7447 }
7448 else
7449 {
7450 if ( mData->mSession.mState == SessionState_Locked
7451 || mData->mSession.mState == SessionState_Spawning
7452 || mData->mSession.mState == SessionState_Unlocking)
7453 return setError(VBOX_E_INVALID_OBJECT_STATE,
7454 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7455 mUserData->s.strName.c_str());
7456
7457 /* may not be busy */
7458 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7459 }
7460
7461 /* get the path to the executable */
7462 char szPath[RTPATH_MAX];
7463 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7464 size_t cchBufLeft = strlen(szPath);
7465 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7466 szPath[cchBufLeft] = 0;
7467 char *pszNamePart = szPath + cchBufLeft;
7468 cchBufLeft = sizeof(szPath) - cchBufLeft;
7469
7470 int vrc = VINF_SUCCESS;
7471 RTPROCESS pid = NIL_RTPROCESS;
7472
7473 RTENV env = RTENV_DEFAULT;
7474
7475 if (!strEnvironment.isEmpty())
7476 {
7477 char *newEnvStr = NULL;
7478
7479 do
7480 {
7481 /* clone the current environment */
7482 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7483 AssertRCBreakStmt(vrc2, vrc = vrc2);
7484
7485 newEnvStr = RTStrDup(strEnvironment.c_str());
7486 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7487
7488 /* put new variables to the environment
7489 * (ignore empty variable names here since RTEnv API
7490 * intentionally doesn't do that) */
7491 char *var = newEnvStr;
7492 for (char *p = newEnvStr; *p; ++p)
7493 {
7494 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7495 {
7496 *p = '\0';
7497 if (*var)
7498 {
7499 char *val = strchr(var, '=');
7500 if (val)
7501 {
7502 *val++ = '\0';
7503 vrc2 = RTEnvSetEx(env, var, val);
7504 }
7505 else
7506 vrc2 = RTEnvUnsetEx(env, var);
7507 if (RT_FAILURE(vrc2))
7508 break;
7509 }
7510 var = p + 1;
7511 }
7512 }
7513 if (RT_SUCCESS(vrc2) && *var)
7514 vrc2 = RTEnvPutEx(env, var);
7515
7516 AssertRCBreakStmt(vrc2, vrc = vrc2);
7517 }
7518 while (0);
7519
7520 if (newEnvStr != NULL)
7521 RTStrFree(newEnvStr);
7522 }
7523
7524 /* Hardened startup logging */
7525#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7526 Utf8Str strSupStartLogArg("--sup-startup-log=");
7527 {
7528 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7529 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7530 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7531 {
7532 Utf8Str strStartupLogDir = strStartupLogFile;
7533 strStartupLogDir.stripFilename();
7534 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7535 file without stripping the file. */
7536 }
7537 strSupStartLogArg.append(strStartupLogFile);
7538 }
7539 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7540#else
7541 const char *pszSupStartupLogArg = NULL;
7542#endif
7543
7544 Utf8Str strCanonicalName;
7545
7546#ifdef VBOX_WITH_QTGUI
7547 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7548 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7549 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7550 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7551 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7552 {
7553 strCanonicalName = "GUI/Qt";
7554# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7555 /* Modify the base path so that we don't need to use ".." below. */
7556 RTPathStripTrailingSlash(szPath);
7557 RTPathStripFilename(szPath);
7558 cchBufLeft = strlen(szPath);
7559 pszNamePart = szPath + cchBufLeft;
7560 cchBufLeft = sizeof(szPath) - cchBufLeft;
7561
7562# define OSX_APP_NAME "VirtualBoxVM"
7563# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7564
7565 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7566 if ( strAppOverride.contains(".")
7567 || strAppOverride.contains("/")
7568 || strAppOverride.contains("\\")
7569 || strAppOverride.contains(":"))
7570 strAppOverride.setNull();
7571 Utf8Str strAppPath;
7572 if (!strAppOverride.isEmpty())
7573 {
7574 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7575 Utf8Str strFullPath(szPath);
7576 strFullPath.append(strAppPath);
7577 /* there is a race, but people using this deserve the failure */
7578 if (!RTFileExists(strFullPath.c_str()))
7579 strAppOverride.setNull();
7580 }
7581 if (strAppOverride.isEmpty())
7582 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7583 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7584 strcpy(pszNamePart, strAppPath.c_str());
7585# else
7586 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7587 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7588 strcpy(pszNamePart, s_szVirtualBox_exe);
7589# endif
7590
7591 Utf8Str idStr = mData->mUuid.toString();
7592 const char *apszArgs[] =
7593 {
7594 szPath,
7595 "--comment", mUserData->s.strName.c_str(),
7596 "--startvm", idStr.c_str(),
7597 "--no-startvm-errormsgbox",
7598 NULL, /* For "--separate". */
7599 NULL, /* For "--sup-startup-log". */
7600 NULL
7601 };
7602 unsigned iArg = 6;
7603 if (fSeparate)
7604 apszArgs[iArg++] = "--separate";
7605 apszArgs[iArg++] = pszSupStartupLogArg;
7606
7607 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7608 }
7609#else /* !VBOX_WITH_QTGUI */
7610 if (0)
7611 ;
7612#endif /* VBOX_WITH_QTGUI */
7613
7614 else
7615
7616#ifdef VBOX_WITH_VBOXSDL
7617 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7618 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7619 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7620 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7621 {
7622 strCanonicalName = "GUI/SDL";
7623 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7624 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7625 strcpy(pszNamePart, s_szVBoxSDL_exe);
7626
7627 Utf8Str idStr = mData->mUuid.toString();
7628 const char *apszArgs[] =
7629 {
7630 szPath,
7631 "--comment", mUserData->s.strName.c_str(),
7632 "--startvm", idStr.c_str(),
7633 NULL, /* For "--separate". */
7634 NULL, /* For "--sup-startup-log". */
7635 NULL
7636 };
7637 unsigned iArg = 5;
7638 if (fSeparate)
7639 apszArgs[iArg++] = "--separate";
7640 apszArgs[iArg++] = pszSupStartupLogArg;
7641
7642 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7643 }
7644#else /* !VBOX_WITH_VBOXSDL */
7645 if (0)
7646 ;
7647#endif /* !VBOX_WITH_VBOXSDL */
7648
7649 else
7650
7651#ifdef VBOX_WITH_HEADLESS
7652 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7653 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7654 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7655 )
7656 {
7657 strCanonicalName = "headless";
7658 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7659 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7660 * and a VM works even if the server has not been installed.
7661 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7662 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7663 * differently in 4.0 and 3.x.
7664 */
7665 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7666 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7667 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7668
7669 Utf8Str idStr = mData->mUuid.toString();
7670 const char *apszArgs[] =
7671 {
7672 szPath,
7673 "--comment", mUserData->s.strName.c_str(),
7674 "--startvm", idStr.c_str(),
7675 "--vrde", "config",
7676 NULL, /* For "--capture". */
7677 NULL, /* For "--sup-startup-log". */
7678 NULL
7679 };
7680 unsigned iArg = 7;
7681 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7682 apszArgs[iArg++] = "--capture";
7683 apszArgs[iArg++] = pszSupStartupLogArg;
7684
7685# ifdef RT_OS_WINDOWS
7686 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7687# else
7688 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7689# endif
7690 }
7691#else /* !VBOX_WITH_HEADLESS */
7692 if (0)
7693 ;
7694#endif /* !VBOX_WITH_HEADLESS */
7695 else
7696 {
7697 RTEnvDestroy(env);
7698 return setError(E_INVALIDARG,
7699 tr("Invalid frontend name: '%s'"),
7700 strFrontend.c_str());
7701 }
7702
7703 RTEnvDestroy(env);
7704
7705 if (RT_FAILURE(vrc))
7706 return setError(VBOX_E_IPRT_ERROR,
7707 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7708 mUserData->s.strName.c_str(), vrc);
7709
7710 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7711
7712 if (!fSeparate)
7713 {
7714 /*
7715 * Note that we don't release the lock here before calling the client,
7716 * because it doesn't need to call us back if called with a NULL argument.
7717 * Releasing the lock here is dangerous because we didn't prepare the
7718 * launch data yet, but the client we've just started may happen to be
7719 * too fast and call LockMachine() that will fail (because of PID, etc.),
7720 * so that the Machine will never get out of the Spawning session state.
7721 */
7722
7723 /* inform the session that it will be a remote one */
7724 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7725#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7726 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7727#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7728 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7729#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7730 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7731
7732 if (FAILED(rc))
7733 {
7734 /* restore the session state */
7735 mData->mSession.mState = SessionState_Unlocked;
7736 alock.release();
7737 mParent->i_addProcessToReap(pid);
7738 /* The failure may occur w/o any error info (from RPC), so provide one */
7739 return setError(VBOX_E_VM_ERROR,
7740 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7741 }
7742
7743 /* attach launch data to the machine */
7744 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7745 mData->mSession.mRemoteControls.push_back(aControl);
7746 mData->mSession.mProgress = aProgress;
7747 mData->mSession.mPID = pid;
7748 mData->mSession.mState = SessionState_Spawning;
7749 Assert(strCanonicalName.isNotEmpty());
7750 mData->mSession.mName = strCanonicalName;
7751 }
7752 else
7753 {
7754 /* For separate UI process we declare the launch as completed instantly, as the
7755 * actual headless VM start may or may not come. No point in remembering anything
7756 * yet, as what matters for us is when the headless VM gets started. */
7757 aProgress->i_notifyComplete(S_OK);
7758 }
7759
7760 alock.release();
7761 mParent->i_addProcessToReap(pid);
7762
7763 LogFlowThisFuncLeave();
7764 return S_OK;
7765}
7766
7767/**
7768 * Returns @c true if the given session machine instance has an open direct
7769 * session (and optionally also for direct sessions which are closing) and
7770 * returns the session control machine instance if so.
7771 *
7772 * Note that when the method returns @c false, the arguments remain unchanged.
7773 *
7774 * @param aMachine Session machine object.
7775 * @param aControl Direct session control object (optional).
7776 * @param aAllowClosing If true then additionally a session which is currently
7777 * being closed will also be allowed.
7778 *
7779 * @note locks this object for reading.
7780 */
7781bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7782 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7783 bool aAllowClosing /*= false*/)
7784{
7785 AutoLimitedCaller autoCaller(this);
7786 AssertComRCReturn(autoCaller.rc(), false);
7787
7788 /* just return false for inaccessible machines */
7789 if (getObjectState().getState() != ObjectState::Ready)
7790 return false;
7791
7792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7793
7794 if ( ( mData->mSession.mState == SessionState_Locked
7795 && mData->mSession.mLockType == LockType_VM)
7796 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7797 )
7798 {
7799 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7800
7801 aMachine = mData->mSession.mMachine;
7802
7803 if (aControl != NULL)
7804 *aControl = mData->mSession.mDirectControl;
7805
7806 return true;
7807 }
7808
7809 return false;
7810}
7811
7812/**
7813 * Returns @c true if the given machine has an spawning direct session.
7814 *
7815 * @note locks this object for reading.
7816 */
7817bool Machine::i_isSessionSpawning()
7818{
7819 AutoLimitedCaller autoCaller(this);
7820 AssertComRCReturn(autoCaller.rc(), false);
7821
7822 /* just return false for inaccessible machines */
7823 if (getObjectState().getState() != ObjectState::Ready)
7824 return false;
7825
7826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7827
7828 if (mData->mSession.mState == SessionState_Spawning)
7829 return true;
7830
7831 return false;
7832}
7833
7834/**
7835 * Called from the client watcher thread to check for unexpected client process
7836 * death during Session_Spawning state (e.g. before it successfully opened a
7837 * direct session).
7838 *
7839 * On Win32 and on OS/2, this method is called only when we've got the
7840 * direct client's process termination notification, so it always returns @c
7841 * true.
7842 *
7843 * On other platforms, this method returns @c true if the client process is
7844 * terminated and @c false if it's still alive.
7845 *
7846 * @note Locks this object for writing.
7847 */
7848bool Machine::i_checkForSpawnFailure()
7849{
7850 AutoCaller autoCaller(this);
7851 if (!autoCaller.isOk())
7852 {
7853 /* nothing to do */
7854 LogFlowThisFunc(("Already uninitialized!\n"));
7855 return true;
7856 }
7857
7858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7859
7860 if (mData->mSession.mState != SessionState_Spawning)
7861 {
7862 /* nothing to do */
7863 LogFlowThisFunc(("Not spawning any more!\n"));
7864 return true;
7865 }
7866
7867 HRESULT rc = S_OK;
7868
7869 /* PID not yet initialized, skip check. */
7870 if (mData->mSession.mPID == NIL_RTPROCESS)
7871 return false;
7872
7873 RTPROCSTATUS status;
7874 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7875
7876 if (vrc != VERR_PROCESS_RUNNING)
7877 {
7878 Utf8Str strExtraInfo;
7879
7880#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7881 /* If the startup logfile exists and is of non-zero length, tell the
7882 user to look there for more details to encourage them to attach it
7883 when reporting startup issues. */
7884 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7885 uint64_t cbStartupLogFile = 0;
7886 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7887 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7888 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7889#endif
7890
7891 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7892 rc = setError(E_FAIL,
7893 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7894 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7895 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7896 rc = setError(E_FAIL,
7897 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7898 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7899 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7900 rc = setError(E_FAIL,
7901 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7902 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7903 else
7904 rc = setError(E_FAIL,
7905 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7906 i_getName().c_str(), vrc, strExtraInfo.c_str());
7907 }
7908
7909 if (FAILED(rc))
7910 {
7911 /* Close the remote session, remove the remote control from the list
7912 * and reset session state to Closed (@note keep the code in sync with
7913 * the relevant part in LockMachine()). */
7914
7915 Assert(mData->mSession.mRemoteControls.size() == 1);
7916 if (mData->mSession.mRemoteControls.size() == 1)
7917 {
7918 ErrorInfoKeeper eik;
7919 mData->mSession.mRemoteControls.front()->Uninitialize();
7920 }
7921
7922 mData->mSession.mRemoteControls.clear();
7923 mData->mSession.mState = SessionState_Unlocked;
7924
7925 /* finalize the progress after setting the state */
7926 if (!mData->mSession.mProgress.isNull())
7927 {
7928 mData->mSession.mProgress->notifyComplete(rc);
7929 mData->mSession.mProgress.setNull();
7930 }
7931
7932 mData->mSession.mPID = NIL_RTPROCESS;
7933
7934 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7935 return true;
7936 }
7937
7938 return false;
7939}
7940
7941/**
7942 * Checks whether the machine can be registered. If so, commits and saves
7943 * all settings.
7944 *
7945 * @note Must be called from mParent's write lock. Locks this object and
7946 * children for writing.
7947 */
7948HRESULT Machine::i_prepareRegister()
7949{
7950 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7951
7952 AutoLimitedCaller autoCaller(this);
7953 AssertComRCReturnRC(autoCaller.rc());
7954
7955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7956
7957 /* wait for state dependents to drop to zero */
7958 i_ensureNoStateDependencies();
7959
7960 if (!mData->mAccessible)
7961 return setError(VBOX_E_INVALID_OBJECT_STATE,
7962 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7963 mUserData->s.strName.c_str(),
7964 mData->mUuid.toString().c_str());
7965
7966 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7967
7968 if (mData->mRegistered)
7969 return setError(VBOX_E_INVALID_OBJECT_STATE,
7970 tr("The machine '%s' with UUID {%s} is already registered"),
7971 mUserData->s.strName.c_str(),
7972 mData->mUuid.toString().c_str());
7973
7974 HRESULT rc = S_OK;
7975
7976 // Ensure the settings are saved. If we are going to be registered and
7977 // no config file exists yet, create it by calling i_saveSettings() too.
7978 if ( (mData->flModifications)
7979 || (!mData->pMachineConfigFile->fileExists())
7980 )
7981 {
7982 rc = i_saveSettings(NULL);
7983 // no need to check whether VirtualBox.xml needs saving too since
7984 // we can't have a machine XML file rename pending
7985 if (FAILED(rc)) return rc;
7986 }
7987
7988 /* more config checking goes here */
7989
7990 if (SUCCEEDED(rc))
7991 {
7992 /* we may have had implicit modifications we want to fix on success */
7993 i_commit();
7994
7995 mData->mRegistered = true;
7996 }
7997 else
7998 {
7999 /* we may have had implicit modifications we want to cancel on failure*/
8000 i_rollback(false /* aNotify */);
8001 }
8002
8003 return rc;
8004}
8005
8006/**
8007 * Increases the number of objects dependent on the machine state or on the
8008 * registered state. Guarantees that these two states will not change at least
8009 * until #releaseStateDependency() is called.
8010 *
8011 * Depending on the @a aDepType value, additional state checks may be made.
8012 * These checks will set extended error info on failure. See
8013 * #checkStateDependency() for more info.
8014 *
8015 * If this method returns a failure, the dependency is not added and the caller
8016 * is not allowed to rely on any particular machine state or registration state
8017 * value and may return the failed result code to the upper level.
8018 *
8019 * @param aDepType Dependency type to add.
8020 * @param aState Current machine state (NULL if not interested).
8021 * @param aRegistered Current registered state (NULL if not interested).
8022 *
8023 * @note Locks this object for writing.
8024 */
8025HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8026 MachineState_T *aState /* = NULL */,
8027 BOOL *aRegistered /* = NULL */)
8028{
8029 AutoCaller autoCaller(this);
8030 AssertComRCReturnRC(autoCaller.rc());
8031
8032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8033
8034 HRESULT rc = i_checkStateDependency(aDepType);
8035 if (FAILED(rc)) return rc;
8036
8037 {
8038 if (mData->mMachineStateChangePending != 0)
8039 {
8040 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8041 * drop to zero so don't add more. It may make sense to wait a bit
8042 * and retry before reporting an error (since the pending state
8043 * transition should be really quick) but let's just assert for
8044 * now to see if it ever happens on practice. */
8045
8046 AssertFailed();
8047
8048 return setError(E_ACCESSDENIED,
8049 tr("Machine state change is in progress. Please retry the operation later."));
8050 }
8051
8052 ++mData->mMachineStateDeps;
8053 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8054 }
8055
8056 if (aState)
8057 *aState = mData->mMachineState;
8058 if (aRegistered)
8059 *aRegistered = mData->mRegistered;
8060
8061 return S_OK;
8062}
8063
8064/**
8065 * Decreases the number of objects dependent on the machine state.
8066 * Must always complete the #addStateDependency() call after the state
8067 * dependency is no more necessary.
8068 */
8069void Machine::i_releaseStateDependency()
8070{
8071 AutoCaller autoCaller(this);
8072 AssertComRCReturnVoid(autoCaller.rc());
8073
8074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8075
8076 /* releaseStateDependency() w/o addStateDependency()? */
8077 AssertReturnVoid(mData->mMachineStateDeps != 0);
8078 -- mData->mMachineStateDeps;
8079
8080 if (mData->mMachineStateDeps == 0)
8081 {
8082 /* inform i_ensureNoStateDependencies() that there are no more deps */
8083 if (mData->mMachineStateChangePending != 0)
8084 {
8085 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8086 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8087 }
8088 }
8089}
8090
8091Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8092{
8093 /* start with nothing found */
8094 Utf8Str strResult("");
8095
8096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8097
8098 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8099 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8100 // found:
8101 strResult = it->second; // source is a Utf8Str
8102
8103 return strResult;
8104}
8105
8106// protected methods
8107/////////////////////////////////////////////////////////////////////////////
8108
8109/**
8110 * Performs machine state checks based on the @a aDepType value. If a check
8111 * fails, this method will set extended error info, otherwise it will return
8112 * S_OK. It is supposed, that on failure, the caller will immediately return
8113 * the return value of this method to the upper level.
8114 *
8115 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8116 *
8117 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8118 * current state of this machine object allows to change settings of the
8119 * machine (i.e. the machine is not registered, or registered but not running
8120 * and not saved). It is useful to call this method from Machine setters
8121 * before performing any change.
8122 *
8123 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8124 * as for MutableStateDep except that if the machine is saved, S_OK is also
8125 * returned. This is useful in setters which allow changing machine
8126 * properties when it is in the saved state.
8127 *
8128 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8129 * if the current state of this machine object allows to change runtime
8130 * changeable settings of the machine (i.e. the machine is not registered, or
8131 * registered but either running or not running and not saved). It is useful
8132 * to call this method from Machine setters before performing any changes to
8133 * runtime changeable settings.
8134 *
8135 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8136 * the same as for MutableOrRunningStateDep except that if the machine is
8137 * saved, S_OK is also returned. This is useful in setters which allow
8138 * changing runtime and saved state changeable machine properties.
8139 *
8140 * @param aDepType Dependency type to check.
8141 *
8142 * @note Non Machine based classes should use #addStateDependency() and
8143 * #releaseStateDependency() methods or the smart AutoStateDependency
8144 * template.
8145 *
8146 * @note This method must be called from under this object's read or write
8147 * lock.
8148 */
8149HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8150{
8151 switch (aDepType)
8152 {
8153 case AnyStateDep:
8154 {
8155 break;
8156 }
8157 case MutableStateDep:
8158 {
8159 if ( mData->mRegistered
8160 && ( !i_isSessionMachine()
8161 || ( mData->mMachineState != MachineState_Aborted
8162 && mData->mMachineState != MachineState_Teleported
8163 && mData->mMachineState != MachineState_PoweredOff
8164 )
8165 )
8166 )
8167 return setError(VBOX_E_INVALID_VM_STATE,
8168 tr("The machine is not mutable (state is %s)"),
8169 Global::stringifyMachineState(mData->mMachineState));
8170 break;
8171 }
8172 case MutableOrSavedStateDep:
8173 {
8174 if ( mData->mRegistered
8175 && ( !i_isSessionMachine()
8176 || ( mData->mMachineState != MachineState_Aborted
8177 && mData->mMachineState != MachineState_Teleported
8178 && mData->mMachineState != MachineState_Saved
8179 && mData->mMachineState != MachineState_PoweredOff
8180 )
8181 )
8182 )
8183 return setError(VBOX_E_INVALID_VM_STATE,
8184 tr("The machine is not mutable (state is %s)"),
8185 Global::stringifyMachineState(mData->mMachineState));
8186 break;
8187 }
8188 case MutableOrRunningStateDep:
8189 {
8190 if ( mData->mRegistered
8191 && ( !i_isSessionMachine()
8192 || ( mData->mMachineState != MachineState_Aborted
8193 && mData->mMachineState != MachineState_Teleported
8194 && mData->mMachineState != MachineState_PoweredOff
8195 && !Global::IsOnline(mData->mMachineState)
8196 )
8197 )
8198 )
8199 return setError(VBOX_E_INVALID_VM_STATE,
8200 tr("The machine is not mutable (state is %s)"),
8201 Global::stringifyMachineState(mData->mMachineState));
8202 break;
8203 }
8204 case MutableOrSavedOrRunningStateDep:
8205 {
8206 if ( mData->mRegistered
8207 && ( !i_isSessionMachine()
8208 || ( mData->mMachineState != MachineState_Aborted
8209 && mData->mMachineState != MachineState_Teleported
8210 && mData->mMachineState != MachineState_Saved
8211 && mData->mMachineState != MachineState_PoweredOff
8212 && !Global::IsOnline(mData->mMachineState)
8213 )
8214 )
8215 )
8216 return setError(VBOX_E_INVALID_VM_STATE,
8217 tr("The machine is not mutable (state is %s)"),
8218 Global::stringifyMachineState(mData->mMachineState));
8219 break;
8220 }
8221 }
8222
8223 return S_OK;
8224}
8225
8226/**
8227 * Helper to initialize all associated child objects and allocate data
8228 * structures.
8229 *
8230 * This method must be called as a part of the object's initialization procedure
8231 * (usually done in the #init() method).
8232 *
8233 * @note Must be called only from #init() or from #registeredInit().
8234 */
8235HRESULT Machine::initDataAndChildObjects()
8236{
8237 AutoCaller autoCaller(this);
8238 AssertComRCReturnRC(autoCaller.rc());
8239 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8240 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8241
8242 AssertReturn(!mData->mAccessible, E_FAIL);
8243
8244 /* allocate data structures */
8245 mSSData.allocate();
8246 mUserData.allocate();
8247 mHWData.allocate();
8248 mMediaData.allocate();
8249 mStorageControllers.allocate();
8250 mUSBControllers.allocate();
8251
8252 /* initialize mOSTypeId */
8253 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8254
8255 /* create associated BIOS settings object */
8256 unconst(mBIOSSettings).createObject();
8257 mBIOSSettings->init(this);
8258
8259 /* create an associated VRDE object (default is disabled) */
8260 unconst(mVRDEServer).createObject();
8261 mVRDEServer->init(this);
8262
8263 /* create associated serial port objects */
8264 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8265 {
8266 unconst(mSerialPorts[slot]).createObject();
8267 mSerialPorts[slot]->init(this, slot);
8268 }
8269
8270 /* create associated parallel port objects */
8271 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8272 {
8273 unconst(mParallelPorts[slot]).createObject();
8274 mParallelPorts[slot]->init(this, slot);
8275 }
8276
8277 /* create the audio adapter object (always present, default is disabled) */
8278 unconst(mAudioAdapter).createObject();
8279 mAudioAdapter->init(this);
8280
8281 /* create the USB device filters object (always present) */
8282 unconst(mUSBDeviceFilters).createObject();
8283 mUSBDeviceFilters->init(this);
8284
8285 /* create associated network adapter objects */
8286 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8287 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8288 {
8289 unconst(mNetworkAdapters[slot]).createObject();
8290 mNetworkAdapters[slot]->init(this, slot);
8291 }
8292
8293 /* create the bandwidth control */
8294 unconst(mBandwidthControl).createObject();
8295 mBandwidthControl->init(this);
8296
8297 return S_OK;
8298}
8299
8300/**
8301 * Helper to uninitialize all associated child objects and to free all data
8302 * structures.
8303 *
8304 * This method must be called as a part of the object's uninitialization
8305 * procedure (usually done in the #uninit() method).
8306 *
8307 * @note Must be called only from #uninit() or from #registeredInit().
8308 */
8309void Machine::uninitDataAndChildObjects()
8310{
8311 AutoCaller autoCaller(this);
8312 AssertComRCReturnVoid(autoCaller.rc());
8313 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8314 || getObjectState().getState() == ObjectState::Limited);
8315
8316 /* tell all our other child objects we've been uninitialized */
8317 if (mBandwidthControl)
8318 {
8319 mBandwidthControl->uninit();
8320 unconst(mBandwidthControl).setNull();
8321 }
8322
8323 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8324 {
8325 if (mNetworkAdapters[slot])
8326 {
8327 mNetworkAdapters[slot]->uninit();
8328 unconst(mNetworkAdapters[slot]).setNull();
8329 }
8330 }
8331
8332 if (mUSBDeviceFilters)
8333 {
8334 mUSBDeviceFilters->uninit();
8335 unconst(mUSBDeviceFilters).setNull();
8336 }
8337
8338 if (mAudioAdapter)
8339 {
8340 mAudioAdapter->uninit();
8341 unconst(mAudioAdapter).setNull();
8342 }
8343
8344 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8345 {
8346 if (mParallelPorts[slot])
8347 {
8348 mParallelPorts[slot]->uninit();
8349 unconst(mParallelPorts[slot]).setNull();
8350 }
8351 }
8352
8353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8354 {
8355 if (mSerialPorts[slot])
8356 {
8357 mSerialPorts[slot]->uninit();
8358 unconst(mSerialPorts[slot]).setNull();
8359 }
8360 }
8361
8362 if (mVRDEServer)
8363 {
8364 mVRDEServer->uninit();
8365 unconst(mVRDEServer).setNull();
8366 }
8367
8368 if (mBIOSSettings)
8369 {
8370 mBIOSSettings->uninit();
8371 unconst(mBIOSSettings).setNull();
8372 }
8373
8374 /* Deassociate media (only when a real Machine or a SnapshotMachine
8375 * instance is uninitialized; SessionMachine instances refer to real
8376 * Machine media). This is necessary for a clean re-initialization of
8377 * the VM after successfully re-checking the accessibility state. Note
8378 * that in case of normal Machine or SnapshotMachine uninitialization (as
8379 * a result of unregistering or deleting the snapshot), outdated media
8380 * attachments will already be uninitialized and deleted, so this
8381 * code will not affect them. */
8382 if ( !!mMediaData
8383 && (!i_isSessionMachine())
8384 )
8385 {
8386 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8387 it != mMediaData->mAttachments.end();
8388 ++it)
8389 {
8390 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8391 if (pMedium.isNull())
8392 continue;
8393 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8394 AssertComRC(rc);
8395 }
8396 }
8397
8398 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8399 {
8400 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8401 if (mData->mFirstSnapshot)
8402 {
8403 // snapshots tree is protected by machine write lock; strictly
8404 // this isn't necessary here since we're deleting the entire
8405 // machine, but otherwise we assert in Snapshot::uninit()
8406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8407 mData->mFirstSnapshot->uninit();
8408 mData->mFirstSnapshot.setNull();
8409 }
8410
8411 mData->mCurrentSnapshot.setNull();
8412 }
8413
8414 /* free data structures (the essential mData structure is not freed here
8415 * since it may be still in use) */
8416 mMediaData.free();
8417 mStorageControllers.free();
8418 mUSBControllers.free();
8419 mHWData.free();
8420 mUserData.free();
8421 mSSData.free();
8422}
8423
8424/**
8425 * Returns a pointer to the Machine object for this machine that acts like a
8426 * parent for complex machine data objects such as shared folders, etc.
8427 *
8428 * For primary Machine objects and for SnapshotMachine objects, returns this
8429 * object's pointer itself. For SessionMachine objects, returns the peer
8430 * (primary) machine pointer.
8431 */
8432Machine* Machine::i_getMachine()
8433{
8434 if (i_isSessionMachine())
8435 return (Machine*)mPeer;
8436 return this;
8437}
8438
8439/**
8440 * Makes sure that there are no machine state dependents. If necessary, waits
8441 * for the number of dependents to drop to zero.
8442 *
8443 * Make sure this method is called from under this object's write lock to
8444 * guarantee that no new dependents may be added when this method returns
8445 * control to the caller.
8446 *
8447 * @note Locks this object for writing. The lock will be released while waiting
8448 * (if necessary).
8449 *
8450 * @warning To be used only in methods that change the machine state!
8451 */
8452void Machine::i_ensureNoStateDependencies()
8453{
8454 AssertReturnVoid(isWriteLockOnCurrentThread());
8455
8456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8457
8458 /* Wait for all state dependents if necessary */
8459 if (mData->mMachineStateDeps != 0)
8460 {
8461 /* lazy semaphore creation */
8462 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8463 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8464
8465 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8466 mData->mMachineStateDeps));
8467
8468 ++mData->mMachineStateChangePending;
8469
8470 /* reset the semaphore before waiting, the last dependent will signal
8471 * it */
8472 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8473
8474 alock.release();
8475
8476 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8477
8478 alock.acquire();
8479
8480 -- mData->mMachineStateChangePending;
8481 }
8482}
8483
8484/**
8485 * Changes the machine state and informs callbacks.
8486 *
8487 * This method is not intended to fail so it either returns S_OK or asserts (and
8488 * returns a failure).
8489 *
8490 * @note Locks this object for writing.
8491 */
8492HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8493{
8494 LogFlowThisFuncEnter();
8495 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8496 Assert(aMachineState != MachineState_Null);
8497
8498 AutoCaller autoCaller(this);
8499 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8500
8501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8502
8503 /* wait for state dependents to drop to zero */
8504 i_ensureNoStateDependencies();
8505
8506 MachineState_T const enmOldState = mData->mMachineState;
8507 if (enmOldState != aMachineState)
8508 {
8509 mData->mMachineState = aMachineState;
8510 RTTimeNow(&mData->mLastStateChange);
8511
8512#ifdef VBOX_WITH_DTRACE_R3_MAIN
8513 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8514#endif
8515 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8516 }
8517
8518 LogFlowThisFuncLeave();
8519 return S_OK;
8520}
8521
8522/**
8523 * Searches for a shared folder with the given logical name
8524 * in the collection of shared folders.
8525 *
8526 * @param aName logical name of the shared folder
8527 * @param aSharedFolder where to return the found object
8528 * @param aSetError whether to set the error info if the folder is
8529 * not found
8530 * @return
8531 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8532 *
8533 * @note
8534 * must be called from under the object's lock!
8535 */
8536HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8537 ComObjPtr<SharedFolder> &aSharedFolder,
8538 bool aSetError /* = false */)
8539{
8540 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8541 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8542 it != mHWData->mSharedFolders.end();
8543 ++it)
8544 {
8545 SharedFolder *pSF = *it;
8546 AutoCaller autoCaller(pSF);
8547 if (pSF->i_getName() == aName)
8548 {
8549 aSharedFolder = pSF;
8550 rc = S_OK;
8551 break;
8552 }
8553 }
8554
8555 if (aSetError && FAILED(rc))
8556 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8557
8558 return rc;
8559}
8560
8561/**
8562 * Initializes all machine instance data from the given settings structures
8563 * from XML. The exception is the machine UUID which needs special handling
8564 * depending on the caller's use case, so the caller needs to set that herself.
8565 *
8566 * This gets called in several contexts during machine initialization:
8567 *
8568 * -- When machine XML exists on disk already and needs to be loaded into memory,
8569 * for example, from registeredInit() to load all registered machines on
8570 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8571 * attached to the machine should be part of some media registry already.
8572 *
8573 * -- During OVF import, when a machine config has been constructed from an
8574 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8575 * ensure that the media listed as attachments in the config (which have
8576 * been imported from the OVF) receive the correct registry ID.
8577 *
8578 * -- During VM cloning.
8579 *
8580 * @param config Machine settings from XML.
8581 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8582 * for each attached medium in the config.
8583 * @return
8584 */
8585HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8586 const Guid *puuidRegistry)
8587{
8588 // copy name, description, OS type, teleporter, UTC etc.
8589 mUserData->s = config.machineUserData;
8590
8591 // Decode the Icon overide data from config userdata and set onto Machine.
8592 #define DECODE_STR_MAX _1M
8593 const char* pszStr = config.machineUserData.ovIcon.c_str();
8594 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8595 if (cbOut > DECODE_STR_MAX)
8596 return setError(E_FAIL,
8597 tr("Icon Data too long.'%d' > '%d'"),
8598 cbOut,
8599 DECODE_STR_MAX);
8600 mUserData->mIcon.resize(cbOut);
8601 int vrc = VINF_SUCCESS;
8602 if (cbOut)
8603 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8604 if (RT_FAILURE(vrc))
8605 {
8606 mUserData->mIcon.resize(0);
8607 return setError(E_FAIL,
8608 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8609 pszStr,
8610 vrc);
8611 }
8612
8613 // look up the object by Id to check it is valid
8614 ComPtr<IGuestOSType> guestOSType;
8615 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8616 guestOSType.asOutParam());
8617 if (FAILED(rc)) return rc;
8618
8619 // stateFile (optional)
8620 if (config.strStateFile.isEmpty())
8621 mSSData->strStateFilePath.setNull();
8622 else
8623 {
8624 Utf8Str stateFilePathFull(config.strStateFile);
8625 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8626 if (RT_FAILURE(vrc))
8627 return setError(E_FAIL,
8628 tr("Invalid saved state file path '%s' (%Rrc)"),
8629 config.strStateFile.c_str(),
8630 vrc);
8631 mSSData->strStateFilePath = stateFilePathFull;
8632 }
8633
8634 // snapshot folder needs special processing so set it again
8635 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8636 if (FAILED(rc)) return rc;
8637
8638 /* Copy the extra data items (Not in any case config is already the same as
8639 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8640 * make sure the extra data map is copied). */
8641 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8642
8643 /* currentStateModified (optional, default is true) */
8644 mData->mCurrentStateModified = config.fCurrentStateModified;
8645
8646 mData->mLastStateChange = config.timeLastStateChange;
8647
8648 /*
8649 * note: all mUserData members must be assigned prior this point because
8650 * we need to commit changes in order to let mUserData be shared by all
8651 * snapshot machine instances.
8652 */
8653 mUserData.commitCopy();
8654
8655 // machine registry, if present (must be loaded before snapshots)
8656 if (config.canHaveOwnMediaRegistry())
8657 {
8658 // determine machine folder
8659 Utf8Str strMachineFolder = i_getSettingsFileFull();
8660 strMachineFolder.stripFilename();
8661 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8662 config.mediaRegistry,
8663 strMachineFolder);
8664 if (FAILED(rc)) return rc;
8665 }
8666
8667 /* Snapshot node (optional) */
8668 size_t cRootSnapshots;
8669 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8670 {
8671 // there must be only one root snapshot
8672 Assert(cRootSnapshots == 1);
8673
8674 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8675
8676 rc = i_loadSnapshot(snap,
8677 config.uuidCurrentSnapshot,
8678 NULL); // no parent == first snapshot
8679 if (FAILED(rc)) return rc;
8680 }
8681
8682 // hardware data
8683 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8684 if (FAILED(rc)) return rc;
8685
8686 // load storage controllers
8687 rc = i_loadStorageControllers(config.storageMachine,
8688 puuidRegistry,
8689 NULL /* puuidSnapshot */);
8690 if (FAILED(rc)) return rc;
8691
8692 /*
8693 * NOTE: the assignment below must be the last thing to do,
8694 * otherwise it will be not possible to change the settings
8695 * somewhere in the code above because all setters will be
8696 * blocked by i_checkStateDependency(MutableStateDep).
8697 */
8698
8699 /* set the machine state to Aborted or Saved when appropriate */
8700 if (config.fAborted)
8701 {
8702 mSSData->strStateFilePath.setNull();
8703
8704 /* no need to use i_setMachineState() during init() */
8705 mData->mMachineState = MachineState_Aborted;
8706 }
8707 else if (!mSSData->strStateFilePath.isEmpty())
8708 {
8709 /* no need to use i_setMachineState() during init() */
8710 mData->mMachineState = MachineState_Saved;
8711 }
8712
8713 // after loading settings, we are no longer different from the XML on disk
8714 mData->flModifications = 0;
8715
8716 return S_OK;
8717}
8718
8719/**
8720 * Recursively loads all snapshots starting from the given.
8721 *
8722 * @param aNode <Snapshot> node.
8723 * @param aCurSnapshotId Current snapshot ID from the settings file.
8724 * @param aParentSnapshot Parent snapshot.
8725 */
8726HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8727 const Guid &aCurSnapshotId,
8728 Snapshot *aParentSnapshot)
8729{
8730 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8731 AssertReturn(!i_isSessionMachine(), E_FAIL);
8732
8733 HRESULT rc = S_OK;
8734
8735 Utf8Str strStateFile;
8736 if (!data.strStateFile.isEmpty())
8737 {
8738 /* optional */
8739 strStateFile = data.strStateFile;
8740 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8741 if (RT_FAILURE(vrc))
8742 return setError(E_FAIL,
8743 tr("Invalid saved state file path '%s' (%Rrc)"),
8744 strStateFile.c_str(),
8745 vrc);
8746 }
8747
8748 /* create a snapshot machine object */
8749 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8750 pSnapshotMachine.createObject();
8751 rc = pSnapshotMachine->initFromSettings(this,
8752 data.hardware,
8753 &data.debugging,
8754 &data.autostart,
8755 data.storage,
8756 data.uuid.ref(),
8757 strStateFile);
8758 if (FAILED(rc)) return rc;
8759
8760 /* create a snapshot object */
8761 ComObjPtr<Snapshot> pSnapshot;
8762 pSnapshot.createObject();
8763 /* initialize the snapshot */
8764 rc = pSnapshot->init(mParent, // VirtualBox object
8765 data.uuid,
8766 data.strName,
8767 data.strDescription,
8768 data.timestamp,
8769 pSnapshotMachine,
8770 aParentSnapshot);
8771 if (FAILED(rc)) return rc;
8772
8773 /* memorize the first snapshot if necessary */
8774 if (!mData->mFirstSnapshot)
8775 mData->mFirstSnapshot = pSnapshot;
8776
8777 /* memorize the current snapshot when appropriate */
8778 if ( !mData->mCurrentSnapshot
8779 && pSnapshot->i_getId() == aCurSnapshotId
8780 )
8781 mData->mCurrentSnapshot = pSnapshot;
8782
8783 // now create the children
8784 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8785 it != data.llChildSnapshots.end();
8786 ++it)
8787 {
8788 const settings::Snapshot &childData = *it;
8789 // recurse
8790 rc = i_loadSnapshot(childData,
8791 aCurSnapshotId,
8792 pSnapshot); // parent = the one we created above
8793 if (FAILED(rc)) return rc;
8794 }
8795
8796 return rc;
8797}
8798
8799/**
8800 * Loads settings into mHWData.
8801 *
8802 * @param data Reference to the hardware settings.
8803 * @param pDbg Pointer to the debugging settings.
8804 * @param pAutostart Pointer to the autostart settings.
8805 */
8806HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8807 const settings::Autostart *pAutostart)
8808{
8809 AssertReturn(!i_isSessionMachine(), E_FAIL);
8810
8811 HRESULT rc = S_OK;
8812
8813 try
8814 {
8815 /* The hardware version attribute (optional). */
8816 mHWData->mHWVersion = data.strVersion;
8817 mHWData->mHardwareUUID = data.uuid;
8818
8819 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8820 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8821 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8822 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8823 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8824 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8825 mHWData->mPAEEnabled = data.fPAE;
8826 mHWData->mLongMode = data.enmLongMode;
8827 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8828 mHWData->mCPUCount = data.cCPUs;
8829 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8830 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8831 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8832
8833 // cpu
8834 if (mHWData->mCPUHotPlugEnabled)
8835 {
8836 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8837 it != data.llCpus.end();
8838 ++it)
8839 {
8840 const settings::Cpu &cpu = *it;
8841
8842 mHWData->mCPUAttached[cpu.ulId] = true;
8843 }
8844 }
8845
8846 // cpuid leafs
8847 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8848 it != data.llCpuIdLeafs.end();
8849 ++it)
8850 {
8851 const settings::CpuIdLeaf &leaf = *it;
8852
8853 switch (leaf.ulId)
8854 {
8855 case 0x0:
8856 case 0x1:
8857 case 0x2:
8858 case 0x3:
8859 case 0x4:
8860 case 0x5:
8861 case 0x6:
8862 case 0x7:
8863 case 0x8:
8864 case 0x9:
8865 case 0xA:
8866 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8867 break;
8868
8869 case 0x80000000:
8870 case 0x80000001:
8871 case 0x80000002:
8872 case 0x80000003:
8873 case 0x80000004:
8874 case 0x80000005:
8875 case 0x80000006:
8876 case 0x80000007:
8877 case 0x80000008:
8878 case 0x80000009:
8879 case 0x8000000A:
8880 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8881 break;
8882
8883 default:
8884 /* just ignore */
8885 break;
8886 }
8887 }
8888
8889 mHWData->mMemorySize = data.ulMemorySizeMB;
8890 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8891
8892 // boot order
8893 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8894 {
8895 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8896 if (it == data.mapBootOrder.end())
8897 mHWData->mBootOrder[i] = DeviceType_Null;
8898 else
8899 mHWData->mBootOrder[i] = it->second;
8900 }
8901
8902 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8903 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8904 mHWData->mMonitorCount = data.cMonitors;
8905 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8906 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8907 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8908 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8909 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8910 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8911 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8912 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8913 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8914 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8915 if (!data.strVideoCaptureFile.isEmpty())
8916 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8917 else
8918 mHWData->mVideoCaptureFile.setNull();
8919 mHWData->mFirmwareType = data.firmwareType;
8920 mHWData->mPointingHIDType = data.pointingHIDType;
8921 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8922 mHWData->mChipsetType = data.chipsetType;
8923 mHWData->mParavirtProvider = data.paravirtProvider;
8924 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8925 mHWData->mHPETEnabled = data.fHPETEnabled;
8926
8927 /* VRDEServer */
8928 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8929 if (FAILED(rc)) return rc;
8930
8931 /* BIOS */
8932 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8933 if (FAILED(rc)) return rc;
8934
8935 // Bandwidth control (must come before network adapters)
8936 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8937 if (FAILED(rc)) return rc;
8938
8939 /* Shared folders */
8940 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8941 it != data.usbSettings.llUSBControllers.end();
8942 ++it)
8943 {
8944 const settings::USBController &settingsCtrl = *it;
8945 ComObjPtr<USBController> newCtrl;
8946
8947 newCtrl.createObject();
8948 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8949 mUSBControllers->push_back(newCtrl);
8950 }
8951
8952 /* USB device filters */
8953 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8954 if (FAILED(rc)) return rc;
8955
8956 // network adapters
8957 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8958 size_t oldCount = mNetworkAdapters.size();
8959 if (newCount > oldCount)
8960 {
8961 mNetworkAdapters.resize(newCount);
8962 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8963 {
8964 unconst(mNetworkAdapters[slot]).createObject();
8965 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8966 }
8967 }
8968 else if (newCount < oldCount)
8969 mNetworkAdapters.resize(newCount);
8970 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8971 it != data.llNetworkAdapters.end();
8972 ++it)
8973 {
8974 const settings::NetworkAdapter &nic = *it;
8975
8976 /* slot unicity is guaranteed by XML Schema */
8977 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8978 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8979 if (FAILED(rc)) return rc;
8980 }
8981
8982 // serial ports
8983 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8984 it != data.llSerialPorts.end();
8985 ++it)
8986 {
8987 const settings::SerialPort &s = *it;
8988
8989 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8990 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8991 if (FAILED(rc)) return rc;
8992 }
8993
8994 // parallel ports (optional)
8995 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8996 it != data.llParallelPorts.end();
8997 ++it)
8998 {
8999 const settings::ParallelPort &p = *it;
9000
9001 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9002 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9003 if (FAILED(rc)) return rc;
9004 }
9005
9006 /* AudioAdapter */
9007 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9008 if (FAILED(rc)) return rc;
9009
9010 /* Shared folders */
9011 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9012 it != data.llSharedFolders.end();
9013 ++it)
9014 {
9015 const settings::SharedFolder &sf = *it;
9016
9017 ComObjPtr<SharedFolder> sharedFolder;
9018 /* Check for double entries. Not allowed! */
9019 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9020 if (SUCCEEDED(rc))
9021 return setError(VBOX_E_OBJECT_IN_USE,
9022 tr("Shared folder named '%s' already exists"),
9023 sf.strName.c_str());
9024
9025 /* Create the new shared folder. Don't break on error. This will be
9026 * reported when the machine starts. */
9027 sharedFolder.createObject();
9028 rc = sharedFolder->init(i_getMachine(),
9029 sf.strName,
9030 sf.strHostPath,
9031 RT_BOOL(sf.fWritable),
9032 RT_BOOL(sf.fAutoMount),
9033 false /* fFailOnError */);
9034 if (FAILED(rc)) return rc;
9035 mHWData->mSharedFolders.push_back(sharedFolder);
9036 }
9037
9038 // Clipboard
9039 mHWData->mClipboardMode = data.clipboardMode;
9040
9041 // drag'n'drop
9042 mHWData->mDnDMode = data.dndMode;
9043
9044 // guest settings
9045 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9046
9047 // IO settings
9048 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9049 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9050
9051 // Host PCI devices
9052 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9053 it != data.pciAttachments.end();
9054 ++it)
9055 {
9056 const settings::HostPCIDeviceAttachment &hpda = *it;
9057 ComObjPtr<PCIDeviceAttachment> pda;
9058
9059 pda.createObject();
9060 pda->i_loadSettings(this, hpda);
9061 mHWData->mPCIDeviceAssignments.push_back(pda);
9062 }
9063
9064 /*
9065 * (The following isn't really real hardware, but it lives in HWData
9066 * for reasons of convenience.)
9067 */
9068
9069#ifdef VBOX_WITH_GUEST_PROPS
9070 /* Guest properties (optional) */
9071
9072 /* Only load transient guest properties for configs which have saved
9073 * state, because there shouldn't be any for powered off VMs. The same
9074 * logic applies for snapshots, as offline snapshots shouldn't have
9075 * any such properties. They confuse the code in various places.
9076 * Note: can't rely on the machine state, as it isn't set yet. */
9077 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9078 /* apologies for the hacky unconst() usage, but this needs hacking
9079 * actually inconsistent settings into consistency, otherwise there
9080 * will be some corner cases where the inconsistency survives
9081 * surprisingly long without getting fixed, especially for snapshots
9082 * as there are no config changes. */
9083 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9084 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9085 it != llGuestProperties.end();
9086 /*nothing*/)
9087 {
9088 const settings::GuestProperty &prop = *it;
9089 uint32_t fFlags = guestProp::NILFLAG;
9090 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9091 if ( fSkipTransientGuestProperties
9092 && ( fFlags & guestProp::TRANSIENT
9093 || fFlags & guestProp::TRANSRESET))
9094 {
9095 it = llGuestProperties.erase(it);
9096 continue;
9097 }
9098 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9099 mHWData->mGuestProperties[prop.strName] = property;
9100 ++it;
9101 }
9102
9103 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9104#endif /* VBOX_WITH_GUEST_PROPS defined */
9105
9106 rc = i_loadDebugging(pDbg);
9107 if (FAILED(rc))
9108 return rc;
9109
9110 mHWData->mAutostart = *pAutostart;
9111
9112 /* default frontend */
9113 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9114 }
9115 catch(std::bad_alloc &)
9116 {
9117 return E_OUTOFMEMORY;
9118 }
9119
9120 AssertComRC(rc);
9121 return rc;
9122}
9123
9124/**
9125 * Called from Machine::loadHardware() to load the debugging settings of the
9126 * machine.
9127 *
9128 * @param pDbg Pointer to the settings.
9129 */
9130HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9131{
9132 mHWData->mDebugging = *pDbg;
9133 /* no more processing currently required, this will probably change. */
9134 return S_OK;
9135}
9136
9137/**
9138 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9139 *
9140 * @param data
9141 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9142 * @param puuidSnapshot
9143 * @return
9144 */
9145HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9146 const Guid *puuidRegistry,
9147 const Guid *puuidSnapshot)
9148{
9149 AssertReturn(!i_isSessionMachine(), E_FAIL);
9150
9151 HRESULT rc = S_OK;
9152
9153 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9154 it != data.llStorageControllers.end();
9155 ++it)
9156 {
9157 const settings::StorageController &ctlData = *it;
9158
9159 ComObjPtr<StorageController> pCtl;
9160 /* Try to find one with the name first. */
9161 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9162 if (SUCCEEDED(rc))
9163 return setError(VBOX_E_OBJECT_IN_USE,
9164 tr("Storage controller named '%s' already exists"),
9165 ctlData.strName.c_str());
9166
9167 pCtl.createObject();
9168 rc = pCtl->init(this,
9169 ctlData.strName,
9170 ctlData.storageBus,
9171 ctlData.ulInstance,
9172 ctlData.fBootable);
9173 if (FAILED(rc)) return rc;
9174
9175 mStorageControllers->push_back(pCtl);
9176
9177 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9178 if (FAILED(rc)) return rc;
9179
9180 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9181 if (FAILED(rc)) return rc;
9182
9183 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9184 if (FAILED(rc)) return rc;
9185
9186 /* Set IDE emulation settings (only for AHCI controller). */
9187 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9188 {
9189 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9190 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9191 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9192 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9193 )
9194 return rc;
9195 }
9196
9197 /* Load the attached devices now. */
9198 rc = i_loadStorageDevices(pCtl,
9199 ctlData,
9200 puuidRegistry,
9201 puuidSnapshot);
9202 if (FAILED(rc)) return rc;
9203 }
9204
9205 return S_OK;
9206}
9207
9208/**
9209 * Called from i_loadStorageControllers for a controller's devices.
9210 *
9211 * @param aStorageController
9212 * @param data
9213 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9214 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9215 * @return
9216 */
9217HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9218 const settings::StorageController &data,
9219 const Guid *puuidRegistry,
9220 const Guid *puuidSnapshot)
9221{
9222 HRESULT rc = S_OK;
9223
9224 /* paranoia: detect duplicate attachments */
9225 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9226 it != data.llAttachedDevices.end();
9227 ++it)
9228 {
9229 const settings::AttachedDevice &ad = *it;
9230
9231 for (settings::AttachedDevicesList::const_iterator it2 = it;
9232 it2 != data.llAttachedDevices.end();
9233 ++it2)
9234 {
9235 if (it == it2)
9236 continue;
9237
9238 const settings::AttachedDevice &ad2 = *it2;
9239
9240 if ( ad.lPort == ad2.lPort
9241 && ad.lDevice == ad2.lDevice)
9242 {
9243 return setError(E_FAIL,
9244 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9245 aStorageController->i_getName().c_str(),
9246 ad.lPort,
9247 ad.lDevice,
9248 mUserData->s.strName.c_str());
9249 }
9250 }
9251 }
9252
9253 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9254 it != data.llAttachedDevices.end();
9255 ++it)
9256 {
9257 const settings::AttachedDevice &dev = *it;
9258 ComObjPtr<Medium> medium;
9259
9260 switch (dev.deviceType)
9261 {
9262 case DeviceType_Floppy:
9263 case DeviceType_DVD:
9264 if (dev.strHostDriveSrc.isNotEmpty())
9265 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9266 false /* fRefresh */, medium);
9267 else
9268 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9269 dev.uuid,
9270 false /* fRefresh */,
9271 false /* aSetError */,
9272 medium);
9273 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9274 // This is not an error. The host drive or UUID might have vanished, so just go
9275 // ahead without this removeable medium attachment
9276 rc = S_OK;
9277 break;
9278
9279 case DeviceType_HardDisk:
9280 {
9281 /* find a hard disk by UUID */
9282 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9283 if (FAILED(rc))
9284 {
9285 if (i_isSnapshotMachine())
9286 {
9287 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9288 // so the user knows that the bad disk is in a snapshot somewhere
9289 com::ErrorInfo info;
9290 return setError(E_FAIL,
9291 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9292 puuidSnapshot->raw(),
9293 info.getText().raw());
9294 }
9295 else
9296 return rc;
9297 }
9298
9299 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9300
9301 if (medium->i_getType() == MediumType_Immutable)
9302 {
9303 if (i_isSnapshotMachine())
9304 return setError(E_FAIL,
9305 tr("Immutable 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("Immutable 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 (medium->i_getType() == MediumType_MultiAttach)
9322 {
9323 if (i_isSnapshotMachine())
9324 return setError(E_FAIL,
9325 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9326 "of the virtual machine '%s' ('%s')"),
9327 medium->i_getLocationFull().c_str(),
9328 dev.uuid.raw(),
9329 puuidSnapshot->raw(),
9330 mUserData->s.strName.c_str(),
9331 mData->m_strConfigFileFull.c_str());
9332
9333 return setError(E_FAIL,
9334 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9335 medium->i_getLocationFull().c_str(),
9336 dev.uuid.raw(),
9337 mUserData->s.strName.c_str(),
9338 mData->m_strConfigFileFull.c_str());
9339 }
9340
9341 if ( !i_isSnapshotMachine()
9342 && medium->i_getChildren().size() != 0
9343 )
9344 return setError(E_FAIL,
9345 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9346 "because it has %d differencing child hard disks"),
9347 medium->i_getLocationFull().c_str(),
9348 dev.uuid.raw(),
9349 mUserData->s.strName.c_str(),
9350 mData->m_strConfigFileFull.c_str(),
9351 medium->i_getChildren().size());
9352
9353 if (i_findAttachment(mMediaData->mAttachments,
9354 medium))
9355 return setError(E_FAIL,
9356 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9357 medium->i_getLocationFull().c_str(),
9358 dev.uuid.raw(),
9359 mUserData->s.strName.c_str(),
9360 mData->m_strConfigFileFull.c_str());
9361
9362 break;
9363 }
9364
9365 default:
9366 return setError(E_FAIL,
9367 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9368 medium->i_getLocationFull().c_str(),
9369 mUserData->s.strName.c_str(),
9370 mData->m_strConfigFileFull.c_str());
9371 }
9372
9373 if (FAILED(rc))
9374 break;
9375
9376 /* Bandwidth groups are loaded at this point. */
9377 ComObjPtr<BandwidthGroup> pBwGroup;
9378
9379 if (!dev.strBwGroup.isEmpty())
9380 {
9381 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9382 if (FAILED(rc))
9383 return setError(E_FAIL,
9384 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9385 medium->i_getLocationFull().c_str(),
9386 dev.strBwGroup.c_str(),
9387 mUserData->s.strName.c_str(),
9388 mData->m_strConfigFileFull.c_str());
9389 pBwGroup->i_reference();
9390 }
9391
9392 const Bstr controllerName = aStorageController->i_getName();
9393 ComObjPtr<MediumAttachment> pAttachment;
9394 pAttachment.createObject();
9395 rc = pAttachment->init(this,
9396 medium,
9397 controllerName,
9398 dev.lPort,
9399 dev.lDevice,
9400 dev.deviceType,
9401 false,
9402 dev.fPassThrough,
9403 dev.fTempEject,
9404 dev.fNonRotational,
9405 dev.fDiscard,
9406 dev.fHotPluggable,
9407 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9408 if (FAILED(rc)) break;
9409
9410 /* associate the medium with this machine and snapshot */
9411 if (!medium.isNull())
9412 {
9413 AutoCaller medCaller(medium);
9414 if (FAILED(medCaller.rc())) return medCaller.rc();
9415 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9416
9417 if (i_isSnapshotMachine())
9418 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9419 else
9420 rc = medium->i_addBackReference(mData->mUuid);
9421 /* If the medium->addBackReference fails it sets an appropriate
9422 * error message, so no need to do any guesswork here. */
9423
9424 if (puuidRegistry)
9425 // caller wants registry ID to be set on all attached media (OVF import case)
9426 medium->i_addRegistry(*puuidRegistry);
9427 }
9428
9429 if (FAILED(rc))
9430 break;
9431
9432 /* back up mMediaData to let registeredInit() properly rollback on failure
9433 * (= limited accessibility) */
9434 i_setModified(IsModified_Storage);
9435 mMediaData.backup();
9436 mMediaData->mAttachments.push_back(pAttachment);
9437 }
9438
9439 return rc;
9440}
9441
9442/**
9443 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9444 *
9445 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9446 * @param aSnapshot where to return the found snapshot
9447 * @param aSetError true to set extended error info on failure
9448 */
9449HRESULT Machine::i_findSnapshotById(const Guid &aId,
9450 ComObjPtr<Snapshot> &aSnapshot,
9451 bool aSetError /* = false */)
9452{
9453 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9454
9455 if (!mData->mFirstSnapshot)
9456 {
9457 if (aSetError)
9458 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9459 return E_FAIL;
9460 }
9461
9462 if (aId.isZero())
9463 aSnapshot = mData->mFirstSnapshot;
9464 else
9465 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9466
9467 if (!aSnapshot)
9468 {
9469 if (aSetError)
9470 return setError(E_FAIL,
9471 tr("Could not find a snapshot with UUID {%s}"),
9472 aId.toString().c_str());
9473 return E_FAIL;
9474 }
9475
9476 return S_OK;
9477}
9478
9479/**
9480 * Returns the snapshot with the given name or fails of no such snapshot.
9481 *
9482 * @param aName snapshot name to find
9483 * @param aSnapshot where to return the found snapshot
9484 * @param aSetError true to set extended error info on failure
9485 */
9486HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9487 ComObjPtr<Snapshot> &aSnapshot,
9488 bool aSetError /* = false */)
9489{
9490 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9491
9492 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9493
9494 if (!mData->mFirstSnapshot)
9495 {
9496 if (aSetError)
9497 return setError(VBOX_E_OBJECT_NOT_FOUND,
9498 tr("This machine does not have any snapshots"));
9499 return VBOX_E_OBJECT_NOT_FOUND;
9500 }
9501
9502 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9503
9504 if (!aSnapshot)
9505 {
9506 if (aSetError)
9507 return setError(VBOX_E_OBJECT_NOT_FOUND,
9508 tr("Could not find a snapshot named '%s'"), strName.c_str());
9509 return VBOX_E_OBJECT_NOT_FOUND;
9510 }
9511
9512 return S_OK;
9513}
9514
9515/**
9516 * Returns a storage controller object with the given name.
9517 *
9518 * @param aName storage controller name to find
9519 * @param aStorageController where to return the found storage controller
9520 * @param aSetError true to set extended error info on failure
9521 */
9522HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9523 ComObjPtr<StorageController> &aStorageController,
9524 bool aSetError /* = false */)
9525{
9526 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9527
9528 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9529 it != mStorageControllers->end();
9530 ++it)
9531 {
9532 if ((*it)->i_getName() == aName)
9533 {
9534 aStorageController = (*it);
9535 return S_OK;
9536 }
9537 }
9538
9539 if (aSetError)
9540 return setError(VBOX_E_OBJECT_NOT_FOUND,
9541 tr("Could not find a storage controller named '%s'"),
9542 aName.c_str());
9543 return VBOX_E_OBJECT_NOT_FOUND;
9544}
9545
9546/**
9547 * Returns a USB controller object with the given name.
9548 *
9549 * @param aName USB controller name to find
9550 * @param aUSBController where to return the found USB controller
9551 * @param aSetError true to set extended error info on failure
9552 */
9553HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9554 ComObjPtr<USBController> &aUSBController,
9555 bool aSetError /* = false */)
9556{
9557 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9558
9559 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9560 it != mUSBControllers->end();
9561 ++it)
9562 {
9563 if ((*it)->i_getName() == aName)
9564 {
9565 aUSBController = (*it);
9566 return S_OK;
9567 }
9568 }
9569
9570 if (aSetError)
9571 return setError(VBOX_E_OBJECT_NOT_FOUND,
9572 tr("Could not find a storage controller named '%s'"),
9573 aName.c_str());
9574 return VBOX_E_OBJECT_NOT_FOUND;
9575}
9576
9577/**
9578 * Returns the number of USB controller instance of the given type.
9579 *
9580 * @param enmType USB controller type.
9581 */
9582ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9583{
9584 ULONG cCtrls = 0;
9585
9586 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9587 it != mUSBControllers->end();
9588 ++it)
9589 {
9590 if ((*it)->i_getControllerType() == enmType)
9591 cCtrls++;
9592 }
9593
9594 return cCtrls;
9595}
9596
9597HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9598 MediaData::AttachmentList &atts)
9599{
9600 AutoCaller autoCaller(this);
9601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9602
9603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9604
9605 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9606 it != mMediaData->mAttachments.end();
9607 ++it)
9608 {
9609 const ComObjPtr<MediumAttachment> &pAtt = *it;
9610 // should never happen, but deal with NULL pointers in the list.
9611 AssertStmt(!pAtt.isNull(), continue);
9612
9613 // getControllerName() needs caller+read lock
9614 AutoCaller autoAttCaller(pAtt);
9615 if (FAILED(autoAttCaller.rc()))
9616 {
9617 atts.clear();
9618 return autoAttCaller.rc();
9619 }
9620 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9621
9622 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9623 atts.push_back(pAtt);
9624 }
9625
9626 return S_OK;
9627}
9628
9629
9630/**
9631 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9632 * file if the machine name was changed and about creating a new settings file
9633 * if this is a new machine.
9634 *
9635 * @note Must be never called directly but only from #saveSettings().
9636 */
9637HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9638{
9639 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9640
9641 HRESULT rc = S_OK;
9642
9643 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9644
9645 /// @todo need to handle primary group change, too
9646
9647 /* attempt to rename the settings file if machine name is changed */
9648 if ( mUserData->s.fNameSync
9649 && mUserData.isBackedUp()
9650 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9651 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9652 )
9653 {
9654 bool dirRenamed = false;
9655 bool fileRenamed = false;
9656
9657 Utf8Str configFile, newConfigFile;
9658 Utf8Str configFilePrev, newConfigFilePrev;
9659 Utf8Str configDir, newConfigDir;
9660
9661 do
9662 {
9663 int vrc = VINF_SUCCESS;
9664
9665 Utf8Str name = mUserData.backedUpData()->s.strName;
9666 Utf8Str newName = mUserData->s.strName;
9667 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9668 if (group == "/")
9669 group.setNull();
9670 Utf8Str newGroup = mUserData->s.llGroups.front();
9671 if (newGroup == "/")
9672 newGroup.setNull();
9673
9674 configFile = mData->m_strConfigFileFull;
9675
9676 /* first, rename the directory if it matches the group and machine name */
9677 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9678 group.c_str(), RTPATH_DELIMITER, name.c_str());
9679 /** @todo hack, make somehow use of ComposeMachineFilename */
9680 if (mUserData->s.fDirectoryIncludesUUID)
9681 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9682 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9683 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9684 /** @todo hack, make somehow use of ComposeMachineFilename */
9685 if (mUserData->s.fDirectoryIncludesUUID)
9686 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9687 configDir = configFile;
9688 configDir.stripFilename();
9689 newConfigDir = configDir;
9690 if ( configDir.length() >= groupPlusName.length()
9691 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9692 groupPlusName.c_str()))
9693 {
9694 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9695 Utf8Str newConfigBaseDir(newConfigDir);
9696 newConfigDir.append(newGroupPlusName);
9697 /* consistency: use \ if appropriate on the platform */
9698 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9699 /* new dir and old dir cannot be equal here because of 'if'
9700 * above and because name != newName */
9701 Assert(configDir != newConfigDir);
9702 if (!fSettingsFileIsNew)
9703 {
9704 /* perform real rename only if the machine is not new */
9705 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9706 if ( vrc == VERR_FILE_NOT_FOUND
9707 || vrc == VERR_PATH_NOT_FOUND)
9708 {
9709 /* create the parent directory, then retry renaming */
9710 Utf8Str parent(newConfigDir);
9711 parent.stripFilename();
9712 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9713 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9714 }
9715 if (RT_FAILURE(vrc))
9716 {
9717 rc = setError(E_FAIL,
9718 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9719 configDir.c_str(),
9720 newConfigDir.c_str(),
9721 vrc);
9722 break;
9723 }
9724 /* delete subdirectories which are no longer needed */
9725 Utf8Str dir(configDir);
9726 dir.stripFilename();
9727 while (dir != newConfigBaseDir && dir != ".")
9728 {
9729 vrc = RTDirRemove(dir.c_str());
9730 if (RT_FAILURE(vrc))
9731 break;
9732 dir.stripFilename();
9733 }
9734 dirRenamed = true;
9735 }
9736 }
9737
9738 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9739 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9740
9741 /* then try to rename the settings file itself */
9742 if (newConfigFile != configFile)
9743 {
9744 /* get the path to old settings file in renamed directory */
9745 configFile = Utf8StrFmt("%s%c%s",
9746 newConfigDir.c_str(),
9747 RTPATH_DELIMITER,
9748 RTPathFilename(configFile.c_str()));
9749 if (!fSettingsFileIsNew)
9750 {
9751 /* perform real rename only if the machine is not new */
9752 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9753 if (RT_FAILURE(vrc))
9754 {
9755 rc = setError(E_FAIL,
9756 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9757 configFile.c_str(),
9758 newConfigFile.c_str(),
9759 vrc);
9760 break;
9761 }
9762 fileRenamed = true;
9763 configFilePrev = configFile;
9764 configFilePrev += "-prev";
9765 newConfigFilePrev = newConfigFile;
9766 newConfigFilePrev += "-prev";
9767 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9768 }
9769 }
9770
9771 // update m_strConfigFileFull amd mConfigFile
9772 mData->m_strConfigFileFull = newConfigFile;
9773 // compute the relative path too
9774 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9775
9776 // store the old and new so that VirtualBox::i_saveSettings() can update
9777 // the media registry
9778 if ( mData->mRegistered
9779 && (configDir != newConfigDir || configFile != newConfigFile))
9780 {
9781 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9782
9783 if (pfNeedsGlobalSaveSettings)
9784 *pfNeedsGlobalSaveSettings = true;
9785 }
9786
9787 // in the saved state file path, replace the old directory with the new directory
9788 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9789 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9790
9791 // and do the same thing for the saved state file paths of all the online snapshots
9792 if (mData->mFirstSnapshot)
9793 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9794 newConfigDir.c_str());
9795 }
9796 while (0);
9797
9798 if (FAILED(rc))
9799 {
9800 /* silently try to rename everything back */
9801 if (fileRenamed)
9802 {
9803 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9804 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9805 }
9806 if (dirRenamed)
9807 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9808 }
9809
9810 if (FAILED(rc)) return rc;
9811 }
9812
9813 if (fSettingsFileIsNew)
9814 {
9815 /* create a virgin config file */
9816 int vrc = VINF_SUCCESS;
9817
9818 /* ensure the settings directory exists */
9819 Utf8Str path(mData->m_strConfigFileFull);
9820 path.stripFilename();
9821 if (!RTDirExists(path.c_str()))
9822 {
9823 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9824 if (RT_FAILURE(vrc))
9825 {
9826 return setError(E_FAIL,
9827 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9828 path.c_str(),
9829 vrc);
9830 }
9831 }
9832
9833 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9834 path = Utf8Str(mData->m_strConfigFileFull);
9835 RTFILE f = NIL_RTFILE;
9836 vrc = RTFileOpen(&f, path.c_str(),
9837 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9838 if (RT_FAILURE(vrc))
9839 return setError(E_FAIL,
9840 tr("Could not create the settings file '%s' (%Rrc)"),
9841 path.c_str(),
9842 vrc);
9843 RTFileClose(f);
9844 }
9845
9846 return rc;
9847}
9848
9849/**
9850 * Saves and commits machine data, user data and hardware data.
9851 *
9852 * Note that on failure, the data remains uncommitted.
9853 *
9854 * @a aFlags may combine the following flags:
9855 *
9856 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9857 * Used when saving settings after an operation that makes them 100%
9858 * correspond to the settings from the current snapshot.
9859 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9860 * #isReallyModified() returns false. This is necessary for cases when we
9861 * change machine data directly, not through the backup()/commit() mechanism.
9862 * - SaveS_Force: settings will be saved without doing a deep compare of the
9863 * settings structures. This is used when this is called because snapshots
9864 * have changed to avoid the overhead of the deep compare.
9865 *
9866 * @note Must be called from under this object's write lock. Locks children for
9867 * writing.
9868 *
9869 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9870 * initialized to false and that will be set to true by this function if
9871 * the caller must invoke VirtualBox::i_saveSettings() because the global
9872 * settings have changed. This will happen if a machine rename has been
9873 * saved and the global machine and media registries will therefore need
9874 * updating.
9875 */
9876HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9877 int aFlags /*= 0*/)
9878{
9879 LogFlowThisFuncEnter();
9880
9881 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9882
9883 /* make sure child objects are unable to modify the settings while we are
9884 * saving them */
9885 i_ensureNoStateDependencies();
9886
9887 AssertReturn(!i_isSnapshotMachine(),
9888 E_FAIL);
9889
9890 HRESULT rc = S_OK;
9891 bool fNeedsWrite = false;
9892
9893 /* First, prepare to save settings. It will care about renaming the
9894 * settings directory and file if the machine name was changed and about
9895 * creating a new settings file if this is a new machine. */
9896 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9897 if (FAILED(rc)) return rc;
9898
9899 // keep a pointer to the current settings structures
9900 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9901 settings::MachineConfigFile *pNewConfig = NULL;
9902
9903 try
9904 {
9905 // make a fresh one to have everyone write stuff into
9906 pNewConfig = new settings::MachineConfigFile(NULL);
9907 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9908
9909 // now go and copy all the settings data from COM to the settings structures
9910 // (this calles i_saveSettings() on all the COM objects in the machine)
9911 i_copyMachineDataToSettings(*pNewConfig);
9912
9913 if (aFlags & SaveS_ResetCurStateModified)
9914 {
9915 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9916 mData->mCurrentStateModified = FALSE;
9917 fNeedsWrite = true; // always, no need to compare
9918 }
9919 else if (aFlags & SaveS_Force)
9920 {
9921 fNeedsWrite = true; // always, no need to compare
9922 }
9923 else
9924 {
9925 if (!mData->mCurrentStateModified)
9926 {
9927 // do a deep compare of the settings that we just saved with the settings
9928 // previously stored in the config file; this invokes MachineConfigFile::operator==
9929 // which does a deep compare of all the settings, which is expensive but less expensive
9930 // than writing out XML in vain
9931 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9932
9933 // could still be modified if any settings changed
9934 mData->mCurrentStateModified = fAnySettingsChanged;
9935
9936 fNeedsWrite = fAnySettingsChanged;
9937 }
9938 else
9939 fNeedsWrite = true;
9940 }
9941
9942 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9943
9944 if (fNeedsWrite)
9945 // now spit it all out!
9946 pNewConfig->write(mData->m_strConfigFileFull);
9947
9948 mData->pMachineConfigFile = pNewConfig;
9949 delete pOldConfig;
9950 i_commit();
9951
9952 // after saving settings, we are no longer different from the XML on disk
9953 mData->flModifications = 0;
9954 }
9955 catch (HRESULT err)
9956 {
9957 // we assume that error info is set by the thrower
9958 rc = err;
9959
9960 // restore old config
9961 delete pNewConfig;
9962 mData->pMachineConfigFile = pOldConfig;
9963 }
9964 catch (...)
9965 {
9966 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9967 }
9968
9969 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9970 {
9971 /* Fire the data change event, even on failure (since we've already
9972 * committed all data). This is done only for SessionMachines because
9973 * mutable Machine instances are always not registered (i.e. private
9974 * to the client process that creates them) and thus don't need to
9975 * inform callbacks. */
9976 if (i_isSessionMachine())
9977 mParent->i_onMachineDataChange(mData->mUuid);
9978 }
9979
9980 LogFlowThisFunc(("rc=%08X\n", rc));
9981 LogFlowThisFuncLeave();
9982 return rc;
9983}
9984
9985/**
9986 * Implementation for saving the machine settings into the given
9987 * settings::MachineConfigFile instance. This copies machine extradata
9988 * from the previous machine config file in the instance data, if any.
9989 *
9990 * This gets called from two locations:
9991 *
9992 * -- Machine::i_saveSettings(), during the regular XML writing;
9993 *
9994 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9995 * exported to OVF and we write the VirtualBox proprietary XML
9996 * into a <vbox:Machine> tag.
9997 *
9998 * This routine fills all the fields in there, including snapshots, *except*
9999 * for the following:
10000 *
10001 * -- fCurrentStateModified. There is some special logic associated with that.
10002 *
10003 * The caller can then call MachineConfigFile::write() or do something else
10004 * with it.
10005 *
10006 * Caller must hold the machine lock!
10007 *
10008 * This throws XML errors and HRESULT, so the caller must have a catch block!
10009 */
10010void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10011{
10012 // deep copy extradata
10013 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10014
10015 config.uuid = mData->mUuid;
10016
10017 // copy name, description, OS type, teleport, UTC etc.
10018 config.machineUserData = mUserData->s;
10019
10020 // Encode the Icon Override data from Machine and store on config userdata.
10021 std::vector<BYTE> iconByte;
10022 getIcon(iconByte);
10023 ssize_t cbData = iconByte.size();
10024 if (cbData > 0)
10025 {
10026 ssize_t cchOut = RTBase64EncodedLength(cbData);
10027 Utf8Str strIconData;
10028 strIconData.reserve(cchOut+1);
10029 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10030 strIconData.mutableRaw(), strIconData.capacity(),
10031 NULL);
10032 if (RT_FAILURE(vrc))
10033 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10034 strIconData.jolt();
10035 config.machineUserData.ovIcon = strIconData;
10036 }
10037 else
10038 config.machineUserData.ovIcon.setNull();
10039
10040 if ( mData->mMachineState == MachineState_Saved
10041 || mData->mMachineState == MachineState_Restoring
10042 // when doing certain snapshot operations we may or may not have
10043 // a saved state in the current state, so keep everything as is
10044 || ( ( mData->mMachineState == MachineState_Snapshotting
10045 || mData->mMachineState == MachineState_DeletingSnapshot
10046 || mData->mMachineState == MachineState_RestoringSnapshot)
10047 && (!mSSData->strStateFilePath.isEmpty())
10048 )
10049 )
10050 {
10051 Assert(!mSSData->strStateFilePath.isEmpty());
10052 /* try to make the file name relative to the settings file dir */
10053 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10054 }
10055 else
10056 {
10057 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10058 config.strStateFile.setNull();
10059 }
10060
10061 if (mData->mCurrentSnapshot)
10062 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10063 else
10064 config.uuidCurrentSnapshot.clear();
10065
10066 config.timeLastStateChange = mData->mLastStateChange;
10067 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10068 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10069
10070 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10071 if (FAILED(rc)) throw rc;
10072
10073 rc = i_saveStorageControllers(config.storageMachine);
10074 if (FAILED(rc)) throw rc;
10075
10076 // save machine's media registry if this is VirtualBox 4.0 or later
10077 if (config.canHaveOwnMediaRegistry())
10078 {
10079 // determine machine folder
10080 Utf8Str strMachineFolder = i_getSettingsFileFull();
10081 strMachineFolder.stripFilename();
10082 mParent->i_saveMediaRegistry(config.mediaRegistry,
10083 i_getId(), // only media with registry ID == machine UUID
10084 strMachineFolder);
10085 // this throws HRESULT
10086 }
10087
10088 // save snapshots
10089 rc = i_saveAllSnapshots(config);
10090 if (FAILED(rc)) throw rc;
10091}
10092
10093/**
10094 * Saves all snapshots of the machine into the given machine config file. Called
10095 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10096 * @param config
10097 * @return
10098 */
10099HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10100{
10101 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10102
10103 HRESULT rc = S_OK;
10104
10105 try
10106 {
10107 config.llFirstSnapshot.clear();
10108
10109 if (mData->mFirstSnapshot)
10110 {
10111 // the settings use a list for "the first snapshot"
10112 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10113
10114 // get reference to the snapshot on the list and work on that
10115 // element straight in the list to avoid excessive copying later
10116 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10117 if (FAILED(rc)) throw rc;
10118 }
10119
10120// if (mType == IsSessionMachine)
10121// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10122
10123 }
10124 catch (HRESULT err)
10125 {
10126 /* we assume that error info is set by the thrower */
10127 rc = err;
10128 }
10129 catch (...)
10130 {
10131 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10132 }
10133
10134 return rc;
10135}
10136
10137/**
10138 * Saves the VM hardware configuration. It is assumed that the
10139 * given node is empty.
10140 *
10141 * @param data Reference to the settings object for the hardware config.
10142 * @param pDbg Pointer to the settings object for the debugging config
10143 * which happens to live in mHWData.
10144 * @param pAutostart Pointer to the settings object for the autostart config
10145 * which happens to live in mHWData.
10146 */
10147HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10148 settings::Autostart *pAutostart)
10149{
10150 HRESULT rc = S_OK;
10151
10152 try
10153 {
10154 /* The hardware version attribute (optional).
10155 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10156 if ( mHWData->mHWVersion == "1"
10157 && mSSData->strStateFilePath.isEmpty()
10158 )
10159 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10160 other point needs to be found where this can be done. */
10161
10162 data.strVersion = mHWData->mHWVersion;
10163 data.uuid = mHWData->mHardwareUUID;
10164
10165 // CPU
10166 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10167 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10168 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10169 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10170 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10171 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10172 data.fPAE = !!mHWData->mPAEEnabled;
10173 data.enmLongMode = mHWData->mLongMode;
10174 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10175 data.cCPUs = mHWData->mCPUCount;
10176 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10177 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10178 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10179
10180 data.llCpus.clear();
10181 if (data.fCpuHotPlug)
10182 {
10183 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10184 {
10185 if (mHWData->mCPUAttached[idx])
10186 {
10187 settings::Cpu cpu;
10188 cpu.ulId = idx;
10189 data.llCpus.push_back(cpu);
10190 }
10191 }
10192 }
10193
10194 /* Standard and Extended CPUID leafs. */
10195 data.llCpuIdLeafs.clear();
10196 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10197 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10198 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10199 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10200 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10201 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10202
10203 // memory
10204 data.ulMemorySizeMB = mHWData->mMemorySize;
10205 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10206
10207 // firmware
10208 data.firmwareType = mHWData->mFirmwareType;
10209
10210 // HID
10211 data.pointingHIDType = mHWData->mPointingHIDType;
10212 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10213
10214 // chipset
10215 data.chipsetType = mHWData->mChipsetType;
10216
10217 // paravirt
10218 data.paravirtProvider = mHWData->mParavirtProvider;
10219
10220
10221 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10222
10223 // HPET
10224 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10225
10226 // boot order
10227 data.mapBootOrder.clear();
10228 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10229 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10230
10231 // display
10232 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10233 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10234 data.cMonitors = mHWData->mMonitorCount;
10235 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10236 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10237 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10238 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10239 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10240 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10241 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10242 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10243 {
10244 if (mHWData->maVideoCaptureScreens[i])
10245 ASMBitSet(&data.u64VideoCaptureScreens, i);
10246 else
10247 ASMBitClear(&data.u64VideoCaptureScreens, i);
10248 }
10249 /* store relative video capture file if possible */
10250 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10251
10252 /* VRDEServer settings (optional) */
10253 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10254 if (FAILED(rc)) throw rc;
10255
10256 /* BIOS (required) */
10257 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10258 if (FAILED(rc)) throw rc;
10259
10260 /* USB Controller (required) */
10261 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10262 {
10263 ComObjPtr<USBController> ctrl = *it;
10264 settings::USBController settingsCtrl;
10265
10266 settingsCtrl.strName = ctrl->i_getName();
10267 settingsCtrl.enmType = ctrl->i_getControllerType();
10268
10269 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10270 }
10271
10272 /* USB device filters (required) */
10273 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10274 if (FAILED(rc)) throw rc;
10275
10276 /* Network adapters (required) */
10277 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10278 data.llNetworkAdapters.clear();
10279 /* Write out only the nominal number of network adapters for this
10280 * chipset type. Since Machine::commit() hasn't been called there
10281 * may be extra NIC settings in the vector. */
10282 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10283 {
10284 settings::NetworkAdapter nic;
10285 nic.ulSlot = (uint32_t)slot;
10286 /* paranoia check... must not be NULL, but must not crash either. */
10287 if (mNetworkAdapters[slot])
10288 {
10289 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10290 if (FAILED(rc)) throw rc;
10291
10292 data.llNetworkAdapters.push_back(nic);
10293 }
10294 }
10295
10296 /* Serial ports */
10297 data.llSerialPorts.clear();
10298 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10299 {
10300 settings::SerialPort s;
10301 s.ulSlot = slot;
10302 rc = mSerialPorts[slot]->i_saveSettings(s);
10303 if (FAILED(rc)) return rc;
10304
10305 data.llSerialPorts.push_back(s);
10306 }
10307
10308 /* Parallel ports */
10309 data.llParallelPorts.clear();
10310 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10311 {
10312 settings::ParallelPort p;
10313 p.ulSlot = slot;
10314 rc = mParallelPorts[slot]->i_saveSettings(p);
10315 if (FAILED(rc)) return rc;
10316
10317 data.llParallelPorts.push_back(p);
10318 }
10319
10320 /* Audio adapter */
10321 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10322 if (FAILED(rc)) return rc;
10323
10324 /* Shared folders */
10325 data.llSharedFolders.clear();
10326 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10327 it != mHWData->mSharedFolders.end();
10328 ++it)
10329 {
10330 SharedFolder *pSF = *it;
10331 AutoCaller sfCaller(pSF);
10332 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10333 settings::SharedFolder sf;
10334 sf.strName = pSF->i_getName();
10335 sf.strHostPath = pSF->i_getHostPath();
10336 sf.fWritable = !!pSF->i_isWritable();
10337 sf.fAutoMount = !!pSF->i_isAutoMounted();
10338
10339 data.llSharedFolders.push_back(sf);
10340 }
10341
10342 // clipboard
10343 data.clipboardMode = mHWData->mClipboardMode;
10344
10345 // drag'n'drop
10346 data.dndMode = mHWData->mDnDMode;
10347
10348 /* Guest */
10349 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10350
10351 // IO settings
10352 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10353 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10354
10355 /* BandwidthControl (required) */
10356 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10357 if (FAILED(rc)) throw rc;
10358
10359 /* Host PCI devices */
10360 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10361 it != mHWData->mPCIDeviceAssignments.end();
10362 ++it)
10363 {
10364 ComObjPtr<PCIDeviceAttachment> pda = *it;
10365 settings::HostPCIDeviceAttachment hpda;
10366
10367 rc = pda->i_saveSettings(hpda);
10368 if (FAILED(rc)) throw rc;
10369
10370 data.pciAttachments.push_back(hpda);
10371 }
10372
10373
10374 // guest properties
10375 data.llGuestProperties.clear();
10376#ifdef VBOX_WITH_GUEST_PROPS
10377 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10378 it != mHWData->mGuestProperties.end();
10379 ++it)
10380 {
10381 HWData::GuestProperty property = it->second;
10382
10383 /* Remove transient guest properties at shutdown unless we
10384 * are saving state. Note that restoring snapshot intentionally
10385 * keeps them, they will be removed if appropriate once the final
10386 * machine state is set (as crashes etc. need to work). */
10387 if ( ( mData->mMachineState == MachineState_PoweredOff
10388 || mData->mMachineState == MachineState_Aborted
10389 || mData->mMachineState == MachineState_Teleported)
10390 && ( property.mFlags & guestProp::TRANSIENT
10391 || property.mFlags & guestProp::TRANSRESET))
10392 continue;
10393 settings::GuestProperty prop;
10394 prop.strName = it->first;
10395 prop.strValue = property.strValue;
10396 prop.timestamp = property.mTimestamp;
10397 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10398 guestProp::writeFlags(property.mFlags, szFlags);
10399 prop.strFlags = szFlags;
10400
10401 data.llGuestProperties.push_back(prop);
10402 }
10403
10404 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10405 /* I presume this doesn't require a backup(). */
10406 mData->mGuestPropertiesModified = FALSE;
10407#endif /* VBOX_WITH_GUEST_PROPS defined */
10408
10409 *pDbg = mHWData->mDebugging;
10410 *pAutostart = mHWData->mAutostart;
10411
10412 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10413 }
10414 catch(std::bad_alloc &)
10415 {
10416 return E_OUTOFMEMORY;
10417 }
10418
10419 AssertComRC(rc);
10420 return rc;
10421}
10422
10423/**
10424 * Saves the storage controller configuration.
10425 *
10426 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10427 */
10428HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10429{
10430 data.llStorageControllers.clear();
10431
10432 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10433 it != mStorageControllers->end();
10434 ++it)
10435 {
10436 HRESULT rc;
10437 ComObjPtr<StorageController> pCtl = *it;
10438
10439 settings::StorageController ctl;
10440 ctl.strName = pCtl->i_getName();
10441 ctl.controllerType = pCtl->i_getControllerType();
10442 ctl.storageBus = pCtl->i_getStorageBus();
10443 ctl.ulInstance = pCtl->i_getInstance();
10444 ctl.fBootable = pCtl->i_getBootable();
10445
10446 /* Save the port count. */
10447 ULONG portCount;
10448 rc = pCtl->COMGETTER(PortCount)(&portCount);
10449 ComAssertComRCRet(rc, rc);
10450 ctl.ulPortCount = portCount;
10451
10452 /* Save fUseHostIOCache */
10453 BOOL fUseHostIOCache;
10454 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10455 ComAssertComRCRet(rc, rc);
10456 ctl.fUseHostIOCache = !!fUseHostIOCache;
10457
10458 /* Save IDE emulation settings. */
10459 if (ctl.controllerType == StorageControllerType_IntelAhci)
10460 {
10461 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10462 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10463 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10464 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10465 )
10466 ComAssertComRCRet(rc, rc);
10467 }
10468
10469 /* save the devices now. */
10470 rc = i_saveStorageDevices(pCtl, ctl);
10471 ComAssertComRCRet(rc, rc);
10472
10473 data.llStorageControllers.push_back(ctl);
10474 }
10475
10476 return S_OK;
10477}
10478
10479/**
10480 * Saves the hard disk configuration.
10481 */
10482HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10483 settings::StorageController &data)
10484{
10485 MediaData::AttachmentList atts;
10486
10487 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10488 if (FAILED(rc)) return rc;
10489
10490 data.llAttachedDevices.clear();
10491 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10492 it != atts.end();
10493 ++it)
10494 {
10495 settings::AttachedDevice dev;
10496 IMediumAttachment *iA = *it;
10497 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10498 Medium *pMedium = pAttach->i_getMedium();
10499
10500 dev.deviceType = pAttach->i_getType();
10501 dev.lPort = pAttach->i_getPort();
10502 dev.lDevice = pAttach->i_getDevice();
10503 dev.fPassThrough = pAttach->i_getPassthrough();
10504 dev.fHotPluggable = pAttach->i_getHotPluggable();
10505 if (pMedium)
10506 {
10507 if (pMedium->i_isHostDrive())
10508 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10509 else
10510 dev.uuid = pMedium->i_getId();
10511 dev.fTempEject = pAttach->i_getTempEject();
10512 dev.fNonRotational = pAttach->i_getNonRotational();
10513 dev.fDiscard = pAttach->i_getDiscard();
10514 }
10515
10516 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10517
10518 data.llAttachedDevices.push_back(dev);
10519 }
10520
10521 return S_OK;
10522}
10523
10524/**
10525 * Saves machine state settings as defined by aFlags
10526 * (SaveSTS_* values).
10527 *
10528 * @param aFlags Combination of SaveSTS_* flags.
10529 *
10530 * @note Locks objects for writing.
10531 */
10532HRESULT Machine::i_saveStateSettings(int aFlags)
10533{
10534 if (aFlags == 0)
10535 return S_OK;
10536
10537 AutoCaller autoCaller(this);
10538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10539
10540 /* This object's write lock is also necessary to serialize file access
10541 * (prevent concurrent reads and writes) */
10542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10543
10544 HRESULT rc = S_OK;
10545
10546 Assert(mData->pMachineConfigFile);
10547
10548 try
10549 {
10550 if (aFlags & SaveSTS_CurStateModified)
10551 mData->pMachineConfigFile->fCurrentStateModified = true;
10552
10553 if (aFlags & SaveSTS_StateFilePath)
10554 {
10555 if (!mSSData->strStateFilePath.isEmpty())
10556 /* try to make the file name relative to the settings file dir */
10557 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10558 else
10559 mData->pMachineConfigFile->strStateFile.setNull();
10560 }
10561
10562 if (aFlags & SaveSTS_StateTimeStamp)
10563 {
10564 Assert( mData->mMachineState != MachineState_Aborted
10565 || mSSData->strStateFilePath.isEmpty());
10566
10567 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10568
10569 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10570//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10571 }
10572
10573 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10574 }
10575 catch (...)
10576 {
10577 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10578 }
10579
10580 return rc;
10581}
10582
10583/**
10584 * Ensures that the given medium is added to a media registry. If this machine
10585 * was created with 4.0 or later, then the machine registry is used. Otherwise
10586 * the global VirtualBox media registry is used.
10587 *
10588 * Caller must NOT hold machine lock, media tree or any medium locks!
10589 *
10590 * @param pMedium
10591 */
10592void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10593{
10594 /* Paranoia checks: do not hold machine or media tree locks. */
10595 AssertReturnVoid(!isWriteLockOnCurrentThread());
10596 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10597
10598 ComObjPtr<Medium> pBase;
10599 {
10600 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10601 pBase = pMedium->i_getBase();
10602 }
10603
10604 /* Paranoia checks: do not hold medium locks. */
10605 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10606 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10607
10608 // decide which medium registry to use now that the medium is attached:
10609 Guid uuid;
10610 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10611 // machine XML is VirtualBox 4.0 or higher:
10612 uuid = i_getId(); // machine UUID
10613 else
10614 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10615
10616 if (pMedium->i_addRegistry(uuid))
10617 mParent->i_markRegistryModified(uuid);
10618
10619 /* For more complex hard disk structures it can happen that the base
10620 * medium isn't yet associated with any medium registry. Do that now. */
10621 if (pMedium != pBase)
10622 {
10623 /* Tree lock needed by Medium::addRegistry when recursing. */
10624 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10625 if (pBase->i_addRegistryRecursive(uuid))
10626 {
10627 treeLock.release();
10628 mParent->i_markRegistryModified(uuid);
10629 }
10630 }
10631}
10632
10633/**
10634 * Creates differencing hard disks for all normal hard disks attached to this
10635 * machine and a new set of attachments to refer to created disks.
10636 *
10637 * Used when taking a snapshot or when deleting the current state. Gets called
10638 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10639 *
10640 * This method assumes that mMediaData contains the original hard disk attachments
10641 * it needs to create diffs for. On success, these attachments will be replaced
10642 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10643 * called to delete created diffs which will also rollback mMediaData and restore
10644 * whatever was backed up before calling this method.
10645 *
10646 * Attachments with non-normal hard disks are left as is.
10647 *
10648 * If @a aOnline is @c false then the original hard disks that require implicit
10649 * diffs will be locked for reading. Otherwise it is assumed that they are
10650 * already locked for writing (when the VM was started). Note that in the latter
10651 * case it is responsibility of the caller to lock the newly created diffs for
10652 * writing if this method succeeds.
10653 *
10654 * @param aProgress Progress object to run (must contain at least as
10655 * many operations left as the number of hard disks
10656 * attached).
10657 * @param aOnline Whether the VM was online prior to this operation.
10658 *
10659 * @note The progress object is not marked as completed, neither on success nor
10660 * on failure. This is a responsibility of the caller.
10661 *
10662 * @note Locks this object and the media tree for writing.
10663 */
10664HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10665 ULONG aWeight,
10666 bool aOnline)
10667{
10668 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10669
10670 AutoCaller autoCaller(this);
10671 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10672
10673 AutoMultiWriteLock2 alock(this->lockHandle(),
10674 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10675
10676 /* must be in a protective state because we release the lock below */
10677 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10678 || mData->mMachineState == MachineState_OnlineSnapshotting
10679 || mData->mMachineState == MachineState_LiveSnapshotting
10680 || mData->mMachineState == MachineState_RestoringSnapshot
10681 || mData->mMachineState == MachineState_DeletingSnapshot
10682 , E_FAIL);
10683
10684 HRESULT rc = S_OK;
10685
10686 // use appropriate locked media map (online or offline)
10687 MediumLockListMap lockedMediaOffline;
10688 MediumLockListMap *lockedMediaMap;
10689 if (aOnline)
10690 lockedMediaMap = &mData->mSession.mLockedMedia;
10691 else
10692 lockedMediaMap = &lockedMediaOffline;
10693
10694 try
10695 {
10696 if (!aOnline)
10697 {
10698 /* lock all attached hard disks early to detect "in use"
10699 * situations before creating actual diffs */
10700 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10701 it != mMediaData->mAttachments.end();
10702 ++it)
10703 {
10704 MediumAttachment* pAtt = *it;
10705 if (pAtt->i_getType() == DeviceType_HardDisk)
10706 {
10707 Medium* pMedium = pAtt->i_getMedium();
10708 Assert(pMedium);
10709
10710 MediumLockList *pMediumLockList(new MediumLockList());
10711 alock.release();
10712 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10713 false /* fMediumLockWrite */,
10714 false /* fMediumLockWriteAll */,
10715 NULL,
10716 *pMediumLockList);
10717 alock.acquire();
10718 if (FAILED(rc))
10719 {
10720 delete pMediumLockList;
10721 throw rc;
10722 }
10723 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10724 if (FAILED(rc))
10725 {
10726 throw setError(rc,
10727 tr("Collecting locking information for all attached media failed"));
10728 }
10729 }
10730 }
10731
10732 /* Now lock all media. If this fails, nothing is locked. */
10733 alock.release();
10734 rc = lockedMediaMap->Lock();
10735 alock.acquire();
10736 if (FAILED(rc))
10737 {
10738 throw setError(rc,
10739 tr("Locking of attached media failed"));
10740 }
10741 }
10742
10743 /* remember the current list (note that we don't use backup() since
10744 * mMediaData may be already backed up) */
10745 MediaData::AttachmentList atts = mMediaData->mAttachments;
10746
10747 /* start from scratch */
10748 mMediaData->mAttachments.clear();
10749
10750 /* go through remembered attachments and create diffs for normal hard
10751 * disks and attach them */
10752 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10753 it != atts.end();
10754 ++it)
10755 {
10756 MediumAttachment* pAtt = *it;
10757
10758 DeviceType_T devType = pAtt->i_getType();
10759 Medium* pMedium = pAtt->i_getMedium();
10760
10761 if ( devType != DeviceType_HardDisk
10762 || pMedium == NULL
10763 || pMedium->i_getType() != MediumType_Normal)
10764 {
10765 /* copy the attachment as is */
10766
10767 /** @todo the progress object created in SessionMachine::TakeSnaphot
10768 * only expects operations for hard disks. Later other
10769 * device types need to show up in the progress as well. */
10770 if (devType == DeviceType_HardDisk)
10771 {
10772 if (pMedium == NULL)
10773 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10774 aWeight); // weight
10775 else
10776 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10777 pMedium->i_getBase()->i_getName().c_str()).raw(),
10778 aWeight); // weight
10779 }
10780
10781 mMediaData->mAttachments.push_back(pAtt);
10782 continue;
10783 }
10784
10785 /* need a diff */
10786 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10787 pMedium->i_getBase()->i_getName().c_str()).raw(),
10788 aWeight); // weight
10789
10790 Utf8Str strFullSnapshotFolder;
10791 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10792
10793 ComObjPtr<Medium> diff;
10794 diff.createObject();
10795 // store the diff in the same registry as the parent
10796 // (this cannot fail here because we can't create implicit diffs for
10797 // unregistered images)
10798 Guid uuidRegistryParent;
10799 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10800 Assert(fInRegistry); NOREF(fInRegistry);
10801 rc = diff->init(mParent,
10802 pMedium->i_getPreferredDiffFormat(),
10803 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10804 uuidRegistryParent,
10805 DeviceType_HardDisk);
10806 if (FAILED(rc)) throw rc;
10807
10808 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10809 * the push_back? Looks like we're going to release medium with the
10810 * wrong kind of lock (general issue with if we fail anywhere at all)
10811 * and an orphaned VDI in the snapshots folder. */
10812
10813 /* update the appropriate lock list */
10814 MediumLockList *pMediumLockList;
10815 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10816 AssertComRCThrowRC(rc);
10817 if (aOnline)
10818 {
10819 alock.release();
10820 /* The currently attached medium will be read-only, change
10821 * the lock type to read. */
10822 rc = pMediumLockList->Update(pMedium, false);
10823 alock.acquire();
10824 AssertComRCThrowRC(rc);
10825 }
10826
10827 /* release the locks before the potentially lengthy operation */
10828 alock.release();
10829 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10830 pMediumLockList,
10831 NULL /* aProgress */,
10832 true /* aWait */);
10833 alock.acquire();
10834 if (FAILED(rc)) throw rc;
10835
10836 /* actual lock list update is done in Medium::commitMedia */
10837
10838 rc = diff->i_addBackReference(mData->mUuid);
10839 AssertComRCThrowRC(rc);
10840
10841 /* add a new attachment */
10842 ComObjPtr<MediumAttachment> attachment;
10843 attachment.createObject();
10844 rc = attachment->init(this,
10845 diff,
10846 pAtt->i_getControllerName(),
10847 pAtt->i_getPort(),
10848 pAtt->i_getDevice(),
10849 DeviceType_HardDisk,
10850 true /* aImplicit */,
10851 false /* aPassthrough */,
10852 false /* aTempEject */,
10853 pAtt->i_getNonRotational(),
10854 pAtt->i_getDiscard(),
10855 pAtt->i_getHotPluggable(),
10856 pAtt->i_getBandwidthGroup());
10857 if (FAILED(rc)) throw rc;
10858
10859 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10860 AssertComRCThrowRC(rc);
10861 mMediaData->mAttachments.push_back(attachment);
10862 }
10863 }
10864 catch (HRESULT aRC) { rc = aRC; }
10865
10866 /* unlock all hard disks we locked when there is no VM */
10867 if (!aOnline)
10868 {
10869 ErrorInfoKeeper eik;
10870
10871 HRESULT rc1 = lockedMediaMap->Clear();
10872 AssertComRC(rc1);
10873 }
10874
10875 return rc;
10876}
10877
10878/**
10879 * Deletes implicit differencing hard disks created either by
10880 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10881 *
10882 * Note that to delete hard disks created by #AttachDevice() this method is
10883 * called from #fixupMedia() when the changes are rolled back.
10884 *
10885 * @note Locks this object and the media tree for writing.
10886 */
10887HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10888{
10889 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10890
10891 AutoCaller autoCaller(this);
10892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10893
10894 AutoMultiWriteLock2 alock(this->lockHandle(),
10895 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10896
10897 /* We absolutely must have backed up state. */
10898 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10899
10900 /* Check if there are any implicitly created diff images. */
10901 bool fImplicitDiffs = false;
10902 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10903 it != mMediaData->mAttachments.end();
10904 ++it)
10905 {
10906 const ComObjPtr<MediumAttachment> &pAtt = *it;
10907 if (pAtt->i_isImplicit())
10908 {
10909 fImplicitDiffs = true;
10910 break;
10911 }
10912 }
10913 /* If there is nothing to do, leave early. This saves lots of image locking
10914 * effort. It also avoids a MachineStateChanged event without real reason.
10915 * This is important e.g. when loading a VM config, because there should be
10916 * no events. Otherwise API clients can become thoroughly confused for
10917 * inaccessible VMs (the code for loading VM configs uses this method for
10918 * cleanup if the config makes no sense), as they take such events as an
10919 * indication that the VM is alive, and they would force the VM config to
10920 * be reread, leading to an endless loop. */
10921 if (!fImplicitDiffs)
10922 return S_OK;
10923
10924 HRESULT rc = S_OK;
10925 MachineState_T oldState = mData->mMachineState;
10926
10927 /* will release the lock before the potentially lengthy operation,
10928 * so protect with the special state (unless already protected) */
10929 if ( oldState != MachineState_Snapshotting
10930 && oldState != MachineState_OnlineSnapshotting
10931 && oldState != MachineState_LiveSnapshotting
10932 && oldState != MachineState_RestoringSnapshot
10933 && oldState != MachineState_DeletingSnapshot
10934 && oldState != MachineState_DeletingSnapshotOnline
10935 && oldState != MachineState_DeletingSnapshotPaused
10936 )
10937 i_setMachineState(MachineState_SettingUp);
10938
10939 // use appropriate locked media map (online or offline)
10940 MediumLockListMap lockedMediaOffline;
10941 MediumLockListMap *lockedMediaMap;
10942 if (aOnline)
10943 lockedMediaMap = &mData->mSession.mLockedMedia;
10944 else
10945 lockedMediaMap = &lockedMediaOffline;
10946
10947 try
10948 {
10949 if (!aOnline)
10950 {
10951 /* lock all attached hard disks early to detect "in use"
10952 * situations before deleting actual diffs */
10953 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10954 it != mMediaData->mAttachments.end();
10955 ++it)
10956 {
10957 MediumAttachment* pAtt = *it;
10958 if (pAtt->i_getType() == DeviceType_HardDisk)
10959 {
10960 Medium* pMedium = pAtt->i_getMedium();
10961 Assert(pMedium);
10962
10963 MediumLockList *pMediumLockList(new MediumLockList());
10964 alock.release();
10965 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10966 false /* fMediumLockWrite */,
10967 false /* fMediumLockWriteAll */,
10968 NULL,
10969 *pMediumLockList);
10970 alock.acquire();
10971
10972 if (FAILED(rc))
10973 {
10974 delete pMediumLockList;
10975 throw rc;
10976 }
10977
10978 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10979 if (FAILED(rc))
10980 throw rc;
10981 }
10982 }
10983
10984 if (FAILED(rc))
10985 throw rc;
10986 } // end of offline
10987
10988 /* Lock lists are now up to date and include implicitly created media */
10989
10990 /* Go through remembered attachments and delete all implicitly created
10991 * diffs and fix up the attachment information */
10992 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10993 MediaData::AttachmentList implicitAtts;
10994 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10995 it != mMediaData->mAttachments.end();
10996 ++it)
10997 {
10998 ComObjPtr<MediumAttachment> pAtt = *it;
10999 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11000 if (pMedium.isNull())
11001 continue;
11002
11003 // Implicit attachments go on the list for deletion and back references are removed.
11004 if (pAtt->i_isImplicit())
11005 {
11006 /* Deassociate and mark for deletion */
11007 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11008 rc = pMedium->i_removeBackReference(mData->mUuid);
11009 if (FAILED(rc))
11010 throw rc;
11011 implicitAtts.push_back(pAtt);
11012 continue;
11013 }
11014
11015 /* Was this medium attached before? */
11016 if (!i_findAttachment(oldAtts, pMedium))
11017 {
11018 /* no: de-associate */
11019 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11020 rc = pMedium->i_removeBackReference(mData->mUuid);
11021 if (FAILED(rc))
11022 throw rc;
11023 continue;
11024 }
11025 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11026 }
11027
11028 /* If there are implicit attachments to delete, throw away the lock
11029 * map contents (which will unlock all media) since the medium
11030 * attachments will be rolled back. Below we need to completely
11031 * recreate the lock map anyway since it is infinitely complex to
11032 * do this incrementally (would need reconstructing each attachment
11033 * change, which would be extremely hairy). */
11034 if (implicitAtts.size() != 0)
11035 {
11036 ErrorInfoKeeper eik;
11037
11038 HRESULT rc1 = lockedMediaMap->Clear();
11039 AssertComRC(rc1);
11040 }
11041
11042 /* rollback hard disk changes */
11043 mMediaData.rollback();
11044
11045 MultiResult mrc(S_OK);
11046
11047 // Delete unused implicit diffs.
11048 if (implicitAtts.size() != 0)
11049 {
11050 alock.release();
11051
11052 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11053 {
11054 // Remove medium associated with this attachment.
11055 ComObjPtr<MediumAttachment> pAtt = *it;
11056 Assert(pAtt);
11057 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11058 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11059 Assert(pMedium);
11060
11061 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11062 // continue on delete failure, just collect error messages
11063 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11064 pMedium->i_getLocationFull().c_str() ));
11065 mrc = rc;
11066 }
11067 // Clear the list of deleted implicit attachments now, while not
11068 // holding the lock, as it will ultimately trigger Medium::uninit()
11069 // calls which assume that the media tree lock isn't held.
11070 implicitAtts.clear();
11071
11072 alock.acquire();
11073
11074 /* if there is a VM recreate media lock map as mentioned above,
11075 * otherwise it is a waste of time and we leave things unlocked */
11076 if (aOnline)
11077 {
11078 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11079 /* must never be NULL, but better safe than sorry */
11080 if (!pMachine.isNull())
11081 {
11082 alock.release();
11083 rc = mData->mSession.mMachine->i_lockMedia();
11084 alock.acquire();
11085 if (FAILED(rc))
11086 throw rc;
11087 }
11088 }
11089 }
11090 }
11091 catch (HRESULT aRC) {rc = aRC;}
11092
11093 if (mData->mMachineState == MachineState_SettingUp)
11094 i_setMachineState(oldState);
11095
11096 /* unlock all hard disks we locked when there is no VM */
11097 if (!aOnline)
11098 {
11099 ErrorInfoKeeper eik;
11100
11101 HRESULT rc1 = lockedMediaMap->Clear();
11102 AssertComRC(rc1);
11103 }
11104
11105 return rc;
11106}
11107
11108
11109/**
11110 * Looks through the given list of media attachments for one with the given parameters
11111 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11112 * can be searched as well if needed.
11113 *
11114 * @param list
11115 * @param aControllerName
11116 * @param aControllerPort
11117 * @param aDevice
11118 * @return
11119 */
11120MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11121 IN_BSTR aControllerName,
11122 LONG aControllerPort,
11123 LONG aDevice)
11124{
11125 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11126 {
11127 MediumAttachment *pAttach = *it;
11128 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11129 return pAttach;
11130 }
11131
11132 return NULL;
11133}
11134
11135/**
11136 * Looks through the given list of media attachments for one with the given parameters
11137 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11138 * can be searched as well if needed.
11139 *
11140 * @param list
11141 * @param aControllerName
11142 * @param aControllerPort
11143 * @param aDevice
11144 * @return
11145 */
11146MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11147 ComObjPtr<Medium> pMedium)
11148{
11149 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11150 {
11151 MediumAttachment *pAttach = *it;
11152 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11153 if (pMediumThis == pMedium)
11154 return pAttach;
11155 }
11156
11157 return NULL;
11158}
11159
11160/**
11161 * Looks through the given list of media attachments for one with the given parameters
11162 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11163 * can be searched as well if needed.
11164 *
11165 * @param list
11166 * @param aControllerName
11167 * @param aControllerPort
11168 * @param aDevice
11169 * @return
11170 */
11171MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11172 Guid &id)
11173{
11174 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11175 {
11176 MediumAttachment *pAttach = *it;
11177 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11178 if (pMediumThis->i_getId() == id)
11179 return pAttach;
11180 }
11181
11182 return NULL;
11183}
11184
11185/**
11186 * Main implementation for Machine::DetachDevice. This also gets called
11187 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11188 *
11189 * @param pAttach Medium attachment to detach.
11190 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11191 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11192 * SnapshotMachine, and this must be its snapshot.
11193 * @return
11194 */
11195HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11196 AutoWriteLock &writeLock,
11197 Snapshot *pSnapshot)
11198{
11199 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11200 DeviceType_T mediumType = pAttach->i_getType();
11201
11202 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11203
11204 if (pAttach->i_isImplicit())
11205 {
11206 /* attempt to implicitly delete the implicitly created diff */
11207
11208 /// @todo move the implicit flag from MediumAttachment to Medium
11209 /// and forbid any hard disk operation when it is implicit. Or maybe
11210 /// a special media state for it to make it even more simple.
11211
11212 Assert(mMediaData.isBackedUp());
11213
11214 /* will release the lock before the potentially lengthy operation, so
11215 * protect with the special state */
11216 MachineState_T oldState = mData->mMachineState;
11217 i_setMachineState(MachineState_SettingUp);
11218
11219 writeLock.release();
11220
11221 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11222 true /*aWait*/);
11223
11224 writeLock.acquire();
11225
11226 i_setMachineState(oldState);
11227
11228 if (FAILED(rc)) return rc;
11229 }
11230
11231 i_setModified(IsModified_Storage);
11232 mMediaData.backup();
11233 mMediaData->mAttachments.remove(pAttach);
11234
11235 if (!oldmedium.isNull())
11236 {
11237 // if this is from a snapshot, do not defer detachment to commitMedia()
11238 if (pSnapshot)
11239 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11240 // else if non-hard disk media, do not defer detachment to commitMedia() either
11241 else if (mediumType != DeviceType_HardDisk)
11242 oldmedium->i_removeBackReference(mData->mUuid);
11243 }
11244
11245 return S_OK;
11246}
11247
11248/**
11249 * Goes thru all media of the given list and
11250 *
11251 * 1) calls i_detachDevice() on each of them for this machine and
11252 * 2) adds all Medium objects found in the process to the given list,
11253 * depending on cleanupMode.
11254 *
11255 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11256 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11257 * media to the list.
11258 *
11259 * This gets called from Machine::Unregister, both for the actual Machine and
11260 * the SnapshotMachine objects that might be found in the snapshots.
11261 *
11262 * Requires caller and locking. The machine lock must be passed in because it
11263 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11264 *
11265 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11266 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11267 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11268 * Full, then all media get added;
11269 * otherwise no media get added.
11270 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11271 * @return
11272 */
11273HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11274 Snapshot *pSnapshot,
11275 CleanupMode_T cleanupMode,
11276 MediaList &llMedia)
11277{
11278 Assert(isWriteLockOnCurrentThread());
11279
11280 HRESULT rc;
11281
11282 // make a temporary list because i_detachDevice invalidates iterators into
11283 // mMediaData->mAttachments
11284 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11285
11286 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11287 {
11288 ComObjPtr<MediumAttachment> &pAttach = *it;
11289 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11290
11291 if (!pMedium.isNull())
11292 {
11293 AutoCaller mac(pMedium);
11294 if (FAILED(mac.rc())) return mac.rc();
11295 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11296 DeviceType_T devType = pMedium->i_getDeviceType();
11297 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11298 && devType == DeviceType_HardDisk)
11299 || (cleanupMode == CleanupMode_Full)
11300 )
11301 {
11302 llMedia.push_back(pMedium);
11303 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11304 /* Not allowed to keep this lock as below we need the parent
11305 * medium lock, and the lock order is parent to child. */
11306 lock.release();
11307 /*
11308 * Search for medias which are not attached to any machine, but
11309 * in the chain to an attached disk. Mediums are only consided
11310 * if they are:
11311 * - have only one child
11312 * - no references to any machines
11313 * - are of normal medium type
11314 */
11315 while (!pParent.isNull())
11316 {
11317 AutoCaller mac1(pParent);
11318 if (FAILED(mac1.rc())) return mac1.rc();
11319 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11320 if (pParent->i_getChildren().size() == 1)
11321 {
11322 if ( pParent->i_getMachineBackRefCount() == 0
11323 && pParent->i_getType() == MediumType_Normal
11324 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11325 llMedia.push_back(pParent);
11326 }
11327 else
11328 break;
11329 pParent = pParent->i_getParent();
11330 }
11331 }
11332 }
11333
11334 // real machine: then we need to use the proper method
11335 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11336
11337 if (FAILED(rc))
11338 return rc;
11339 }
11340
11341 return S_OK;
11342}
11343
11344/**
11345 * Perform deferred hard disk detachments.
11346 *
11347 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11348 * backed up).
11349 *
11350 * If @a aOnline is @c true then this method will also unlock the old hard disks
11351 * for which the new implicit diffs were created and will lock these new diffs for
11352 * writing.
11353 *
11354 * @param aOnline Whether the VM was online prior to this operation.
11355 *
11356 * @note Locks this object for writing!
11357 */
11358void Machine::i_commitMedia(bool aOnline /*= false*/)
11359{
11360 AutoCaller autoCaller(this);
11361 AssertComRCReturnVoid(autoCaller.rc());
11362
11363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11364
11365 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11366
11367 HRESULT rc = S_OK;
11368
11369 /* no attach/detach operations -- nothing to do */
11370 if (!mMediaData.isBackedUp())
11371 return;
11372
11373 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11374 bool fMediaNeedsLocking = false;
11375
11376 /* enumerate new attachments */
11377 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11378 it != mMediaData->mAttachments.end();
11379 ++it)
11380 {
11381 MediumAttachment *pAttach = *it;
11382
11383 pAttach->i_commit();
11384
11385 Medium* pMedium = pAttach->i_getMedium();
11386 bool fImplicit = pAttach->i_isImplicit();
11387
11388 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11389 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11390 fImplicit));
11391
11392 /** @todo convert all this Machine-based voodoo to MediumAttachment
11393 * based commit logic. */
11394 if (fImplicit)
11395 {
11396 /* convert implicit attachment to normal */
11397 pAttach->i_setImplicit(false);
11398
11399 if ( aOnline
11400 && pMedium
11401 && pAttach->i_getType() == DeviceType_HardDisk
11402 )
11403 {
11404 /* update the appropriate lock list */
11405 MediumLockList *pMediumLockList;
11406 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11407 AssertComRC(rc);
11408 if (pMediumLockList)
11409 {
11410 /* unlock if there's a need to change the locking */
11411 if (!fMediaNeedsLocking)
11412 {
11413 rc = mData->mSession.mLockedMedia.Unlock();
11414 AssertComRC(rc);
11415 fMediaNeedsLocking = true;
11416 }
11417 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11418 AssertComRC(rc);
11419 rc = pMediumLockList->Append(pMedium, true);
11420 AssertComRC(rc);
11421 }
11422 }
11423
11424 continue;
11425 }
11426
11427 if (pMedium)
11428 {
11429 /* was this medium attached before? */
11430 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11431 {
11432 MediumAttachment *pOldAttach = *oldIt;
11433 if (pOldAttach->i_getMedium() == pMedium)
11434 {
11435 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11436
11437 /* yes: remove from old to avoid de-association */
11438 oldAtts.erase(oldIt);
11439 break;
11440 }
11441 }
11442 }
11443 }
11444
11445 /* enumerate remaining old attachments and de-associate from the
11446 * current machine state */
11447 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11448 {
11449 MediumAttachment *pAttach = *it;
11450 Medium* pMedium = pAttach->i_getMedium();
11451
11452 /* Detach only hard disks, since DVD/floppy media is detached
11453 * instantly in MountMedium. */
11454 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11455 {
11456 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11457
11458 /* now de-associate from the current machine state */
11459 rc = pMedium->i_removeBackReference(mData->mUuid);
11460 AssertComRC(rc);
11461
11462 if (aOnline)
11463 {
11464 /* unlock since medium is not used anymore */
11465 MediumLockList *pMediumLockList;
11466 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11467 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11468 {
11469 /* this happens for online snapshots, there the attachment
11470 * is changing, but only to a diff image created under
11471 * the old one, so there is no separate lock list */
11472 Assert(!pMediumLockList);
11473 }
11474 else
11475 {
11476 AssertComRC(rc);
11477 if (pMediumLockList)
11478 {
11479 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11480 AssertComRC(rc);
11481 }
11482 }
11483 }
11484 }
11485 }
11486
11487 /* take media locks again so that the locking state is consistent */
11488 if (fMediaNeedsLocking)
11489 {
11490 Assert(aOnline);
11491 rc = mData->mSession.mLockedMedia.Lock();
11492 AssertComRC(rc);
11493 }
11494
11495 /* commit the hard disk changes */
11496 mMediaData.commit();
11497
11498 if (i_isSessionMachine())
11499 {
11500 /*
11501 * Update the parent machine to point to the new owner.
11502 * This is necessary because the stored parent will point to the
11503 * session machine otherwise and cause crashes or errors later
11504 * when the session machine gets invalid.
11505 */
11506 /** @todo Change the MediumAttachment class to behave like any other
11507 * class in this regard by creating peer MediumAttachment
11508 * objects for session machines and share the data with the peer
11509 * machine.
11510 */
11511 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11512 it != mMediaData->mAttachments.end();
11513 ++it)
11514 (*it)->i_updateParentMachine(mPeer);
11515
11516 /* attach new data to the primary machine and reshare it */
11517 mPeer->mMediaData.attach(mMediaData);
11518 }
11519
11520 return;
11521}
11522
11523/**
11524 * Perform deferred deletion of implicitly created diffs.
11525 *
11526 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11527 * backed up).
11528 *
11529 * @note Locks this object for writing!
11530 */
11531void Machine::i_rollbackMedia()
11532{
11533 AutoCaller autoCaller(this);
11534 AssertComRCReturnVoid(autoCaller.rc());
11535
11536 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11537 LogFlowThisFunc(("Entering rollbackMedia\n"));
11538
11539 HRESULT rc = S_OK;
11540
11541 /* no attach/detach operations -- nothing to do */
11542 if (!mMediaData.isBackedUp())
11543 return;
11544
11545 /* enumerate new attachments */
11546 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11547 it != mMediaData->mAttachments.end();
11548 ++it)
11549 {
11550 MediumAttachment *pAttach = *it;
11551 /* Fix up the backrefs for DVD/floppy media. */
11552 if (pAttach->i_getType() != DeviceType_HardDisk)
11553 {
11554 Medium* pMedium = pAttach->i_getMedium();
11555 if (pMedium)
11556 {
11557 rc = pMedium->i_removeBackReference(mData->mUuid);
11558 AssertComRC(rc);
11559 }
11560 }
11561
11562 (*it)->i_rollback();
11563
11564 pAttach = *it;
11565 /* Fix up the backrefs for DVD/floppy media. */
11566 if (pAttach->i_getType() != DeviceType_HardDisk)
11567 {
11568 Medium* pMedium = pAttach->i_getMedium();
11569 if (pMedium)
11570 {
11571 rc = pMedium->i_addBackReference(mData->mUuid);
11572 AssertComRC(rc);
11573 }
11574 }
11575 }
11576
11577 /** @todo convert all this Machine-based voodoo to MediumAttachment
11578 * based rollback logic. */
11579 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11580
11581 return;
11582}
11583
11584/**
11585 * Returns true if the settings file is located in the directory named exactly
11586 * as the machine; this means, among other things, that the machine directory
11587 * should be auto-renamed.
11588 *
11589 * @param aSettingsDir if not NULL, the full machine settings file directory
11590 * name will be assigned there.
11591 *
11592 * @note Doesn't lock anything.
11593 * @note Not thread safe (must be called from this object's lock).
11594 */
11595bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11596{
11597 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11598 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11599 if (aSettingsDir)
11600 *aSettingsDir = strMachineDirName;
11601 strMachineDirName.stripPath(); // vmname
11602 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11603 strConfigFileOnly.stripPath() // vmname.vbox
11604 .stripSuffix(); // vmname
11605 /** @todo hack, make somehow use of ComposeMachineFilename */
11606 if (mUserData->s.fDirectoryIncludesUUID)
11607 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11608
11609 AssertReturn(!strMachineDirName.isEmpty(), false);
11610 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11611
11612 return strMachineDirName == strConfigFileOnly;
11613}
11614
11615/**
11616 * Discards all changes to machine settings.
11617 *
11618 * @param aNotify Whether to notify the direct session about changes or not.
11619 *
11620 * @note Locks objects for writing!
11621 */
11622void Machine::i_rollback(bool aNotify)
11623{
11624 AutoCaller autoCaller(this);
11625 AssertComRCReturn(autoCaller.rc(), (void)0);
11626
11627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11628
11629 if (!mStorageControllers.isNull())
11630 {
11631 if (mStorageControllers.isBackedUp())
11632 {
11633 /* unitialize all new devices (absent in the backed up list). */
11634 StorageControllerList::const_iterator it = mStorageControllers->begin();
11635 StorageControllerList *backedList = mStorageControllers.backedUpData();
11636 while (it != mStorageControllers->end())
11637 {
11638 if ( std::find(backedList->begin(), backedList->end(), *it)
11639 == backedList->end()
11640 )
11641 {
11642 (*it)->uninit();
11643 }
11644 ++it;
11645 }
11646
11647 /* restore the list */
11648 mStorageControllers.rollback();
11649 }
11650
11651 /* rollback any changes to devices after restoring the list */
11652 if (mData->flModifications & IsModified_Storage)
11653 {
11654 StorageControllerList::const_iterator it = mStorageControllers->begin();
11655 while (it != mStorageControllers->end())
11656 {
11657 (*it)->i_rollback();
11658 ++it;
11659 }
11660 }
11661 }
11662
11663 if (!mUSBControllers.isNull())
11664 {
11665 if (mUSBControllers.isBackedUp())
11666 {
11667 /* unitialize all new devices (absent in the backed up list). */
11668 USBControllerList::const_iterator it = mUSBControllers->begin();
11669 USBControllerList *backedList = mUSBControllers.backedUpData();
11670 while (it != mUSBControllers->end())
11671 {
11672 if ( std::find(backedList->begin(), backedList->end(), *it)
11673 == backedList->end()
11674 )
11675 {
11676 (*it)->uninit();
11677 }
11678 ++it;
11679 }
11680
11681 /* restore the list */
11682 mUSBControllers.rollback();
11683 }
11684
11685 /* rollback any changes to devices after restoring the list */
11686 if (mData->flModifications & IsModified_USB)
11687 {
11688 USBControllerList::const_iterator it = mUSBControllers->begin();
11689 while (it != mUSBControllers->end())
11690 {
11691 (*it)->i_rollback();
11692 ++it;
11693 }
11694 }
11695 }
11696
11697 mUserData.rollback();
11698
11699 mHWData.rollback();
11700
11701 if (mData->flModifications & IsModified_Storage)
11702 i_rollbackMedia();
11703
11704 if (mBIOSSettings)
11705 mBIOSSettings->i_rollback();
11706
11707 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11708 mVRDEServer->i_rollback();
11709
11710 if (mAudioAdapter)
11711 mAudioAdapter->i_rollback();
11712
11713 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11714 mUSBDeviceFilters->i_rollback();
11715
11716 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11717 mBandwidthControl->i_rollback();
11718
11719 if (!mHWData.isNull())
11720 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11721 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11722 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11723 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11724
11725 if (mData->flModifications & IsModified_NetworkAdapters)
11726 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11727 if ( mNetworkAdapters[slot]
11728 && mNetworkAdapters[slot]->i_isModified())
11729 {
11730 mNetworkAdapters[slot]->i_rollback();
11731 networkAdapters[slot] = mNetworkAdapters[slot];
11732 }
11733
11734 if (mData->flModifications & IsModified_SerialPorts)
11735 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11736 if ( mSerialPorts[slot]
11737 && mSerialPorts[slot]->i_isModified())
11738 {
11739 mSerialPorts[slot]->i_rollback();
11740 serialPorts[slot] = mSerialPorts[slot];
11741 }
11742
11743 if (mData->flModifications & IsModified_ParallelPorts)
11744 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11745 if ( mParallelPorts[slot]
11746 && mParallelPorts[slot]->i_isModified())
11747 {
11748 mParallelPorts[slot]->i_rollback();
11749 parallelPorts[slot] = mParallelPorts[slot];
11750 }
11751
11752 if (aNotify)
11753 {
11754 /* inform the direct session about changes */
11755
11756 ComObjPtr<Machine> that = this;
11757 uint32_t flModifications = mData->flModifications;
11758 alock.release();
11759
11760 if (flModifications & IsModified_SharedFolders)
11761 that->i_onSharedFolderChange();
11762
11763 if (flModifications & IsModified_VRDEServer)
11764 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11765 if (flModifications & IsModified_USB)
11766 that->i_onUSBControllerChange();
11767
11768 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11769 if (networkAdapters[slot])
11770 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11771 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11772 if (serialPorts[slot])
11773 that->i_onSerialPortChange(serialPorts[slot]);
11774 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11775 if (parallelPorts[slot])
11776 that->i_onParallelPortChange(parallelPorts[slot]);
11777
11778 if (flModifications & IsModified_Storage)
11779 that->i_onStorageControllerChange();
11780
11781#if 0
11782 if (flModifications & IsModified_BandwidthControl)
11783 that->onBandwidthControlChange();
11784#endif
11785 }
11786}
11787
11788/**
11789 * Commits all the changes to machine settings.
11790 *
11791 * Note that this operation is supposed to never fail.
11792 *
11793 * @note Locks this object and children for writing.
11794 */
11795void Machine::i_commit()
11796{
11797 AutoCaller autoCaller(this);
11798 AssertComRCReturnVoid(autoCaller.rc());
11799
11800 AutoCaller peerCaller(mPeer);
11801 AssertComRCReturnVoid(peerCaller.rc());
11802
11803 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11804
11805 /*
11806 * use safe commit to ensure Snapshot machines (that share mUserData)
11807 * will still refer to a valid memory location
11808 */
11809 mUserData.commitCopy();
11810
11811 mHWData.commit();
11812
11813 if (mMediaData.isBackedUp())
11814 i_commitMedia(Global::IsOnline(mData->mMachineState));
11815
11816 mBIOSSettings->i_commit();
11817 mVRDEServer->i_commit();
11818 mAudioAdapter->i_commit();
11819 mUSBDeviceFilters->i_commit();
11820 mBandwidthControl->i_commit();
11821
11822 /* Since mNetworkAdapters is a list which might have been changed (resized)
11823 * without using the Backupable<> template we need to handle the copying
11824 * of the list entries manually, including the creation of peers for the
11825 * new objects. */
11826 bool commitNetworkAdapters = false;
11827 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11828 if (mPeer)
11829 {
11830 /* commit everything, even the ones which will go away */
11831 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11832 mNetworkAdapters[slot]->i_commit();
11833 /* copy over the new entries, creating a peer and uninit the original */
11834 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11835 for (size_t slot = 0; slot < newSize; slot++)
11836 {
11837 /* look if this adapter has a peer device */
11838 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11839 if (!peer)
11840 {
11841 /* no peer means the adapter is a newly created one;
11842 * create a peer owning data this data share it with */
11843 peer.createObject();
11844 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11845 }
11846 mPeer->mNetworkAdapters[slot] = peer;
11847 }
11848 /* uninit any no longer needed network adapters */
11849 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11850 mNetworkAdapters[slot]->uninit();
11851 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11852 {
11853 if (mPeer->mNetworkAdapters[slot])
11854 mPeer->mNetworkAdapters[slot]->uninit();
11855 }
11856 /* Keep the original network adapter count until this point, so that
11857 * discarding a chipset type change will not lose settings. */
11858 mNetworkAdapters.resize(newSize);
11859 mPeer->mNetworkAdapters.resize(newSize);
11860 }
11861 else
11862 {
11863 /* we have no peer (our parent is the newly created machine);
11864 * just commit changes to the network adapters */
11865 commitNetworkAdapters = true;
11866 }
11867 if (commitNetworkAdapters)
11868 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11869 mNetworkAdapters[slot]->i_commit();
11870
11871 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11872 mSerialPorts[slot]->i_commit();
11873 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11874 mParallelPorts[slot]->i_commit();
11875
11876 bool commitStorageControllers = false;
11877
11878 if (mStorageControllers.isBackedUp())
11879 {
11880 mStorageControllers.commit();
11881
11882 if (mPeer)
11883 {
11884 /* Commit all changes to new controllers (this will reshare data with
11885 * peers for those who have peers) */
11886 StorageControllerList *newList = new StorageControllerList();
11887 StorageControllerList::const_iterator it = mStorageControllers->begin();
11888 while (it != mStorageControllers->end())
11889 {
11890 (*it)->i_commit();
11891
11892 /* look if this controller has a peer device */
11893 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11894 if (!peer)
11895 {
11896 /* no peer means the device is a newly created one;
11897 * create a peer owning data this device share it with */
11898 peer.createObject();
11899 peer->init(mPeer, *it, true /* aReshare */);
11900 }
11901 else
11902 {
11903 /* remove peer from the old list */
11904 mPeer->mStorageControllers->remove(peer);
11905 }
11906 /* and add it to the new list */
11907 newList->push_back(peer);
11908
11909 ++it;
11910 }
11911
11912 /* uninit old peer's controllers that are left */
11913 it = mPeer->mStorageControllers->begin();
11914 while (it != mPeer->mStorageControllers->end())
11915 {
11916 (*it)->uninit();
11917 ++it;
11918 }
11919
11920 /* attach new list of controllers to our peer */
11921 mPeer->mStorageControllers.attach(newList);
11922 }
11923 else
11924 {
11925 /* we have no peer (our parent is the newly created machine);
11926 * just commit changes to devices */
11927 commitStorageControllers = true;
11928 }
11929 }
11930 else
11931 {
11932 /* the list of controllers itself is not changed,
11933 * just commit changes to controllers themselves */
11934 commitStorageControllers = true;
11935 }
11936
11937 if (commitStorageControllers)
11938 {
11939 StorageControllerList::const_iterator it = mStorageControllers->begin();
11940 while (it != mStorageControllers->end())
11941 {
11942 (*it)->i_commit();
11943 ++it;
11944 }
11945 }
11946
11947 bool commitUSBControllers = false;
11948
11949 if (mUSBControllers.isBackedUp())
11950 {
11951 mUSBControllers.commit();
11952
11953 if (mPeer)
11954 {
11955 /* Commit all changes to new controllers (this will reshare data with
11956 * peers for those who have peers) */
11957 USBControllerList *newList = new USBControllerList();
11958 USBControllerList::const_iterator it = mUSBControllers->begin();
11959 while (it != mUSBControllers->end())
11960 {
11961 (*it)->i_commit();
11962
11963 /* look if this controller has a peer device */
11964 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11965 if (!peer)
11966 {
11967 /* no peer means the device is a newly created one;
11968 * create a peer owning data this device share it with */
11969 peer.createObject();
11970 peer->init(mPeer, *it, true /* aReshare */);
11971 }
11972 else
11973 {
11974 /* remove peer from the old list */
11975 mPeer->mUSBControllers->remove(peer);
11976 }
11977 /* and add it to the new list */
11978 newList->push_back(peer);
11979
11980 ++it;
11981 }
11982
11983 /* uninit old peer's controllers that are left */
11984 it = mPeer->mUSBControllers->begin();
11985 while (it != mPeer->mUSBControllers->end())
11986 {
11987 (*it)->uninit();
11988 ++it;
11989 }
11990
11991 /* attach new list of controllers to our peer */
11992 mPeer->mUSBControllers.attach(newList);
11993 }
11994 else
11995 {
11996 /* we have no peer (our parent is the newly created machine);
11997 * just commit changes to devices */
11998 commitUSBControllers = true;
11999 }
12000 }
12001 else
12002 {
12003 /* the list of controllers itself is not changed,
12004 * just commit changes to controllers themselves */
12005 commitUSBControllers = true;
12006 }
12007
12008 if (commitUSBControllers)
12009 {
12010 USBControllerList::const_iterator it = mUSBControllers->begin();
12011 while (it != mUSBControllers->end())
12012 {
12013 (*it)->i_commit();
12014 ++it;
12015 }
12016 }
12017
12018 if (i_isSessionMachine())
12019 {
12020 /* attach new data to the primary machine and reshare it */
12021 mPeer->mUserData.attach(mUserData);
12022 mPeer->mHWData.attach(mHWData);
12023 /* mMediaData is reshared by fixupMedia */
12024 // mPeer->mMediaData.attach(mMediaData);
12025 Assert(mPeer->mMediaData.data() == mMediaData.data());
12026 }
12027}
12028
12029/**
12030 * Copies all the hardware data from the given machine.
12031 *
12032 * Currently, only called when the VM is being restored from a snapshot. In
12033 * particular, this implies that the VM is not running during this method's
12034 * call.
12035 *
12036 * @note This method must be called from under this object's lock.
12037 *
12038 * @note This method doesn't call #commit(), so all data remains backed up and
12039 * unsaved.
12040 */
12041void Machine::i_copyFrom(Machine *aThat)
12042{
12043 AssertReturnVoid(!i_isSnapshotMachine());
12044 AssertReturnVoid(aThat->i_isSnapshotMachine());
12045
12046 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12047
12048 mHWData.assignCopy(aThat->mHWData);
12049
12050 // create copies of all shared folders (mHWData after attaching a copy
12051 // contains just references to original objects)
12052 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12053 it != mHWData->mSharedFolders.end();
12054 ++it)
12055 {
12056 ComObjPtr<SharedFolder> folder;
12057 folder.createObject();
12058 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12059 AssertComRC(rc);
12060 *it = folder;
12061 }
12062
12063 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12064 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12065 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12066 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12067 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12068
12069 /* create private copies of all controllers */
12070 mStorageControllers.backup();
12071 mStorageControllers->clear();
12072 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12073 it != aThat->mStorageControllers->end();
12074 ++it)
12075 {
12076 ComObjPtr<StorageController> ctrl;
12077 ctrl.createObject();
12078 ctrl->initCopy(this, *it);
12079 mStorageControllers->push_back(ctrl);
12080 }
12081
12082 /* create private copies of all USB controllers */
12083 mUSBControllers.backup();
12084 mUSBControllers->clear();
12085 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12086 it != aThat->mUSBControllers->end();
12087 ++it)
12088 {
12089 ComObjPtr<USBController> ctrl;
12090 ctrl.createObject();
12091 ctrl->initCopy(this, *it);
12092 mUSBControllers->push_back(ctrl);
12093 }
12094
12095 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12096 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12097 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12098 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12099 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12100 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12101 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12102}
12103
12104/**
12105 * Returns whether the given storage controller is hotplug capable.
12106 *
12107 * @returns true if the controller supports hotplugging
12108 * false otherwise.
12109 * @param enmCtrlType The controller type to check for.
12110 */
12111bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12112{
12113 ComPtr<ISystemProperties> systemProperties;
12114 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12115 if (FAILED(rc))
12116 return false;
12117
12118 BOOL aHotplugCapable = FALSE;
12119 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12120
12121 return RT_BOOL(aHotplugCapable);
12122}
12123
12124#ifdef VBOX_WITH_RESOURCE_USAGE_API
12125
12126void Machine::i_getDiskList(MediaList &list)
12127{
12128 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12129 it != mMediaData->mAttachments.end();
12130 ++it)
12131 {
12132 MediumAttachment* pAttach = *it;
12133 /* just in case */
12134 AssertStmt(pAttach, continue);
12135
12136 AutoCaller localAutoCallerA(pAttach);
12137 if (FAILED(localAutoCallerA.rc())) continue;
12138
12139 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12140
12141 if (pAttach->i_getType() == DeviceType_HardDisk)
12142 list.push_back(pAttach->i_getMedium());
12143 }
12144}
12145
12146void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12147{
12148 AssertReturnVoid(isWriteLockOnCurrentThread());
12149 AssertPtrReturnVoid(aCollector);
12150
12151 pm::CollectorHAL *hal = aCollector->getHAL();
12152 /* Create sub metrics */
12153 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12154 "Percentage of processor time spent in user mode by the VM process.");
12155 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12156 "Percentage of processor time spent in kernel mode by the VM process.");
12157 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12158 "Size of resident portion of VM process in memory.");
12159 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12160 "Actual size of all VM disks combined.");
12161 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12162 "Network receive rate.");
12163 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12164 "Network transmit rate.");
12165 /* Create and register base metrics */
12166 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12167 cpuLoadUser, cpuLoadKernel);
12168 aCollector->registerBaseMetric(cpuLoad);
12169 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12170 ramUsageUsed);
12171 aCollector->registerBaseMetric(ramUsage);
12172 MediaList disks;
12173 i_getDiskList(disks);
12174 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12175 diskUsageUsed);
12176 aCollector->registerBaseMetric(diskUsage);
12177
12178 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12179 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12180 new pm::AggregateAvg()));
12181 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12182 new pm::AggregateMin()));
12183 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12184 new pm::AggregateMax()));
12185 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12186 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12187 new pm::AggregateAvg()));
12188 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12189 new pm::AggregateMin()));
12190 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12191 new pm::AggregateMax()));
12192
12193 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12194 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12195 new pm::AggregateAvg()));
12196 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12197 new pm::AggregateMin()));
12198 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12199 new pm::AggregateMax()));
12200
12201 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12202 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12203 new pm::AggregateAvg()));
12204 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12205 new pm::AggregateMin()));
12206 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12207 new pm::AggregateMax()));
12208
12209
12210 /* Guest metrics collector */
12211 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12212 aCollector->registerGuest(mCollectorGuest);
12213 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12214 this, __PRETTY_FUNCTION__, mCollectorGuest));
12215
12216 /* Create sub metrics */
12217 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12218 "Percentage of processor time spent in user mode as seen by the guest.");
12219 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12220 "Percentage of processor time spent in kernel mode as seen by the guest.");
12221 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12222 "Percentage of processor time spent idling as seen by the guest.");
12223
12224 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12225 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12226 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12227 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12228 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12229 pm::SubMetric *guestMemCache = new pm::SubMetric(
12230 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12231
12232 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12233 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12234
12235 /* Create and register base metrics */
12236 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12237 machineNetRx, machineNetTx);
12238 aCollector->registerBaseMetric(machineNetRate);
12239
12240 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12241 guestLoadUser, guestLoadKernel, guestLoadIdle);
12242 aCollector->registerBaseMetric(guestCpuLoad);
12243
12244 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12245 guestMemTotal, guestMemFree,
12246 guestMemBalloon, guestMemShared,
12247 guestMemCache, guestPagedTotal);
12248 aCollector->registerBaseMetric(guestCpuMem);
12249
12250 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12251 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12252 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12253 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12254
12255 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12256 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12257 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12258 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12259
12260 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12264
12265 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12266 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12269
12270 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12271 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12274
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12284
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12296 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12301 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12304}
12305
12306void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12307{
12308 AssertReturnVoid(isWriteLockOnCurrentThread());
12309
12310 if (aCollector)
12311 {
12312 aCollector->unregisterMetricsFor(aMachine);
12313 aCollector->unregisterBaseMetricsFor(aMachine);
12314 }
12315}
12316
12317#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12318
12319
12320////////////////////////////////////////////////////////////////////////////////
12321
12322DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12323
12324HRESULT SessionMachine::FinalConstruct()
12325{
12326 LogFlowThisFunc(("\n"));
12327
12328 mClientToken = NULL;
12329
12330 return BaseFinalConstruct();
12331}
12332
12333void SessionMachine::FinalRelease()
12334{
12335 LogFlowThisFunc(("\n"));
12336
12337 Assert(!mClientToken);
12338 /* paranoia, should not hang around any more */
12339 if (mClientToken)
12340 {
12341 delete mClientToken;
12342 mClientToken = NULL;
12343 }
12344
12345 uninit(Uninit::Unexpected);
12346
12347 BaseFinalRelease();
12348}
12349
12350/**
12351 * @note Must be called only by Machine::LockMachine() from its own write lock.
12352 */
12353HRESULT SessionMachine::init(Machine *aMachine)
12354{
12355 LogFlowThisFuncEnter();
12356 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12357
12358 AssertReturn(aMachine, E_INVALIDARG);
12359
12360 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12361
12362 /* Enclose the state transition NotReady->InInit->Ready */
12363 AutoInitSpan autoInitSpan(this);
12364 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12365
12366 HRESULT rc = S_OK;
12367
12368 /* create the machine client token */
12369 try
12370 {
12371 mClientToken = new ClientToken(aMachine, this);
12372 if (!mClientToken->isReady())
12373 {
12374 delete mClientToken;
12375 mClientToken = NULL;
12376 rc = E_FAIL;
12377 }
12378 }
12379 catch (std::bad_alloc &)
12380 {
12381 rc = E_OUTOFMEMORY;
12382 }
12383 if (FAILED(rc))
12384 return rc;
12385
12386 /* memorize the peer Machine */
12387 unconst(mPeer) = aMachine;
12388 /* share the parent pointer */
12389 unconst(mParent) = aMachine->mParent;
12390
12391 /* take the pointers to data to share */
12392 mData.share(aMachine->mData);
12393 mSSData.share(aMachine->mSSData);
12394
12395 mUserData.share(aMachine->mUserData);
12396 mHWData.share(aMachine->mHWData);
12397 mMediaData.share(aMachine->mMediaData);
12398
12399 mStorageControllers.allocate();
12400 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12401 it != aMachine->mStorageControllers->end();
12402 ++it)
12403 {
12404 ComObjPtr<StorageController> ctl;
12405 ctl.createObject();
12406 ctl->init(this, *it);
12407 mStorageControllers->push_back(ctl);
12408 }
12409
12410 mUSBControllers.allocate();
12411 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12412 it != aMachine->mUSBControllers->end();
12413 ++it)
12414 {
12415 ComObjPtr<USBController> ctl;
12416 ctl.createObject();
12417 ctl->init(this, *it);
12418 mUSBControllers->push_back(ctl);
12419 }
12420
12421 unconst(mBIOSSettings).createObject();
12422 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12423 /* create another VRDEServer object that will be mutable */
12424 unconst(mVRDEServer).createObject();
12425 mVRDEServer->init(this, aMachine->mVRDEServer);
12426 /* create another audio adapter object that will be mutable */
12427 unconst(mAudioAdapter).createObject();
12428 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12429 /* create a list of serial ports that will be mutable */
12430 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12431 {
12432 unconst(mSerialPorts[slot]).createObject();
12433 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12434 }
12435 /* create a list of parallel ports that will be mutable */
12436 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12437 {
12438 unconst(mParallelPorts[slot]).createObject();
12439 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12440 }
12441
12442 /* create another USB device filters object that will be mutable */
12443 unconst(mUSBDeviceFilters).createObject();
12444 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12445
12446 /* create a list of network adapters that will be mutable */
12447 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12448 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12449 {
12450 unconst(mNetworkAdapters[slot]).createObject();
12451 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12452 }
12453
12454 /* create another bandwidth control object that will be mutable */
12455 unconst(mBandwidthControl).createObject();
12456 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12457
12458 /* default is to delete saved state on Saved -> PoweredOff transition */
12459 mRemoveSavedState = true;
12460
12461 /* Confirm a successful initialization when it's the case */
12462 autoInitSpan.setSucceeded();
12463
12464 miNATNetworksStarted = 0;
12465
12466 LogFlowThisFuncLeave();
12467 return rc;
12468}
12469
12470/**
12471 * Uninitializes this session object. If the reason is other than
12472 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12473 * or the client watcher code.
12474 *
12475 * @param aReason uninitialization reason
12476 *
12477 * @note Locks mParent + this object for writing.
12478 */
12479void SessionMachine::uninit(Uninit::Reason aReason)
12480{
12481 LogFlowThisFuncEnter();
12482 LogFlowThisFunc(("reason=%d\n", aReason));
12483
12484 /*
12485 * Strongly reference ourselves to prevent this object deletion after
12486 * mData->mSession.mMachine.setNull() below (which can release the last
12487 * reference and call the destructor). Important: this must be done before
12488 * accessing any members (and before AutoUninitSpan that does it as well).
12489 * This self reference will be released as the very last step on return.
12490 */
12491 ComObjPtr<SessionMachine> selfRef = this;
12492
12493 /* Enclose the state transition Ready->InUninit->NotReady */
12494 AutoUninitSpan autoUninitSpan(this);
12495 if (autoUninitSpan.uninitDone())
12496 {
12497 LogFlowThisFunc(("Already uninitialized\n"));
12498 LogFlowThisFuncLeave();
12499 return;
12500 }
12501
12502 if (autoUninitSpan.initFailed())
12503 {
12504 /* We've been called by init() because it's failed. It's not really
12505 * necessary (nor it's safe) to perform the regular uninit sequence
12506 * below, the following is enough.
12507 */
12508 LogFlowThisFunc(("Initialization failed.\n"));
12509 /* destroy the machine client token */
12510 if (mClientToken)
12511 {
12512 delete mClientToken;
12513 mClientToken = NULL;
12514 }
12515 uninitDataAndChildObjects();
12516 mData.free();
12517 unconst(mParent) = NULL;
12518 unconst(mPeer) = NULL;
12519 LogFlowThisFuncLeave();
12520 return;
12521 }
12522
12523 MachineState_T lastState;
12524 {
12525 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12526 lastState = mData->mMachineState;
12527 }
12528 NOREF(lastState);
12529
12530#ifdef VBOX_WITH_USB
12531 // release all captured USB devices, but do this before requesting the locks below
12532 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12533 {
12534 /* Console::captureUSBDevices() is called in the VM process only after
12535 * setting the machine state to Starting or Restoring.
12536 * Console::detachAllUSBDevices() will be called upon successful
12537 * termination. So, we need to release USB devices only if there was
12538 * an abnormal termination of a running VM.
12539 *
12540 * This is identical to SessionMachine::DetachAllUSBDevices except
12541 * for the aAbnormal argument. */
12542 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12543 AssertComRC(rc);
12544 NOREF(rc);
12545
12546 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12547 if (service)
12548 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12549 }
12550#endif /* VBOX_WITH_USB */
12551
12552 // we need to lock this object in uninit() because the lock is shared
12553 // with mPeer (as well as data we modify below). mParent lock is needed
12554 // by several calls to it, and USB needs host lock.
12555 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12556
12557#ifdef VBOX_WITH_RESOURCE_USAGE_API
12558 /*
12559 * It is safe to call Machine::i_unregisterMetrics() here because
12560 * PerformanceCollector::samplerCallback no longer accesses guest methods
12561 * holding the lock.
12562 */
12563 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12564 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12565 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12566 this, __PRETTY_FUNCTION__, mCollectorGuest));
12567 if (mCollectorGuest)
12568 {
12569 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12570 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12571 mCollectorGuest = NULL;
12572 }
12573#endif
12574
12575 if (aReason == Uninit::Abnormal)
12576 {
12577 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12578 Global::IsOnlineOrTransient(lastState)));
12579
12580 /* reset the state to Aborted */
12581 if (mData->mMachineState != MachineState_Aborted)
12582 i_setMachineState(MachineState_Aborted);
12583 }
12584
12585 // any machine settings modified?
12586 if (mData->flModifications)
12587 {
12588 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12589 i_rollback(false /* aNotify */);
12590 }
12591
12592 mData->mSession.mPID = NIL_RTPROCESS;
12593
12594 if (aReason == Uninit::Unexpected)
12595 {
12596 /* Uninitialization didn't come from #checkForDeath(), so tell the
12597 * client watcher thread to update the set of machines that have open
12598 * sessions. */
12599 mParent->i_updateClientWatcher();
12600 }
12601
12602 /* uninitialize all remote controls */
12603 if (mData->mSession.mRemoteControls.size())
12604 {
12605 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12606 mData->mSession.mRemoteControls.size()));
12607
12608 Data::Session::RemoteControlList::iterator it =
12609 mData->mSession.mRemoteControls.begin();
12610 while (it != mData->mSession.mRemoteControls.end())
12611 {
12612 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12613 HRESULT rc = (*it)->Uninitialize();
12614 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12615 if (FAILED(rc))
12616 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12617 ++it;
12618 }
12619 mData->mSession.mRemoteControls.clear();
12620 }
12621
12622 /* Remove all references to the NAT network service. The service will stop
12623 * if all references (also from other VMs) are removed. */
12624 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12625 {
12626 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12627 {
12628 NetworkAttachmentType_T type;
12629 HRESULT hrc;
12630
12631 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12632 if ( SUCCEEDED(hrc)
12633 && type == NetworkAttachmentType_NATNetwork)
12634 {
12635 Bstr name;
12636 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12637 if (SUCCEEDED(hrc))
12638 {
12639 multilock.release();
12640 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12641 mUserData->s.strName.c_str(), name.raw()));
12642 mParent->i_natNetworkRefDec(name.raw());
12643 multilock.acquire();
12644 }
12645 }
12646 }
12647 }
12648
12649 /*
12650 * An expected uninitialization can come only from #checkForDeath().
12651 * Otherwise it means that something's gone really wrong (for example,
12652 * the Session implementation has released the VirtualBox reference
12653 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12654 * etc). However, it's also possible, that the client releases the IPC
12655 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12656 * but the VirtualBox release event comes first to the server process.
12657 * This case is practically possible, so we should not assert on an
12658 * unexpected uninit, just log a warning.
12659 */
12660
12661 if ((aReason == Uninit::Unexpected))
12662 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12663
12664 if (aReason != Uninit::Normal)
12665 {
12666 mData->mSession.mDirectControl.setNull();
12667 }
12668 else
12669 {
12670 /* this must be null here (see #OnSessionEnd()) */
12671 Assert(mData->mSession.mDirectControl.isNull());
12672 Assert(mData->mSession.mState == SessionState_Unlocking);
12673 Assert(!mData->mSession.mProgress.isNull());
12674 }
12675 if (mData->mSession.mProgress)
12676 {
12677 if (aReason == Uninit::Normal)
12678 mData->mSession.mProgress->i_notifyComplete(S_OK);
12679 else
12680 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12681 COM_IIDOF(ISession),
12682 getComponentName(),
12683 tr("The VM session was aborted"));
12684 mData->mSession.mProgress.setNull();
12685 }
12686
12687 /* remove the association between the peer machine and this session machine */
12688 Assert( (SessionMachine*)mData->mSession.mMachine == this
12689 || aReason == Uninit::Unexpected);
12690
12691 /* reset the rest of session data */
12692 mData->mSession.mLockType = LockType_Null;
12693 mData->mSession.mMachine.setNull();
12694 mData->mSession.mState = SessionState_Unlocked;
12695 mData->mSession.mName.setNull();
12696
12697 /* destroy the machine client token before leaving the exclusive lock */
12698 if (mClientToken)
12699 {
12700 delete mClientToken;
12701 mClientToken = NULL;
12702 }
12703
12704 /* fire an event */
12705 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12706
12707 uninitDataAndChildObjects();
12708
12709 /* free the essential data structure last */
12710 mData.free();
12711
12712 /* release the exclusive lock before setting the below two to NULL */
12713 multilock.release();
12714
12715 unconst(mParent) = NULL;
12716 unconst(mPeer) = NULL;
12717
12718 LogFlowThisFuncLeave();
12719}
12720
12721// util::Lockable interface
12722////////////////////////////////////////////////////////////////////////////////
12723
12724/**
12725 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12726 * with the primary Machine instance (mPeer).
12727 */
12728RWLockHandle *SessionMachine::lockHandle() const
12729{
12730 AssertReturn(mPeer != NULL, NULL);
12731 return mPeer->lockHandle();
12732}
12733
12734// IInternalMachineControl methods
12735////////////////////////////////////////////////////////////////////////////////
12736
12737/**
12738 * Passes collected guest statistics to performance collector object
12739 */
12740HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12741 ULONG aCpuKernel, ULONG aCpuIdle,
12742 ULONG aMemTotal, ULONG aMemFree,
12743 ULONG aMemBalloon, ULONG aMemShared,
12744 ULONG aMemCache, ULONG aPageTotal,
12745 ULONG aAllocVMM, ULONG aFreeVMM,
12746 ULONG aBalloonedVMM, ULONG aSharedVMM,
12747 ULONG aVmNetRx, ULONG aVmNetTx)
12748{
12749#ifdef VBOX_WITH_RESOURCE_USAGE_API
12750 if (mCollectorGuest)
12751 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12752 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12753 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12754 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12755
12756 return S_OK;
12757#else
12758 NOREF(aValidStats);
12759 NOREF(aCpuUser);
12760 NOREF(aCpuKernel);
12761 NOREF(aCpuIdle);
12762 NOREF(aMemTotal);
12763 NOREF(aMemFree);
12764 NOREF(aMemBalloon);
12765 NOREF(aMemShared);
12766 NOREF(aMemCache);
12767 NOREF(aPageTotal);
12768 NOREF(aAllocVMM);
12769 NOREF(aFreeVMM);
12770 NOREF(aBalloonedVMM);
12771 NOREF(aSharedVMM);
12772 NOREF(aVmNetRx);
12773 NOREF(aVmNetTx);
12774 return E_NOTIMPL;
12775#endif
12776}
12777
12778////////////////////////////////////////////////////////////////////////////////
12779//
12780// SessionMachine task records
12781//
12782////////////////////////////////////////////////////////////////////////////////
12783
12784/**
12785 * Task record for saving the machine state.
12786 */
12787struct SessionMachine::SaveStateTask
12788 : public Machine::Task
12789{
12790 SaveStateTask(SessionMachine *m,
12791 Progress *p,
12792 const Utf8Str &t,
12793 Reason_T enmReason,
12794 const Utf8Str &strStateFilePath)
12795 : Task(m, p, t),
12796 m_enmReason(enmReason),
12797 m_strStateFilePath(strStateFilePath)
12798 {}
12799
12800 void handler()
12801 {
12802 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12803 }
12804
12805 Reason_T m_enmReason;
12806 Utf8Str m_strStateFilePath;
12807};
12808
12809/**
12810 * Task thread implementation for SessionMachine::SaveState(), called from
12811 * SessionMachine::taskHandler().
12812 *
12813 * @note Locks this object for writing.
12814 *
12815 * @param task
12816 * @return
12817 */
12818void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12819{
12820 LogFlowThisFuncEnter();
12821
12822 AutoCaller autoCaller(this);
12823 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12824 if (FAILED(autoCaller.rc()))
12825 {
12826 /* we might have been uninitialized because the session was accidentally
12827 * closed by the client, so don't assert */
12828 HRESULT rc = setError(E_FAIL,
12829 tr("The session has been accidentally closed"));
12830 task.m_pProgress->i_notifyComplete(rc);
12831 LogFlowThisFuncLeave();
12832 return;
12833 }
12834
12835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12836
12837 HRESULT rc = S_OK;
12838
12839 try
12840 {
12841 ComPtr<IInternalSessionControl> directControl;
12842 if (mData->mSession.mLockType == LockType_VM)
12843 directControl = mData->mSession.mDirectControl;
12844 if (directControl.isNull())
12845 throw setError(VBOX_E_INVALID_VM_STATE,
12846 tr("Trying to save state without a running VM"));
12847 alock.release();
12848 BOOL fSuspendedBySave;
12849 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12850 Assert(!fSuspendedBySave);
12851 alock.acquire();
12852
12853 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12854 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12855 throw E_FAIL);
12856
12857 if (SUCCEEDED(rc))
12858 {
12859 mSSData->strStateFilePath = task.m_strStateFilePath;
12860
12861 /* save all VM settings */
12862 rc = i_saveSettings(NULL);
12863 // no need to check whether VirtualBox.xml needs saving also since
12864 // we can't have a name change pending at this point
12865 }
12866 else
12867 {
12868 // On failure, set the state to the state we had at the beginning.
12869 i_setMachineState(task.m_machineStateBackup);
12870 i_updateMachineStateOnClient();
12871
12872 // Delete the saved state file (might have been already created).
12873 // No need to check whether this is shared with a snapshot here
12874 // because we certainly created a fresh saved state file here.
12875 RTFileDelete(task.m_strStateFilePath.c_str());
12876 }
12877 }
12878 catch (HRESULT aRC) { rc = aRC; }
12879
12880 task.m_pProgress->i_notifyComplete(rc);
12881
12882 LogFlowThisFuncLeave();
12883}
12884
12885/**
12886 * @note Locks this object for writing.
12887 */
12888HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12889{
12890 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12891}
12892
12893HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12894{
12895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12896
12897 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12898 if (FAILED(rc)) return rc;
12899
12900 if ( mData->mMachineState != MachineState_Running
12901 && mData->mMachineState != MachineState_Paused
12902 )
12903 return setError(VBOX_E_INVALID_VM_STATE,
12904 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12905 Global::stringifyMachineState(mData->mMachineState));
12906
12907 ComObjPtr<Progress> pProgress;
12908 pProgress.createObject();
12909 rc = pProgress->init(i_getVirtualBox(),
12910 static_cast<IMachine *>(this) /* aInitiator */,
12911 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12912 FALSE /* aCancelable */);
12913 if (FAILED(rc))
12914 return rc;
12915
12916 Utf8Str strStateFilePath;
12917 i_composeSavedStateFilename(strStateFilePath);
12918
12919 /* create and start the task on a separate thread (note that it will not
12920 * start working until we release alock) */
12921 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12922 rc = pTask->createThread();
12923 if (FAILED(rc))
12924 return rc;
12925
12926 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12927 i_setMachineState(MachineState_Saving);
12928 i_updateMachineStateOnClient();
12929
12930 pProgress.queryInterfaceTo(aProgress.asOutParam());
12931
12932 return S_OK;
12933}
12934
12935/**
12936 * @note Locks this object for writing.
12937 */
12938HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12939{
12940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12941
12942 HRESULT rc = i_checkStateDependency(MutableStateDep);
12943 if (FAILED(rc)) return rc;
12944
12945 if ( mData->mMachineState != MachineState_PoweredOff
12946 && mData->mMachineState != MachineState_Teleported
12947 && mData->mMachineState != MachineState_Aborted
12948 )
12949 return setError(VBOX_E_INVALID_VM_STATE,
12950 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12951 Global::stringifyMachineState(mData->mMachineState));
12952
12953 com::Utf8Str stateFilePathFull;
12954 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12955 if (RT_FAILURE(vrc))
12956 return setError(VBOX_E_FILE_ERROR,
12957 tr("Invalid saved state file path '%s' (%Rrc)"),
12958 aSavedStateFile.c_str(),
12959 vrc);
12960
12961 mSSData->strStateFilePath = stateFilePathFull;
12962
12963 /* The below i_setMachineState() will detect the state transition and will
12964 * update the settings file */
12965
12966 return i_setMachineState(MachineState_Saved);
12967}
12968
12969/**
12970 * @note Locks this object for writing.
12971 */
12972HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12973{
12974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12975
12976 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12977 if (FAILED(rc)) return rc;
12978
12979 if (mData->mMachineState != MachineState_Saved)
12980 return setError(VBOX_E_INVALID_VM_STATE,
12981 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12982 Global::stringifyMachineState(mData->mMachineState));
12983
12984 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12985
12986 /*
12987 * Saved -> PoweredOff transition will be detected in the SessionMachine
12988 * and properly handled.
12989 */
12990 rc = i_setMachineState(MachineState_PoweredOff);
12991 return rc;
12992}
12993
12994
12995/**
12996 * @note Locks the same as #i_setMachineState() does.
12997 */
12998HRESULT SessionMachine::updateState(MachineState_T aState)
12999{
13000 return i_setMachineState(aState);
13001}
13002
13003/**
13004 * @note Locks this object for writing.
13005 */
13006HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13007{
13008 IProgress* pProgress(aProgress);
13009
13010 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13011
13012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13013
13014 if (mData->mSession.mState != SessionState_Locked)
13015 return VBOX_E_INVALID_OBJECT_STATE;
13016
13017 if (!mData->mSession.mProgress.isNull())
13018 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13019
13020 /* If we didn't reference the NAT network service yet, add a reference to
13021 * force a start */
13022 if (miNATNetworksStarted < 1)
13023 {
13024 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13025 {
13026 NetworkAttachmentType_T type;
13027 HRESULT hrc;
13028 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13029 if ( SUCCEEDED(hrc)
13030 && type == NetworkAttachmentType_NATNetwork)
13031 {
13032 Bstr name;
13033 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13034 if (SUCCEEDED(hrc))
13035 {
13036 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13037 mUserData->s.strName.c_str(), name.raw()));
13038 mPeer->lockHandle()->unlockWrite();
13039 mParent->i_natNetworkRefInc(name.raw());
13040#ifdef RT_LOCK_STRICT
13041 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13042#else
13043 mPeer->lockHandle()->lockWrite();
13044#endif
13045 }
13046 }
13047 }
13048 miNATNetworksStarted++;
13049 }
13050
13051 LogFlowThisFunc(("returns S_OK.\n"));
13052 return S_OK;
13053}
13054
13055/**
13056 * @note Locks this object for writing.
13057 */
13058HRESULT SessionMachine::endPowerUp(LONG aResult)
13059{
13060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13061
13062 if (mData->mSession.mState != SessionState_Locked)
13063 return VBOX_E_INVALID_OBJECT_STATE;
13064
13065 /* Finalize the LaunchVMProcess progress object. */
13066 if (mData->mSession.mProgress)
13067 {
13068 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13069 mData->mSession.mProgress.setNull();
13070 }
13071
13072 if (SUCCEEDED((HRESULT)aResult))
13073 {
13074#ifdef VBOX_WITH_RESOURCE_USAGE_API
13075 /* The VM has been powered up successfully, so it makes sense
13076 * now to offer the performance metrics for a running machine
13077 * object. Doing it earlier wouldn't be safe. */
13078 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13079 mData->mSession.mPID);
13080#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13081 }
13082
13083 return S_OK;
13084}
13085
13086/**
13087 * @note Locks this object for writing.
13088 */
13089HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13090{
13091 LogFlowThisFuncEnter();
13092
13093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13094
13095 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13096 E_FAIL);
13097
13098 /* create a progress object to track operation completion */
13099 ComObjPtr<Progress> pProgress;
13100 pProgress.createObject();
13101 pProgress->init(i_getVirtualBox(),
13102 static_cast<IMachine *>(this) /* aInitiator */,
13103 Bstr(tr("Stopping the virtual machine")).raw(),
13104 FALSE /* aCancelable */);
13105
13106 /* fill in the console task data */
13107 mConsoleTaskData.mLastState = mData->mMachineState;
13108 mConsoleTaskData.mProgress = pProgress;
13109
13110 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13111 i_setMachineState(MachineState_Stopping);
13112
13113 pProgress.queryInterfaceTo(aProgress.asOutParam());
13114
13115 return S_OK;
13116}
13117
13118/**
13119 * @note Locks this object for writing.
13120 */
13121HRESULT SessionMachine::endPoweringDown(LONG aResult,
13122 const com::Utf8Str &aErrMsg)
13123{
13124 LogFlowThisFuncEnter();
13125
13126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13127
13128 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13129 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13130 && mConsoleTaskData.mLastState != MachineState_Null,
13131 E_FAIL);
13132
13133 /*
13134 * On failure, set the state to the state we had when BeginPoweringDown()
13135 * was called (this is expected by Console::PowerDown() and the associated
13136 * task). On success the VM process already changed the state to
13137 * MachineState_PoweredOff, so no need to do anything.
13138 */
13139 if (FAILED(aResult))
13140 i_setMachineState(mConsoleTaskData.mLastState);
13141
13142 /* notify the progress object about operation completion */
13143 Assert(mConsoleTaskData.mProgress);
13144 if (SUCCEEDED(aResult))
13145 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13146 else
13147 {
13148 if (aErrMsg.length())
13149 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13150 COM_IIDOF(ISession),
13151 getComponentName(),
13152 aErrMsg.c_str());
13153 else
13154 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13155 }
13156
13157 /* clear out the temporary saved state data */
13158 mConsoleTaskData.mLastState = MachineState_Null;
13159 mConsoleTaskData.mProgress.setNull();
13160
13161 LogFlowThisFuncLeave();
13162 return S_OK;
13163}
13164
13165
13166/**
13167 * Goes through the USB filters of the given machine to see if the given
13168 * device matches any filter or not.
13169 *
13170 * @note Locks the same as USBController::hasMatchingFilter() does.
13171 */
13172HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13173 BOOL *aMatched,
13174 ULONG *aMaskedInterfaces)
13175{
13176 LogFlowThisFunc(("\n"));
13177
13178#ifdef VBOX_WITH_USB
13179 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13180#else
13181 NOREF(aDevice);
13182 NOREF(aMaskedInterfaces);
13183 *aMatched = FALSE;
13184#endif
13185
13186 return S_OK;
13187}
13188
13189/**
13190 * @note Locks the same as Host::captureUSBDevice() does.
13191 */
13192HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13193{
13194 LogFlowThisFunc(("\n"));
13195
13196#ifdef VBOX_WITH_USB
13197 /* if captureDeviceForVM() fails, it must have set extended error info */
13198 clearError();
13199 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13200 if (FAILED(rc)) return rc;
13201
13202 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13203 AssertReturn(service, E_FAIL);
13204 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13205#else
13206 NOREF(aId);
13207 return E_NOTIMPL;
13208#endif
13209}
13210
13211/**
13212 * @note Locks the same as Host::detachUSBDevice() does.
13213 */
13214HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13215 BOOL aDone)
13216{
13217 LogFlowThisFunc(("\n"));
13218
13219#ifdef VBOX_WITH_USB
13220 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13221 AssertReturn(service, E_FAIL);
13222 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13223#else
13224 NOREF(aId);
13225 NOREF(aDone);
13226 return E_NOTIMPL;
13227#endif
13228}
13229
13230/**
13231 * Inserts all machine filters to the USB proxy service and then calls
13232 * Host::autoCaptureUSBDevices().
13233 *
13234 * Called by Console from the VM process upon VM startup.
13235 *
13236 * @note Locks what called methods lock.
13237 */
13238HRESULT SessionMachine::autoCaptureUSBDevices()
13239{
13240 LogFlowThisFunc(("\n"));
13241
13242#ifdef VBOX_WITH_USB
13243 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13244 AssertComRC(rc);
13245 NOREF(rc);
13246
13247 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13248 AssertReturn(service, E_FAIL);
13249 return service->autoCaptureDevicesForVM(this);
13250#else
13251 return S_OK;
13252#endif
13253}
13254
13255/**
13256 * Removes all machine filters from the USB proxy service and then calls
13257 * Host::detachAllUSBDevices().
13258 *
13259 * Called by Console from the VM process upon normal VM termination or by
13260 * SessionMachine::uninit() upon abnormal VM termination (from under the
13261 * Machine/SessionMachine lock).
13262 *
13263 * @note Locks what called methods lock.
13264 */
13265HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13266{
13267 LogFlowThisFunc(("\n"));
13268
13269#ifdef VBOX_WITH_USB
13270 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13271 AssertComRC(rc);
13272 NOREF(rc);
13273
13274 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13275 AssertReturn(service, E_FAIL);
13276 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13277#else
13278 NOREF(aDone);
13279 return S_OK;
13280#endif
13281}
13282
13283/**
13284 * @note Locks this object for writing.
13285 */
13286HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13287 ComPtr<IProgress> &aProgress)
13288{
13289 LogFlowThisFuncEnter();
13290
13291 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13292 /*
13293 * We don't assert below because it might happen that a non-direct session
13294 * informs us it is closed right after we've been uninitialized -- it's ok.
13295 */
13296
13297 /* get IInternalSessionControl interface */
13298 ComPtr<IInternalSessionControl> control(aSession);
13299
13300 ComAssertRet(!control.isNull(), E_INVALIDARG);
13301
13302 /* Creating a Progress object requires the VirtualBox lock, and
13303 * thus locking it here is required by the lock order rules. */
13304 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13305
13306 if (control == mData->mSession.mDirectControl)
13307 {
13308 /* The direct session is being normally closed by the client process
13309 * ----------------------------------------------------------------- */
13310
13311 /* go to the closing state (essential for all open*Session() calls and
13312 * for #checkForDeath()) */
13313 Assert(mData->mSession.mState == SessionState_Locked);
13314 mData->mSession.mState = SessionState_Unlocking;
13315
13316 /* set direct control to NULL to release the remote instance */
13317 mData->mSession.mDirectControl.setNull();
13318 LogFlowThisFunc(("Direct control is set to NULL\n"));
13319
13320 if (mData->mSession.mProgress)
13321 {
13322 /* finalize the progress, someone might wait if a frontend
13323 * closes the session before powering on the VM. */
13324 mData->mSession.mProgress->notifyComplete(E_FAIL,
13325 COM_IIDOF(ISession),
13326 getComponentName(),
13327 tr("The VM session was closed before any attempt to power it on"));
13328 mData->mSession.mProgress.setNull();
13329 }
13330
13331 /* Create the progress object the client will use to wait until
13332 * #checkForDeath() is called to uninitialize this session object after
13333 * it releases the IPC semaphore.
13334 * Note! Because we're "reusing" mProgress here, this must be a proxy
13335 * object just like for LaunchVMProcess. */
13336 Assert(mData->mSession.mProgress.isNull());
13337 ComObjPtr<ProgressProxy> progress;
13338 progress.createObject();
13339 ComPtr<IUnknown> pPeer(mPeer);
13340 progress->init(mParent, pPeer,
13341 Bstr(tr("Closing session")).raw(),
13342 FALSE /* aCancelable */);
13343 progress.queryInterfaceTo(aProgress.asOutParam());
13344 mData->mSession.mProgress = progress;
13345 }
13346 else
13347 {
13348 /* the remote session is being normally closed */
13349 Data::Session::RemoteControlList::iterator it =
13350 mData->mSession.mRemoteControls.begin();
13351 while (it != mData->mSession.mRemoteControls.end())
13352 {
13353 if (control == *it)
13354 break;
13355 ++it;
13356 }
13357 BOOL found = it != mData->mSession.mRemoteControls.end();
13358 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13359 E_INVALIDARG);
13360 // This MUST be erase(it), not remove(*it) as the latter triggers a
13361 // very nasty use after free due to the place where the value "lives".
13362 mData->mSession.mRemoteControls.erase(it);
13363 }
13364
13365 /* signal the client watcher thread, because the client is going away */
13366 mParent->i_updateClientWatcher();
13367
13368 LogFlowThisFuncLeave();
13369 return S_OK;
13370}
13371
13372HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13373 std::vector<com::Utf8Str> &aValues,
13374 std::vector<LONG64> &aTimestamps,
13375 std::vector<com::Utf8Str> &aFlags)
13376{
13377 LogFlowThisFunc(("\n"));
13378
13379#ifdef VBOX_WITH_GUEST_PROPS
13380 using namespace guestProp;
13381
13382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13383
13384 size_t cEntries = mHWData->mGuestProperties.size();
13385 aNames.resize(cEntries);
13386 aValues.resize(cEntries);
13387 aTimestamps.resize(cEntries);
13388 aFlags.resize(cEntries);
13389
13390 size_t i = 0;
13391 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13392 it != mHWData->mGuestProperties.end();
13393 ++it, ++i)
13394 {
13395 char szFlags[MAX_FLAGS_LEN + 1];
13396 aNames[i] = it->first;
13397 aValues[i] = it->second.strValue;
13398 aTimestamps[i] = it->second.mTimestamp;
13399
13400 /* If it is NULL, keep it NULL. */
13401 if (it->second.mFlags)
13402 {
13403 writeFlags(it->second.mFlags, szFlags);
13404 aFlags[i] = szFlags;
13405 }
13406 else
13407 aFlags[i] = "";
13408 }
13409 return S_OK;
13410#else
13411 ReturnComNotImplemented();
13412#endif
13413}
13414
13415HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13416 const com::Utf8Str &aValue,
13417 LONG64 aTimestamp,
13418 const com::Utf8Str &aFlags,
13419 BOOL *aNotify)
13420{
13421 LogFlowThisFunc(("\n"));
13422
13423#ifdef VBOX_WITH_GUEST_PROPS
13424 using namespace guestProp;
13425
13426 *aNotify = FALSE;
13427
13428 try
13429 {
13430 /*
13431 * Convert input up front.
13432 */
13433 uint32_t fFlags = NILFLAG;
13434 if (aFlags.length())
13435 {
13436 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13437 AssertRCReturn(vrc, E_INVALIDARG);
13438 }
13439
13440 /*
13441 * Now grab the object lock, validate the state and do the update.
13442 */
13443
13444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13445
13446 switch (mData->mMachineState)
13447 {
13448 case MachineState_Paused:
13449 case MachineState_Running:
13450 case MachineState_Teleporting:
13451 case MachineState_TeleportingPausedVM:
13452 case MachineState_OnlineSnapshotting:
13453 case MachineState_LiveSnapshotting:
13454 case MachineState_DeletingSnapshotOnline:
13455 case MachineState_DeletingSnapshotPaused:
13456 case MachineState_Saving:
13457 case MachineState_Stopping:
13458 break;
13459
13460 default:
13461 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13462 VBOX_E_INVALID_VM_STATE);
13463 }
13464
13465 i_setModified(IsModified_MachineData);
13466 mHWData.backup();
13467
13468 bool fDelete = !aValue.length();
13469 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13470 if (it != mHWData->mGuestProperties.end())
13471 {
13472 if (!fDelete)
13473 {
13474 it->second.strValue = aValue;
13475 it->second.mTimestamp = aTimestamp;
13476 it->second.mFlags = fFlags;
13477 }
13478 else
13479 mHWData->mGuestProperties.erase(it);
13480
13481 mData->mGuestPropertiesModified = TRUE;
13482 }
13483 else if (!fDelete)
13484 {
13485 HWData::GuestProperty prop;
13486 prop.strValue = aValue;
13487 prop.mTimestamp = aTimestamp;
13488 prop.mFlags = fFlags;
13489
13490 mHWData->mGuestProperties[aName] = prop;
13491 mData->mGuestPropertiesModified = TRUE;
13492 }
13493
13494 /*
13495 * Send a callback notification if appropriate
13496 */
13497 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13498 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13499 RTSTR_MAX,
13500 aName.c_str(),
13501 RTSTR_MAX, NULL)
13502 )
13503 {
13504 alock.release();
13505
13506 mParent->i_onGuestPropertyChange(mData->mUuid,
13507 Bstr(aName).raw(),
13508 Bstr(aValue).raw(),
13509 Bstr(aFlags).raw());
13510 *aNotify = TRUE;
13511 }
13512 }
13513 catch (...)
13514 {
13515 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13516 }
13517 return S_OK;
13518#else
13519 ReturnComNotImplemented();
13520#endif
13521}
13522
13523
13524HRESULT SessionMachine::lockMedia()
13525{
13526 AutoMultiWriteLock2 alock(this->lockHandle(),
13527 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13528
13529 AssertReturn( mData->mMachineState == MachineState_Starting
13530 || mData->mMachineState == MachineState_Restoring
13531 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13532
13533 clearError();
13534 alock.release();
13535 return i_lockMedia();
13536}
13537
13538HRESULT SessionMachine::unlockMedia()
13539{
13540 HRESULT hrc = i_unlockMedia();
13541 return hrc;
13542}
13543
13544HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13545 ComPtr<IMediumAttachment> &aNewAttachment)
13546{
13547 // request the host lock first, since might be calling Host methods for getting host drives;
13548 // next, protect the media tree all the while we're in here, as well as our member variables
13549 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13550 this->lockHandle(),
13551 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13552
13553 IMediumAttachment *iAttach = aAttachment;
13554 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13555
13556 Bstr ctrlName;
13557 LONG lPort;
13558 LONG lDevice;
13559 bool fTempEject;
13560 {
13561 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13562
13563 /* Need to query the details first, as the IMediumAttachment reference
13564 * might be to the original settings, which we are going to change. */
13565 ctrlName = pAttach->i_getControllerName();
13566 lPort = pAttach->i_getPort();
13567 lDevice = pAttach->i_getDevice();
13568 fTempEject = pAttach->i_getTempEject();
13569 }
13570
13571 if (!fTempEject)
13572 {
13573 /* Remember previously mounted medium. The medium before taking the
13574 * backup is not necessarily the same thing. */
13575 ComObjPtr<Medium> oldmedium;
13576 oldmedium = pAttach->i_getMedium();
13577
13578 i_setModified(IsModified_Storage);
13579 mMediaData.backup();
13580
13581 // The backup operation makes the pAttach reference point to the
13582 // old settings. Re-get the correct reference.
13583 pAttach = i_findAttachment(mMediaData->mAttachments,
13584 ctrlName.raw(),
13585 lPort,
13586 lDevice);
13587
13588 {
13589 AutoCaller autoAttachCaller(this);
13590 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13591
13592 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13593 if (!oldmedium.isNull())
13594 oldmedium->i_removeBackReference(mData->mUuid);
13595
13596 pAttach->i_updateMedium(NULL);
13597 pAttach->i_updateEjected();
13598 }
13599
13600 i_setModified(IsModified_Storage);
13601 }
13602 else
13603 {
13604 {
13605 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13606 pAttach->i_updateEjected();
13607 }
13608 }
13609
13610 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13611
13612 return S_OK;
13613}
13614
13615// public methods only for internal purposes
13616/////////////////////////////////////////////////////////////////////////////
13617
13618#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13619/**
13620 * Called from the client watcher thread to check for expected or unexpected
13621 * death of the client process that has a direct session to this machine.
13622 *
13623 * On Win32 and on OS/2, this method is called only when we've got the
13624 * mutex (i.e. the client has either died or terminated normally) so it always
13625 * returns @c true (the client is terminated, the session machine is
13626 * uninitialized).
13627 *
13628 * On other platforms, the method returns @c true if the client process has
13629 * terminated normally or abnormally and the session machine was uninitialized,
13630 * and @c false if the client process is still alive.
13631 *
13632 * @note Locks this object for writing.
13633 */
13634bool SessionMachine::i_checkForDeath()
13635{
13636 Uninit::Reason reason;
13637 bool terminated = false;
13638
13639 /* Enclose autoCaller with a block because calling uninit() from under it
13640 * will deadlock. */
13641 {
13642 AutoCaller autoCaller(this);
13643 if (!autoCaller.isOk())
13644 {
13645 /* return true if not ready, to cause the client watcher to exclude
13646 * the corresponding session from watching */
13647 LogFlowThisFunc(("Already uninitialized!\n"));
13648 return true;
13649 }
13650
13651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13652
13653 /* Determine the reason of death: if the session state is Closing here,
13654 * everything is fine. Otherwise it means that the client did not call
13655 * OnSessionEnd() before it released the IPC semaphore. This may happen
13656 * either because the client process has abnormally terminated, or
13657 * because it simply forgot to call ISession::Close() before exiting. We
13658 * threat the latter also as an abnormal termination (see
13659 * Session::uninit() for details). */
13660 reason = mData->mSession.mState == SessionState_Unlocking ?
13661 Uninit::Normal :
13662 Uninit::Abnormal;
13663
13664 if (mClientToken)
13665 terminated = mClientToken->release();
13666 } /* AutoCaller block */
13667
13668 if (terminated)
13669 uninit(reason);
13670
13671 return terminated;
13672}
13673
13674void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13675{
13676 LogFlowThisFunc(("\n"));
13677
13678 strTokenId.setNull();
13679
13680 AutoCaller autoCaller(this);
13681 AssertComRCReturnVoid(autoCaller.rc());
13682
13683 Assert(mClientToken);
13684 if (mClientToken)
13685 mClientToken->getId(strTokenId);
13686}
13687#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13688IToken *SessionMachine::i_getToken()
13689{
13690 LogFlowThisFunc(("\n"));
13691
13692 AutoCaller autoCaller(this);
13693 AssertComRCReturn(autoCaller.rc(), NULL);
13694
13695 Assert(mClientToken);
13696 if (mClientToken)
13697 return mClientToken->getToken();
13698 else
13699 return NULL;
13700}
13701#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13702
13703Machine::ClientToken *SessionMachine::i_getClientToken()
13704{
13705 LogFlowThisFunc(("\n"));
13706
13707 AutoCaller autoCaller(this);
13708 AssertComRCReturn(autoCaller.rc(), NULL);
13709
13710 return mClientToken;
13711}
13712
13713
13714/**
13715 * @note Locks this object for reading.
13716 */
13717HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13718{
13719 LogFlowThisFunc(("\n"));
13720
13721 AutoCaller autoCaller(this);
13722 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13723
13724 ComPtr<IInternalSessionControl> directControl;
13725 {
13726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13727 if (mData->mSession.mLockType == LockType_VM)
13728 directControl = mData->mSession.mDirectControl;
13729 }
13730
13731 /* ignore notifications sent after #OnSessionEnd() is called */
13732 if (!directControl)
13733 return S_OK;
13734
13735 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13736}
13737
13738/**
13739 * @note Locks this object for reading.
13740 */
13741HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13742 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13743 IN_BSTR aGuestIp, LONG aGuestPort)
13744{
13745 LogFlowThisFunc(("\n"));
13746
13747 AutoCaller autoCaller(this);
13748 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13749
13750 ComPtr<IInternalSessionControl> directControl;
13751 {
13752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13753 if (mData->mSession.mLockType == LockType_VM)
13754 directControl = mData->mSession.mDirectControl;
13755 }
13756
13757 /* ignore notifications sent after #OnSessionEnd() is called */
13758 if (!directControl)
13759 return S_OK;
13760 /*
13761 * instead acting like callback we ask IVirtualBox deliver corresponding event
13762 */
13763
13764 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13765 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13766 return S_OK;
13767}
13768
13769/**
13770 * @note Locks this object for reading.
13771 */
13772HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13773{
13774 LogFlowThisFunc(("\n"));
13775
13776 AutoCaller autoCaller(this);
13777 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13778
13779 ComPtr<IInternalSessionControl> directControl;
13780 {
13781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13782 if (mData->mSession.mLockType == LockType_VM)
13783 directControl = mData->mSession.mDirectControl;
13784 }
13785
13786 /* ignore notifications sent after #OnSessionEnd() is called */
13787 if (!directControl)
13788 return S_OK;
13789
13790 return directControl->OnSerialPortChange(serialPort);
13791}
13792
13793/**
13794 * @note Locks this object for reading.
13795 */
13796HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800 AutoCaller autoCaller(this);
13801 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13802
13803 ComPtr<IInternalSessionControl> directControl;
13804 {
13805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13806 if (mData->mSession.mLockType == LockType_VM)
13807 directControl = mData->mSession.mDirectControl;
13808 }
13809
13810 /* ignore notifications sent after #OnSessionEnd() is called */
13811 if (!directControl)
13812 return S_OK;
13813
13814 return directControl->OnParallelPortChange(parallelPort);
13815}
13816
13817/**
13818 * @note Locks this object for reading.
13819 */
13820HRESULT SessionMachine::i_onStorageControllerChange()
13821{
13822 LogFlowThisFunc(("\n"));
13823
13824 AutoCaller autoCaller(this);
13825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13826
13827 ComPtr<IInternalSessionControl> directControl;
13828 {
13829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13830 if (mData->mSession.mLockType == LockType_VM)
13831 directControl = mData->mSession.mDirectControl;
13832 }
13833
13834 /* ignore notifications sent after #OnSessionEnd() is called */
13835 if (!directControl)
13836 return S_OK;
13837
13838 return directControl->OnStorageControllerChange();
13839}
13840
13841/**
13842 * @note Locks this object for reading.
13843 */
13844HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13845{
13846 LogFlowThisFunc(("\n"));
13847
13848 AutoCaller autoCaller(this);
13849 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13850
13851 ComPtr<IInternalSessionControl> directControl;
13852 {
13853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13854 if (mData->mSession.mLockType == LockType_VM)
13855 directControl = mData->mSession.mDirectControl;
13856 }
13857
13858 /* ignore notifications sent after #OnSessionEnd() is called */
13859 if (!directControl)
13860 return S_OK;
13861
13862 return directControl->OnMediumChange(aAttachment, aForce);
13863}
13864
13865/**
13866 * @note Locks this object for reading.
13867 */
13868HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13869{
13870 LogFlowThisFunc(("\n"));
13871
13872 AutoCaller autoCaller(this);
13873 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13874
13875 ComPtr<IInternalSessionControl> directControl;
13876 {
13877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13878 if (mData->mSession.mLockType == LockType_VM)
13879 directControl = mData->mSession.mDirectControl;
13880 }
13881
13882 /* ignore notifications sent after #OnSessionEnd() is called */
13883 if (!directControl)
13884 return S_OK;
13885
13886 return directControl->OnCPUChange(aCPU, aRemove);
13887}
13888
13889HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13890{
13891 LogFlowThisFunc(("\n"));
13892
13893 AutoCaller autoCaller(this);
13894 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13895
13896 ComPtr<IInternalSessionControl> directControl;
13897 {
13898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13899 if (mData->mSession.mLockType == LockType_VM)
13900 directControl = mData->mSession.mDirectControl;
13901 }
13902
13903 /* ignore notifications sent after #OnSessionEnd() is called */
13904 if (!directControl)
13905 return S_OK;
13906
13907 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13908}
13909
13910/**
13911 * @note Locks this object for reading.
13912 */
13913HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13914{
13915 LogFlowThisFunc(("\n"));
13916
13917 AutoCaller autoCaller(this);
13918 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13919
13920 ComPtr<IInternalSessionControl> directControl;
13921 {
13922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13923 if (mData->mSession.mLockType == LockType_VM)
13924 directControl = mData->mSession.mDirectControl;
13925 }
13926
13927 /* ignore notifications sent after #OnSessionEnd() is called */
13928 if (!directControl)
13929 return S_OK;
13930
13931 return directControl->OnVRDEServerChange(aRestart);
13932}
13933
13934/**
13935 * @note Locks this object for reading.
13936 */
13937HRESULT SessionMachine::i_onVideoCaptureChange()
13938{
13939 LogFlowThisFunc(("\n"));
13940
13941 AutoCaller autoCaller(this);
13942 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13943
13944 ComPtr<IInternalSessionControl> directControl;
13945 {
13946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13947 if (mData->mSession.mLockType == LockType_VM)
13948 directControl = mData->mSession.mDirectControl;
13949 }
13950
13951 /* ignore notifications sent after #OnSessionEnd() is called */
13952 if (!directControl)
13953 return S_OK;
13954
13955 return directControl->OnVideoCaptureChange();
13956}
13957
13958/**
13959 * @note Locks this object for reading.
13960 */
13961HRESULT SessionMachine::i_onUSBControllerChange()
13962{
13963 LogFlowThisFunc(("\n"));
13964
13965 AutoCaller autoCaller(this);
13966 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13967
13968 ComPtr<IInternalSessionControl> directControl;
13969 {
13970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13971 if (mData->mSession.mLockType == LockType_VM)
13972 directControl = mData->mSession.mDirectControl;
13973 }
13974
13975 /* ignore notifications sent after #OnSessionEnd() is called */
13976 if (!directControl)
13977 return S_OK;
13978
13979 return directControl->OnUSBControllerChange();
13980}
13981
13982/**
13983 * @note Locks this object for reading.
13984 */
13985HRESULT SessionMachine::i_onSharedFolderChange()
13986{
13987 LogFlowThisFunc(("\n"));
13988
13989 AutoCaller autoCaller(this);
13990 AssertComRCReturnRC(autoCaller.rc());
13991
13992 ComPtr<IInternalSessionControl> directControl;
13993 {
13994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13995 if (mData->mSession.mLockType == LockType_VM)
13996 directControl = mData->mSession.mDirectControl;
13997 }
13998
13999 /* ignore notifications sent after #OnSessionEnd() is called */
14000 if (!directControl)
14001 return S_OK;
14002
14003 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14004}
14005
14006/**
14007 * @note Locks this object for reading.
14008 */
14009HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14010{
14011 LogFlowThisFunc(("\n"));
14012
14013 AutoCaller autoCaller(this);
14014 AssertComRCReturnRC(autoCaller.rc());
14015
14016 ComPtr<IInternalSessionControl> directControl;
14017 {
14018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14019 if (mData->mSession.mLockType == LockType_VM)
14020 directControl = mData->mSession.mDirectControl;
14021 }
14022
14023 /* ignore notifications sent after #OnSessionEnd() is called */
14024 if (!directControl)
14025 return S_OK;
14026
14027 return directControl->OnClipboardModeChange(aClipboardMode);
14028}
14029
14030/**
14031 * @note Locks this object for reading.
14032 */
14033HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14034{
14035 LogFlowThisFunc(("\n"));
14036
14037 AutoCaller autoCaller(this);
14038 AssertComRCReturnRC(autoCaller.rc());
14039
14040 ComPtr<IInternalSessionControl> directControl;
14041 {
14042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14043 if (mData->mSession.mLockType == LockType_VM)
14044 directControl = mData->mSession.mDirectControl;
14045 }
14046
14047 /* ignore notifications sent after #OnSessionEnd() is called */
14048 if (!directControl)
14049 return S_OK;
14050
14051 return directControl->OnDnDModeChange(aDnDMode);
14052}
14053
14054/**
14055 * @note Locks this object for reading.
14056 */
14057HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14058{
14059 LogFlowThisFunc(("\n"));
14060
14061 AutoCaller autoCaller(this);
14062 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14063
14064 ComPtr<IInternalSessionControl> directControl;
14065 {
14066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14067 if (mData->mSession.mLockType == LockType_VM)
14068 directControl = mData->mSession.mDirectControl;
14069 }
14070
14071 /* ignore notifications sent after #OnSessionEnd() is called */
14072 if (!directControl)
14073 return S_OK;
14074
14075 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14076}
14077
14078/**
14079 * @note Locks this object for reading.
14080 */
14081HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14082{
14083 LogFlowThisFunc(("\n"));
14084
14085 AutoCaller autoCaller(this);
14086 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14087
14088 ComPtr<IInternalSessionControl> directControl;
14089 {
14090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14091 if (mData->mSession.mLockType == LockType_VM)
14092 directControl = mData->mSession.mDirectControl;
14093 }
14094
14095 /* ignore notifications sent after #OnSessionEnd() is called */
14096 if (!directControl)
14097 return S_OK;
14098
14099 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14100}
14101
14102/**
14103 * Returns @c true if this machine's USB controller reports it has a matching
14104 * filter for the given USB device and @c false otherwise.
14105 *
14106 * @note locks this object for reading.
14107 */
14108bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14109{
14110 AutoCaller autoCaller(this);
14111 /* silently return if not ready -- this method may be called after the
14112 * direct machine session has been called */
14113 if (!autoCaller.isOk())
14114 return false;
14115
14116#ifdef VBOX_WITH_USB
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118
14119 switch (mData->mMachineState)
14120 {
14121 case MachineState_Starting:
14122 case MachineState_Restoring:
14123 case MachineState_TeleportingIn:
14124 case MachineState_Paused:
14125 case MachineState_Running:
14126 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14127 * elsewhere... */
14128 alock.release();
14129 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14130 default: break;
14131 }
14132#else
14133 NOREF(aDevice);
14134 NOREF(aMaskedIfs);
14135#endif
14136 return false;
14137}
14138
14139/**
14140 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14141 */
14142HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14143 IVirtualBoxErrorInfo *aError,
14144 ULONG aMaskedIfs,
14145 const com::Utf8Str &aCaptureFilename)
14146{
14147 LogFlowThisFunc(("\n"));
14148
14149 AutoCaller autoCaller(this);
14150
14151 /* This notification may happen after the machine object has been
14152 * uninitialized (the session was closed), so don't assert. */
14153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14154
14155 ComPtr<IInternalSessionControl> directControl;
14156 {
14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14158 if (mData->mSession.mLockType == LockType_VM)
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* fail on notifications sent after #OnSessionEnd() is called, it is
14163 * expected by the caller */
14164 if (!directControl)
14165 return E_FAIL;
14166
14167 /* No locks should be held at this point. */
14168 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14169 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14170
14171 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14172}
14173
14174/**
14175 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14176 */
14177HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14178 IVirtualBoxErrorInfo *aError)
14179{
14180 LogFlowThisFunc(("\n"));
14181
14182 AutoCaller autoCaller(this);
14183
14184 /* This notification may happen after the machine object has been
14185 * uninitialized (the session was closed), so don't assert. */
14186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14187
14188 ComPtr<IInternalSessionControl> directControl;
14189 {
14190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14191 if (mData->mSession.mLockType == LockType_VM)
14192 directControl = mData->mSession.mDirectControl;
14193 }
14194
14195 /* fail on notifications sent after #OnSessionEnd() is called, it is
14196 * expected by the caller */
14197 if (!directControl)
14198 return E_FAIL;
14199
14200 /* No locks should be held at this point. */
14201 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14202 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14203
14204 return directControl->OnUSBDeviceDetach(aId, aError);
14205}
14206
14207// protected methods
14208/////////////////////////////////////////////////////////////////////////////
14209
14210/**
14211 * Deletes the given file if it is no longer in use by either the current machine state
14212 * (if the machine is "saved") or any of the machine's snapshots.
14213 *
14214 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14215 * but is different for each SnapshotMachine. When calling this, the order of calling this
14216 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14217 * is therefore critical. I know, it's all rather messy.
14218 *
14219 * @param strStateFile
14220 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14221 * the test for whether the saved state file is in use.
14222 */
14223void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14224 Snapshot *pSnapshotToIgnore)
14225{
14226 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14227 if ( (strStateFile.isNotEmpty())
14228 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14229 )
14230 // ... and it must also not be shared with other snapshots
14231 if ( !mData->mFirstSnapshot
14232 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14233 // this checks the SnapshotMachine's state file paths
14234 )
14235 RTFileDelete(strStateFile.c_str());
14236}
14237
14238/**
14239 * Locks the attached media.
14240 *
14241 * All attached hard disks are locked for writing and DVD/floppy are locked for
14242 * reading. Parents of attached hard disks (if any) are locked for reading.
14243 *
14244 * This method also performs accessibility check of all media it locks: if some
14245 * media is inaccessible, the method will return a failure and a bunch of
14246 * extended error info objects per each inaccessible medium.
14247 *
14248 * Note that this method is atomic: if it returns a success, all media are
14249 * locked as described above; on failure no media is locked at all (all
14250 * succeeded individual locks will be undone).
14251 *
14252 * The caller is responsible for doing the necessary state sanity checks.
14253 *
14254 * The locks made by this method must be undone by calling #unlockMedia() when
14255 * no more needed.
14256 */
14257HRESULT SessionMachine::i_lockMedia()
14258{
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14261
14262 AutoMultiWriteLock2 alock(this->lockHandle(),
14263 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14264
14265 /* bail out if trying to lock things with already set up locking */
14266 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14267
14268 MultiResult mrc(S_OK);
14269
14270 /* Collect locking information for all medium objects attached to the VM. */
14271 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14272 it != mMediaData->mAttachments.end();
14273 ++it)
14274 {
14275 MediumAttachment* pAtt = *it;
14276 DeviceType_T devType = pAtt->i_getType();
14277 Medium *pMedium = pAtt->i_getMedium();
14278
14279 MediumLockList *pMediumLockList(new MediumLockList());
14280 // There can be attachments without a medium (floppy/dvd), and thus
14281 // it's impossible to create a medium lock list. It still makes sense
14282 // to have the empty medium lock list in the map in case a medium is
14283 // attached later.
14284 if (pMedium != NULL)
14285 {
14286 MediumType_T mediumType = pMedium->i_getType();
14287 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14288 || mediumType == MediumType_Shareable;
14289 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14290
14291 alock.release();
14292 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14293 !fIsReadOnlyLock /* fMediumLockWrite */,
14294 false /* fMediumLockWriteAll */,
14295 NULL,
14296 *pMediumLockList);
14297 alock.acquire();
14298 if (FAILED(mrc))
14299 {
14300 delete pMediumLockList;
14301 mData->mSession.mLockedMedia.Clear();
14302 break;
14303 }
14304 }
14305
14306 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14307 if (FAILED(rc))
14308 {
14309 mData->mSession.mLockedMedia.Clear();
14310 mrc = setError(rc,
14311 tr("Collecting locking information for all attached media failed"));
14312 break;
14313 }
14314 }
14315
14316 if (SUCCEEDED(mrc))
14317 {
14318 /* Now lock all media. If this fails, nothing is locked. */
14319 alock.release();
14320 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14321 alock.acquire();
14322 if (FAILED(rc))
14323 {
14324 mrc = setError(rc,
14325 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14326 }
14327 }
14328
14329 return mrc;
14330}
14331
14332/**
14333 * Undoes the locks made by by #lockMedia().
14334 */
14335HRESULT SessionMachine::i_unlockMedia()
14336{
14337 AutoCaller autoCaller(this);
14338 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14339
14340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14341
14342 /* we may be holding important error info on the current thread;
14343 * preserve it */
14344 ErrorInfoKeeper eik;
14345
14346 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14347 AssertComRC(rc);
14348 return rc;
14349}
14350
14351/**
14352 * Helper to change the machine state (reimplementation).
14353 *
14354 * @note Locks this object for writing.
14355 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14356 * it can cause crashes in random places due to unexpectedly committing
14357 * the current settings. The caller is responsible for that. The call
14358 * to saveStateSettings is fine, because this method does not commit.
14359 */
14360HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14361{
14362 LogFlowThisFuncEnter();
14363 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14364
14365 AutoCaller autoCaller(this);
14366 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14367
14368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14369
14370 MachineState_T oldMachineState = mData->mMachineState;
14371
14372 AssertMsgReturn(oldMachineState != aMachineState,
14373 ("oldMachineState=%s, aMachineState=%s\n",
14374 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14375 E_FAIL);
14376
14377 HRESULT rc = S_OK;
14378
14379 int stsFlags = 0;
14380 bool deleteSavedState = false;
14381
14382 /* detect some state transitions */
14383
14384 if ( ( oldMachineState == MachineState_Saved
14385 && aMachineState == MachineState_Restoring)
14386 || ( ( oldMachineState == MachineState_PoweredOff
14387 || oldMachineState == MachineState_Teleported
14388 || oldMachineState == MachineState_Aborted
14389 )
14390 && ( aMachineState == MachineState_TeleportingIn
14391 || aMachineState == MachineState_Starting
14392 )
14393 )
14394 )
14395 {
14396 /* The EMT thread is about to start */
14397
14398 /* Nothing to do here for now... */
14399
14400 /// @todo NEWMEDIA don't let mDVDDrive and other children
14401 /// change anything when in the Starting/Restoring state
14402 }
14403 else if ( ( oldMachineState == MachineState_Running
14404 || oldMachineState == MachineState_Paused
14405 || oldMachineState == MachineState_Teleporting
14406 || oldMachineState == MachineState_OnlineSnapshotting
14407 || oldMachineState == MachineState_LiveSnapshotting
14408 || oldMachineState == MachineState_Stuck
14409 || oldMachineState == MachineState_Starting
14410 || oldMachineState == MachineState_Stopping
14411 || oldMachineState == MachineState_Saving
14412 || oldMachineState == MachineState_Restoring
14413 || oldMachineState == MachineState_TeleportingPausedVM
14414 || oldMachineState == MachineState_TeleportingIn
14415 )
14416 && ( aMachineState == MachineState_PoweredOff
14417 || aMachineState == MachineState_Saved
14418 || aMachineState == MachineState_Teleported
14419 || aMachineState == MachineState_Aborted
14420 )
14421 )
14422 {
14423 /* The EMT thread has just stopped, unlock attached media. Note that as
14424 * opposed to locking that is done from Console, we do unlocking here
14425 * because the VM process may have aborted before having a chance to
14426 * properly unlock all media it locked. */
14427
14428 unlockMedia();
14429 }
14430
14431 if (oldMachineState == MachineState_Restoring)
14432 {
14433 if (aMachineState != MachineState_Saved)
14434 {
14435 /*
14436 * delete the saved state file once the machine has finished
14437 * restoring from it (note that Console sets the state from
14438 * Restoring to Saved if the VM couldn't restore successfully,
14439 * to give the user an ability to fix an error and retry --
14440 * we keep the saved state file in this case)
14441 */
14442 deleteSavedState = true;
14443 }
14444 }
14445 else if ( oldMachineState == MachineState_Saved
14446 && ( aMachineState == MachineState_PoweredOff
14447 || aMachineState == MachineState_Aborted
14448 || aMachineState == MachineState_Teleported
14449 )
14450 )
14451 {
14452 /*
14453 * delete the saved state after SessionMachine::ForgetSavedState() is called
14454 * or if the VM process (owning a direct VM session) crashed while the
14455 * VM was Saved
14456 */
14457
14458 /// @todo (dmik)
14459 // Not sure that deleting the saved state file just because of the
14460 // client death before it attempted to restore the VM is a good
14461 // thing. But when it crashes we need to go to the Aborted state
14462 // which cannot have the saved state file associated... The only
14463 // way to fix this is to make the Aborted condition not a VM state
14464 // but a bool flag: i.e., when a crash occurs, set it to true and
14465 // change the state to PoweredOff or Saved depending on the
14466 // saved state presence.
14467
14468 deleteSavedState = true;
14469 mData->mCurrentStateModified = TRUE;
14470 stsFlags |= SaveSTS_CurStateModified;
14471 }
14472
14473 if ( aMachineState == MachineState_Starting
14474 || aMachineState == MachineState_Restoring
14475 || aMachineState == MachineState_TeleportingIn
14476 )
14477 {
14478 /* set the current state modified flag to indicate that the current
14479 * state is no more identical to the state in the
14480 * current snapshot */
14481 if (!mData->mCurrentSnapshot.isNull())
14482 {
14483 mData->mCurrentStateModified = TRUE;
14484 stsFlags |= SaveSTS_CurStateModified;
14485 }
14486 }
14487
14488 if (deleteSavedState)
14489 {
14490 if (mRemoveSavedState)
14491 {
14492 Assert(!mSSData->strStateFilePath.isEmpty());
14493
14494 // it is safe to delete the saved state file if ...
14495 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14496 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14497 // ... none of the snapshots share the saved state file
14498 )
14499 RTFileDelete(mSSData->strStateFilePath.c_str());
14500 }
14501
14502 mSSData->strStateFilePath.setNull();
14503 stsFlags |= SaveSTS_StateFilePath;
14504 }
14505
14506 /* redirect to the underlying peer machine */
14507 mPeer->i_setMachineState(aMachineState);
14508
14509 if ( oldMachineState != MachineState_RestoringSnapshot
14510 && ( aMachineState == MachineState_PoweredOff
14511 || aMachineState == MachineState_Teleported
14512 || aMachineState == MachineState_Aborted
14513 || aMachineState == MachineState_Saved))
14514 {
14515 /* the machine has stopped execution
14516 * (or the saved state file was adopted) */
14517 stsFlags |= SaveSTS_StateTimeStamp;
14518 }
14519
14520 if ( ( oldMachineState == MachineState_PoweredOff
14521 || oldMachineState == MachineState_Aborted
14522 || oldMachineState == MachineState_Teleported
14523 )
14524 && aMachineState == MachineState_Saved)
14525 {
14526 /* the saved state file was adopted */
14527 Assert(!mSSData->strStateFilePath.isEmpty());
14528 stsFlags |= SaveSTS_StateFilePath;
14529 }
14530
14531#ifdef VBOX_WITH_GUEST_PROPS
14532 if ( aMachineState == MachineState_PoweredOff
14533 || aMachineState == MachineState_Aborted
14534 || aMachineState == MachineState_Teleported)
14535 {
14536 /* Make sure any transient guest properties get removed from the
14537 * property store on shutdown. */
14538 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14539
14540 /* remove it from the settings representation */
14541 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14542 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14543 it != llGuestProperties.end();
14544 /*nothing*/)
14545 {
14546 const settings::GuestProperty &prop = *it;
14547 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14548 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14549 {
14550 it = llGuestProperties.erase(it);
14551 fNeedsSaving = true;
14552 }
14553 else
14554 {
14555 ++it;
14556 }
14557 }
14558
14559 /* Additionally remove it from the HWData representation. Required to
14560 * keep everything in sync, as this is what the API keeps using. */
14561 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14562 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14563 it != llHWGuestProperties.end();
14564 /*nothing*/)
14565 {
14566 uint32_t fFlags = it->second.mFlags;
14567 if ( fFlags & guestProp::TRANSIENT
14568 || fFlags & guestProp::TRANSRESET)
14569 {
14570 /* iterator where we need to continue after the erase call
14571 * (C++03 is a fact still, and it doesn't return the iterator
14572 * which would allow continuing) */
14573 HWData::GuestPropertyMap::iterator it2 = it;
14574 ++it2;
14575 llHWGuestProperties.erase(it);
14576 it = it2;
14577 fNeedsSaving = true;
14578 }
14579 else
14580 {
14581 ++it;
14582 }
14583 }
14584
14585 if (fNeedsSaving)
14586 {
14587 mData->mCurrentStateModified = TRUE;
14588 stsFlags |= SaveSTS_CurStateModified;
14589 }
14590 }
14591#endif /* VBOX_WITH_GUEST_PROPS */
14592
14593 rc = i_saveStateSettings(stsFlags);
14594
14595 if ( ( oldMachineState != MachineState_PoweredOff
14596 && oldMachineState != MachineState_Aborted
14597 && oldMachineState != MachineState_Teleported
14598 )
14599 && ( aMachineState == MachineState_PoweredOff
14600 || aMachineState == MachineState_Aborted
14601 || aMachineState == MachineState_Teleported
14602 )
14603 )
14604 {
14605 /* we've been shut down for any reason */
14606 /* no special action so far */
14607 }
14608
14609 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14610 LogFlowThisFuncLeave();
14611 return rc;
14612}
14613
14614/**
14615 * Sends the current machine state value to the VM process.
14616 *
14617 * @note Locks this object for reading, then calls a client process.
14618 */
14619HRESULT SessionMachine::i_updateMachineStateOnClient()
14620{
14621 AutoCaller autoCaller(this);
14622 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14623
14624 ComPtr<IInternalSessionControl> directControl;
14625 {
14626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14627 AssertReturn(!!mData, E_FAIL);
14628 if (mData->mSession.mLockType == LockType_VM)
14629 directControl = mData->mSession.mDirectControl;
14630
14631 /* directControl may be already set to NULL here in #OnSessionEnd()
14632 * called too early by the direct session process while there is still
14633 * some operation (like deleting the snapshot) in progress. The client
14634 * process in this case is waiting inside Session::close() for the
14635 * "end session" process object to complete, while #uninit() called by
14636 * #checkForDeath() on the Watcher thread is waiting for the pending
14637 * operation to complete. For now, we accept this inconsistent behavior
14638 * and simply do nothing here. */
14639
14640 if (mData->mSession.mState == SessionState_Unlocking)
14641 return S_OK;
14642 }
14643
14644 /* ignore notifications sent after #OnSessionEnd() is called */
14645 if (!directControl)
14646 return S_OK;
14647
14648 return directControl->UpdateMachineState(mData->mMachineState);
14649}
14650
14651
14652/**
14653 * Static Machine method that can get passed to RTThreadCreate to
14654 * have a thread started for a Task. See Machine::Task.
14655 */
14656/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14657{
14658 AssertReturn(pvUser, VERR_INVALID_POINTER);
14659
14660 Task *pTask = static_cast<Task *>(pvUser);
14661 pTask->handler();
14662 /** @todo r=klaus it would be safer to update the progress object here,
14663 * as it avoids possible races due to scoping issues/tricks in the handler */
14664 // it's our responsibility to delete the task
14665 delete pTask;
14666
14667 return 0;
14668}
14669
14670/*static*/
14671HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14672{
14673 va_list args;
14674 va_start(args, pcszMsg);
14675 HRESULT rc = setErrorInternal(aResultCode,
14676 getStaticClassIID(),
14677 getStaticComponentName(),
14678 Utf8Str(pcszMsg, args),
14679 false /* aWarning */,
14680 true /* aLogIt */);
14681 va_end(args);
14682 return rc;
14683}
14684
14685
14686HRESULT Machine::updateState(MachineState_T aState)
14687{
14688 NOREF(aState);
14689 ReturnComNotImplemented();
14690}
14691
14692HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14693{
14694 NOREF(aProgress);
14695 ReturnComNotImplemented();
14696}
14697
14698HRESULT Machine::endPowerUp(LONG aResult)
14699{
14700 NOREF(aResult);
14701 ReturnComNotImplemented();
14702}
14703
14704HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14705{
14706 NOREF(aProgress);
14707 ReturnComNotImplemented();
14708}
14709
14710HRESULT Machine::endPoweringDown(LONG aResult,
14711 const com::Utf8Str &aErrMsg)
14712{
14713 NOREF(aResult);
14714 NOREF(aErrMsg);
14715 ReturnComNotImplemented();
14716}
14717
14718HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14719 BOOL *aMatched,
14720 ULONG *aMaskedInterfaces)
14721{
14722 NOREF(aDevice);
14723 NOREF(aMatched);
14724 NOREF(aMaskedInterfaces);
14725 ReturnComNotImplemented();
14726
14727}
14728
14729HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14730{
14731 NOREF(aId); NOREF(aCaptureFilename);
14732 ReturnComNotImplemented();
14733}
14734
14735HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14736 BOOL aDone)
14737{
14738 NOREF(aId);
14739 NOREF(aDone);
14740 ReturnComNotImplemented();
14741}
14742
14743HRESULT Machine::autoCaptureUSBDevices()
14744{
14745 ReturnComNotImplemented();
14746}
14747
14748HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14749{
14750 NOREF(aDone);
14751 ReturnComNotImplemented();
14752}
14753
14754HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14755 ComPtr<IProgress> &aProgress)
14756{
14757 NOREF(aSession);
14758 NOREF(aProgress);
14759 ReturnComNotImplemented();
14760}
14761
14762HRESULT Machine::finishOnlineMergeMedium()
14763{
14764 ReturnComNotImplemented();
14765}
14766
14767HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14768 std::vector<com::Utf8Str> &aValues,
14769 std::vector<LONG64> &aTimestamps,
14770 std::vector<com::Utf8Str> &aFlags)
14771{
14772 NOREF(aNames);
14773 NOREF(aValues);
14774 NOREF(aTimestamps);
14775 NOREF(aFlags);
14776 ReturnComNotImplemented();
14777}
14778
14779HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14780 const com::Utf8Str &aValue,
14781 LONG64 aTimestamp,
14782 const com::Utf8Str &aFlags,
14783 BOOL *aNotify)
14784{
14785 NOREF(aName);
14786 NOREF(aValue);
14787 NOREF(aTimestamp);
14788 NOREF(aFlags);
14789 NOREF(aNotify);
14790 ReturnComNotImplemented();
14791}
14792
14793HRESULT Machine::lockMedia()
14794{
14795 ReturnComNotImplemented();
14796}
14797
14798HRESULT Machine::unlockMedia()
14799{
14800 ReturnComNotImplemented();
14801}
14802
14803HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14804 ComPtr<IMediumAttachment> &aNewAttachment)
14805{
14806 NOREF(aAttachment);
14807 NOREF(aNewAttachment);
14808 ReturnComNotImplemented();
14809}
14810
14811HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14812 ULONG aCpuUser,
14813 ULONG aCpuKernel,
14814 ULONG aCpuIdle,
14815 ULONG aMemTotal,
14816 ULONG aMemFree,
14817 ULONG aMemBalloon,
14818 ULONG aMemShared,
14819 ULONG aMemCache,
14820 ULONG aPagedTotal,
14821 ULONG aMemAllocTotal,
14822 ULONG aMemFreeTotal,
14823 ULONG aMemBalloonTotal,
14824 ULONG aMemSharedTotal,
14825 ULONG aVmNetRx,
14826 ULONG aVmNetTx)
14827{
14828 NOREF(aValidStats);
14829 NOREF(aCpuUser);
14830 NOREF(aCpuKernel);
14831 NOREF(aCpuIdle);
14832 NOREF(aMemTotal);
14833 NOREF(aMemFree);
14834 NOREF(aMemBalloon);
14835 NOREF(aMemShared);
14836 NOREF(aMemCache);
14837 NOREF(aPagedTotal);
14838 NOREF(aMemAllocTotal);
14839 NOREF(aMemFreeTotal);
14840 NOREF(aMemBalloonTotal);
14841 NOREF(aMemSharedTotal);
14842 NOREF(aVmNetRx);
14843 NOREF(aVmNetTx);
14844 ReturnComNotImplemented();
14845}
Note: See TracBrowser for help on using the repository browser.

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