VirtualBox

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

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

Main/Machine: fix handling of transient guest properties (both on saving config due to machine state change, important for restoring snapshots and for loading config as there might be VM configs out there which have leftovers). Additionally: for restoring snapshots the last machine state change timestamp is not updated when the final machine state is set, to match the snapshot timestamp.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 511.5 KB
Line 
1/* $Id: MachineImpl.cpp 55833 2015-05-12 17:52:23Z 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 /* Only load transient guest properties for configs which have saved
9071 * state, because there shouldn't be any for powered off VMs. The same
9072 * logic applies for snapshots, as offline snapshots shouldn't have
9073 * any such properties. They confuse the code in various places.
9074 * Note: can't rely on the machine state, as it isn't set yet. */
9075 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9076 /* Guest properties (optional) */
9077 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9078 it != data.llGuestProperties.end();
9079 ++it)
9080 {
9081 const settings::GuestProperty &prop = *it;
9082 uint32_t fFlags = guestProp::NILFLAG;
9083 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9084 if ( fSkipTransientGuestProperties
9085 && ( fFlags & guestProp::TRANSIENT
9086 || fFlags & guestProp::TRANSRESET))
9087 continue;
9088 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9089 mHWData->mGuestProperties[prop.strName] = property;
9090 }
9091
9092 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9093#endif /* VBOX_WITH_GUEST_PROPS defined */
9094
9095 rc = i_loadDebugging(pDbg);
9096 if (FAILED(rc))
9097 return rc;
9098
9099 mHWData->mAutostart = *pAutostart;
9100
9101 /* default frontend */
9102 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9103 }
9104 catch(std::bad_alloc &)
9105 {
9106 return E_OUTOFMEMORY;
9107 }
9108
9109 AssertComRC(rc);
9110 return rc;
9111}
9112
9113/**
9114 * Called from Machine::loadHardware() to load the debugging settings of the
9115 * machine.
9116 *
9117 * @param pDbg Pointer to the settings.
9118 */
9119HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9120{
9121 mHWData->mDebugging = *pDbg;
9122 /* no more processing currently required, this will probably change. */
9123 return S_OK;
9124}
9125
9126/**
9127 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9128 *
9129 * @param data
9130 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9131 * @param puuidSnapshot
9132 * @return
9133 */
9134HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9135 const Guid *puuidRegistry,
9136 const Guid *puuidSnapshot)
9137{
9138 AssertReturn(!i_isSessionMachine(), E_FAIL);
9139
9140 HRESULT rc = S_OK;
9141
9142 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9143 it != data.llStorageControllers.end();
9144 ++it)
9145 {
9146 const settings::StorageController &ctlData = *it;
9147
9148 ComObjPtr<StorageController> pCtl;
9149 /* Try to find one with the name first. */
9150 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9151 if (SUCCEEDED(rc))
9152 return setError(VBOX_E_OBJECT_IN_USE,
9153 tr("Storage controller named '%s' already exists"),
9154 ctlData.strName.c_str());
9155
9156 pCtl.createObject();
9157 rc = pCtl->init(this,
9158 ctlData.strName,
9159 ctlData.storageBus,
9160 ctlData.ulInstance,
9161 ctlData.fBootable);
9162 if (FAILED(rc)) return rc;
9163
9164 mStorageControllers->push_back(pCtl);
9165
9166 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9167 if (FAILED(rc)) return rc;
9168
9169 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9170 if (FAILED(rc)) return rc;
9171
9172 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9173 if (FAILED(rc)) return rc;
9174
9175 /* Set IDE emulation settings (only for AHCI controller). */
9176 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9177 {
9178 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9179 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9180 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9181 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9182 )
9183 return rc;
9184 }
9185
9186 /* Load the attached devices now. */
9187 rc = i_loadStorageDevices(pCtl,
9188 ctlData,
9189 puuidRegistry,
9190 puuidSnapshot);
9191 if (FAILED(rc)) return rc;
9192 }
9193
9194 return S_OK;
9195}
9196
9197/**
9198 * Called from i_loadStorageControllers for a controller's devices.
9199 *
9200 * @param aStorageController
9201 * @param data
9202 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9203 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9204 * @return
9205 */
9206HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9207 const settings::StorageController &data,
9208 const Guid *puuidRegistry,
9209 const Guid *puuidSnapshot)
9210{
9211 HRESULT rc = S_OK;
9212
9213 /* paranoia: detect duplicate attachments */
9214 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9215 it != data.llAttachedDevices.end();
9216 ++it)
9217 {
9218 const settings::AttachedDevice &ad = *it;
9219
9220 for (settings::AttachedDevicesList::const_iterator it2 = it;
9221 it2 != data.llAttachedDevices.end();
9222 ++it2)
9223 {
9224 if (it == it2)
9225 continue;
9226
9227 const settings::AttachedDevice &ad2 = *it2;
9228
9229 if ( ad.lPort == ad2.lPort
9230 && ad.lDevice == ad2.lDevice)
9231 {
9232 return setError(E_FAIL,
9233 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9234 aStorageController->i_getName().c_str(),
9235 ad.lPort,
9236 ad.lDevice,
9237 mUserData->s.strName.c_str());
9238 }
9239 }
9240 }
9241
9242 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9243 it != data.llAttachedDevices.end();
9244 ++it)
9245 {
9246 const settings::AttachedDevice &dev = *it;
9247 ComObjPtr<Medium> medium;
9248
9249 switch (dev.deviceType)
9250 {
9251 case DeviceType_Floppy:
9252 case DeviceType_DVD:
9253 if (dev.strHostDriveSrc.isNotEmpty())
9254 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9255 false /* fRefresh */, medium);
9256 else
9257 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9258 dev.uuid,
9259 false /* fRefresh */,
9260 false /* aSetError */,
9261 medium);
9262 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9263 // This is not an error. The host drive or UUID might have vanished, so just go
9264 // ahead without this removeable medium attachment
9265 rc = S_OK;
9266 break;
9267
9268 case DeviceType_HardDisk:
9269 {
9270 /* find a hard disk by UUID */
9271 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9272 if (FAILED(rc))
9273 {
9274 if (i_isSnapshotMachine())
9275 {
9276 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9277 // so the user knows that the bad disk is in a snapshot somewhere
9278 com::ErrorInfo info;
9279 return setError(E_FAIL,
9280 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9281 puuidSnapshot->raw(),
9282 info.getText().raw());
9283 }
9284 else
9285 return rc;
9286 }
9287
9288 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9289
9290 if (medium->i_getType() == MediumType_Immutable)
9291 {
9292 if (i_isSnapshotMachine())
9293 return setError(E_FAIL,
9294 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9295 "of the virtual machine '%s' ('%s')"),
9296 medium->i_getLocationFull().c_str(),
9297 dev.uuid.raw(),
9298 puuidSnapshot->raw(),
9299 mUserData->s.strName.c_str(),
9300 mData->m_strConfigFileFull.c_str());
9301
9302 return setError(E_FAIL,
9303 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9304 medium->i_getLocationFull().c_str(),
9305 dev.uuid.raw(),
9306 mUserData->s.strName.c_str(),
9307 mData->m_strConfigFileFull.c_str());
9308 }
9309
9310 if (medium->i_getType() == MediumType_MultiAttach)
9311 {
9312 if (i_isSnapshotMachine())
9313 return setError(E_FAIL,
9314 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9315 "of the virtual machine '%s' ('%s')"),
9316 medium->i_getLocationFull().c_str(),
9317 dev.uuid.raw(),
9318 puuidSnapshot->raw(),
9319 mUserData->s.strName.c_str(),
9320 mData->m_strConfigFileFull.c_str());
9321
9322 return setError(E_FAIL,
9323 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9324 medium->i_getLocationFull().c_str(),
9325 dev.uuid.raw(),
9326 mUserData->s.strName.c_str(),
9327 mData->m_strConfigFileFull.c_str());
9328 }
9329
9330 if ( !i_isSnapshotMachine()
9331 && medium->i_getChildren().size() != 0
9332 )
9333 return setError(E_FAIL,
9334 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9335 "because it has %d differencing child hard disks"),
9336 medium->i_getLocationFull().c_str(),
9337 dev.uuid.raw(),
9338 mUserData->s.strName.c_str(),
9339 mData->m_strConfigFileFull.c_str(),
9340 medium->i_getChildren().size());
9341
9342 if (i_findAttachment(mMediaData->mAttachments,
9343 medium))
9344 return setError(E_FAIL,
9345 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9346 medium->i_getLocationFull().c_str(),
9347 dev.uuid.raw(),
9348 mUserData->s.strName.c_str(),
9349 mData->m_strConfigFileFull.c_str());
9350
9351 break;
9352 }
9353
9354 default:
9355 return setError(E_FAIL,
9356 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9357 medium->i_getLocationFull().c_str(),
9358 mUserData->s.strName.c_str(),
9359 mData->m_strConfigFileFull.c_str());
9360 }
9361
9362 if (FAILED(rc))
9363 break;
9364
9365 /* Bandwidth groups are loaded at this point. */
9366 ComObjPtr<BandwidthGroup> pBwGroup;
9367
9368 if (!dev.strBwGroup.isEmpty())
9369 {
9370 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9371 if (FAILED(rc))
9372 return setError(E_FAIL,
9373 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9374 medium->i_getLocationFull().c_str(),
9375 dev.strBwGroup.c_str(),
9376 mUserData->s.strName.c_str(),
9377 mData->m_strConfigFileFull.c_str());
9378 pBwGroup->i_reference();
9379 }
9380
9381 const Bstr controllerName = aStorageController->i_getName();
9382 ComObjPtr<MediumAttachment> pAttachment;
9383 pAttachment.createObject();
9384 rc = pAttachment->init(this,
9385 medium,
9386 controllerName,
9387 dev.lPort,
9388 dev.lDevice,
9389 dev.deviceType,
9390 false,
9391 dev.fPassThrough,
9392 dev.fTempEject,
9393 dev.fNonRotational,
9394 dev.fDiscard,
9395 dev.fHotPluggable,
9396 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9397 if (FAILED(rc)) break;
9398
9399 /* associate the medium with this machine and snapshot */
9400 if (!medium.isNull())
9401 {
9402 AutoCaller medCaller(medium);
9403 if (FAILED(medCaller.rc())) return medCaller.rc();
9404 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9405
9406 if (i_isSnapshotMachine())
9407 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9408 else
9409 rc = medium->i_addBackReference(mData->mUuid);
9410 /* If the medium->addBackReference fails it sets an appropriate
9411 * error message, so no need to do any guesswork here. */
9412
9413 if (puuidRegistry)
9414 // caller wants registry ID to be set on all attached media (OVF import case)
9415 medium->i_addRegistry(*puuidRegistry);
9416 }
9417
9418 if (FAILED(rc))
9419 break;
9420
9421 /* back up mMediaData to let registeredInit() properly rollback on failure
9422 * (= limited accessibility) */
9423 i_setModified(IsModified_Storage);
9424 mMediaData.backup();
9425 mMediaData->mAttachments.push_back(pAttachment);
9426 }
9427
9428 return rc;
9429}
9430
9431/**
9432 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9433 *
9434 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9435 * @param aSnapshot where to return the found snapshot
9436 * @param aSetError true to set extended error info on failure
9437 */
9438HRESULT Machine::i_findSnapshotById(const Guid &aId,
9439 ComObjPtr<Snapshot> &aSnapshot,
9440 bool aSetError /* = false */)
9441{
9442 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9443
9444 if (!mData->mFirstSnapshot)
9445 {
9446 if (aSetError)
9447 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9448 return E_FAIL;
9449 }
9450
9451 if (aId.isZero())
9452 aSnapshot = mData->mFirstSnapshot;
9453 else
9454 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9455
9456 if (!aSnapshot)
9457 {
9458 if (aSetError)
9459 return setError(E_FAIL,
9460 tr("Could not find a snapshot with UUID {%s}"),
9461 aId.toString().c_str());
9462 return E_FAIL;
9463 }
9464
9465 return S_OK;
9466}
9467
9468/**
9469 * Returns the snapshot with the given name or fails of no such snapshot.
9470 *
9471 * @param aName snapshot name to find
9472 * @param aSnapshot where to return the found snapshot
9473 * @param aSetError true to set extended error info on failure
9474 */
9475HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9476 ComObjPtr<Snapshot> &aSnapshot,
9477 bool aSetError /* = false */)
9478{
9479 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9480
9481 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9482
9483 if (!mData->mFirstSnapshot)
9484 {
9485 if (aSetError)
9486 return setError(VBOX_E_OBJECT_NOT_FOUND,
9487 tr("This machine does not have any snapshots"));
9488 return VBOX_E_OBJECT_NOT_FOUND;
9489 }
9490
9491 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9492
9493 if (!aSnapshot)
9494 {
9495 if (aSetError)
9496 return setError(VBOX_E_OBJECT_NOT_FOUND,
9497 tr("Could not find a snapshot named '%s'"), strName.c_str());
9498 return VBOX_E_OBJECT_NOT_FOUND;
9499 }
9500
9501 return S_OK;
9502}
9503
9504/**
9505 * Returns a storage controller object with the given name.
9506 *
9507 * @param aName storage controller name to find
9508 * @param aStorageController where to return the found storage controller
9509 * @param aSetError true to set extended error info on failure
9510 */
9511HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9512 ComObjPtr<StorageController> &aStorageController,
9513 bool aSetError /* = false */)
9514{
9515 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9516
9517 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9518 it != mStorageControllers->end();
9519 ++it)
9520 {
9521 if ((*it)->i_getName() == aName)
9522 {
9523 aStorageController = (*it);
9524 return S_OK;
9525 }
9526 }
9527
9528 if (aSetError)
9529 return setError(VBOX_E_OBJECT_NOT_FOUND,
9530 tr("Could not find a storage controller named '%s'"),
9531 aName.c_str());
9532 return VBOX_E_OBJECT_NOT_FOUND;
9533}
9534
9535/**
9536 * Returns a USB controller object with the given name.
9537 *
9538 * @param aName USB controller name to find
9539 * @param aUSBController where to return the found USB controller
9540 * @param aSetError true to set extended error info on failure
9541 */
9542HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9543 ComObjPtr<USBController> &aUSBController,
9544 bool aSetError /* = false */)
9545{
9546 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9547
9548 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9549 it != mUSBControllers->end();
9550 ++it)
9551 {
9552 if ((*it)->i_getName() == aName)
9553 {
9554 aUSBController = (*it);
9555 return S_OK;
9556 }
9557 }
9558
9559 if (aSetError)
9560 return setError(VBOX_E_OBJECT_NOT_FOUND,
9561 tr("Could not find a storage controller named '%s'"),
9562 aName.c_str());
9563 return VBOX_E_OBJECT_NOT_FOUND;
9564}
9565
9566/**
9567 * Returns the number of USB controller instance of the given type.
9568 *
9569 * @param enmType USB controller type.
9570 */
9571ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9572{
9573 ULONG cCtrls = 0;
9574
9575 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9576 it != mUSBControllers->end();
9577 ++it)
9578 {
9579 if ((*it)->i_getControllerType() == enmType)
9580 cCtrls++;
9581 }
9582
9583 return cCtrls;
9584}
9585
9586HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9587 MediaData::AttachmentList &atts)
9588{
9589 AutoCaller autoCaller(this);
9590 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9591
9592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9593
9594 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9595 it != mMediaData->mAttachments.end();
9596 ++it)
9597 {
9598 const ComObjPtr<MediumAttachment> &pAtt = *it;
9599 // should never happen, but deal with NULL pointers in the list.
9600 AssertStmt(!pAtt.isNull(), continue);
9601
9602 // getControllerName() needs caller+read lock
9603 AutoCaller autoAttCaller(pAtt);
9604 if (FAILED(autoAttCaller.rc()))
9605 {
9606 atts.clear();
9607 return autoAttCaller.rc();
9608 }
9609 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9610
9611 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9612 atts.push_back(pAtt);
9613 }
9614
9615 return S_OK;
9616}
9617
9618
9619/**
9620 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9621 * file if the machine name was changed and about creating a new settings file
9622 * if this is a new machine.
9623 *
9624 * @note Must be never called directly but only from #saveSettings().
9625 */
9626HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9627{
9628 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9629
9630 HRESULT rc = S_OK;
9631
9632 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9633
9634 /// @todo need to handle primary group change, too
9635
9636 /* attempt to rename the settings file if machine name is changed */
9637 if ( mUserData->s.fNameSync
9638 && mUserData.isBackedUp()
9639 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9640 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9641 )
9642 {
9643 bool dirRenamed = false;
9644 bool fileRenamed = false;
9645
9646 Utf8Str configFile, newConfigFile;
9647 Utf8Str configFilePrev, newConfigFilePrev;
9648 Utf8Str configDir, newConfigDir;
9649
9650 do
9651 {
9652 int vrc = VINF_SUCCESS;
9653
9654 Utf8Str name = mUserData.backedUpData()->s.strName;
9655 Utf8Str newName = mUserData->s.strName;
9656 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9657 if (group == "/")
9658 group.setNull();
9659 Utf8Str newGroup = mUserData->s.llGroups.front();
9660 if (newGroup == "/")
9661 newGroup.setNull();
9662
9663 configFile = mData->m_strConfigFileFull;
9664
9665 /* first, rename the directory if it matches the group and machine name */
9666 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9667 group.c_str(), RTPATH_DELIMITER, name.c_str());
9668 /** @todo hack, make somehow use of ComposeMachineFilename */
9669 if (mUserData->s.fDirectoryIncludesUUID)
9670 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9671 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9672 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9673 /** @todo hack, make somehow use of ComposeMachineFilename */
9674 if (mUserData->s.fDirectoryIncludesUUID)
9675 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9676 configDir = configFile;
9677 configDir.stripFilename();
9678 newConfigDir = configDir;
9679 if ( configDir.length() >= groupPlusName.length()
9680 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9681 groupPlusName.c_str()))
9682 {
9683 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9684 Utf8Str newConfigBaseDir(newConfigDir);
9685 newConfigDir.append(newGroupPlusName);
9686 /* consistency: use \ if appropriate on the platform */
9687 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9688 /* new dir and old dir cannot be equal here because of 'if'
9689 * above and because name != newName */
9690 Assert(configDir != newConfigDir);
9691 if (!fSettingsFileIsNew)
9692 {
9693 /* perform real rename only if the machine is not new */
9694 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9695 if ( vrc == VERR_FILE_NOT_FOUND
9696 || vrc == VERR_PATH_NOT_FOUND)
9697 {
9698 /* create the parent directory, then retry renaming */
9699 Utf8Str parent(newConfigDir);
9700 parent.stripFilename();
9701 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9702 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9703 }
9704 if (RT_FAILURE(vrc))
9705 {
9706 rc = setError(E_FAIL,
9707 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9708 configDir.c_str(),
9709 newConfigDir.c_str(),
9710 vrc);
9711 break;
9712 }
9713 /* delete subdirectories which are no longer needed */
9714 Utf8Str dir(configDir);
9715 dir.stripFilename();
9716 while (dir != newConfigBaseDir && dir != ".")
9717 {
9718 vrc = RTDirRemove(dir.c_str());
9719 if (RT_FAILURE(vrc))
9720 break;
9721 dir.stripFilename();
9722 }
9723 dirRenamed = true;
9724 }
9725 }
9726
9727 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9728 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9729
9730 /* then try to rename the settings file itself */
9731 if (newConfigFile != configFile)
9732 {
9733 /* get the path to old settings file in renamed directory */
9734 configFile = Utf8StrFmt("%s%c%s",
9735 newConfigDir.c_str(),
9736 RTPATH_DELIMITER,
9737 RTPathFilename(configFile.c_str()));
9738 if (!fSettingsFileIsNew)
9739 {
9740 /* perform real rename only if the machine is not new */
9741 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9742 if (RT_FAILURE(vrc))
9743 {
9744 rc = setError(E_FAIL,
9745 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9746 configFile.c_str(),
9747 newConfigFile.c_str(),
9748 vrc);
9749 break;
9750 }
9751 fileRenamed = true;
9752 configFilePrev = configFile;
9753 configFilePrev += "-prev";
9754 newConfigFilePrev = newConfigFile;
9755 newConfigFilePrev += "-prev";
9756 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9757 }
9758 }
9759
9760 // update m_strConfigFileFull amd mConfigFile
9761 mData->m_strConfigFileFull = newConfigFile;
9762 // compute the relative path too
9763 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9764
9765 // store the old and new so that VirtualBox::i_saveSettings() can update
9766 // the media registry
9767 if ( mData->mRegistered
9768 && (configDir != newConfigDir || configFile != newConfigFile))
9769 {
9770 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9771
9772 if (pfNeedsGlobalSaveSettings)
9773 *pfNeedsGlobalSaveSettings = true;
9774 }
9775
9776 // in the saved state file path, replace the old directory with the new directory
9777 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9778 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9779
9780 // and do the same thing for the saved state file paths of all the online snapshots
9781 if (mData->mFirstSnapshot)
9782 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9783 newConfigDir.c_str());
9784 }
9785 while (0);
9786
9787 if (FAILED(rc))
9788 {
9789 /* silently try to rename everything back */
9790 if (fileRenamed)
9791 {
9792 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9793 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9794 }
9795 if (dirRenamed)
9796 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9797 }
9798
9799 if (FAILED(rc)) return rc;
9800 }
9801
9802 if (fSettingsFileIsNew)
9803 {
9804 /* create a virgin config file */
9805 int vrc = VINF_SUCCESS;
9806
9807 /* ensure the settings directory exists */
9808 Utf8Str path(mData->m_strConfigFileFull);
9809 path.stripFilename();
9810 if (!RTDirExists(path.c_str()))
9811 {
9812 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9813 if (RT_FAILURE(vrc))
9814 {
9815 return setError(E_FAIL,
9816 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9817 path.c_str(),
9818 vrc);
9819 }
9820 }
9821
9822 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9823 path = Utf8Str(mData->m_strConfigFileFull);
9824 RTFILE f = NIL_RTFILE;
9825 vrc = RTFileOpen(&f, path.c_str(),
9826 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9827 if (RT_FAILURE(vrc))
9828 return setError(E_FAIL,
9829 tr("Could not create the settings file '%s' (%Rrc)"),
9830 path.c_str(),
9831 vrc);
9832 RTFileClose(f);
9833 }
9834
9835 return rc;
9836}
9837
9838/**
9839 * Saves and commits machine data, user data and hardware data.
9840 *
9841 * Note that on failure, the data remains uncommitted.
9842 *
9843 * @a aFlags may combine the following flags:
9844 *
9845 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9846 * Used when saving settings after an operation that makes them 100%
9847 * correspond to the settings from the current snapshot.
9848 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9849 * #isReallyModified() returns false. This is necessary for cases when we
9850 * change machine data directly, not through the backup()/commit() mechanism.
9851 * - SaveS_Force: settings will be saved without doing a deep compare of the
9852 * settings structures. This is used when this is called because snapshots
9853 * have changed to avoid the overhead of the deep compare.
9854 *
9855 * @note Must be called from under this object's write lock. Locks children for
9856 * writing.
9857 *
9858 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9859 * initialized to false and that will be set to true by this function if
9860 * the caller must invoke VirtualBox::i_saveSettings() because the global
9861 * settings have changed. This will happen if a machine rename has been
9862 * saved and the global machine and media registries will therefore need
9863 * updating.
9864 */
9865HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9866 int aFlags /*= 0*/)
9867{
9868 LogFlowThisFuncEnter();
9869
9870 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9871
9872 /* make sure child objects are unable to modify the settings while we are
9873 * saving them */
9874 i_ensureNoStateDependencies();
9875
9876 AssertReturn(!i_isSnapshotMachine(),
9877 E_FAIL);
9878
9879 HRESULT rc = S_OK;
9880 bool fNeedsWrite = false;
9881
9882 /* First, prepare to save settings. It will care about renaming the
9883 * settings directory and file if the machine name was changed and about
9884 * creating a new settings file if this is a new machine. */
9885 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9886 if (FAILED(rc)) return rc;
9887
9888 // keep a pointer to the current settings structures
9889 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9890 settings::MachineConfigFile *pNewConfig = NULL;
9891
9892 try
9893 {
9894 // make a fresh one to have everyone write stuff into
9895 pNewConfig = new settings::MachineConfigFile(NULL);
9896 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9897
9898 // now go and copy all the settings data from COM to the settings structures
9899 // (this calles i_saveSettings() on all the COM objects in the machine)
9900 i_copyMachineDataToSettings(*pNewConfig);
9901
9902 if (aFlags & SaveS_ResetCurStateModified)
9903 {
9904 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9905 mData->mCurrentStateModified = FALSE;
9906 fNeedsWrite = true; // always, no need to compare
9907 }
9908 else if (aFlags & SaveS_Force)
9909 {
9910 fNeedsWrite = true; // always, no need to compare
9911 }
9912 else
9913 {
9914 if (!mData->mCurrentStateModified)
9915 {
9916 // do a deep compare of the settings that we just saved with the settings
9917 // previously stored in the config file; this invokes MachineConfigFile::operator==
9918 // which does a deep compare of all the settings, which is expensive but less expensive
9919 // than writing out XML in vain
9920 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9921
9922 // could still be modified if any settings changed
9923 mData->mCurrentStateModified = fAnySettingsChanged;
9924
9925 fNeedsWrite = fAnySettingsChanged;
9926 }
9927 else
9928 fNeedsWrite = true;
9929 }
9930
9931 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9932
9933 if (fNeedsWrite)
9934 // now spit it all out!
9935 pNewConfig->write(mData->m_strConfigFileFull);
9936
9937 mData->pMachineConfigFile = pNewConfig;
9938 delete pOldConfig;
9939 i_commit();
9940
9941 // after saving settings, we are no longer different from the XML on disk
9942 mData->flModifications = 0;
9943 }
9944 catch (HRESULT err)
9945 {
9946 // we assume that error info is set by the thrower
9947 rc = err;
9948
9949 // restore old config
9950 delete pNewConfig;
9951 mData->pMachineConfigFile = pOldConfig;
9952 }
9953 catch (...)
9954 {
9955 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9956 }
9957
9958 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9959 {
9960 /* Fire the data change event, even on failure (since we've already
9961 * committed all data). This is done only for SessionMachines because
9962 * mutable Machine instances are always not registered (i.e. private
9963 * to the client process that creates them) and thus don't need to
9964 * inform callbacks. */
9965 if (i_isSessionMachine())
9966 mParent->i_onMachineDataChange(mData->mUuid);
9967 }
9968
9969 LogFlowThisFunc(("rc=%08X\n", rc));
9970 LogFlowThisFuncLeave();
9971 return rc;
9972}
9973
9974/**
9975 * Implementation for saving the machine settings into the given
9976 * settings::MachineConfigFile instance. This copies machine extradata
9977 * from the previous machine config file in the instance data, if any.
9978 *
9979 * This gets called from two locations:
9980 *
9981 * -- Machine::i_saveSettings(), during the regular XML writing;
9982 *
9983 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9984 * exported to OVF and we write the VirtualBox proprietary XML
9985 * into a <vbox:Machine> tag.
9986 *
9987 * This routine fills all the fields in there, including snapshots, *except*
9988 * for the following:
9989 *
9990 * -- fCurrentStateModified. There is some special logic associated with that.
9991 *
9992 * The caller can then call MachineConfigFile::write() or do something else
9993 * with it.
9994 *
9995 * Caller must hold the machine lock!
9996 *
9997 * This throws XML errors and HRESULT, so the caller must have a catch block!
9998 */
9999void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10000{
10001 // deep copy extradata
10002 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10003
10004 config.uuid = mData->mUuid;
10005
10006 // copy name, description, OS type, teleport, UTC etc.
10007 config.machineUserData = mUserData->s;
10008
10009 // Encode the Icon Override data from Machine and store on config userdata.
10010 std::vector<BYTE> iconByte;
10011 getIcon(iconByte);
10012 ssize_t cbData = iconByte.size();
10013 if (cbData > 0)
10014 {
10015 ssize_t cchOut = RTBase64EncodedLength(cbData);
10016 Utf8Str strIconData;
10017 strIconData.reserve(cchOut+1);
10018 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10019 strIconData.mutableRaw(), strIconData.capacity(),
10020 NULL);
10021 if (RT_FAILURE(vrc))
10022 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10023 strIconData.jolt();
10024 config.machineUserData.ovIcon = strIconData;
10025 }
10026 else
10027 config.machineUserData.ovIcon.setNull();
10028
10029 if ( mData->mMachineState == MachineState_Saved
10030 || mData->mMachineState == MachineState_Restoring
10031 // when doing certain snapshot operations we may or may not have
10032 // a saved state in the current state, so keep everything as is
10033 || ( ( mData->mMachineState == MachineState_Snapshotting
10034 || mData->mMachineState == MachineState_DeletingSnapshot
10035 || mData->mMachineState == MachineState_RestoringSnapshot)
10036 && (!mSSData->strStateFilePath.isEmpty())
10037 )
10038 )
10039 {
10040 Assert(!mSSData->strStateFilePath.isEmpty());
10041 /* try to make the file name relative to the settings file dir */
10042 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10043 }
10044 else
10045 {
10046 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10047 config.strStateFile.setNull();
10048 }
10049
10050 if (mData->mCurrentSnapshot)
10051 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10052 else
10053 config.uuidCurrentSnapshot.clear();
10054
10055 config.timeLastStateChange = mData->mLastStateChange;
10056 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10057 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10058
10059 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10060 if (FAILED(rc)) throw rc;
10061
10062 rc = i_saveStorageControllers(config.storageMachine);
10063 if (FAILED(rc)) throw rc;
10064
10065 // save machine's media registry if this is VirtualBox 4.0 or later
10066 if (config.canHaveOwnMediaRegistry())
10067 {
10068 // determine machine folder
10069 Utf8Str strMachineFolder = i_getSettingsFileFull();
10070 strMachineFolder.stripFilename();
10071 mParent->i_saveMediaRegistry(config.mediaRegistry,
10072 i_getId(), // only media with registry ID == machine UUID
10073 strMachineFolder);
10074 // this throws HRESULT
10075 }
10076
10077 // save snapshots
10078 rc = i_saveAllSnapshots(config);
10079 if (FAILED(rc)) throw rc;
10080}
10081
10082/**
10083 * Saves all snapshots of the machine into the given machine config file. Called
10084 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10085 * @param config
10086 * @return
10087 */
10088HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10089{
10090 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10091
10092 HRESULT rc = S_OK;
10093
10094 try
10095 {
10096 config.llFirstSnapshot.clear();
10097
10098 if (mData->mFirstSnapshot)
10099 {
10100 // the settings use a list for "the first snapshot"
10101 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10102
10103 // get reference to the snapshot on the list and work on that
10104 // element straight in the list to avoid excessive copying later
10105 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10106 if (FAILED(rc)) throw rc;
10107 }
10108
10109// if (mType == IsSessionMachine)
10110// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10111
10112 }
10113 catch (HRESULT err)
10114 {
10115 /* we assume that error info is set by the thrower */
10116 rc = err;
10117 }
10118 catch (...)
10119 {
10120 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10121 }
10122
10123 return rc;
10124}
10125
10126/**
10127 * Saves the VM hardware configuration. It is assumed that the
10128 * given node is empty.
10129 *
10130 * @param data Reference to the settings object for the hardware config.
10131 * @param pDbg Pointer to the settings object for the debugging config
10132 * which happens to live in mHWData.
10133 * @param pAutostart Pointer to the settings object for the autostart config
10134 * which happens to live in mHWData.
10135 */
10136HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10137 settings::Autostart *pAutostart)
10138{
10139 HRESULT rc = S_OK;
10140
10141 try
10142 {
10143 /* The hardware version attribute (optional).
10144 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10145 if ( mHWData->mHWVersion == "1"
10146 && mSSData->strStateFilePath.isEmpty()
10147 )
10148 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10149 other point needs to be found where this can be done. */
10150
10151 data.strVersion = mHWData->mHWVersion;
10152 data.uuid = mHWData->mHardwareUUID;
10153
10154 // CPU
10155 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10156 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10157 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10158 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10159 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10160 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10161 data.fPAE = !!mHWData->mPAEEnabled;
10162 data.enmLongMode = mHWData->mLongMode;
10163 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10164 data.cCPUs = mHWData->mCPUCount;
10165 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10166 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10167 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10168
10169 data.llCpus.clear();
10170 if (data.fCpuHotPlug)
10171 {
10172 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10173 {
10174 if (mHWData->mCPUAttached[idx])
10175 {
10176 settings::Cpu cpu;
10177 cpu.ulId = idx;
10178 data.llCpus.push_back(cpu);
10179 }
10180 }
10181 }
10182
10183 /* Standard and Extended CPUID leafs. */
10184 data.llCpuIdLeafs.clear();
10185 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10186 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10187 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10188 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10189 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10190 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10191
10192 // memory
10193 data.ulMemorySizeMB = mHWData->mMemorySize;
10194 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10195
10196 // firmware
10197 data.firmwareType = mHWData->mFirmwareType;
10198
10199 // HID
10200 data.pointingHIDType = mHWData->mPointingHIDType;
10201 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10202
10203 // chipset
10204 data.chipsetType = mHWData->mChipsetType;
10205
10206 // paravirt
10207 data.paravirtProvider = mHWData->mParavirtProvider;
10208
10209
10210 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10211
10212 // HPET
10213 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10214
10215 // boot order
10216 data.mapBootOrder.clear();
10217 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10218 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10219
10220 // display
10221 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10222 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10223 data.cMonitors = mHWData->mMonitorCount;
10224 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10225 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10226 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10227 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10228 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10229 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10230 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10231 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10232 {
10233 if (mHWData->maVideoCaptureScreens[i])
10234 ASMBitSet(&data.u64VideoCaptureScreens, i);
10235 else
10236 ASMBitClear(&data.u64VideoCaptureScreens, i);
10237 }
10238 /* store relative video capture file if possible */
10239 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10240
10241 /* VRDEServer settings (optional) */
10242 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10243 if (FAILED(rc)) throw rc;
10244
10245 /* BIOS (required) */
10246 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10247 if (FAILED(rc)) throw rc;
10248
10249 /* USB Controller (required) */
10250 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10251 {
10252 ComObjPtr<USBController> ctrl = *it;
10253 settings::USBController settingsCtrl;
10254
10255 settingsCtrl.strName = ctrl->i_getName();
10256 settingsCtrl.enmType = ctrl->i_getControllerType();
10257
10258 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10259 }
10260
10261 /* USB device filters (required) */
10262 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10263 if (FAILED(rc)) throw rc;
10264
10265 /* Network adapters (required) */
10266 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10267 data.llNetworkAdapters.clear();
10268 /* Write out only the nominal number of network adapters for this
10269 * chipset type. Since Machine::commit() hasn't been called there
10270 * may be extra NIC settings in the vector. */
10271 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10272 {
10273 settings::NetworkAdapter nic;
10274 nic.ulSlot = (uint32_t)slot;
10275 /* paranoia check... must not be NULL, but must not crash either. */
10276 if (mNetworkAdapters[slot])
10277 {
10278 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10279 if (FAILED(rc)) throw rc;
10280
10281 data.llNetworkAdapters.push_back(nic);
10282 }
10283 }
10284
10285 /* Serial ports */
10286 data.llSerialPorts.clear();
10287 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10288 {
10289 settings::SerialPort s;
10290 s.ulSlot = slot;
10291 rc = mSerialPorts[slot]->i_saveSettings(s);
10292 if (FAILED(rc)) return rc;
10293
10294 data.llSerialPorts.push_back(s);
10295 }
10296
10297 /* Parallel ports */
10298 data.llParallelPorts.clear();
10299 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10300 {
10301 settings::ParallelPort p;
10302 p.ulSlot = slot;
10303 rc = mParallelPorts[slot]->i_saveSettings(p);
10304 if (FAILED(rc)) return rc;
10305
10306 data.llParallelPorts.push_back(p);
10307 }
10308
10309 /* Audio adapter */
10310 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10311 if (FAILED(rc)) return rc;
10312
10313 /* Shared folders */
10314 data.llSharedFolders.clear();
10315 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10316 it != mHWData->mSharedFolders.end();
10317 ++it)
10318 {
10319 SharedFolder *pSF = *it;
10320 AutoCaller sfCaller(pSF);
10321 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10322 settings::SharedFolder sf;
10323 sf.strName = pSF->i_getName();
10324 sf.strHostPath = pSF->i_getHostPath();
10325 sf.fWritable = !!pSF->i_isWritable();
10326 sf.fAutoMount = !!pSF->i_isAutoMounted();
10327
10328 data.llSharedFolders.push_back(sf);
10329 }
10330
10331 // clipboard
10332 data.clipboardMode = mHWData->mClipboardMode;
10333
10334 // drag'n'drop
10335 data.dndMode = mHWData->mDnDMode;
10336
10337 /* Guest */
10338 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10339
10340 // IO settings
10341 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10342 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10343
10344 /* BandwidthControl (required) */
10345 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10346 if (FAILED(rc)) throw rc;
10347
10348 /* Host PCI devices */
10349 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10350 it != mHWData->mPCIDeviceAssignments.end();
10351 ++it)
10352 {
10353 ComObjPtr<PCIDeviceAttachment> pda = *it;
10354 settings::HostPCIDeviceAttachment hpda;
10355
10356 rc = pda->i_saveSettings(hpda);
10357 if (FAILED(rc)) throw rc;
10358
10359 data.pciAttachments.push_back(hpda);
10360 }
10361
10362
10363 // guest properties
10364 data.llGuestProperties.clear();
10365#ifdef VBOX_WITH_GUEST_PROPS
10366 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10367 it != mHWData->mGuestProperties.end();
10368 ++it)
10369 {
10370 HWData::GuestProperty property = it->second;
10371
10372 /* Remove transient guest properties at shutdown unless we
10373 * are saving state. Note that restoring snapshot intentionally
10374 * keeps them, they will be removed if appropriate once the final
10375 * machine state is set (as crashes etc. need to work). */
10376 if ( ( mData->mMachineState == MachineState_PoweredOff
10377 || mData->mMachineState == MachineState_Aborted
10378 || mData->mMachineState == MachineState_Teleported)
10379 && ( property.mFlags & guestProp::TRANSIENT
10380 || property.mFlags & guestProp::TRANSRESET))
10381 continue;
10382 settings::GuestProperty prop;
10383 prop.strName = it->first;
10384 prop.strValue = property.strValue;
10385 prop.timestamp = property.mTimestamp;
10386 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10387 guestProp::writeFlags(property.mFlags, szFlags);
10388 prop.strFlags = szFlags;
10389
10390 data.llGuestProperties.push_back(prop);
10391 }
10392
10393 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10394 /* I presume this doesn't require a backup(). */
10395 mData->mGuestPropertiesModified = FALSE;
10396#endif /* VBOX_WITH_GUEST_PROPS defined */
10397
10398 *pDbg = mHWData->mDebugging;
10399 *pAutostart = mHWData->mAutostart;
10400
10401 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10402 }
10403 catch(std::bad_alloc &)
10404 {
10405 return E_OUTOFMEMORY;
10406 }
10407
10408 AssertComRC(rc);
10409 return rc;
10410}
10411
10412/**
10413 * Saves the storage controller configuration.
10414 *
10415 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10416 */
10417HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10418{
10419 data.llStorageControllers.clear();
10420
10421 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10422 it != mStorageControllers->end();
10423 ++it)
10424 {
10425 HRESULT rc;
10426 ComObjPtr<StorageController> pCtl = *it;
10427
10428 settings::StorageController ctl;
10429 ctl.strName = pCtl->i_getName();
10430 ctl.controllerType = pCtl->i_getControllerType();
10431 ctl.storageBus = pCtl->i_getStorageBus();
10432 ctl.ulInstance = pCtl->i_getInstance();
10433 ctl.fBootable = pCtl->i_getBootable();
10434
10435 /* Save the port count. */
10436 ULONG portCount;
10437 rc = pCtl->COMGETTER(PortCount)(&portCount);
10438 ComAssertComRCRet(rc, rc);
10439 ctl.ulPortCount = portCount;
10440
10441 /* Save fUseHostIOCache */
10442 BOOL fUseHostIOCache;
10443 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10444 ComAssertComRCRet(rc, rc);
10445 ctl.fUseHostIOCache = !!fUseHostIOCache;
10446
10447 /* Save IDE emulation settings. */
10448 if (ctl.controllerType == StorageControllerType_IntelAhci)
10449 {
10450 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10451 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10452 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10453 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10454 )
10455 ComAssertComRCRet(rc, rc);
10456 }
10457
10458 /* save the devices now. */
10459 rc = i_saveStorageDevices(pCtl, ctl);
10460 ComAssertComRCRet(rc, rc);
10461
10462 data.llStorageControllers.push_back(ctl);
10463 }
10464
10465 return S_OK;
10466}
10467
10468/**
10469 * Saves the hard disk configuration.
10470 */
10471HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10472 settings::StorageController &data)
10473{
10474 MediaData::AttachmentList atts;
10475
10476 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10477 if (FAILED(rc)) return rc;
10478
10479 data.llAttachedDevices.clear();
10480 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10481 it != atts.end();
10482 ++it)
10483 {
10484 settings::AttachedDevice dev;
10485 IMediumAttachment *iA = *it;
10486 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10487 Medium *pMedium = pAttach->i_getMedium();
10488
10489 dev.deviceType = pAttach->i_getType();
10490 dev.lPort = pAttach->i_getPort();
10491 dev.lDevice = pAttach->i_getDevice();
10492 dev.fPassThrough = pAttach->i_getPassthrough();
10493 dev.fHotPluggable = pAttach->i_getHotPluggable();
10494 if (pMedium)
10495 {
10496 if (pMedium->i_isHostDrive())
10497 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10498 else
10499 dev.uuid = pMedium->i_getId();
10500 dev.fTempEject = pAttach->i_getTempEject();
10501 dev.fNonRotational = pAttach->i_getNonRotational();
10502 dev.fDiscard = pAttach->i_getDiscard();
10503 }
10504
10505 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10506
10507 data.llAttachedDevices.push_back(dev);
10508 }
10509
10510 return S_OK;
10511}
10512
10513/**
10514 * Saves machine state settings as defined by aFlags
10515 * (SaveSTS_* values).
10516 *
10517 * @param aFlags Combination of SaveSTS_* flags.
10518 *
10519 * @note Locks objects for writing.
10520 */
10521HRESULT Machine::i_saveStateSettings(int aFlags)
10522{
10523 if (aFlags == 0)
10524 return S_OK;
10525
10526 AutoCaller autoCaller(this);
10527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10528
10529 /* This object's write lock is also necessary to serialize file access
10530 * (prevent concurrent reads and writes) */
10531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10532
10533 HRESULT rc = S_OK;
10534
10535 Assert(mData->pMachineConfigFile);
10536
10537 try
10538 {
10539 if (aFlags & SaveSTS_CurStateModified)
10540 mData->pMachineConfigFile->fCurrentStateModified = true;
10541
10542 if (aFlags & SaveSTS_StateFilePath)
10543 {
10544 if (!mSSData->strStateFilePath.isEmpty())
10545 /* try to make the file name relative to the settings file dir */
10546 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10547 else
10548 mData->pMachineConfigFile->strStateFile.setNull();
10549 }
10550
10551 if (aFlags & SaveSTS_StateTimeStamp)
10552 {
10553 Assert( mData->mMachineState != MachineState_Aborted
10554 || mSSData->strStateFilePath.isEmpty());
10555
10556 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10557
10558 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10559//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10560 }
10561
10562 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10563 }
10564 catch (...)
10565 {
10566 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10567 }
10568
10569 return rc;
10570}
10571
10572/**
10573 * Ensures that the given medium is added to a media registry. If this machine
10574 * was created with 4.0 or later, then the machine registry is used. Otherwise
10575 * the global VirtualBox media registry is used.
10576 *
10577 * Caller must NOT hold machine lock, media tree or any medium locks!
10578 *
10579 * @param pMedium
10580 */
10581void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10582{
10583 /* Paranoia checks: do not hold machine or media tree locks. */
10584 AssertReturnVoid(!isWriteLockOnCurrentThread());
10585 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10586
10587 ComObjPtr<Medium> pBase;
10588 {
10589 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10590 pBase = pMedium->i_getBase();
10591 }
10592
10593 /* Paranoia checks: do not hold medium locks. */
10594 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10595 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10596
10597 // decide which medium registry to use now that the medium is attached:
10598 Guid uuid;
10599 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10600 // machine XML is VirtualBox 4.0 or higher:
10601 uuid = i_getId(); // machine UUID
10602 else
10603 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10604
10605 if (pMedium->i_addRegistry(uuid))
10606 mParent->i_markRegistryModified(uuid);
10607
10608 /* For more complex hard disk structures it can happen that the base
10609 * medium isn't yet associated with any medium registry. Do that now. */
10610 if (pMedium != pBase)
10611 {
10612 /* Tree lock needed by Medium::addRegistry when recursing. */
10613 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10614 if (pBase->i_addRegistryRecursive(uuid))
10615 {
10616 treeLock.release();
10617 mParent->i_markRegistryModified(uuid);
10618 }
10619 }
10620}
10621
10622/**
10623 * Creates differencing hard disks for all normal hard disks attached to this
10624 * machine and a new set of attachments to refer to created disks.
10625 *
10626 * Used when taking a snapshot or when deleting the current state. Gets called
10627 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10628 *
10629 * This method assumes that mMediaData contains the original hard disk attachments
10630 * it needs to create diffs for. On success, these attachments will be replaced
10631 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10632 * called to delete created diffs which will also rollback mMediaData and restore
10633 * whatever was backed up before calling this method.
10634 *
10635 * Attachments with non-normal hard disks are left as is.
10636 *
10637 * If @a aOnline is @c false then the original hard disks that require implicit
10638 * diffs will be locked for reading. Otherwise it is assumed that they are
10639 * already locked for writing (when the VM was started). Note that in the latter
10640 * case it is responsibility of the caller to lock the newly created diffs for
10641 * writing if this method succeeds.
10642 *
10643 * @param aProgress Progress object to run (must contain at least as
10644 * many operations left as the number of hard disks
10645 * attached).
10646 * @param aOnline Whether the VM was online prior to this operation.
10647 *
10648 * @note The progress object is not marked as completed, neither on success nor
10649 * on failure. This is a responsibility of the caller.
10650 *
10651 * @note Locks this object and the media tree for writing.
10652 */
10653HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10654 ULONG aWeight,
10655 bool aOnline)
10656{
10657 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10658
10659 AutoCaller autoCaller(this);
10660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10661
10662 AutoMultiWriteLock2 alock(this->lockHandle(),
10663 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10664
10665 /* must be in a protective state because we release the lock below */
10666 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10667 || mData->mMachineState == MachineState_OnlineSnapshotting
10668 || mData->mMachineState == MachineState_LiveSnapshotting
10669 || mData->mMachineState == MachineState_RestoringSnapshot
10670 || mData->mMachineState == MachineState_DeletingSnapshot
10671 , E_FAIL);
10672
10673 HRESULT rc = S_OK;
10674
10675 // use appropriate locked media map (online or offline)
10676 MediumLockListMap lockedMediaOffline;
10677 MediumLockListMap *lockedMediaMap;
10678 if (aOnline)
10679 lockedMediaMap = &mData->mSession.mLockedMedia;
10680 else
10681 lockedMediaMap = &lockedMediaOffline;
10682
10683 try
10684 {
10685 if (!aOnline)
10686 {
10687 /* lock all attached hard disks early to detect "in use"
10688 * situations before creating actual diffs */
10689 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10690 it != mMediaData->mAttachments.end();
10691 ++it)
10692 {
10693 MediumAttachment* pAtt = *it;
10694 if (pAtt->i_getType() == DeviceType_HardDisk)
10695 {
10696 Medium* pMedium = pAtt->i_getMedium();
10697 Assert(pMedium);
10698
10699 MediumLockList *pMediumLockList(new MediumLockList());
10700 alock.release();
10701 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10702 false /* fMediumLockWrite */,
10703 false /* fMediumLockWriteAll */,
10704 NULL,
10705 *pMediumLockList);
10706 alock.acquire();
10707 if (FAILED(rc))
10708 {
10709 delete pMediumLockList;
10710 throw rc;
10711 }
10712 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10713 if (FAILED(rc))
10714 {
10715 throw setError(rc,
10716 tr("Collecting locking information for all attached media failed"));
10717 }
10718 }
10719 }
10720
10721 /* Now lock all media. If this fails, nothing is locked. */
10722 alock.release();
10723 rc = lockedMediaMap->Lock();
10724 alock.acquire();
10725 if (FAILED(rc))
10726 {
10727 throw setError(rc,
10728 tr("Locking of attached media failed"));
10729 }
10730 }
10731
10732 /* remember the current list (note that we don't use backup() since
10733 * mMediaData may be already backed up) */
10734 MediaData::AttachmentList atts = mMediaData->mAttachments;
10735
10736 /* start from scratch */
10737 mMediaData->mAttachments.clear();
10738
10739 /* go through remembered attachments and create diffs for normal hard
10740 * disks and attach them */
10741 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10742 it != atts.end();
10743 ++it)
10744 {
10745 MediumAttachment* pAtt = *it;
10746
10747 DeviceType_T devType = pAtt->i_getType();
10748 Medium* pMedium = pAtt->i_getMedium();
10749
10750 if ( devType != DeviceType_HardDisk
10751 || pMedium == NULL
10752 || pMedium->i_getType() != MediumType_Normal)
10753 {
10754 /* copy the attachment as is */
10755
10756 /** @todo the progress object created in SessionMachine::TakeSnaphot
10757 * only expects operations for hard disks. Later other
10758 * device types need to show up in the progress as well. */
10759 if (devType == DeviceType_HardDisk)
10760 {
10761 if (pMedium == NULL)
10762 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10763 aWeight); // weight
10764 else
10765 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10766 pMedium->i_getBase()->i_getName().c_str()).raw(),
10767 aWeight); // weight
10768 }
10769
10770 mMediaData->mAttachments.push_back(pAtt);
10771 continue;
10772 }
10773
10774 /* need a diff */
10775 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10776 pMedium->i_getBase()->i_getName().c_str()).raw(),
10777 aWeight); // weight
10778
10779 Utf8Str strFullSnapshotFolder;
10780 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10781
10782 ComObjPtr<Medium> diff;
10783 diff.createObject();
10784 // store the diff in the same registry as the parent
10785 // (this cannot fail here because we can't create implicit diffs for
10786 // unregistered images)
10787 Guid uuidRegistryParent;
10788 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10789 Assert(fInRegistry); NOREF(fInRegistry);
10790 rc = diff->init(mParent,
10791 pMedium->i_getPreferredDiffFormat(),
10792 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10793 uuidRegistryParent,
10794 DeviceType_HardDisk);
10795 if (FAILED(rc)) throw rc;
10796
10797 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10798 * the push_back? Looks like we're going to release medium with the
10799 * wrong kind of lock (general issue with if we fail anywhere at all)
10800 * and an orphaned VDI in the snapshots folder. */
10801
10802 /* update the appropriate lock list */
10803 MediumLockList *pMediumLockList;
10804 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10805 AssertComRCThrowRC(rc);
10806 if (aOnline)
10807 {
10808 alock.release();
10809 /* The currently attached medium will be read-only, change
10810 * the lock type to read. */
10811 rc = pMediumLockList->Update(pMedium, false);
10812 alock.acquire();
10813 AssertComRCThrowRC(rc);
10814 }
10815
10816 /* release the locks before the potentially lengthy operation */
10817 alock.release();
10818 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10819 pMediumLockList,
10820 NULL /* aProgress */,
10821 true /* aWait */);
10822 alock.acquire();
10823 if (FAILED(rc)) throw rc;
10824
10825 /* actual lock list update is done in Medium::commitMedia */
10826
10827 rc = diff->i_addBackReference(mData->mUuid);
10828 AssertComRCThrowRC(rc);
10829
10830 /* add a new attachment */
10831 ComObjPtr<MediumAttachment> attachment;
10832 attachment.createObject();
10833 rc = attachment->init(this,
10834 diff,
10835 pAtt->i_getControllerName(),
10836 pAtt->i_getPort(),
10837 pAtt->i_getDevice(),
10838 DeviceType_HardDisk,
10839 true /* aImplicit */,
10840 false /* aPassthrough */,
10841 false /* aTempEject */,
10842 pAtt->i_getNonRotational(),
10843 pAtt->i_getDiscard(),
10844 pAtt->i_getHotPluggable(),
10845 pAtt->i_getBandwidthGroup());
10846 if (FAILED(rc)) throw rc;
10847
10848 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10849 AssertComRCThrowRC(rc);
10850 mMediaData->mAttachments.push_back(attachment);
10851 }
10852 }
10853 catch (HRESULT aRC) { rc = aRC; }
10854
10855 /* unlock all hard disks we locked when there is no VM */
10856 if (!aOnline)
10857 {
10858 ErrorInfoKeeper eik;
10859
10860 HRESULT rc1 = lockedMediaMap->Clear();
10861 AssertComRC(rc1);
10862 }
10863
10864 return rc;
10865}
10866
10867/**
10868 * Deletes implicit differencing hard disks created either by
10869 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10870 *
10871 * Note that to delete hard disks created by #AttachDevice() this method is
10872 * called from #fixupMedia() when the changes are rolled back.
10873 *
10874 * @note Locks this object and the media tree for writing.
10875 */
10876HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10877{
10878 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10879
10880 AutoCaller autoCaller(this);
10881 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10882
10883 AutoMultiWriteLock2 alock(this->lockHandle(),
10884 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10885
10886 /* We absolutely must have backed up state. */
10887 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10888
10889 /* Check if there are any implicitly created diff images. */
10890 bool fImplicitDiffs = false;
10891 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10892 it != mMediaData->mAttachments.end();
10893 ++it)
10894 {
10895 const ComObjPtr<MediumAttachment> &pAtt = *it;
10896 if (pAtt->i_isImplicit())
10897 {
10898 fImplicitDiffs = true;
10899 break;
10900 }
10901 }
10902 /* If there is nothing to do, leave early. This saves lots of image locking
10903 * effort. It also avoids a MachineStateChanged event without real reason.
10904 * This is important e.g. when loading a VM config, because there should be
10905 * no events. Otherwise API clients can become thoroughly confused for
10906 * inaccessible VMs (the code for loading VM configs uses this method for
10907 * cleanup if the config makes no sense), as they take such events as an
10908 * indication that the VM is alive, and they would force the VM config to
10909 * be reread, leading to an endless loop. */
10910 if (!fImplicitDiffs)
10911 return S_OK;
10912
10913 HRESULT rc = S_OK;
10914 MachineState_T oldState = mData->mMachineState;
10915
10916 /* will release the lock before the potentially lengthy operation,
10917 * so protect with the special state (unless already protected) */
10918 if ( oldState != MachineState_Snapshotting
10919 && oldState != MachineState_OnlineSnapshotting
10920 && oldState != MachineState_LiveSnapshotting
10921 && oldState != MachineState_RestoringSnapshot
10922 && oldState != MachineState_DeletingSnapshot
10923 && oldState != MachineState_DeletingSnapshotOnline
10924 && oldState != MachineState_DeletingSnapshotPaused
10925 )
10926 i_setMachineState(MachineState_SettingUp);
10927
10928 // use appropriate locked media map (online or offline)
10929 MediumLockListMap lockedMediaOffline;
10930 MediumLockListMap *lockedMediaMap;
10931 if (aOnline)
10932 lockedMediaMap = &mData->mSession.mLockedMedia;
10933 else
10934 lockedMediaMap = &lockedMediaOffline;
10935
10936 try
10937 {
10938 if (!aOnline)
10939 {
10940 /* lock all attached hard disks early to detect "in use"
10941 * situations before deleting actual diffs */
10942 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10943 it != mMediaData->mAttachments.end();
10944 ++it)
10945 {
10946 MediumAttachment* pAtt = *it;
10947 if (pAtt->i_getType() == DeviceType_HardDisk)
10948 {
10949 Medium* pMedium = pAtt->i_getMedium();
10950 Assert(pMedium);
10951
10952 MediumLockList *pMediumLockList(new MediumLockList());
10953 alock.release();
10954 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10955 false /* fMediumLockWrite */,
10956 false /* fMediumLockWriteAll */,
10957 NULL,
10958 *pMediumLockList);
10959 alock.acquire();
10960
10961 if (FAILED(rc))
10962 {
10963 delete pMediumLockList;
10964 throw rc;
10965 }
10966
10967 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10968 if (FAILED(rc))
10969 throw rc;
10970 }
10971 }
10972
10973 if (FAILED(rc))
10974 throw rc;
10975 } // end of offline
10976
10977 /* Lock lists are now up to date and include implicitly created media */
10978
10979 /* Go through remembered attachments and delete all implicitly created
10980 * diffs and fix up the attachment information */
10981 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10982 MediaData::AttachmentList implicitAtts;
10983 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10984 it != mMediaData->mAttachments.end();
10985 ++it)
10986 {
10987 ComObjPtr<MediumAttachment> pAtt = *it;
10988 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10989 if (pMedium.isNull())
10990 continue;
10991
10992 // Implicit attachments go on the list for deletion and back references are removed.
10993 if (pAtt->i_isImplicit())
10994 {
10995 /* Deassociate and mark for deletion */
10996 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10997 rc = pMedium->i_removeBackReference(mData->mUuid);
10998 if (FAILED(rc))
10999 throw rc;
11000 implicitAtts.push_back(pAtt);
11001 continue;
11002 }
11003
11004 /* Was this medium attached before? */
11005 if (!i_findAttachment(oldAtts, pMedium))
11006 {
11007 /* no: de-associate */
11008 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11009 rc = pMedium->i_removeBackReference(mData->mUuid);
11010 if (FAILED(rc))
11011 throw rc;
11012 continue;
11013 }
11014 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11015 }
11016
11017 /* If there are implicit attachments to delete, throw away the lock
11018 * map contents (which will unlock all media) since the medium
11019 * attachments will be rolled back. Below we need to completely
11020 * recreate the lock map anyway since it is infinitely complex to
11021 * do this incrementally (would need reconstructing each attachment
11022 * change, which would be extremely hairy). */
11023 if (implicitAtts.size() != 0)
11024 {
11025 ErrorInfoKeeper eik;
11026
11027 HRESULT rc1 = lockedMediaMap->Clear();
11028 AssertComRC(rc1);
11029 }
11030
11031 /* rollback hard disk changes */
11032 mMediaData.rollback();
11033
11034 MultiResult mrc(S_OK);
11035
11036 // Delete unused implicit diffs.
11037 if (implicitAtts.size() != 0)
11038 {
11039 alock.release();
11040
11041 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11042 {
11043 // Remove medium associated with this attachment.
11044 ComObjPtr<MediumAttachment> pAtt = *it;
11045 Assert(pAtt);
11046 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11047 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11048 Assert(pMedium);
11049
11050 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11051 // continue on delete failure, just collect error messages
11052 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11053 pMedium->i_getLocationFull().c_str() ));
11054 mrc = rc;
11055 }
11056 // Clear the list of deleted implicit attachments now, while not
11057 // holding the lock, as it will ultimately trigger Medium::uninit()
11058 // calls which assume that the media tree lock isn't held.
11059 implicitAtts.clear();
11060
11061 alock.acquire();
11062
11063 /* if there is a VM recreate media lock map as mentioned above,
11064 * otherwise it is a waste of time and we leave things unlocked */
11065 if (aOnline)
11066 {
11067 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11068 /* must never be NULL, but better safe than sorry */
11069 if (!pMachine.isNull())
11070 {
11071 alock.release();
11072 rc = mData->mSession.mMachine->i_lockMedia();
11073 alock.acquire();
11074 if (FAILED(rc))
11075 throw rc;
11076 }
11077 }
11078 }
11079 }
11080 catch (HRESULT aRC) {rc = aRC;}
11081
11082 if (mData->mMachineState == MachineState_SettingUp)
11083 i_setMachineState(oldState);
11084
11085 /* unlock all hard disks we locked when there is no VM */
11086 if (!aOnline)
11087 {
11088 ErrorInfoKeeper eik;
11089
11090 HRESULT rc1 = lockedMediaMap->Clear();
11091 AssertComRC(rc1);
11092 }
11093
11094 return rc;
11095}
11096
11097
11098/**
11099 * Looks through the given list of media attachments for one with the given parameters
11100 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11101 * can be searched as well if needed.
11102 *
11103 * @param list
11104 * @param aControllerName
11105 * @param aControllerPort
11106 * @param aDevice
11107 * @return
11108 */
11109MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11110 IN_BSTR aControllerName,
11111 LONG aControllerPort,
11112 LONG aDevice)
11113{
11114 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11115 {
11116 MediumAttachment *pAttach = *it;
11117 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11118 return pAttach;
11119 }
11120
11121 return NULL;
11122}
11123
11124/**
11125 * Looks through the given list of media attachments for one with the given parameters
11126 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11127 * can be searched as well if needed.
11128 *
11129 * @param list
11130 * @param aControllerName
11131 * @param aControllerPort
11132 * @param aDevice
11133 * @return
11134 */
11135MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11136 ComObjPtr<Medium> pMedium)
11137{
11138 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11139 {
11140 MediumAttachment *pAttach = *it;
11141 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11142 if (pMediumThis == pMedium)
11143 return pAttach;
11144 }
11145
11146 return NULL;
11147}
11148
11149/**
11150 * Looks through the given list of media attachments for one with the given parameters
11151 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11152 * can be searched as well if needed.
11153 *
11154 * @param list
11155 * @param aControllerName
11156 * @param aControllerPort
11157 * @param aDevice
11158 * @return
11159 */
11160MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11161 Guid &id)
11162{
11163 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11164 {
11165 MediumAttachment *pAttach = *it;
11166 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11167 if (pMediumThis->i_getId() == id)
11168 return pAttach;
11169 }
11170
11171 return NULL;
11172}
11173
11174/**
11175 * Main implementation for Machine::DetachDevice. This also gets called
11176 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11177 *
11178 * @param pAttach Medium attachment to detach.
11179 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11180 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11181 * SnapshotMachine, and this must be its snapshot.
11182 * @return
11183 */
11184HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11185 AutoWriteLock &writeLock,
11186 Snapshot *pSnapshot)
11187{
11188 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11189 DeviceType_T mediumType = pAttach->i_getType();
11190
11191 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11192
11193 if (pAttach->i_isImplicit())
11194 {
11195 /* attempt to implicitly delete the implicitly created diff */
11196
11197 /// @todo move the implicit flag from MediumAttachment to Medium
11198 /// and forbid any hard disk operation when it is implicit. Or maybe
11199 /// a special media state for it to make it even more simple.
11200
11201 Assert(mMediaData.isBackedUp());
11202
11203 /* will release the lock before the potentially lengthy operation, so
11204 * protect with the special state */
11205 MachineState_T oldState = mData->mMachineState;
11206 i_setMachineState(MachineState_SettingUp);
11207
11208 writeLock.release();
11209
11210 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11211 true /*aWait*/);
11212
11213 writeLock.acquire();
11214
11215 i_setMachineState(oldState);
11216
11217 if (FAILED(rc)) return rc;
11218 }
11219
11220 i_setModified(IsModified_Storage);
11221 mMediaData.backup();
11222 mMediaData->mAttachments.remove(pAttach);
11223
11224 if (!oldmedium.isNull())
11225 {
11226 // if this is from a snapshot, do not defer detachment to commitMedia()
11227 if (pSnapshot)
11228 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11229 // else if non-hard disk media, do not defer detachment to commitMedia() either
11230 else if (mediumType != DeviceType_HardDisk)
11231 oldmedium->i_removeBackReference(mData->mUuid);
11232 }
11233
11234 return S_OK;
11235}
11236
11237/**
11238 * Goes thru all media of the given list and
11239 *
11240 * 1) calls i_detachDevice() on each of them for this machine and
11241 * 2) adds all Medium objects found in the process to the given list,
11242 * depending on cleanupMode.
11243 *
11244 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11245 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11246 * media to the list.
11247 *
11248 * This gets called from Machine::Unregister, both for the actual Machine and
11249 * the SnapshotMachine objects that might be found in the snapshots.
11250 *
11251 * Requires caller and locking. The machine lock must be passed in because it
11252 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11253 *
11254 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11255 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11256 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11257 * Full, then all media get added;
11258 * otherwise no media get added.
11259 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11260 * @return
11261 */
11262HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11263 Snapshot *pSnapshot,
11264 CleanupMode_T cleanupMode,
11265 MediaList &llMedia)
11266{
11267 Assert(isWriteLockOnCurrentThread());
11268
11269 HRESULT rc;
11270
11271 // make a temporary list because i_detachDevice invalidates iterators into
11272 // mMediaData->mAttachments
11273 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11274
11275 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11276 {
11277 ComObjPtr<MediumAttachment> &pAttach = *it;
11278 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11279
11280 if (!pMedium.isNull())
11281 {
11282 AutoCaller mac(pMedium);
11283 if (FAILED(mac.rc())) return mac.rc();
11284 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11285 DeviceType_T devType = pMedium->i_getDeviceType();
11286 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11287 && devType == DeviceType_HardDisk)
11288 || (cleanupMode == CleanupMode_Full)
11289 )
11290 {
11291 llMedia.push_back(pMedium);
11292 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11293 /* Not allowed to keep this lock as below we need the parent
11294 * medium lock, and the lock order is parent to child. */
11295 lock.release();
11296 /*
11297 * Search for medias which are not attached to any machine, but
11298 * in the chain to an attached disk. Mediums are only consided
11299 * if they are:
11300 * - have only one child
11301 * - no references to any machines
11302 * - are of normal medium type
11303 */
11304 while (!pParent.isNull())
11305 {
11306 AutoCaller mac1(pParent);
11307 if (FAILED(mac1.rc())) return mac1.rc();
11308 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11309 if (pParent->i_getChildren().size() == 1)
11310 {
11311 if ( pParent->i_getMachineBackRefCount() == 0
11312 && pParent->i_getType() == MediumType_Normal
11313 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11314 llMedia.push_back(pParent);
11315 }
11316 else
11317 break;
11318 pParent = pParent->i_getParent();
11319 }
11320 }
11321 }
11322
11323 // real machine: then we need to use the proper method
11324 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11325
11326 if (FAILED(rc))
11327 return rc;
11328 }
11329
11330 return S_OK;
11331}
11332
11333/**
11334 * Perform deferred hard disk detachments.
11335 *
11336 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11337 * backed up).
11338 *
11339 * If @a aOnline is @c true then this method will also unlock the old hard disks
11340 * for which the new implicit diffs were created and will lock these new diffs for
11341 * writing.
11342 *
11343 * @param aOnline Whether the VM was online prior to this operation.
11344 *
11345 * @note Locks this object for writing!
11346 */
11347void Machine::i_commitMedia(bool aOnline /*= false*/)
11348{
11349 AutoCaller autoCaller(this);
11350 AssertComRCReturnVoid(autoCaller.rc());
11351
11352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11353
11354 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11355
11356 HRESULT rc = S_OK;
11357
11358 /* no attach/detach operations -- nothing to do */
11359 if (!mMediaData.isBackedUp())
11360 return;
11361
11362 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11363 bool fMediaNeedsLocking = false;
11364
11365 /* enumerate new attachments */
11366 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11367 it != mMediaData->mAttachments.end();
11368 ++it)
11369 {
11370 MediumAttachment *pAttach = *it;
11371
11372 pAttach->i_commit();
11373
11374 Medium* pMedium = pAttach->i_getMedium();
11375 bool fImplicit = pAttach->i_isImplicit();
11376
11377 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11378 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11379 fImplicit));
11380
11381 /** @todo convert all this Machine-based voodoo to MediumAttachment
11382 * based commit logic. */
11383 if (fImplicit)
11384 {
11385 /* convert implicit attachment to normal */
11386 pAttach->i_setImplicit(false);
11387
11388 if ( aOnline
11389 && pMedium
11390 && pAttach->i_getType() == DeviceType_HardDisk
11391 )
11392 {
11393 /* update the appropriate lock list */
11394 MediumLockList *pMediumLockList;
11395 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11396 AssertComRC(rc);
11397 if (pMediumLockList)
11398 {
11399 /* unlock if there's a need to change the locking */
11400 if (!fMediaNeedsLocking)
11401 {
11402 rc = mData->mSession.mLockedMedia.Unlock();
11403 AssertComRC(rc);
11404 fMediaNeedsLocking = true;
11405 }
11406 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11407 AssertComRC(rc);
11408 rc = pMediumLockList->Append(pMedium, true);
11409 AssertComRC(rc);
11410 }
11411 }
11412
11413 continue;
11414 }
11415
11416 if (pMedium)
11417 {
11418 /* was this medium attached before? */
11419 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11420 {
11421 MediumAttachment *pOldAttach = *oldIt;
11422 if (pOldAttach->i_getMedium() == pMedium)
11423 {
11424 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11425
11426 /* yes: remove from old to avoid de-association */
11427 oldAtts.erase(oldIt);
11428 break;
11429 }
11430 }
11431 }
11432 }
11433
11434 /* enumerate remaining old attachments and de-associate from the
11435 * current machine state */
11436 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11437 {
11438 MediumAttachment *pAttach = *it;
11439 Medium* pMedium = pAttach->i_getMedium();
11440
11441 /* Detach only hard disks, since DVD/floppy media is detached
11442 * instantly in MountMedium. */
11443 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11444 {
11445 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11446
11447 /* now de-associate from the current machine state */
11448 rc = pMedium->i_removeBackReference(mData->mUuid);
11449 AssertComRC(rc);
11450
11451 if (aOnline)
11452 {
11453 /* unlock since medium is not used anymore */
11454 MediumLockList *pMediumLockList;
11455 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11456 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11457 {
11458 /* this happens for online snapshots, there the attachment
11459 * is changing, but only to a diff image created under
11460 * the old one, so there is no separate lock list */
11461 Assert(!pMediumLockList);
11462 }
11463 else
11464 {
11465 AssertComRC(rc);
11466 if (pMediumLockList)
11467 {
11468 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11469 AssertComRC(rc);
11470 }
11471 }
11472 }
11473 }
11474 }
11475
11476 /* take media locks again so that the locking state is consistent */
11477 if (fMediaNeedsLocking)
11478 {
11479 Assert(aOnline);
11480 rc = mData->mSession.mLockedMedia.Lock();
11481 AssertComRC(rc);
11482 }
11483
11484 /* commit the hard disk changes */
11485 mMediaData.commit();
11486
11487 if (i_isSessionMachine())
11488 {
11489 /*
11490 * Update the parent machine to point to the new owner.
11491 * This is necessary because the stored parent will point to the
11492 * session machine otherwise and cause crashes or errors later
11493 * when the session machine gets invalid.
11494 */
11495 /** @todo Change the MediumAttachment class to behave like any other
11496 * class in this regard by creating peer MediumAttachment
11497 * objects for session machines and share the data with the peer
11498 * machine.
11499 */
11500 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11501 it != mMediaData->mAttachments.end();
11502 ++it)
11503 (*it)->i_updateParentMachine(mPeer);
11504
11505 /* attach new data to the primary machine and reshare it */
11506 mPeer->mMediaData.attach(mMediaData);
11507 }
11508
11509 return;
11510}
11511
11512/**
11513 * Perform deferred deletion of implicitly created diffs.
11514 *
11515 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11516 * backed up).
11517 *
11518 * @note Locks this object for writing!
11519 */
11520void Machine::i_rollbackMedia()
11521{
11522 AutoCaller autoCaller(this);
11523 AssertComRCReturnVoid(autoCaller.rc());
11524
11525 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11526 LogFlowThisFunc(("Entering rollbackMedia\n"));
11527
11528 HRESULT rc = S_OK;
11529
11530 /* no attach/detach operations -- nothing to do */
11531 if (!mMediaData.isBackedUp())
11532 return;
11533
11534 /* enumerate new attachments */
11535 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11536 it != mMediaData->mAttachments.end();
11537 ++it)
11538 {
11539 MediumAttachment *pAttach = *it;
11540 /* Fix up the backrefs for DVD/floppy media. */
11541 if (pAttach->i_getType() != DeviceType_HardDisk)
11542 {
11543 Medium* pMedium = pAttach->i_getMedium();
11544 if (pMedium)
11545 {
11546 rc = pMedium->i_removeBackReference(mData->mUuid);
11547 AssertComRC(rc);
11548 }
11549 }
11550
11551 (*it)->i_rollback();
11552
11553 pAttach = *it;
11554 /* Fix up the backrefs for DVD/floppy media. */
11555 if (pAttach->i_getType() != DeviceType_HardDisk)
11556 {
11557 Medium* pMedium = pAttach->i_getMedium();
11558 if (pMedium)
11559 {
11560 rc = pMedium->i_addBackReference(mData->mUuid);
11561 AssertComRC(rc);
11562 }
11563 }
11564 }
11565
11566 /** @todo convert all this Machine-based voodoo to MediumAttachment
11567 * based rollback logic. */
11568 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11569
11570 return;
11571}
11572
11573/**
11574 * Returns true if the settings file is located in the directory named exactly
11575 * as the machine; this means, among other things, that the machine directory
11576 * should be auto-renamed.
11577 *
11578 * @param aSettingsDir if not NULL, the full machine settings file directory
11579 * name will be assigned there.
11580 *
11581 * @note Doesn't lock anything.
11582 * @note Not thread safe (must be called from this object's lock).
11583 */
11584bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11585{
11586 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11587 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11588 if (aSettingsDir)
11589 *aSettingsDir = strMachineDirName;
11590 strMachineDirName.stripPath(); // vmname
11591 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11592 strConfigFileOnly.stripPath() // vmname.vbox
11593 .stripSuffix(); // vmname
11594 /** @todo hack, make somehow use of ComposeMachineFilename */
11595 if (mUserData->s.fDirectoryIncludesUUID)
11596 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11597
11598 AssertReturn(!strMachineDirName.isEmpty(), false);
11599 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11600
11601 return strMachineDirName == strConfigFileOnly;
11602}
11603
11604/**
11605 * Discards all changes to machine settings.
11606 *
11607 * @param aNotify Whether to notify the direct session about changes or not.
11608 *
11609 * @note Locks objects for writing!
11610 */
11611void Machine::i_rollback(bool aNotify)
11612{
11613 AutoCaller autoCaller(this);
11614 AssertComRCReturn(autoCaller.rc(), (void)0);
11615
11616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11617
11618 if (!mStorageControllers.isNull())
11619 {
11620 if (mStorageControllers.isBackedUp())
11621 {
11622 /* unitialize all new devices (absent in the backed up list). */
11623 StorageControllerList::const_iterator it = mStorageControllers->begin();
11624 StorageControllerList *backedList = mStorageControllers.backedUpData();
11625 while (it != mStorageControllers->end())
11626 {
11627 if ( std::find(backedList->begin(), backedList->end(), *it)
11628 == backedList->end()
11629 )
11630 {
11631 (*it)->uninit();
11632 }
11633 ++it;
11634 }
11635
11636 /* restore the list */
11637 mStorageControllers.rollback();
11638 }
11639
11640 /* rollback any changes to devices after restoring the list */
11641 if (mData->flModifications & IsModified_Storage)
11642 {
11643 StorageControllerList::const_iterator it = mStorageControllers->begin();
11644 while (it != mStorageControllers->end())
11645 {
11646 (*it)->i_rollback();
11647 ++it;
11648 }
11649 }
11650 }
11651
11652 if (!mUSBControllers.isNull())
11653 {
11654 if (mUSBControllers.isBackedUp())
11655 {
11656 /* unitialize all new devices (absent in the backed up list). */
11657 USBControllerList::const_iterator it = mUSBControllers->begin();
11658 USBControllerList *backedList = mUSBControllers.backedUpData();
11659 while (it != mUSBControllers->end())
11660 {
11661 if ( std::find(backedList->begin(), backedList->end(), *it)
11662 == backedList->end()
11663 )
11664 {
11665 (*it)->uninit();
11666 }
11667 ++it;
11668 }
11669
11670 /* restore the list */
11671 mUSBControllers.rollback();
11672 }
11673
11674 /* rollback any changes to devices after restoring the list */
11675 if (mData->flModifications & IsModified_USB)
11676 {
11677 USBControllerList::const_iterator it = mUSBControllers->begin();
11678 while (it != mUSBControllers->end())
11679 {
11680 (*it)->i_rollback();
11681 ++it;
11682 }
11683 }
11684 }
11685
11686 mUserData.rollback();
11687
11688 mHWData.rollback();
11689
11690 if (mData->flModifications & IsModified_Storage)
11691 i_rollbackMedia();
11692
11693 if (mBIOSSettings)
11694 mBIOSSettings->i_rollback();
11695
11696 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11697 mVRDEServer->i_rollback();
11698
11699 if (mAudioAdapter)
11700 mAudioAdapter->i_rollback();
11701
11702 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11703 mUSBDeviceFilters->i_rollback();
11704
11705 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11706 mBandwidthControl->i_rollback();
11707
11708 if (!mHWData.isNull())
11709 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11710 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11711 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11712 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11713
11714 if (mData->flModifications & IsModified_NetworkAdapters)
11715 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11716 if ( mNetworkAdapters[slot]
11717 && mNetworkAdapters[slot]->i_isModified())
11718 {
11719 mNetworkAdapters[slot]->i_rollback();
11720 networkAdapters[slot] = mNetworkAdapters[slot];
11721 }
11722
11723 if (mData->flModifications & IsModified_SerialPorts)
11724 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11725 if ( mSerialPorts[slot]
11726 && mSerialPorts[slot]->i_isModified())
11727 {
11728 mSerialPorts[slot]->i_rollback();
11729 serialPorts[slot] = mSerialPorts[slot];
11730 }
11731
11732 if (mData->flModifications & IsModified_ParallelPorts)
11733 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11734 if ( mParallelPorts[slot]
11735 && mParallelPorts[slot]->i_isModified())
11736 {
11737 mParallelPorts[slot]->i_rollback();
11738 parallelPorts[slot] = mParallelPorts[slot];
11739 }
11740
11741 if (aNotify)
11742 {
11743 /* inform the direct session about changes */
11744
11745 ComObjPtr<Machine> that = this;
11746 uint32_t flModifications = mData->flModifications;
11747 alock.release();
11748
11749 if (flModifications & IsModified_SharedFolders)
11750 that->i_onSharedFolderChange();
11751
11752 if (flModifications & IsModified_VRDEServer)
11753 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11754 if (flModifications & IsModified_USB)
11755 that->i_onUSBControllerChange();
11756
11757 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11758 if (networkAdapters[slot])
11759 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11760 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11761 if (serialPorts[slot])
11762 that->i_onSerialPortChange(serialPorts[slot]);
11763 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11764 if (parallelPorts[slot])
11765 that->i_onParallelPortChange(parallelPorts[slot]);
11766
11767 if (flModifications & IsModified_Storage)
11768 that->i_onStorageControllerChange();
11769
11770#if 0
11771 if (flModifications & IsModified_BandwidthControl)
11772 that->onBandwidthControlChange();
11773#endif
11774 }
11775}
11776
11777/**
11778 * Commits all the changes to machine settings.
11779 *
11780 * Note that this operation is supposed to never fail.
11781 *
11782 * @note Locks this object and children for writing.
11783 */
11784void Machine::i_commit()
11785{
11786 AutoCaller autoCaller(this);
11787 AssertComRCReturnVoid(autoCaller.rc());
11788
11789 AutoCaller peerCaller(mPeer);
11790 AssertComRCReturnVoid(peerCaller.rc());
11791
11792 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11793
11794 /*
11795 * use safe commit to ensure Snapshot machines (that share mUserData)
11796 * will still refer to a valid memory location
11797 */
11798 mUserData.commitCopy();
11799
11800 mHWData.commit();
11801
11802 if (mMediaData.isBackedUp())
11803 i_commitMedia(Global::IsOnline(mData->mMachineState));
11804
11805 mBIOSSettings->i_commit();
11806 mVRDEServer->i_commit();
11807 mAudioAdapter->i_commit();
11808 mUSBDeviceFilters->i_commit();
11809 mBandwidthControl->i_commit();
11810
11811 /* Since mNetworkAdapters is a list which might have been changed (resized)
11812 * without using the Backupable<> template we need to handle the copying
11813 * of the list entries manually, including the creation of peers for the
11814 * new objects. */
11815 bool commitNetworkAdapters = false;
11816 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11817 if (mPeer)
11818 {
11819 /* commit everything, even the ones which will go away */
11820 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11821 mNetworkAdapters[slot]->i_commit();
11822 /* copy over the new entries, creating a peer and uninit the original */
11823 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11824 for (size_t slot = 0; slot < newSize; slot++)
11825 {
11826 /* look if this adapter has a peer device */
11827 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11828 if (!peer)
11829 {
11830 /* no peer means the adapter is a newly created one;
11831 * create a peer owning data this data share it with */
11832 peer.createObject();
11833 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11834 }
11835 mPeer->mNetworkAdapters[slot] = peer;
11836 }
11837 /* uninit any no longer needed network adapters */
11838 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11839 mNetworkAdapters[slot]->uninit();
11840 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11841 {
11842 if (mPeer->mNetworkAdapters[slot])
11843 mPeer->mNetworkAdapters[slot]->uninit();
11844 }
11845 /* Keep the original network adapter count until this point, so that
11846 * discarding a chipset type change will not lose settings. */
11847 mNetworkAdapters.resize(newSize);
11848 mPeer->mNetworkAdapters.resize(newSize);
11849 }
11850 else
11851 {
11852 /* we have no peer (our parent is the newly created machine);
11853 * just commit changes to the network adapters */
11854 commitNetworkAdapters = true;
11855 }
11856 if (commitNetworkAdapters)
11857 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11858 mNetworkAdapters[slot]->i_commit();
11859
11860 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11861 mSerialPorts[slot]->i_commit();
11862 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11863 mParallelPorts[slot]->i_commit();
11864
11865 bool commitStorageControllers = false;
11866
11867 if (mStorageControllers.isBackedUp())
11868 {
11869 mStorageControllers.commit();
11870
11871 if (mPeer)
11872 {
11873 /* Commit all changes to new controllers (this will reshare data with
11874 * peers for those who have peers) */
11875 StorageControllerList *newList = new StorageControllerList();
11876 StorageControllerList::const_iterator it = mStorageControllers->begin();
11877 while (it != mStorageControllers->end())
11878 {
11879 (*it)->i_commit();
11880
11881 /* look if this controller has a peer device */
11882 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11883 if (!peer)
11884 {
11885 /* no peer means the device is a newly created one;
11886 * create a peer owning data this device share it with */
11887 peer.createObject();
11888 peer->init(mPeer, *it, true /* aReshare */);
11889 }
11890 else
11891 {
11892 /* remove peer from the old list */
11893 mPeer->mStorageControllers->remove(peer);
11894 }
11895 /* and add it to the new list */
11896 newList->push_back(peer);
11897
11898 ++it;
11899 }
11900
11901 /* uninit old peer's controllers that are left */
11902 it = mPeer->mStorageControllers->begin();
11903 while (it != mPeer->mStorageControllers->end())
11904 {
11905 (*it)->uninit();
11906 ++it;
11907 }
11908
11909 /* attach new list of controllers to our peer */
11910 mPeer->mStorageControllers.attach(newList);
11911 }
11912 else
11913 {
11914 /* we have no peer (our parent is the newly created machine);
11915 * just commit changes to devices */
11916 commitStorageControllers = true;
11917 }
11918 }
11919 else
11920 {
11921 /* the list of controllers itself is not changed,
11922 * just commit changes to controllers themselves */
11923 commitStorageControllers = true;
11924 }
11925
11926 if (commitStorageControllers)
11927 {
11928 StorageControllerList::const_iterator it = mStorageControllers->begin();
11929 while (it != mStorageControllers->end())
11930 {
11931 (*it)->i_commit();
11932 ++it;
11933 }
11934 }
11935
11936 bool commitUSBControllers = false;
11937
11938 if (mUSBControllers.isBackedUp())
11939 {
11940 mUSBControllers.commit();
11941
11942 if (mPeer)
11943 {
11944 /* Commit all changes to new controllers (this will reshare data with
11945 * peers for those who have peers) */
11946 USBControllerList *newList = new USBControllerList();
11947 USBControllerList::const_iterator it = mUSBControllers->begin();
11948 while (it != mUSBControllers->end())
11949 {
11950 (*it)->i_commit();
11951
11952 /* look if this controller has a peer device */
11953 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11954 if (!peer)
11955 {
11956 /* no peer means the device is a newly created one;
11957 * create a peer owning data this device share it with */
11958 peer.createObject();
11959 peer->init(mPeer, *it, true /* aReshare */);
11960 }
11961 else
11962 {
11963 /* remove peer from the old list */
11964 mPeer->mUSBControllers->remove(peer);
11965 }
11966 /* and add it to the new list */
11967 newList->push_back(peer);
11968
11969 ++it;
11970 }
11971
11972 /* uninit old peer's controllers that are left */
11973 it = mPeer->mUSBControllers->begin();
11974 while (it != mPeer->mUSBControllers->end())
11975 {
11976 (*it)->uninit();
11977 ++it;
11978 }
11979
11980 /* attach new list of controllers to our peer */
11981 mPeer->mUSBControllers.attach(newList);
11982 }
11983 else
11984 {
11985 /* we have no peer (our parent is the newly created machine);
11986 * just commit changes to devices */
11987 commitUSBControllers = true;
11988 }
11989 }
11990 else
11991 {
11992 /* the list of controllers itself is not changed,
11993 * just commit changes to controllers themselves */
11994 commitUSBControllers = true;
11995 }
11996
11997 if (commitUSBControllers)
11998 {
11999 USBControllerList::const_iterator it = mUSBControllers->begin();
12000 while (it != mUSBControllers->end())
12001 {
12002 (*it)->i_commit();
12003 ++it;
12004 }
12005 }
12006
12007 if (i_isSessionMachine())
12008 {
12009 /* attach new data to the primary machine and reshare it */
12010 mPeer->mUserData.attach(mUserData);
12011 mPeer->mHWData.attach(mHWData);
12012 /* mMediaData is reshared by fixupMedia */
12013 // mPeer->mMediaData.attach(mMediaData);
12014 Assert(mPeer->mMediaData.data() == mMediaData.data());
12015 }
12016}
12017
12018/**
12019 * Copies all the hardware data from the given machine.
12020 *
12021 * Currently, only called when the VM is being restored from a snapshot. In
12022 * particular, this implies that the VM is not running during this method's
12023 * call.
12024 *
12025 * @note This method must be called from under this object's lock.
12026 *
12027 * @note This method doesn't call #commit(), so all data remains backed up and
12028 * unsaved.
12029 */
12030void Machine::i_copyFrom(Machine *aThat)
12031{
12032 AssertReturnVoid(!i_isSnapshotMachine());
12033 AssertReturnVoid(aThat->i_isSnapshotMachine());
12034
12035 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12036
12037 mHWData.assignCopy(aThat->mHWData);
12038
12039 // create copies of all shared folders (mHWData after attaching a copy
12040 // contains just references to original objects)
12041 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12042 it != mHWData->mSharedFolders.end();
12043 ++it)
12044 {
12045 ComObjPtr<SharedFolder> folder;
12046 folder.createObject();
12047 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12048 AssertComRC(rc);
12049 *it = folder;
12050 }
12051
12052 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12053 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12054 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12055 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12056 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12057
12058 /* create private copies of all controllers */
12059 mStorageControllers.backup();
12060 mStorageControllers->clear();
12061 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12062 it != aThat->mStorageControllers->end();
12063 ++it)
12064 {
12065 ComObjPtr<StorageController> ctrl;
12066 ctrl.createObject();
12067 ctrl->initCopy(this, *it);
12068 mStorageControllers->push_back(ctrl);
12069 }
12070
12071 /* create private copies of all USB controllers */
12072 mUSBControllers.backup();
12073 mUSBControllers->clear();
12074 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12075 it != aThat->mUSBControllers->end();
12076 ++it)
12077 {
12078 ComObjPtr<USBController> ctrl;
12079 ctrl.createObject();
12080 ctrl->initCopy(this, *it);
12081 mUSBControllers->push_back(ctrl);
12082 }
12083
12084 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12085 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12086 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12087 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12088 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12089 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12090 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12091}
12092
12093/**
12094 * Returns whether the given storage controller is hotplug capable.
12095 *
12096 * @returns true if the controller supports hotplugging
12097 * false otherwise.
12098 * @param enmCtrlType The controller type to check for.
12099 */
12100bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12101{
12102 ComPtr<ISystemProperties> systemProperties;
12103 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12104 if (FAILED(rc))
12105 return false;
12106
12107 BOOL aHotplugCapable = FALSE;
12108 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12109
12110 return RT_BOOL(aHotplugCapable);
12111}
12112
12113#ifdef VBOX_WITH_RESOURCE_USAGE_API
12114
12115void Machine::i_getDiskList(MediaList &list)
12116{
12117 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12118 it != mMediaData->mAttachments.end();
12119 ++it)
12120 {
12121 MediumAttachment* pAttach = *it;
12122 /* just in case */
12123 AssertStmt(pAttach, continue);
12124
12125 AutoCaller localAutoCallerA(pAttach);
12126 if (FAILED(localAutoCallerA.rc())) continue;
12127
12128 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12129
12130 if (pAttach->i_getType() == DeviceType_HardDisk)
12131 list.push_back(pAttach->i_getMedium());
12132 }
12133}
12134
12135void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12136{
12137 AssertReturnVoid(isWriteLockOnCurrentThread());
12138 AssertPtrReturnVoid(aCollector);
12139
12140 pm::CollectorHAL *hal = aCollector->getHAL();
12141 /* Create sub metrics */
12142 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12143 "Percentage of processor time spent in user mode by the VM process.");
12144 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12145 "Percentage of processor time spent in kernel mode by the VM process.");
12146 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12147 "Size of resident portion of VM process in memory.");
12148 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12149 "Actual size of all VM disks combined.");
12150 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12151 "Network receive rate.");
12152 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12153 "Network transmit rate.");
12154 /* Create and register base metrics */
12155 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12156 cpuLoadUser, cpuLoadKernel);
12157 aCollector->registerBaseMetric(cpuLoad);
12158 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12159 ramUsageUsed);
12160 aCollector->registerBaseMetric(ramUsage);
12161 MediaList disks;
12162 i_getDiskList(disks);
12163 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12164 diskUsageUsed);
12165 aCollector->registerBaseMetric(diskUsage);
12166
12167 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12168 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12169 new pm::AggregateAvg()));
12170 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12171 new pm::AggregateMin()));
12172 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12173 new pm::AggregateMax()));
12174 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12175 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12176 new pm::AggregateAvg()));
12177 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12178 new pm::AggregateMin()));
12179 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12180 new pm::AggregateMax()));
12181
12182 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12183 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12184 new pm::AggregateAvg()));
12185 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12186 new pm::AggregateMin()));
12187 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12188 new pm::AggregateMax()));
12189
12190 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12191 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12192 new pm::AggregateAvg()));
12193 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12194 new pm::AggregateMin()));
12195 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12196 new pm::AggregateMax()));
12197
12198
12199 /* Guest metrics collector */
12200 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12201 aCollector->registerGuest(mCollectorGuest);
12202 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12203 this, __PRETTY_FUNCTION__, mCollectorGuest));
12204
12205 /* Create sub metrics */
12206 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12207 "Percentage of processor time spent in user mode as seen by the guest.");
12208 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12209 "Percentage of processor time spent in kernel mode as seen by the guest.");
12210 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12211 "Percentage of processor time spent idling as seen by the guest.");
12212
12213 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12214 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12215 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12216 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12217 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12218 pm::SubMetric *guestMemCache = new pm::SubMetric(
12219 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12220
12221 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12222 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12223
12224 /* Create and register base metrics */
12225 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12226 machineNetRx, machineNetTx);
12227 aCollector->registerBaseMetric(machineNetRate);
12228
12229 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12230 guestLoadUser, guestLoadKernel, guestLoadIdle);
12231 aCollector->registerBaseMetric(guestCpuLoad);
12232
12233 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12234 guestMemTotal, guestMemFree,
12235 guestMemBalloon, guestMemShared,
12236 guestMemCache, guestPagedTotal);
12237 aCollector->registerBaseMetric(guestCpuMem);
12238
12239 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12240 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12245 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12248
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12253
12254 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12258
12259 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12260 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12263
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12268
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12273
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12278
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12283
12284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12288
12289 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12293}
12294
12295void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12296{
12297 AssertReturnVoid(isWriteLockOnCurrentThread());
12298
12299 if (aCollector)
12300 {
12301 aCollector->unregisterMetricsFor(aMachine);
12302 aCollector->unregisterBaseMetricsFor(aMachine);
12303 }
12304}
12305
12306#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12307
12308
12309////////////////////////////////////////////////////////////////////////////////
12310
12311DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12312
12313HRESULT SessionMachine::FinalConstruct()
12314{
12315 LogFlowThisFunc(("\n"));
12316
12317 mClientToken = NULL;
12318
12319 return BaseFinalConstruct();
12320}
12321
12322void SessionMachine::FinalRelease()
12323{
12324 LogFlowThisFunc(("\n"));
12325
12326 Assert(!mClientToken);
12327 /* paranoia, should not hang around any more */
12328 if (mClientToken)
12329 {
12330 delete mClientToken;
12331 mClientToken = NULL;
12332 }
12333
12334 uninit(Uninit::Unexpected);
12335
12336 BaseFinalRelease();
12337}
12338
12339/**
12340 * @note Must be called only by Machine::LockMachine() from its own write lock.
12341 */
12342HRESULT SessionMachine::init(Machine *aMachine)
12343{
12344 LogFlowThisFuncEnter();
12345 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12346
12347 AssertReturn(aMachine, E_INVALIDARG);
12348
12349 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12350
12351 /* Enclose the state transition NotReady->InInit->Ready */
12352 AutoInitSpan autoInitSpan(this);
12353 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12354
12355 HRESULT rc = S_OK;
12356
12357 /* create the machine client token */
12358 try
12359 {
12360 mClientToken = new ClientToken(aMachine, this);
12361 if (!mClientToken->isReady())
12362 {
12363 delete mClientToken;
12364 mClientToken = NULL;
12365 rc = E_FAIL;
12366 }
12367 }
12368 catch (std::bad_alloc &)
12369 {
12370 rc = E_OUTOFMEMORY;
12371 }
12372 if (FAILED(rc))
12373 return rc;
12374
12375 /* memorize the peer Machine */
12376 unconst(mPeer) = aMachine;
12377 /* share the parent pointer */
12378 unconst(mParent) = aMachine->mParent;
12379
12380 /* take the pointers to data to share */
12381 mData.share(aMachine->mData);
12382 mSSData.share(aMachine->mSSData);
12383
12384 mUserData.share(aMachine->mUserData);
12385 mHWData.share(aMachine->mHWData);
12386 mMediaData.share(aMachine->mMediaData);
12387
12388 mStorageControllers.allocate();
12389 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12390 it != aMachine->mStorageControllers->end();
12391 ++it)
12392 {
12393 ComObjPtr<StorageController> ctl;
12394 ctl.createObject();
12395 ctl->init(this, *it);
12396 mStorageControllers->push_back(ctl);
12397 }
12398
12399 mUSBControllers.allocate();
12400 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12401 it != aMachine->mUSBControllers->end();
12402 ++it)
12403 {
12404 ComObjPtr<USBController> ctl;
12405 ctl.createObject();
12406 ctl->init(this, *it);
12407 mUSBControllers->push_back(ctl);
12408 }
12409
12410 unconst(mBIOSSettings).createObject();
12411 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12412 /* create another VRDEServer object that will be mutable */
12413 unconst(mVRDEServer).createObject();
12414 mVRDEServer->init(this, aMachine->mVRDEServer);
12415 /* create another audio adapter object that will be mutable */
12416 unconst(mAudioAdapter).createObject();
12417 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12418 /* create a list of serial ports that will be mutable */
12419 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12420 {
12421 unconst(mSerialPorts[slot]).createObject();
12422 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12423 }
12424 /* create a list of parallel ports that will be mutable */
12425 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12426 {
12427 unconst(mParallelPorts[slot]).createObject();
12428 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12429 }
12430
12431 /* create another USB device filters object that will be mutable */
12432 unconst(mUSBDeviceFilters).createObject();
12433 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12434
12435 /* create a list of network adapters that will be mutable */
12436 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12437 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12438 {
12439 unconst(mNetworkAdapters[slot]).createObject();
12440 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12441 }
12442
12443 /* create another bandwidth control object that will be mutable */
12444 unconst(mBandwidthControl).createObject();
12445 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12446
12447 /* default is to delete saved state on Saved -> PoweredOff transition */
12448 mRemoveSavedState = true;
12449
12450 /* Confirm a successful initialization when it's the case */
12451 autoInitSpan.setSucceeded();
12452
12453 miNATNetworksStarted = 0;
12454
12455 LogFlowThisFuncLeave();
12456 return rc;
12457}
12458
12459/**
12460 * Uninitializes this session object. If the reason is other than
12461 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12462 * or the client watcher code.
12463 *
12464 * @param aReason uninitialization reason
12465 *
12466 * @note Locks mParent + this object for writing.
12467 */
12468void SessionMachine::uninit(Uninit::Reason aReason)
12469{
12470 LogFlowThisFuncEnter();
12471 LogFlowThisFunc(("reason=%d\n", aReason));
12472
12473 /*
12474 * Strongly reference ourselves to prevent this object deletion after
12475 * mData->mSession.mMachine.setNull() below (which can release the last
12476 * reference and call the destructor). Important: this must be done before
12477 * accessing any members (and before AutoUninitSpan that does it as well).
12478 * This self reference will be released as the very last step on return.
12479 */
12480 ComObjPtr<SessionMachine> selfRef = this;
12481
12482 /* Enclose the state transition Ready->InUninit->NotReady */
12483 AutoUninitSpan autoUninitSpan(this);
12484 if (autoUninitSpan.uninitDone())
12485 {
12486 LogFlowThisFunc(("Already uninitialized\n"));
12487 LogFlowThisFuncLeave();
12488 return;
12489 }
12490
12491 if (autoUninitSpan.initFailed())
12492 {
12493 /* We've been called by init() because it's failed. It's not really
12494 * necessary (nor it's safe) to perform the regular uninit sequence
12495 * below, the following is enough.
12496 */
12497 LogFlowThisFunc(("Initialization failed.\n"));
12498 /* destroy the machine client token */
12499 if (mClientToken)
12500 {
12501 delete mClientToken;
12502 mClientToken = NULL;
12503 }
12504 uninitDataAndChildObjects();
12505 mData.free();
12506 unconst(mParent) = NULL;
12507 unconst(mPeer) = NULL;
12508 LogFlowThisFuncLeave();
12509 return;
12510 }
12511
12512 MachineState_T lastState;
12513 {
12514 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12515 lastState = mData->mMachineState;
12516 }
12517 NOREF(lastState);
12518
12519#ifdef VBOX_WITH_USB
12520 // release all captured USB devices, but do this before requesting the locks below
12521 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12522 {
12523 /* Console::captureUSBDevices() is called in the VM process only after
12524 * setting the machine state to Starting or Restoring.
12525 * Console::detachAllUSBDevices() will be called upon successful
12526 * termination. So, we need to release USB devices only if there was
12527 * an abnormal termination of a running VM.
12528 *
12529 * This is identical to SessionMachine::DetachAllUSBDevices except
12530 * for the aAbnormal argument. */
12531 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12532 AssertComRC(rc);
12533 NOREF(rc);
12534
12535 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12536 if (service)
12537 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12538 }
12539#endif /* VBOX_WITH_USB */
12540
12541 // we need to lock this object in uninit() because the lock is shared
12542 // with mPeer (as well as data we modify below). mParent lock is needed
12543 // by several calls to it, and USB needs host lock.
12544 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12545
12546#ifdef VBOX_WITH_RESOURCE_USAGE_API
12547 /*
12548 * It is safe to call Machine::i_unregisterMetrics() here because
12549 * PerformanceCollector::samplerCallback no longer accesses guest methods
12550 * holding the lock.
12551 */
12552 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12553 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12554 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12555 this, __PRETTY_FUNCTION__, mCollectorGuest));
12556 if (mCollectorGuest)
12557 {
12558 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12559 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12560 mCollectorGuest = NULL;
12561 }
12562#endif
12563
12564 if (aReason == Uninit::Abnormal)
12565 {
12566 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12567 Global::IsOnlineOrTransient(lastState)));
12568
12569 /* reset the state to Aborted */
12570 if (mData->mMachineState != MachineState_Aborted)
12571 i_setMachineState(MachineState_Aborted);
12572 }
12573
12574 // any machine settings modified?
12575 if (mData->flModifications)
12576 {
12577 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12578 i_rollback(false /* aNotify */);
12579 }
12580
12581 mData->mSession.mPID = NIL_RTPROCESS;
12582
12583 if (aReason == Uninit::Unexpected)
12584 {
12585 /* Uninitialization didn't come from #checkForDeath(), so tell the
12586 * client watcher thread to update the set of machines that have open
12587 * sessions. */
12588 mParent->i_updateClientWatcher();
12589 }
12590
12591 /* uninitialize all remote controls */
12592 if (mData->mSession.mRemoteControls.size())
12593 {
12594 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12595 mData->mSession.mRemoteControls.size()));
12596
12597 Data::Session::RemoteControlList::iterator it =
12598 mData->mSession.mRemoteControls.begin();
12599 while (it != mData->mSession.mRemoteControls.end())
12600 {
12601 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12602 HRESULT rc = (*it)->Uninitialize();
12603 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12604 if (FAILED(rc))
12605 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12606 ++it;
12607 }
12608 mData->mSession.mRemoteControls.clear();
12609 }
12610
12611 /* Remove all references to the NAT network service. The service will stop
12612 * if all references (also from other VMs) are removed. */
12613 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12614 {
12615 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12616 {
12617 NetworkAttachmentType_T type;
12618 HRESULT hrc;
12619
12620 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12621 if ( SUCCEEDED(hrc)
12622 && type == NetworkAttachmentType_NATNetwork)
12623 {
12624 Bstr name;
12625 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12626 if (SUCCEEDED(hrc))
12627 {
12628 multilock.release();
12629 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12630 mUserData->s.strName.c_str(), name.raw()));
12631 mParent->i_natNetworkRefDec(name.raw());
12632 multilock.acquire();
12633 }
12634 }
12635 }
12636 }
12637
12638 /*
12639 * An expected uninitialization can come only from #checkForDeath().
12640 * Otherwise it means that something's gone really wrong (for example,
12641 * the Session implementation has released the VirtualBox reference
12642 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12643 * etc). However, it's also possible, that the client releases the IPC
12644 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12645 * but the VirtualBox release event comes first to the server process.
12646 * This case is practically possible, so we should not assert on an
12647 * unexpected uninit, just log a warning.
12648 */
12649
12650 if ((aReason == Uninit::Unexpected))
12651 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12652
12653 if (aReason != Uninit::Normal)
12654 {
12655 mData->mSession.mDirectControl.setNull();
12656 }
12657 else
12658 {
12659 /* this must be null here (see #OnSessionEnd()) */
12660 Assert(mData->mSession.mDirectControl.isNull());
12661 Assert(mData->mSession.mState == SessionState_Unlocking);
12662 Assert(!mData->mSession.mProgress.isNull());
12663 }
12664 if (mData->mSession.mProgress)
12665 {
12666 if (aReason == Uninit::Normal)
12667 mData->mSession.mProgress->i_notifyComplete(S_OK);
12668 else
12669 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12670 COM_IIDOF(ISession),
12671 getComponentName(),
12672 tr("The VM session was aborted"));
12673 mData->mSession.mProgress.setNull();
12674 }
12675
12676 /* remove the association between the peer machine and this session machine */
12677 Assert( (SessionMachine*)mData->mSession.mMachine == this
12678 || aReason == Uninit::Unexpected);
12679
12680 /* reset the rest of session data */
12681 mData->mSession.mLockType = LockType_Null;
12682 mData->mSession.mMachine.setNull();
12683 mData->mSession.mState = SessionState_Unlocked;
12684 mData->mSession.mName.setNull();
12685
12686 /* destroy the machine client token before leaving the exclusive lock */
12687 if (mClientToken)
12688 {
12689 delete mClientToken;
12690 mClientToken = NULL;
12691 }
12692
12693 /* fire an event */
12694 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12695
12696 uninitDataAndChildObjects();
12697
12698 /* free the essential data structure last */
12699 mData.free();
12700
12701 /* release the exclusive lock before setting the below two to NULL */
12702 multilock.release();
12703
12704 unconst(mParent) = NULL;
12705 unconst(mPeer) = NULL;
12706
12707 LogFlowThisFuncLeave();
12708}
12709
12710// util::Lockable interface
12711////////////////////////////////////////////////////////////////////////////////
12712
12713/**
12714 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12715 * with the primary Machine instance (mPeer).
12716 */
12717RWLockHandle *SessionMachine::lockHandle() const
12718{
12719 AssertReturn(mPeer != NULL, NULL);
12720 return mPeer->lockHandle();
12721}
12722
12723// IInternalMachineControl methods
12724////////////////////////////////////////////////////////////////////////////////
12725
12726/**
12727 * Passes collected guest statistics to performance collector object
12728 */
12729HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12730 ULONG aCpuKernel, ULONG aCpuIdle,
12731 ULONG aMemTotal, ULONG aMemFree,
12732 ULONG aMemBalloon, ULONG aMemShared,
12733 ULONG aMemCache, ULONG aPageTotal,
12734 ULONG aAllocVMM, ULONG aFreeVMM,
12735 ULONG aBalloonedVMM, ULONG aSharedVMM,
12736 ULONG aVmNetRx, ULONG aVmNetTx)
12737{
12738#ifdef VBOX_WITH_RESOURCE_USAGE_API
12739 if (mCollectorGuest)
12740 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12741 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12742 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12743 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12744
12745 return S_OK;
12746#else
12747 NOREF(aValidStats);
12748 NOREF(aCpuUser);
12749 NOREF(aCpuKernel);
12750 NOREF(aCpuIdle);
12751 NOREF(aMemTotal);
12752 NOREF(aMemFree);
12753 NOREF(aMemBalloon);
12754 NOREF(aMemShared);
12755 NOREF(aMemCache);
12756 NOREF(aPageTotal);
12757 NOREF(aAllocVMM);
12758 NOREF(aFreeVMM);
12759 NOREF(aBalloonedVMM);
12760 NOREF(aSharedVMM);
12761 NOREF(aVmNetRx);
12762 NOREF(aVmNetTx);
12763 return E_NOTIMPL;
12764#endif
12765}
12766
12767////////////////////////////////////////////////////////////////////////////////
12768//
12769// SessionMachine task records
12770//
12771////////////////////////////////////////////////////////////////////////////////
12772
12773/**
12774 * Task record for saving the machine state.
12775 */
12776struct SessionMachine::SaveStateTask
12777 : public Machine::Task
12778{
12779 SaveStateTask(SessionMachine *m,
12780 Progress *p,
12781 const Utf8Str &t,
12782 Reason_T enmReason,
12783 const Utf8Str &strStateFilePath)
12784 : Task(m, p, t),
12785 m_enmReason(enmReason),
12786 m_strStateFilePath(strStateFilePath)
12787 {}
12788
12789 void handler()
12790 {
12791 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12792 }
12793
12794 Reason_T m_enmReason;
12795 Utf8Str m_strStateFilePath;
12796};
12797
12798/**
12799 * Task thread implementation for SessionMachine::SaveState(), called from
12800 * SessionMachine::taskHandler().
12801 *
12802 * @note Locks this object for writing.
12803 *
12804 * @param task
12805 * @return
12806 */
12807void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12808{
12809 LogFlowThisFuncEnter();
12810
12811 AutoCaller autoCaller(this);
12812 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12813 if (FAILED(autoCaller.rc()))
12814 {
12815 /* we might have been uninitialized because the session was accidentally
12816 * closed by the client, so don't assert */
12817 HRESULT rc = setError(E_FAIL,
12818 tr("The session has been accidentally closed"));
12819 task.m_pProgress->i_notifyComplete(rc);
12820 LogFlowThisFuncLeave();
12821 return;
12822 }
12823
12824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12825
12826 HRESULT rc = S_OK;
12827
12828 try
12829 {
12830 ComPtr<IInternalSessionControl> directControl;
12831 if (mData->mSession.mLockType == LockType_VM)
12832 directControl = mData->mSession.mDirectControl;
12833 if (directControl.isNull())
12834 throw setError(VBOX_E_INVALID_VM_STATE,
12835 tr("Trying to save state without a running VM"));
12836 alock.release();
12837 BOOL fSuspendedBySave;
12838 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12839 Assert(!fSuspendedBySave);
12840 alock.acquire();
12841
12842 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12843 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12844 throw E_FAIL);
12845
12846 if (SUCCEEDED(rc))
12847 {
12848 mSSData->strStateFilePath = task.m_strStateFilePath;
12849
12850 /* save all VM settings */
12851 rc = i_saveSettings(NULL);
12852 // no need to check whether VirtualBox.xml needs saving also since
12853 // we can't have a name change pending at this point
12854 }
12855 else
12856 {
12857 // On failure, set the state to the state we had at the beginning.
12858 i_setMachineState(task.m_machineStateBackup);
12859 i_updateMachineStateOnClient();
12860
12861 // Delete the saved state file (might have been already created).
12862 // No need to check whether this is shared with a snapshot here
12863 // because we certainly created a fresh saved state file here.
12864 RTFileDelete(task.m_strStateFilePath.c_str());
12865 }
12866 }
12867 catch (HRESULT aRC) { rc = aRC; }
12868
12869 task.m_pProgress->i_notifyComplete(rc);
12870
12871 LogFlowThisFuncLeave();
12872}
12873
12874/**
12875 * @note Locks this object for writing.
12876 */
12877HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12878{
12879 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12880}
12881
12882HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12883{
12884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12885
12886 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12887 if (FAILED(rc)) return rc;
12888
12889 if ( mData->mMachineState != MachineState_Running
12890 && mData->mMachineState != MachineState_Paused
12891 )
12892 return setError(VBOX_E_INVALID_VM_STATE,
12893 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12894 Global::stringifyMachineState(mData->mMachineState));
12895
12896 ComObjPtr<Progress> pProgress;
12897 pProgress.createObject();
12898 rc = pProgress->init(i_getVirtualBox(),
12899 static_cast<IMachine *>(this) /* aInitiator */,
12900 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12901 FALSE /* aCancelable */);
12902 if (FAILED(rc))
12903 return rc;
12904
12905 Utf8Str strStateFilePath;
12906 i_composeSavedStateFilename(strStateFilePath);
12907
12908 /* create and start the task on a separate thread (note that it will not
12909 * start working until we release alock) */
12910 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12911 rc = pTask->createThread();
12912 if (FAILED(rc))
12913 return rc;
12914
12915 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12916 i_setMachineState(MachineState_Saving);
12917 i_updateMachineStateOnClient();
12918
12919 pProgress.queryInterfaceTo(aProgress.asOutParam());
12920
12921 return S_OK;
12922}
12923
12924/**
12925 * @note Locks this object for writing.
12926 */
12927HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12928{
12929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12930
12931 HRESULT rc = i_checkStateDependency(MutableStateDep);
12932 if (FAILED(rc)) return rc;
12933
12934 if ( mData->mMachineState != MachineState_PoweredOff
12935 && mData->mMachineState != MachineState_Teleported
12936 && mData->mMachineState != MachineState_Aborted
12937 )
12938 return setError(VBOX_E_INVALID_VM_STATE,
12939 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12940 Global::stringifyMachineState(mData->mMachineState));
12941
12942 com::Utf8Str stateFilePathFull;
12943 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12944 if (RT_FAILURE(vrc))
12945 return setError(VBOX_E_FILE_ERROR,
12946 tr("Invalid saved state file path '%s' (%Rrc)"),
12947 aSavedStateFile.c_str(),
12948 vrc);
12949
12950 mSSData->strStateFilePath = stateFilePathFull;
12951
12952 /* The below i_setMachineState() will detect the state transition and will
12953 * update the settings file */
12954
12955 return i_setMachineState(MachineState_Saved);
12956}
12957
12958/**
12959 * @note Locks this object for writing.
12960 */
12961HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12962{
12963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12964
12965 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12966 if (FAILED(rc)) return rc;
12967
12968 if (mData->mMachineState != MachineState_Saved)
12969 return setError(VBOX_E_INVALID_VM_STATE,
12970 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12971 Global::stringifyMachineState(mData->mMachineState));
12972
12973 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12974
12975 /*
12976 * Saved -> PoweredOff transition will be detected in the SessionMachine
12977 * and properly handled.
12978 */
12979 rc = i_setMachineState(MachineState_PoweredOff);
12980 return rc;
12981}
12982
12983
12984/**
12985 * @note Locks the same as #i_setMachineState() does.
12986 */
12987HRESULT SessionMachine::updateState(MachineState_T aState)
12988{
12989 return i_setMachineState(aState);
12990}
12991
12992/**
12993 * @note Locks this object for writing.
12994 */
12995HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12996{
12997 IProgress* pProgress(aProgress);
12998
12999 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13000
13001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13002
13003 if (mData->mSession.mState != SessionState_Locked)
13004 return VBOX_E_INVALID_OBJECT_STATE;
13005
13006 if (!mData->mSession.mProgress.isNull())
13007 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13008
13009 /* If we didn't reference the NAT network service yet, add a reference to
13010 * force a start */
13011 if (miNATNetworksStarted < 1)
13012 {
13013 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13014 {
13015 NetworkAttachmentType_T type;
13016 HRESULT hrc;
13017 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13018 if ( SUCCEEDED(hrc)
13019 && type == NetworkAttachmentType_NATNetwork)
13020 {
13021 Bstr name;
13022 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13023 if (SUCCEEDED(hrc))
13024 {
13025 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13026 mUserData->s.strName.c_str(), name.raw()));
13027 mPeer->lockHandle()->unlockWrite();
13028 mParent->i_natNetworkRefInc(name.raw());
13029#ifdef RT_LOCK_STRICT
13030 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13031#else
13032 mPeer->lockHandle()->lockWrite();
13033#endif
13034 }
13035 }
13036 }
13037 miNATNetworksStarted++;
13038 }
13039
13040 LogFlowThisFunc(("returns S_OK.\n"));
13041 return S_OK;
13042}
13043
13044/**
13045 * @note Locks this object for writing.
13046 */
13047HRESULT SessionMachine::endPowerUp(LONG aResult)
13048{
13049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13050
13051 if (mData->mSession.mState != SessionState_Locked)
13052 return VBOX_E_INVALID_OBJECT_STATE;
13053
13054 /* Finalize the LaunchVMProcess progress object. */
13055 if (mData->mSession.mProgress)
13056 {
13057 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13058 mData->mSession.mProgress.setNull();
13059 }
13060
13061 if (SUCCEEDED((HRESULT)aResult))
13062 {
13063#ifdef VBOX_WITH_RESOURCE_USAGE_API
13064 /* The VM has been powered up successfully, so it makes sense
13065 * now to offer the performance metrics for a running machine
13066 * object. Doing it earlier wouldn't be safe. */
13067 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13068 mData->mSession.mPID);
13069#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13070 }
13071
13072 return S_OK;
13073}
13074
13075/**
13076 * @note Locks this object for writing.
13077 */
13078HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13079{
13080 LogFlowThisFuncEnter();
13081
13082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13083
13084 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13085 E_FAIL);
13086
13087 /* create a progress object to track operation completion */
13088 ComObjPtr<Progress> pProgress;
13089 pProgress.createObject();
13090 pProgress->init(i_getVirtualBox(),
13091 static_cast<IMachine *>(this) /* aInitiator */,
13092 Bstr(tr("Stopping the virtual machine")).raw(),
13093 FALSE /* aCancelable */);
13094
13095 /* fill in the console task data */
13096 mConsoleTaskData.mLastState = mData->mMachineState;
13097 mConsoleTaskData.mProgress = pProgress;
13098
13099 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13100 i_setMachineState(MachineState_Stopping);
13101
13102 pProgress.queryInterfaceTo(aProgress.asOutParam());
13103
13104 return S_OK;
13105}
13106
13107/**
13108 * @note Locks this object for writing.
13109 */
13110HRESULT SessionMachine::endPoweringDown(LONG aResult,
13111 const com::Utf8Str &aErrMsg)
13112{
13113 LogFlowThisFuncEnter();
13114
13115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13116
13117 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13118 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13119 && mConsoleTaskData.mLastState != MachineState_Null,
13120 E_FAIL);
13121
13122 /*
13123 * On failure, set the state to the state we had when BeginPoweringDown()
13124 * was called (this is expected by Console::PowerDown() and the associated
13125 * task). On success the VM process already changed the state to
13126 * MachineState_PoweredOff, so no need to do anything.
13127 */
13128 if (FAILED(aResult))
13129 i_setMachineState(mConsoleTaskData.mLastState);
13130
13131 /* notify the progress object about operation completion */
13132 Assert(mConsoleTaskData.mProgress);
13133 if (SUCCEEDED(aResult))
13134 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13135 else
13136 {
13137 if (aErrMsg.length())
13138 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13139 COM_IIDOF(ISession),
13140 getComponentName(),
13141 aErrMsg.c_str());
13142 else
13143 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13144 }
13145
13146 /* clear out the temporary saved state data */
13147 mConsoleTaskData.mLastState = MachineState_Null;
13148 mConsoleTaskData.mProgress.setNull();
13149
13150 LogFlowThisFuncLeave();
13151 return S_OK;
13152}
13153
13154
13155/**
13156 * Goes through the USB filters of the given machine to see if the given
13157 * device matches any filter or not.
13158 *
13159 * @note Locks the same as USBController::hasMatchingFilter() does.
13160 */
13161HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13162 BOOL *aMatched,
13163 ULONG *aMaskedInterfaces)
13164{
13165 LogFlowThisFunc(("\n"));
13166
13167#ifdef VBOX_WITH_USB
13168 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13169#else
13170 NOREF(aDevice);
13171 NOREF(aMaskedInterfaces);
13172 *aMatched = FALSE;
13173#endif
13174
13175 return S_OK;
13176}
13177
13178/**
13179 * @note Locks the same as Host::captureUSBDevice() does.
13180 */
13181HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13182{
13183 LogFlowThisFunc(("\n"));
13184
13185#ifdef VBOX_WITH_USB
13186 /* if captureDeviceForVM() fails, it must have set extended error info */
13187 clearError();
13188 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13189 if (FAILED(rc)) return rc;
13190
13191 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13192 AssertReturn(service, E_FAIL);
13193 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13194#else
13195 NOREF(aId);
13196 return E_NOTIMPL;
13197#endif
13198}
13199
13200/**
13201 * @note Locks the same as Host::detachUSBDevice() does.
13202 */
13203HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13204 BOOL aDone)
13205{
13206 LogFlowThisFunc(("\n"));
13207
13208#ifdef VBOX_WITH_USB
13209 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13210 AssertReturn(service, E_FAIL);
13211 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13212#else
13213 NOREF(aId);
13214 NOREF(aDone);
13215 return E_NOTIMPL;
13216#endif
13217}
13218
13219/**
13220 * Inserts all machine filters to the USB proxy service and then calls
13221 * Host::autoCaptureUSBDevices().
13222 *
13223 * Called by Console from the VM process upon VM startup.
13224 *
13225 * @note Locks what called methods lock.
13226 */
13227HRESULT SessionMachine::autoCaptureUSBDevices()
13228{
13229 LogFlowThisFunc(("\n"));
13230
13231#ifdef VBOX_WITH_USB
13232 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13233 AssertComRC(rc);
13234 NOREF(rc);
13235
13236 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13237 AssertReturn(service, E_FAIL);
13238 return service->autoCaptureDevicesForVM(this);
13239#else
13240 return S_OK;
13241#endif
13242}
13243
13244/**
13245 * Removes all machine filters from the USB proxy service and then calls
13246 * Host::detachAllUSBDevices().
13247 *
13248 * Called by Console from the VM process upon normal VM termination or by
13249 * SessionMachine::uninit() upon abnormal VM termination (from under the
13250 * Machine/SessionMachine lock).
13251 *
13252 * @note Locks what called methods lock.
13253 */
13254HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13255{
13256 LogFlowThisFunc(("\n"));
13257
13258#ifdef VBOX_WITH_USB
13259 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13260 AssertComRC(rc);
13261 NOREF(rc);
13262
13263 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13264 AssertReturn(service, E_FAIL);
13265 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13266#else
13267 NOREF(aDone);
13268 return S_OK;
13269#endif
13270}
13271
13272/**
13273 * @note Locks this object for writing.
13274 */
13275HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13276 ComPtr<IProgress> &aProgress)
13277{
13278 LogFlowThisFuncEnter();
13279
13280 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13281 /*
13282 * We don't assert below because it might happen that a non-direct session
13283 * informs us it is closed right after we've been uninitialized -- it's ok.
13284 */
13285
13286 /* get IInternalSessionControl interface */
13287 ComPtr<IInternalSessionControl> control(aSession);
13288
13289 ComAssertRet(!control.isNull(), E_INVALIDARG);
13290
13291 /* Creating a Progress object requires the VirtualBox lock, and
13292 * thus locking it here is required by the lock order rules. */
13293 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13294
13295 if (control == mData->mSession.mDirectControl)
13296 {
13297 /* The direct session is being normally closed by the client process
13298 * ----------------------------------------------------------------- */
13299
13300 /* go to the closing state (essential for all open*Session() calls and
13301 * for #checkForDeath()) */
13302 Assert(mData->mSession.mState == SessionState_Locked);
13303 mData->mSession.mState = SessionState_Unlocking;
13304
13305 /* set direct control to NULL to release the remote instance */
13306 mData->mSession.mDirectControl.setNull();
13307 LogFlowThisFunc(("Direct control is set to NULL\n"));
13308
13309 if (mData->mSession.mProgress)
13310 {
13311 /* finalize the progress, someone might wait if a frontend
13312 * closes the session before powering on the VM. */
13313 mData->mSession.mProgress->notifyComplete(E_FAIL,
13314 COM_IIDOF(ISession),
13315 getComponentName(),
13316 tr("The VM session was closed before any attempt to power it on"));
13317 mData->mSession.mProgress.setNull();
13318 }
13319
13320 /* Create the progress object the client will use to wait until
13321 * #checkForDeath() is called to uninitialize this session object after
13322 * it releases the IPC semaphore.
13323 * Note! Because we're "reusing" mProgress here, this must be a proxy
13324 * object just like for LaunchVMProcess. */
13325 Assert(mData->mSession.mProgress.isNull());
13326 ComObjPtr<ProgressProxy> progress;
13327 progress.createObject();
13328 ComPtr<IUnknown> pPeer(mPeer);
13329 progress->init(mParent, pPeer,
13330 Bstr(tr("Closing session")).raw(),
13331 FALSE /* aCancelable */);
13332 progress.queryInterfaceTo(aProgress.asOutParam());
13333 mData->mSession.mProgress = progress;
13334 }
13335 else
13336 {
13337 /* the remote session is being normally closed */
13338 Data::Session::RemoteControlList::iterator it =
13339 mData->mSession.mRemoteControls.begin();
13340 while (it != mData->mSession.mRemoteControls.end())
13341 {
13342 if (control == *it)
13343 break;
13344 ++it;
13345 }
13346 BOOL found = it != mData->mSession.mRemoteControls.end();
13347 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13348 E_INVALIDARG);
13349 // This MUST be erase(it), not remove(*it) as the latter triggers a
13350 // very nasty use after free due to the place where the value "lives".
13351 mData->mSession.mRemoteControls.erase(it);
13352 }
13353
13354 /* signal the client watcher thread, because the client is going away */
13355 mParent->i_updateClientWatcher();
13356
13357 LogFlowThisFuncLeave();
13358 return S_OK;
13359}
13360
13361HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13362 std::vector<com::Utf8Str> &aValues,
13363 std::vector<LONG64> &aTimestamps,
13364 std::vector<com::Utf8Str> &aFlags)
13365{
13366 LogFlowThisFunc(("\n"));
13367
13368#ifdef VBOX_WITH_GUEST_PROPS
13369 using namespace guestProp;
13370
13371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13372
13373 size_t cEntries = mHWData->mGuestProperties.size();
13374 aNames.resize(cEntries);
13375 aValues.resize(cEntries);
13376 aTimestamps.resize(cEntries);
13377 aFlags.resize(cEntries);
13378
13379 size_t i = 0;
13380 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13381 it != mHWData->mGuestProperties.end();
13382 ++it, ++i)
13383 {
13384 char szFlags[MAX_FLAGS_LEN + 1];
13385 aNames[i] = it->first;
13386 aValues[i] = it->second.strValue;
13387 aTimestamps[i] = it->second.mTimestamp;
13388
13389 /* If it is NULL, keep it NULL. */
13390 if (it->second.mFlags)
13391 {
13392 writeFlags(it->second.mFlags, szFlags);
13393 aFlags[i] = szFlags;
13394 }
13395 else
13396 aFlags[i] = "";
13397 }
13398 return S_OK;
13399#else
13400 ReturnComNotImplemented();
13401#endif
13402}
13403
13404HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13405 const com::Utf8Str &aValue,
13406 LONG64 aTimestamp,
13407 const com::Utf8Str &aFlags,
13408 BOOL *aNotify)
13409{
13410 LogFlowThisFunc(("\n"));
13411
13412#ifdef VBOX_WITH_GUEST_PROPS
13413 using namespace guestProp;
13414
13415 *aNotify = FALSE;
13416
13417 try
13418 {
13419 /*
13420 * Convert input up front.
13421 */
13422 uint32_t fFlags = NILFLAG;
13423 if (aFlags.length())
13424 {
13425 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13426 AssertRCReturn(vrc, E_INVALIDARG);
13427 }
13428
13429 /*
13430 * Now grab the object lock, validate the state and do the update.
13431 */
13432
13433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13434
13435 switch (mData->mMachineState)
13436 {
13437 case MachineState_Paused:
13438 case MachineState_Running:
13439 case MachineState_Teleporting:
13440 case MachineState_TeleportingPausedVM:
13441 case MachineState_OnlineSnapshotting:
13442 case MachineState_LiveSnapshotting:
13443 case MachineState_DeletingSnapshotOnline:
13444 case MachineState_DeletingSnapshotPaused:
13445 case MachineState_Saving:
13446 case MachineState_Stopping:
13447 break;
13448
13449 default:
13450 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13451 VBOX_E_INVALID_VM_STATE);
13452 }
13453
13454 i_setModified(IsModified_MachineData);
13455 mHWData.backup();
13456
13457 bool fDelete = !aValue.length();
13458 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13459 if (it != mHWData->mGuestProperties.end())
13460 {
13461 if (!fDelete)
13462 {
13463 it->second.strValue = aValue;
13464 it->second.mTimestamp = aTimestamp;
13465 it->second.mFlags = fFlags;
13466 }
13467 else
13468 mHWData->mGuestProperties.erase(it);
13469
13470 mData->mGuestPropertiesModified = TRUE;
13471 }
13472 else if (!fDelete)
13473 {
13474 HWData::GuestProperty prop;
13475 prop.strValue = aValue;
13476 prop.mTimestamp = aTimestamp;
13477 prop.mFlags = fFlags;
13478
13479 mHWData->mGuestProperties[aName] = prop;
13480 mData->mGuestPropertiesModified = TRUE;
13481 }
13482
13483 /*
13484 * Send a callback notification if appropriate
13485 */
13486 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13487 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13488 RTSTR_MAX,
13489 aName.c_str(),
13490 RTSTR_MAX, NULL)
13491 )
13492 {
13493 alock.release();
13494
13495 mParent->i_onGuestPropertyChange(mData->mUuid,
13496 Bstr(aName).raw(),
13497 Bstr(aValue).raw(),
13498 Bstr(aFlags).raw());
13499 *aNotify = TRUE;
13500 }
13501 }
13502 catch (...)
13503 {
13504 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13505 }
13506 return S_OK;
13507#else
13508 ReturnComNotImplemented();
13509#endif
13510}
13511
13512
13513HRESULT SessionMachine::lockMedia()
13514{
13515 AutoMultiWriteLock2 alock(this->lockHandle(),
13516 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13517
13518 AssertReturn( mData->mMachineState == MachineState_Starting
13519 || mData->mMachineState == MachineState_Restoring
13520 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13521
13522 clearError();
13523 alock.release();
13524 return i_lockMedia();
13525}
13526
13527HRESULT SessionMachine::unlockMedia()
13528{
13529 HRESULT hrc = i_unlockMedia();
13530 return hrc;
13531}
13532
13533HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13534 ComPtr<IMediumAttachment> &aNewAttachment)
13535{
13536 // request the host lock first, since might be calling Host methods for getting host drives;
13537 // next, protect the media tree all the while we're in here, as well as our member variables
13538 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13539 this->lockHandle(),
13540 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13541
13542 IMediumAttachment *iAttach = aAttachment;
13543 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13544
13545 Bstr ctrlName;
13546 LONG lPort;
13547 LONG lDevice;
13548 bool fTempEject;
13549 {
13550 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13551
13552 /* Need to query the details first, as the IMediumAttachment reference
13553 * might be to the original settings, which we are going to change. */
13554 ctrlName = pAttach->i_getControllerName();
13555 lPort = pAttach->i_getPort();
13556 lDevice = pAttach->i_getDevice();
13557 fTempEject = pAttach->i_getTempEject();
13558 }
13559
13560 if (!fTempEject)
13561 {
13562 /* Remember previously mounted medium. The medium before taking the
13563 * backup is not necessarily the same thing. */
13564 ComObjPtr<Medium> oldmedium;
13565 oldmedium = pAttach->i_getMedium();
13566
13567 i_setModified(IsModified_Storage);
13568 mMediaData.backup();
13569
13570 // The backup operation makes the pAttach reference point to the
13571 // old settings. Re-get the correct reference.
13572 pAttach = i_findAttachment(mMediaData->mAttachments,
13573 ctrlName.raw(),
13574 lPort,
13575 lDevice);
13576
13577 {
13578 AutoCaller autoAttachCaller(this);
13579 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13580
13581 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13582 if (!oldmedium.isNull())
13583 oldmedium->i_removeBackReference(mData->mUuid);
13584
13585 pAttach->i_updateMedium(NULL);
13586 pAttach->i_updateEjected();
13587 }
13588
13589 i_setModified(IsModified_Storage);
13590 }
13591 else
13592 {
13593 {
13594 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13595 pAttach->i_updateEjected();
13596 }
13597 }
13598
13599 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13600
13601 return S_OK;
13602}
13603
13604// public methods only for internal purposes
13605/////////////////////////////////////////////////////////////////////////////
13606
13607#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13608/**
13609 * Called from the client watcher thread to check for expected or unexpected
13610 * death of the client process that has a direct session to this machine.
13611 *
13612 * On Win32 and on OS/2, this method is called only when we've got the
13613 * mutex (i.e. the client has either died or terminated normally) so it always
13614 * returns @c true (the client is terminated, the session machine is
13615 * uninitialized).
13616 *
13617 * On other platforms, the method returns @c true if the client process has
13618 * terminated normally or abnormally and the session machine was uninitialized,
13619 * and @c false if the client process is still alive.
13620 *
13621 * @note Locks this object for writing.
13622 */
13623bool SessionMachine::i_checkForDeath()
13624{
13625 Uninit::Reason reason;
13626 bool terminated = false;
13627
13628 /* Enclose autoCaller with a block because calling uninit() from under it
13629 * will deadlock. */
13630 {
13631 AutoCaller autoCaller(this);
13632 if (!autoCaller.isOk())
13633 {
13634 /* return true if not ready, to cause the client watcher to exclude
13635 * the corresponding session from watching */
13636 LogFlowThisFunc(("Already uninitialized!\n"));
13637 return true;
13638 }
13639
13640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13641
13642 /* Determine the reason of death: if the session state is Closing here,
13643 * everything is fine. Otherwise it means that the client did not call
13644 * OnSessionEnd() before it released the IPC semaphore. This may happen
13645 * either because the client process has abnormally terminated, or
13646 * because it simply forgot to call ISession::Close() before exiting. We
13647 * threat the latter also as an abnormal termination (see
13648 * Session::uninit() for details). */
13649 reason = mData->mSession.mState == SessionState_Unlocking ?
13650 Uninit::Normal :
13651 Uninit::Abnormal;
13652
13653 if (mClientToken)
13654 terminated = mClientToken->release();
13655 } /* AutoCaller block */
13656
13657 if (terminated)
13658 uninit(reason);
13659
13660 return terminated;
13661}
13662
13663void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13664{
13665 LogFlowThisFunc(("\n"));
13666
13667 strTokenId.setNull();
13668
13669 AutoCaller autoCaller(this);
13670 AssertComRCReturnVoid(autoCaller.rc());
13671
13672 Assert(mClientToken);
13673 if (mClientToken)
13674 mClientToken->getId(strTokenId);
13675}
13676#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13677IToken *SessionMachine::i_getToken()
13678{
13679 LogFlowThisFunc(("\n"));
13680
13681 AutoCaller autoCaller(this);
13682 AssertComRCReturn(autoCaller.rc(), NULL);
13683
13684 Assert(mClientToken);
13685 if (mClientToken)
13686 return mClientToken->getToken();
13687 else
13688 return NULL;
13689}
13690#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13691
13692Machine::ClientToken *SessionMachine::i_getClientToken()
13693{
13694 LogFlowThisFunc(("\n"));
13695
13696 AutoCaller autoCaller(this);
13697 AssertComRCReturn(autoCaller.rc(), NULL);
13698
13699 return mClientToken;
13700}
13701
13702
13703/**
13704 * @note Locks this object for reading.
13705 */
13706HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13707{
13708 LogFlowThisFunc(("\n"));
13709
13710 AutoCaller autoCaller(this);
13711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13712
13713 ComPtr<IInternalSessionControl> directControl;
13714 {
13715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13716 if (mData->mSession.mLockType == LockType_VM)
13717 directControl = mData->mSession.mDirectControl;
13718 }
13719
13720 /* ignore notifications sent after #OnSessionEnd() is called */
13721 if (!directControl)
13722 return S_OK;
13723
13724 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13725}
13726
13727/**
13728 * @note Locks this object for reading.
13729 */
13730HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13731 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13732 IN_BSTR aGuestIp, LONG aGuestPort)
13733{
13734 LogFlowThisFunc(("\n"));
13735
13736 AutoCaller autoCaller(this);
13737 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13738
13739 ComPtr<IInternalSessionControl> directControl;
13740 {
13741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13742 if (mData->mSession.mLockType == LockType_VM)
13743 directControl = mData->mSession.mDirectControl;
13744 }
13745
13746 /* ignore notifications sent after #OnSessionEnd() is called */
13747 if (!directControl)
13748 return S_OK;
13749 /*
13750 * instead acting like callback we ask IVirtualBox deliver corresponding event
13751 */
13752
13753 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13754 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13755 return S_OK;
13756}
13757
13758/**
13759 * @note Locks this object for reading.
13760 */
13761HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13762{
13763 LogFlowThisFunc(("\n"));
13764
13765 AutoCaller autoCaller(this);
13766 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13767
13768 ComPtr<IInternalSessionControl> directControl;
13769 {
13770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13771 if (mData->mSession.mLockType == LockType_VM)
13772 directControl = mData->mSession.mDirectControl;
13773 }
13774
13775 /* ignore notifications sent after #OnSessionEnd() is called */
13776 if (!directControl)
13777 return S_OK;
13778
13779 return directControl->OnSerialPortChange(serialPort);
13780}
13781
13782/**
13783 * @note Locks this object for reading.
13784 */
13785HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13786{
13787 LogFlowThisFunc(("\n"));
13788
13789 AutoCaller autoCaller(this);
13790 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13791
13792 ComPtr<IInternalSessionControl> directControl;
13793 {
13794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13795 if (mData->mSession.mLockType == LockType_VM)
13796 directControl = mData->mSession.mDirectControl;
13797 }
13798
13799 /* ignore notifications sent after #OnSessionEnd() is called */
13800 if (!directControl)
13801 return S_OK;
13802
13803 return directControl->OnParallelPortChange(parallelPort);
13804}
13805
13806/**
13807 * @note Locks this object for reading.
13808 */
13809HRESULT SessionMachine::i_onStorageControllerChange()
13810{
13811 LogFlowThisFunc(("\n"));
13812
13813 AutoCaller autoCaller(this);
13814 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13815
13816 ComPtr<IInternalSessionControl> directControl;
13817 {
13818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13819 if (mData->mSession.mLockType == LockType_VM)
13820 directControl = mData->mSession.mDirectControl;
13821 }
13822
13823 /* ignore notifications sent after #OnSessionEnd() is called */
13824 if (!directControl)
13825 return S_OK;
13826
13827 return directControl->OnStorageControllerChange();
13828}
13829
13830/**
13831 * @note Locks this object for reading.
13832 */
13833HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13834{
13835 LogFlowThisFunc(("\n"));
13836
13837 AutoCaller autoCaller(this);
13838 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13839
13840 ComPtr<IInternalSessionControl> directControl;
13841 {
13842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13843 if (mData->mSession.mLockType == LockType_VM)
13844 directControl = mData->mSession.mDirectControl;
13845 }
13846
13847 /* ignore notifications sent after #OnSessionEnd() is called */
13848 if (!directControl)
13849 return S_OK;
13850
13851 return directControl->OnMediumChange(aAttachment, aForce);
13852}
13853
13854/**
13855 * @note Locks this object for reading.
13856 */
13857HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13858{
13859 LogFlowThisFunc(("\n"));
13860
13861 AutoCaller autoCaller(this);
13862 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13863
13864 ComPtr<IInternalSessionControl> directControl;
13865 {
13866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13867 if (mData->mSession.mLockType == LockType_VM)
13868 directControl = mData->mSession.mDirectControl;
13869 }
13870
13871 /* ignore notifications sent after #OnSessionEnd() is called */
13872 if (!directControl)
13873 return S_OK;
13874
13875 return directControl->OnCPUChange(aCPU, aRemove);
13876}
13877
13878HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13879{
13880 LogFlowThisFunc(("\n"));
13881
13882 AutoCaller autoCaller(this);
13883 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13884
13885 ComPtr<IInternalSessionControl> directControl;
13886 {
13887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13888 if (mData->mSession.mLockType == LockType_VM)
13889 directControl = mData->mSession.mDirectControl;
13890 }
13891
13892 /* ignore notifications sent after #OnSessionEnd() is called */
13893 if (!directControl)
13894 return S_OK;
13895
13896 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13897}
13898
13899/**
13900 * @note Locks this object for reading.
13901 */
13902HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13903{
13904 LogFlowThisFunc(("\n"));
13905
13906 AutoCaller autoCaller(this);
13907 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13908
13909 ComPtr<IInternalSessionControl> directControl;
13910 {
13911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13912 if (mData->mSession.mLockType == LockType_VM)
13913 directControl = mData->mSession.mDirectControl;
13914 }
13915
13916 /* ignore notifications sent after #OnSessionEnd() is called */
13917 if (!directControl)
13918 return S_OK;
13919
13920 return directControl->OnVRDEServerChange(aRestart);
13921}
13922
13923/**
13924 * @note Locks this object for reading.
13925 */
13926HRESULT SessionMachine::i_onVideoCaptureChange()
13927{
13928 LogFlowThisFunc(("\n"));
13929
13930 AutoCaller autoCaller(this);
13931 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13932
13933 ComPtr<IInternalSessionControl> directControl;
13934 {
13935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13936 if (mData->mSession.mLockType == LockType_VM)
13937 directControl = mData->mSession.mDirectControl;
13938 }
13939
13940 /* ignore notifications sent after #OnSessionEnd() is called */
13941 if (!directControl)
13942 return S_OK;
13943
13944 return directControl->OnVideoCaptureChange();
13945}
13946
13947/**
13948 * @note Locks this object for reading.
13949 */
13950HRESULT SessionMachine::i_onUSBControllerChange()
13951{
13952 LogFlowThisFunc(("\n"));
13953
13954 AutoCaller autoCaller(this);
13955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13956
13957 ComPtr<IInternalSessionControl> directControl;
13958 {
13959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13960 if (mData->mSession.mLockType == LockType_VM)
13961 directControl = mData->mSession.mDirectControl;
13962 }
13963
13964 /* ignore notifications sent after #OnSessionEnd() is called */
13965 if (!directControl)
13966 return S_OK;
13967
13968 return directControl->OnUSBControllerChange();
13969}
13970
13971/**
13972 * @note Locks this object for reading.
13973 */
13974HRESULT SessionMachine::i_onSharedFolderChange()
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturnRC(autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturnRC(autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnClipboardModeChange(aClipboardMode);
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturnRC(autoCaller.rc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 /* ignore notifications sent after #OnSessionEnd() is called */
14037 if (!directControl)
14038 return S_OK;
14039
14040 return directControl->OnDnDModeChange(aDnDMode);
14041}
14042
14043/**
14044 * @note Locks this object for reading.
14045 */
14046HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14065}
14066
14067/**
14068 * @note Locks this object for reading.
14069 */
14070HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14076
14077 ComPtr<IInternalSessionControl> directControl;
14078 {
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080 if (mData->mSession.mLockType == LockType_VM)
14081 directControl = mData->mSession.mDirectControl;
14082 }
14083
14084 /* ignore notifications sent after #OnSessionEnd() is called */
14085 if (!directControl)
14086 return S_OK;
14087
14088 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14089}
14090
14091/**
14092 * Returns @c true if this machine's USB controller reports it has a matching
14093 * filter for the given USB device and @c false otherwise.
14094 *
14095 * @note locks this object for reading.
14096 */
14097bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14098{
14099 AutoCaller autoCaller(this);
14100 /* silently return if not ready -- this method may be called after the
14101 * direct machine session has been called */
14102 if (!autoCaller.isOk())
14103 return false;
14104
14105#ifdef VBOX_WITH_USB
14106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14107
14108 switch (mData->mMachineState)
14109 {
14110 case MachineState_Starting:
14111 case MachineState_Restoring:
14112 case MachineState_TeleportingIn:
14113 case MachineState_Paused:
14114 case MachineState_Running:
14115 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14116 * elsewhere... */
14117 alock.release();
14118 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14119 default: break;
14120 }
14121#else
14122 NOREF(aDevice);
14123 NOREF(aMaskedIfs);
14124#endif
14125 return false;
14126}
14127
14128/**
14129 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14130 */
14131HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14132 IVirtualBoxErrorInfo *aError,
14133 ULONG aMaskedIfs,
14134 const com::Utf8Str &aCaptureFilename)
14135{
14136 LogFlowThisFunc(("\n"));
14137
14138 AutoCaller autoCaller(this);
14139
14140 /* This notification may happen after the machine object has been
14141 * uninitialized (the session was closed), so don't assert. */
14142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 if (mData->mSession.mLockType == LockType_VM)
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* fail on notifications sent after #OnSessionEnd() is called, it is
14152 * expected by the caller */
14153 if (!directControl)
14154 return E_FAIL;
14155
14156 /* No locks should be held at this point. */
14157 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14158 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14159
14160 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14161}
14162
14163/**
14164 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14165 */
14166HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14167 IVirtualBoxErrorInfo *aError)
14168{
14169 LogFlowThisFunc(("\n"));
14170
14171 AutoCaller autoCaller(this);
14172
14173 /* This notification may happen after the machine object has been
14174 * uninitialized (the session was closed), so don't assert. */
14175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14176
14177 ComPtr<IInternalSessionControl> directControl;
14178 {
14179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14180 if (mData->mSession.mLockType == LockType_VM)
14181 directControl = mData->mSession.mDirectControl;
14182 }
14183
14184 /* fail on notifications sent after #OnSessionEnd() is called, it is
14185 * expected by the caller */
14186 if (!directControl)
14187 return E_FAIL;
14188
14189 /* No locks should be held at this point. */
14190 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14191 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14192
14193 return directControl->OnUSBDeviceDetach(aId, aError);
14194}
14195
14196// protected methods
14197/////////////////////////////////////////////////////////////////////////////
14198
14199/**
14200 * Deletes the given file if it is no longer in use by either the current machine state
14201 * (if the machine is "saved") or any of the machine's snapshots.
14202 *
14203 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14204 * but is different for each SnapshotMachine. When calling this, the order of calling this
14205 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14206 * is therefore critical. I know, it's all rather messy.
14207 *
14208 * @param strStateFile
14209 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14210 * the test for whether the saved state file is in use.
14211 */
14212void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14213 Snapshot *pSnapshotToIgnore)
14214{
14215 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14216 if ( (strStateFile.isNotEmpty())
14217 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14218 )
14219 // ... and it must also not be shared with other snapshots
14220 if ( !mData->mFirstSnapshot
14221 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14222 // this checks the SnapshotMachine's state file paths
14223 )
14224 RTFileDelete(strStateFile.c_str());
14225}
14226
14227/**
14228 * Locks the attached media.
14229 *
14230 * All attached hard disks are locked for writing and DVD/floppy are locked for
14231 * reading. Parents of attached hard disks (if any) are locked for reading.
14232 *
14233 * This method also performs accessibility check of all media it locks: if some
14234 * media is inaccessible, the method will return a failure and a bunch of
14235 * extended error info objects per each inaccessible medium.
14236 *
14237 * Note that this method is atomic: if it returns a success, all media are
14238 * locked as described above; on failure no media is locked at all (all
14239 * succeeded individual locks will be undone).
14240 *
14241 * The caller is responsible for doing the necessary state sanity checks.
14242 *
14243 * The locks made by this method must be undone by calling #unlockMedia() when
14244 * no more needed.
14245 */
14246HRESULT SessionMachine::i_lockMedia()
14247{
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14250
14251 AutoMultiWriteLock2 alock(this->lockHandle(),
14252 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14253
14254 /* bail out if trying to lock things with already set up locking */
14255 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14256
14257 MultiResult mrc(S_OK);
14258
14259 /* Collect locking information for all medium objects attached to the VM. */
14260 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14261 it != mMediaData->mAttachments.end();
14262 ++it)
14263 {
14264 MediumAttachment* pAtt = *it;
14265 DeviceType_T devType = pAtt->i_getType();
14266 Medium *pMedium = pAtt->i_getMedium();
14267
14268 MediumLockList *pMediumLockList(new MediumLockList());
14269 // There can be attachments without a medium (floppy/dvd), and thus
14270 // it's impossible to create a medium lock list. It still makes sense
14271 // to have the empty medium lock list in the map in case a medium is
14272 // attached later.
14273 if (pMedium != NULL)
14274 {
14275 MediumType_T mediumType = pMedium->i_getType();
14276 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14277 || mediumType == MediumType_Shareable;
14278 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14279
14280 alock.release();
14281 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14282 !fIsReadOnlyLock /* fMediumLockWrite */,
14283 false /* fMediumLockWriteAll */,
14284 NULL,
14285 *pMediumLockList);
14286 alock.acquire();
14287 if (FAILED(mrc))
14288 {
14289 delete pMediumLockList;
14290 mData->mSession.mLockedMedia.Clear();
14291 break;
14292 }
14293 }
14294
14295 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14296 if (FAILED(rc))
14297 {
14298 mData->mSession.mLockedMedia.Clear();
14299 mrc = setError(rc,
14300 tr("Collecting locking information for all attached media failed"));
14301 break;
14302 }
14303 }
14304
14305 if (SUCCEEDED(mrc))
14306 {
14307 /* Now lock all media. If this fails, nothing is locked. */
14308 alock.release();
14309 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14310 alock.acquire();
14311 if (FAILED(rc))
14312 {
14313 mrc = setError(rc,
14314 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14315 }
14316 }
14317
14318 return mrc;
14319}
14320
14321/**
14322 * Undoes the locks made by by #lockMedia().
14323 */
14324HRESULT SessionMachine::i_unlockMedia()
14325{
14326 AutoCaller autoCaller(this);
14327 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14328
14329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14330
14331 /* we may be holding important error info on the current thread;
14332 * preserve it */
14333 ErrorInfoKeeper eik;
14334
14335 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14336 AssertComRC(rc);
14337 return rc;
14338}
14339
14340/**
14341 * Helper to change the machine state (reimplementation).
14342 *
14343 * @note Locks this object for writing.
14344 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14345 * it can cause crashes in random places due to unexpectedly committing
14346 * the current settings. The caller is responsible for that. The call
14347 * to saveStateSettings is fine, because this method does not commit.
14348 */
14349HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14350{
14351 LogFlowThisFuncEnter();
14352 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14353
14354 AutoCaller autoCaller(this);
14355 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14356
14357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14358
14359 MachineState_T oldMachineState = mData->mMachineState;
14360
14361 AssertMsgReturn(oldMachineState != aMachineState,
14362 ("oldMachineState=%s, aMachineState=%s\n",
14363 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14364 E_FAIL);
14365
14366 HRESULT rc = S_OK;
14367
14368 int stsFlags = 0;
14369 bool deleteSavedState = false;
14370
14371 /* detect some state transitions */
14372
14373 if ( ( oldMachineState == MachineState_Saved
14374 && aMachineState == MachineState_Restoring)
14375 || ( ( oldMachineState == MachineState_PoweredOff
14376 || oldMachineState == MachineState_Teleported
14377 || oldMachineState == MachineState_Aborted
14378 )
14379 && ( aMachineState == MachineState_TeleportingIn
14380 || aMachineState == MachineState_Starting
14381 )
14382 )
14383 )
14384 {
14385 /* The EMT thread is about to start */
14386
14387 /* Nothing to do here for now... */
14388
14389 /// @todo NEWMEDIA don't let mDVDDrive and other children
14390 /// change anything when in the Starting/Restoring state
14391 }
14392 else if ( ( oldMachineState == MachineState_Running
14393 || oldMachineState == MachineState_Paused
14394 || oldMachineState == MachineState_Teleporting
14395 || oldMachineState == MachineState_OnlineSnapshotting
14396 || oldMachineState == MachineState_LiveSnapshotting
14397 || oldMachineState == MachineState_Stuck
14398 || oldMachineState == MachineState_Starting
14399 || oldMachineState == MachineState_Stopping
14400 || oldMachineState == MachineState_Saving
14401 || oldMachineState == MachineState_Restoring
14402 || oldMachineState == MachineState_TeleportingPausedVM
14403 || oldMachineState == MachineState_TeleportingIn
14404 )
14405 && ( aMachineState == MachineState_PoweredOff
14406 || aMachineState == MachineState_Saved
14407 || aMachineState == MachineState_Teleported
14408 || aMachineState == MachineState_Aborted
14409 )
14410 )
14411 {
14412 /* The EMT thread has just stopped, unlock attached media. Note that as
14413 * opposed to locking that is done from Console, we do unlocking here
14414 * because the VM process may have aborted before having a chance to
14415 * properly unlock all media it locked. */
14416
14417 unlockMedia();
14418 }
14419
14420 if (oldMachineState == MachineState_Restoring)
14421 {
14422 if (aMachineState != MachineState_Saved)
14423 {
14424 /*
14425 * delete the saved state file once the machine has finished
14426 * restoring from it (note that Console sets the state from
14427 * Restoring to Saved if the VM couldn't restore successfully,
14428 * to give the user an ability to fix an error and retry --
14429 * we keep the saved state file in this case)
14430 */
14431 deleteSavedState = true;
14432 }
14433 }
14434 else if ( oldMachineState == MachineState_Saved
14435 && ( aMachineState == MachineState_PoweredOff
14436 || aMachineState == MachineState_Aborted
14437 || aMachineState == MachineState_Teleported
14438 )
14439 )
14440 {
14441 /*
14442 * delete the saved state after SessionMachine::ForgetSavedState() is called
14443 * or if the VM process (owning a direct VM session) crashed while the
14444 * VM was Saved
14445 */
14446
14447 /// @todo (dmik)
14448 // Not sure that deleting the saved state file just because of the
14449 // client death before it attempted to restore the VM is a good
14450 // thing. But when it crashes we need to go to the Aborted state
14451 // which cannot have the saved state file associated... The only
14452 // way to fix this is to make the Aborted condition not a VM state
14453 // but a bool flag: i.e., when a crash occurs, set it to true and
14454 // change the state to PoweredOff or Saved depending on the
14455 // saved state presence.
14456
14457 deleteSavedState = true;
14458 mData->mCurrentStateModified = TRUE;
14459 stsFlags |= SaveSTS_CurStateModified;
14460 }
14461
14462 if ( aMachineState == MachineState_Starting
14463 || aMachineState == MachineState_Restoring
14464 || aMachineState == MachineState_TeleportingIn
14465 )
14466 {
14467 /* set the current state modified flag to indicate that the current
14468 * state is no more identical to the state in the
14469 * current snapshot */
14470 if (!mData->mCurrentSnapshot.isNull())
14471 {
14472 mData->mCurrentStateModified = TRUE;
14473 stsFlags |= SaveSTS_CurStateModified;
14474 }
14475 }
14476
14477 if (deleteSavedState)
14478 {
14479 if (mRemoveSavedState)
14480 {
14481 Assert(!mSSData->strStateFilePath.isEmpty());
14482
14483 // it is safe to delete the saved state file if ...
14484 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14485 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14486 // ... none of the snapshots share the saved state file
14487 )
14488 RTFileDelete(mSSData->strStateFilePath.c_str());
14489 }
14490
14491 mSSData->strStateFilePath.setNull();
14492 stsFlags |= SaveSTS_StateFilePath;
14493 }
14494
14495 /* redirect to the underlying peer machine */
14496 mPeer->i_setMachineState(aMachineState);
14497
14498 if ( oldMachineState != MachineState_RestoringSnapshot
14499 && ( aMachineState == MachineState_PoweredOff
14500 || aMachineState == MachineState_Teleported
14501 || aMachineState == MachineState_Aborted
14502 || aMachineState == MachineState_Saved))
14503 {
14504 /* the machine has stopped execution
14505 * (or the saved state file was adopted) */
14506 stsFlags |= SaveSTS_StateTimeStamp;
14507 }
14508
14509 if ( ( oldMachineState == MachineState_PoweredOff
14510 || oldMachineState == MachineState_Aborted
14511 || oldMachineState == MachineState_Teleported
14512 )
14513 && aMachineState == MachineState_Saved)
14514 {
14515 /* the saved state file was adopted */
14516 Assert(!mSSData->strStateFilePath.isEmpty());
14517 stsFlags |= SaveSTS_StateFilePath;
14518 }
14519
14520#ifdef VBOX_WITH_GUEST_PROPS
14521 if ( aMachineState == MachineState_PoweredOff
14522 || aMachineState == MachineState_Aborted
14523 || aMachineState == MachineState_Teleported)
14524 {
14525 /* Make sure any transient guest properties get removed from the
14526 * property store on shutdown. */
14527 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14528
14529 /* remove it from the settings representation */
14530 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14531 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14532 it != llGuestProperties.end();
14533 /*nothing*/)
14534 {
14535 const settings::GuestProperty &prop = *it;
14536 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14537 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14538 {
14539 it = llGuestProperties.erase(it);
14540 fNeedsSaving = true;
14541 }
14542 else
14543 {
14544 ++it;
14545 }
14546 }
14547
14548 /* Additionally remove it from the HWData representation. Required to
14549 * keep everything in sync, as this is what the API keeps using. */
14550 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14551 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14552 it != llHWGuestProperties.end();
14553 /*nothing*/)
14554 {
14555 uint32_t fFlags = it->second.mFlags;
14556 if ( fFlags & guestProp::TRANSIENT
14557 || fFlags & guestProp::TRANSRESET)
14558 {
14559 /* iterator where we need to continue after the erase call
14560 * (C++03 is a fact still, and it doesn't return the iterator
14561 * which would allow continuing) */
14562 HWData::GuestPropertyMap::iterator it2 = it;
14563 ++it2;
14564 llHWGuestProperties.erase(it);
14565 it = it2;
14566 fNeedsSaving = true;
14567 }
14568 else
14569 {
14570 ++it;
14571 }
14572 }
14573
14574 if (fNeedsSaving)
14575 {
14576 mData->mCurrentStateModified = TRUE;
14577 stsFlags |= SaveSTS_CurStateModified;
14578 }
14579 }
14580#endif /* VBOX_WITH_GUEST_PROPS */
14581
14582 rc = i_saveStateSettings(stsFlags);
14583
14584 if ( ( oldMachineState != MachineState_PoweredOff
14585 && oldMachineState != MachineState_Aborted
14586 && oldMachineState != MachineState_Teleported
14587 )
14588 && ( aMachineState == MachineState_PoweredOff
14589 || aMachineState == MachineState_Aborted
14590 || aMachineState == MachineState_Teleported
14591 )
14592 )
14593 {
14594 /* we've been shut down for any reason */
14595 /* no special action so far */
14596 }
14597
14598 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14599 LogFlowThisFuncLeave();
14600 return rc;
14601}
14602
14603/**
14604 * Sends the current machine state value to the VM process.
14605 *
14606 * @note Locks this object for reading, then calls a client process.
14607 */
14608HRESULT SessionMachine::i_updateMachineStateOnClient()
14609{
14610 AutoCaller autoCaller(this);
14611 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14612
14613 ComPtr<IInternalSessionControl> directControl;
14614 {
14615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14616 AssertReturn(!!mData, E_FAIL);
14617 if (mData->mSession.mLockType == LockType_VM)
14618 directControl = mData->mSession.mDirectControl;
14619
14620 /* directControl may be already set to NULL here in #OnSessionEnd()
14621 * called too early by the direct session process while there is still
14622 * some operation (like deleting the snapshot) in progress. The client
14623 * process in this case is waiting inside Session::close() for the
14624 * "end session" process object to complete, while #uninit() called by
14625 * #checkForDeath() on the Watcher thread is waiting for the pending
14626 * operation to complete. For now, we accept this inconsistent behavior
14627 * and simply do nothing here. */
14628
14629 if (mData->mSession.mState == SessionState_Unlocking)
14630 return S_OK;
14631 }
14632
14633 /* ignore notifications sent after #OnSessionEnd() is called */
14634 if (!directControl)
14635 return S_OK;
14636
14637 return directControl->UpdateMachineState(mData->mMachineState);
14638}
14639
14640
14641/**
14642 * Static Machine method that can get passed to RTThreadCreate to
14643 * have a thread started for a Task. See Machine::Task.
14644 */
14645/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14646{
14647 AssertReturn(pvUser, VERR_INVALID_POINTER);
14648
14649 Task *pTask = static_cast<Task *>(pvUser);
14650 pTask->handler();
14651 /** @todo r=klaus it would be safer to update the progress object here,
14652 * as it avoids possible races due to scoping issues/tricks in the handler */
14653 // it's our responsibility to delete the task
14654 delete pTask;
14655
14656 return 0;
14657}
14658
14659/*static*/
14660HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14661{
14662 va_list args;
14663 va_start(args, pcszMsg);
14664 HRESULT rc = setErrorInternal(aResultCode,
14665 getStaticClassIID(),
14666 getStaticComponentName(),
14667 Utf8Str(pcszMsg, args),
14668 false /* aWarning */,
14669 true /* aLogIt */);
14670 va_end(args);
14671 return rc;
14672}
14673
14674
14675HRESULT Machine::updateState(MachineState_T aState)
14676{
14677 NOREF(aState);
14678 ReturnComNotImplemented();
14679}
14680
14681HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14682{
14683 NOREF(aProgress);
14684 ReturnComNotImplemented();
14685}
14686
14687HRESULT Machine::endPowerUp(LONG aResult)
14688{
14689 NOREF(aResult);
14690 ReturnComNotImplemented();
14691}
14692
14693HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14694{
14695 NOREF(aProgress);
14696 ReturnComNotImplemented();
14697}
14698
14699HRESULT Machine::endPoweringDown(LONG aResult,
14700 const com::Utf8Str &aErrMsg)
14701{
14702 NOREF(aResult);
14703 NOREF(aErrMsg);
14704 ReturnComNotImplemented();
14705}
14706
14707HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14708 BOOL *aMatched,
14709 ULONG *aMaskedInterfaces)
14710{
14711 NOREF(aDevice);
14712 NOREF(aMatched);
14713 NOREF(aMaskedInterfaces);
14714 ReturnComNotImplemented();
14715
14716}
14717
14718HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14719{
14720 NOREF(aId); NOREF(aCaptureFilename);
14721 ReturnComNotImplemented();
14722}
14723
14724HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14725 BOOL aDone)
14726{
14727 NOREF(aId);
14728 NOREF(aDone);
14729 ReturnComNotImplemented();
14730}
14731
14732HRESULT Machine::autoCaptureUSBDevices()
14733{
14734 ReturnComNotImplemented();
14735}
14736
14737HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14738{
14739 NOREF(aDone);
14740 ReturnComNotImplemented();
14741}
14742
14743HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14744 ComPtr<IProgress> &aProgress)
14745{
14746 NOREF(aSession);
14747 NOREF(aProgress);
14748 ReturnComNotImplemented();
14749}
14750
14751HRESULT Machine::finishOnlineMergeMedium()
14752{
14753 ReturnComNotImplemented();
14754}
14755
14756HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14757 std::vector<com::Utf8Str> &aValues,
14758 std::vector<LONG64> &aTimestamps,
14759 std::vector<com::Utf8Str> &aFlags)
14760{
14761 NOREF(aNames);
14762 NOREF(aValues);
14763 NOREF(aTimestamps);
14764 NOREF(aFlags);
14765 ReturnComNotImplemented();
14766}
14767
14768HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14769 const com::Utf8Str &aValue,
14770 LONG64 aTimestamp,
14771 const com::Utf8Str &aFlags,
14772 BOOL *aNotify)
14773{
14774 NOREF(aName);
14775 NOREF(aValue);
14776 NOREF(aTimestamp);
14777 NOREF(aFlags);
14778 NOREF(aNotify);
14779 ReturnComNotImplemented();
14780}
14781
14782HRESULT Machine::lockMedia()
14783{
14784 ReturnComNotImplemented();
14785}
14786
14787HRESULT Machine::unlockMedia()
14788{
14789 ReturnComNotImplemented();
14790}
14791
14792HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14793 ComPtr<IMediumAttachment> &aNewAttachment)
14794{
14795 NOREF(aAttachment);
14796 NOREF(aNewAttachment);
14797 ReturnComNotImplemented();
14798}
14799
14800HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14801 ULONG aCpuUser,
14802 ULONG aCpuKernel,
14803 ULONG aCpuIdle,
14804 ULONG aMemTotal,
14805 ULONG aMemFree,
14806 ULONG aMemBalloon,
14807 ULONG aMemShared,
14808 ULONG aMemCache,
14809 ULONG aPagedTotal,
14810 ULONG aMemAllocTotal,
14811 ULONG aMemFreeTotal,
14812 ULONG aMemBalloonTotal,
14813 ULONG aMemSharedTotal,
14814 ULONG aVmNetRx,
14815 ULONG aVmNetTx)
14816{
14817 NOREF(aValidStats);
14818 NOREF(aCpuUser);
14819 NOREF(aCpuKernel);
14820 NOREF(aCpuIdle);
14821 NOREF(aMemTotal);
14822 NOREF(aMemFree);
14823 NOREF(aMemBalloon);
14824 NOREF(aMemShared);
14825 NOREF(aMemCache);
14826 NOREF(aPagedTotal);
14827 NOREF(aMemAllocTotal);
14828 NOREF(aMemFreeTotal);
14829 NOREF(aMemBalloonTotal);
14830 NOREF(aMemSharedTotal);
14831 NOREF(aVmNetRx);
14832 NOREF(aVmNetTx);
14833 ReturnComNotImplemented();
14834}
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