VirtualBox

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

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

Main/Session+Machine: small API cleanup, introducing explicit session names and eliminating some terminology confusion (it never was a session type, which is a totally different thing)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 509.6 KB
Line 
1/* $Id: MachineImpl.cpp 55800 2015-05-11 14:09:09Z 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(CleanupMode_T aCleanupMode,
5068 std::vector<ComPtr<IMedium> > &aMedia)
5069{
5070 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5071 AutoLimitedCaller autoCaller(this);
5072 AssertComRCReturnRC(autoCaller.rc());
5073
5074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5075
5076 Guid id(i_getId());
5077
5078 if (mData->mSession.mState != SessionState_Unlocked)
5079 return setError(VBOX_E_INVALID_OBJECT_STATE,
5080 tr("Cannot unregister the machine '%s' while it is locked"),
5081 mUserData->s.strName.c_str());
5082
5083 // wait for state dependents to drop to zero
5084 i_ensureNoStateDependencies();
5085
5086 if (!mData->mAccessible)
5087 {
5088 // inaccessible maschines can only be unregistered; uninitialize ourselves
5089 // here because currently there may be no unregistered that are inaccessible
5090 // (this state combination is not supported). Note releasing the caller and
5091 // leaving the lock before calling uninit()
5092 alock.release();
5093 autoCaller.release();
5094
5095 uninit();
5096
5097 mParent->i_unregisterMachine(this, id);
5098 // calls VirtualBox::i_saveSettings()
5099
5100 return S_OK;
5101 }
5102
5103 HRESULT rc = S_OK;
5104
5105 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5106 // discard saved state
5107 if (mData->mMachineState == MachineState_Saved)
5108 {
5109 // add the saved state file to the list of files the caller should delete
5110 Assert(!mSSData->strStateFilePath.isEmpty());
5111 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5112
5113 mSSData->strStateFilePath.setNull();
5114
5115 // unconditionally set the machine state to powered off, we now
5116 // know no session has locked the machine
5117 mData->mMachineState = MachineState_PoweredOff;
5118 }
5119
5120 size_t cSnapshots = 0;
5121 if (mData->mFirstSnapshot)
5122 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5123 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5124 // fail now before we start detaching media
5125 return setError(VBOX_E_INVALID_OBJECT_STATE,
5126 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5127 mUserData->s.strName.c_str(), cSnapshots);
5128
5129 // This list collects the medium objects from all medium attachments
5130 // which we will detach from the machine and its snapshots, in a specific
5131 // order which allows for closing all media without getting "media in use"
5132 // errors, simply by going through the list from the front to the back:
5133 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5134 // and must be closed before the parent media from the snapshots, or closing the parents
5135 // will fail because they still have children);
5136 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5137 // the root ("first") snapshot of the machine.
5138 MediaList llMedia;
5139
5140 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5141 && mMediaData->mAttachments.size()
5142 )
5143 {
5144 // we have media attachments: detach them all and add the Medium objects to our list
5145 if (aCleanupMode != CleanupMode_UnregisterOnly)
5146 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5147 else
5148 return setError(VBOX_E_INVALID_OBJECT_STATE,
5149 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5150 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5151 }
5152
5153 if (cSnapshots)
5154 {
5155 // add the media from the medium attachments of the snapshots to llMedia
5156 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5157 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5158 // into the children first
5159
5160 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5161 MachineState_T oldState = mData->mMachineState;
5162 mData->mMachineState = MachineState_DeletingSnapshot;
5163
5164 // make a copy of the first snapshot so the refcount does not drop to 0
5165 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5166 // because of the AutoCaller voodoo)
5167 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5168
5169 // GO!
5170 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5171
5172 mData->mMachineState = oldState;
5173 }
5174
5175 if (FAILED(rc))
5176 {
5177 i_rollbackMedia();
5178 return rc;
5179 }
5180
5181 // commit all the media changes made above
5182 i_commitMedia();
5183
5184 mData->mRegistered = false;
5185
5186 // machine lock no longer needed
5187 alock.release();
5188
5189 // return media to caller
5190 size_t i = 0;
5191 aMedia.resize(llMedia.size());
5192 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5193 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5194
5195 mParent->i_unregisterMachine(this, id);
5196 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5197
5198 return S_OK;
5199}
5200
5201/**
5202 * Task record for deleting a machine config.
5203 */
5204struct Machine::DeleteConfigTask
5205 : public Machine::Task
5206{
5207 DeleteConfigTask(Machine *m,
5208 Progress *p,
5209 const Utf8Str &t,
5210 const RTCList<ComPtr<IMedium> > &llMediums,
5211 const StringsList &llFilesToDelete)
5212 : Task(m, p, t),
5213 m_llMediums(llMediums),
5214 m_llFilesToDelete(llFilesToDelete)
5215 {}
5216
5217 void handler()
5218 {
5219 m_pMachine->i_deleteConfigHandler(*this);
5220 }
5221
5222 RTCList<ComPtr<IMedium> > m_llMediums;
5223 StringsList m_llFilesToDelete;
5224};
5225
5226/**
5227 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5228 * SessionMachine::taskHandler().
5229 *
5230 * @note Locks this object for writing.
5231 *
5232 * @param task
5233 * @return
5234 */
5235void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5236{
5237 LogFlowThisFuncEnter();
5238
5239 AutoCaller autoCaller(this);
5240 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5241 if (FAILED(autoCaller.rc()))
5242 {
5243 /* we might have been uninitialized because the session was accidentally
5244 * closed by the client, so don't assert */
5245 HRESULT rc = setError(E_FAIL,
5246 tr("The session has been accidentally closed"));
5247 task.m_pProgress->i_notifyComplete(rc);
5248 LogFlowThisFuncLeave();
5249 return;
5250 }
5251
5252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5253
5254 HRESULT rc = S_OK;
5255
5256 try
5257 {
5258 ULONG uLogHistoryCount = 3;
5259 ComPtr<ISystemProperties> systemProperties;
5260 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5261 if (FAILED(rc)) throw rc;
5262
5263 if (!systemProperties.isNull())
5264 {
5265 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5266 if (FAILED(rc)) throw rc;
5267 }
5268
5269 MachineState_T oldState = mData->mMachineState;
5270 i_setMachineState(MachineState_SettingUp);
5271 alock.release();
5272 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5273 {
5274 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5275 {
5276 AutoCaller mac(pMedium);
5277 if (FAILED(mac.rc())) throw mac.rc();
5278 Utf8Str strLocation = pMedium->i_getLocationFull();
5279 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5280 if (FAILED(rc)) throw rc;
5281 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5282 }
5283 if (pMedium->i_isMediumFormatFile())
5284 {
5285 ComPtr<IProgress> pProgress2;
5286 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5287 if (FAILED(rc)) throw rc;
5288 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5289 if (FAILED(rc)) throw rc;
5290 /* Check the result of the asynchronous process. */
5291 LONG iRc;
5292 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5293 if (FAILED(rc)) throw rc;
5294 /* If the thread of the progress object has an error, then
5295 * retrieve the error info from there, or it'll be lost. */
5296 if (FAILED(iRc))
5297 throw setError(ProgressErrorInfo(pProgress2));
5298 }
5299
5300 /* Close the medium, deliberately without checking the return
5301 * code, and without leaving any trace in the error info, as
5302 * a failure here is a very minor issue, which shouldn't happen
5303 * as above we even managed to delete the medium. */
5304 {
5305 ErrorInfoKeeper eik;
5306 pMedium->Close();
5307 }
5308 }
5309 i_setMachineState(oldState);
5310 alock.acquire();
5311
5312 // delete the files pushed on the task list by Machine::Delete()
5313 // (this includes saved states of the machine and snapshots and
5314 // medium storage files from the IMedium list passed in, and the
5315 // machine XML file)
5316 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5317 while (it != task.m_llFilesToDelete.end())
5318 {
5319 const Utf8Str &strFile = *it;
5320 LogFunc(("Deleting file %s\n", strFile.c_str()));
5321 int vrc = RTFileDelete(strFile.c_str());
5322 if (RT_FAILURE(vrc))
5323 throw setError(VBOX_E_IPRT_ERROR,
5324 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5325
5326 ++it;
5327 if (it == task.m_llFilesToDelete.end())
5328 {
5329 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5330 if (FAILED(rc)) throw rc;
5331 break;
5332 }
5333
5334 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5335 if (FAILED(rc)) throw rc;
5336 }
5337
5338 /* delete the settings only when the file actually exists */
5339 if (mData->pMachineConfigFile->fileExists())
5340 {
5341 /* Delete any backup or uncommitted XML files. Ignore failures.
5342 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5343 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5344 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5345 RTFileDelete(otherXml.c_str());
5346 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5347 RTFileDelete(otherXml.c_str());
5348
5349 /* delete the Logs folder, nothing important should be left
5350 * there (we don't check for errors because the user might have
5351 * some private files there that we don't want to delete) */
5352 Utf8Str logFolder;
5353 getLogFolder(logFolder);
5354 Assert(logFolder.length());
5355 if (RTDirExists(logFolder.c_str()))
5356 {
5357 /* Delete all VBox.log[.N] files from the Logs folder
5358 * (this must be in sync with the rotation logic in
5359 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5360 * files that may have been created by the GUI. */
5361 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5362 logFolder.c_str(), RTPATH_DELIMITER);
5363 RTFileDelete(log.c_str());
5364 log = Utf8StrFmt("%s%cVBox.png",
5365 logFolder.c_str(), RTPATH_DELIMITER);
5366 RTFileDelete(log.c_str());
5367 for (int i = uLogHistoryCount; i > 0; i--)
5368 {
5369 log = Utf8StrFmt("%s%cVBox.log.%d",
5370 logFolder.c_str(), RTPATH_DELIMITER, i);
5371 RTFileDelete(log.c_str());
5372 log = Utf8StrFmt("%s%cVBox.png.%d",
5373 logFolder.c_str(), RTPATH_DELIMITER, i);
5374 RTFileDelete(log.c_str());
5375 }
5376#if defined(RT_OS_WINDOWS)
5377 log = Utf8StrFmt("%s%cVBoxStartup.log",
5378 logFolder.c_str(), RTPATH_DELIMITER);
5379 RTFileDelete(log.c_str());
5380#endif
5381
5382 RTDirRemove(logFolder.c_str());
5383 }
5384
5385 /* delete the Snapshots folder, nothing important should be left
5386 * there (we don't check for errors because the user might have
5387 * some private files there that we don't want to delete) */
5388 Utf8Str strFullSnapshotFolder;
5389 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5390 Assert(!strFullSnapshotFolder.isEmpty());
5391 if (RTDirExists(strFullSnapshotFolder.c_str()))
5392 RTDirRemove(strFullSnapshotFolder.c_str());
5393
5394 // delete the directory that contains the settings file, but only
5395 // if it matches the VM name
5396 Utf8Str settingsDir;
5397 if (i_isInOwnDir(&settingsDir))
5398 RTDirRemove(settingsDir.c_str());
5399 }
5400
5401 alock.release();
5402
5403 mParent->i_saveModifiedRegistries();
5404 }
5405 catch (HRESULT aRC) { rc = aRC; }
5406
5407 task.m_pProgress->i_notifyComplete(rc);
5408
5409 LogFlowThisFuncLeave();
5410}
5411
5412HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5413{
5414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5415
5416 HRESULT rc = i_checkStateDependency(MutableStateDep);
5417 if (FAILED(rc)) return rc;
5418
5419 if (mData->mRegistered)
5420 return setError(VBOX_E_INVALID_VM_STATE,
5421 tr("Cannot delete settings of a registered machine"));
5422
5423 // collect files to delete
5424 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5425 if (mData->pMachineConfigFile->fileExists())
5426 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5427
5428 RTCList<ComPtr<IMedium> > llMediums;
5429 for (size_t i = 0; i < aMedia.size(); ++i)
5430 {
5431 IMedium *pIMedium(aMedia[i]);
5432 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5433 if (pMedium.isNull())
5434 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5435 SafeArray<BSTR> ids;
5436 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5437 if (FAILED(rc)) return rc;
5438 /* At this point the medium should not have any back references
5439 * anymore. If it has it is attached to another VM and *must* not
5440 * deleted. */
5441 if (ids.size() < 1)
5442 llMediums.append(pMedium);
5443 }
5444
5445 ComObjPtr<Progress> pProgress;
5446 pProgress.createObject();
5447 rc = pProgress->init(i_getVirtualBox(),
5448 static_cast<IMachine*>(this) /* aInitiator */,
5449 Bstr(tr("Deleting files")).raw(),
5450 true /* fCancellable */,
5451 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5452 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5453 if (FAILED(rc))
5454 return rc;
5455
5456 /* create and start the task on a separate thread (note that it will not
5457 * start working until we release alock) */
5458 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5459 rc = pTask->createThread();
5460 if (FAILED(rc))
5461 return rc;
5462
5463 pProgress.queryInterfaceTo(aProgress.asOutParam());
5464
5465 LogFlowFuncLeave();
5466
5467 return S_OK;
5468}
5469
5470HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5471{
5472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5473
5474 ComObjPtr<Snapshot> pSnapshot;
5475 HRESULT rc;
5476
5477 if (aNameOrId.isEmpty())
5478 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5479 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5480 else
5481 {
5482 Guid uuid(aNameOrId);
5483 if (uuid.isValid())
5484 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5485 else
5486 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5487 }
5488 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5489
5490 return rc;
5491}
5492
5493HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5494{
5495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5498 if (FAILED(rc)) return rc;
5499
5500 ComObjPtr<SharedFolder> sharedFolder;
5501 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5502 if (SUCCEEDED(rc))
5503 return setError(VBOX_E_OBJECT_IN_USE,
5504 tr("Shared folder named '%s' already exists"),
5505 aName.c_str());
5506
5507 sharedFolder.createObject();
5508 rc = sharedFolder->init(i_getMachine(),
5509 aName,
5510 aHostPath,
5511 !!aWritable,
5512 !!aAutomount,
5513 true /* fFailOnError */);
5514 if (FAILED(rc)) return rc;
5515
5516 i_setModified(IsModified_SharedFolders);
5517 mHWData.backup();
5518 mHWData->mSharedFolders.push_back(sharedFolder);
5519
5520 /* inform the direct session if any */
5521 alock.release();
5522 i_onSharedFolderChange();
5523
5524 return S_OK;
5525}
5526
5527HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5528{
5529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5530
5531 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5532 if (FAILED(rc)) return rc;
5533
5534 ComObjPtr<SharedFolder> sharedFolder;
5535 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5536 if (FAILED(rc)) return rc;
5537
5538 i_setModified(IsModified_SharedFolders);
5539 mHWData.backup();
5540 mHWData->mSharedFolders.remove(sharedFolder);
5541
5542 /* inform the direct session if any */
5543 alock.release();
5544 i_onSharedFolderChange();
5545
5546 return S_OK;
5547}
5548
5549HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5550{
5551 /* start with No */
5552 *aCanShow = FALSE;
5553
5554 ComPtr<IInternalSessionControl> directControl;
5555 {
5556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5557
5558 if (mData->mSession.mState != SessionState_Locked)
5559 return setError(VBOX_E_INVALID_VM_STATE,
5560 tr("Machine is not locked for session (session state: %s)"),
5561 Global::stringifySessionState(mData->mSession.mState));
5562
5563 if (mData->mSession.mLockType == LockType_VM)
5564 directControl = mData->mSession.mDirectControl;
5565 }
5566
5567 /* ignore calls made after #OnSessionEnd() is called */
5568 if (!directControl)
5569 return S_OK;
5570
5571 LONG64 dummy;
5572 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5573}
5574
5575HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5576{
5577 ComPtr<IInternalSessionControl> directControl;
5578 {
5579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5580
5581 if (mData->mSession.mState != SessionState_Locked)
5582 return setError(E_FAIL,
5583 tr("Machine is not locked for session (session state: %s)"),
5584 Global::stringifySessionState(mData->mSession.mState));
5585
5586 if (mData->mSession.mLockType == LockType_VM)
5587 directControl = mData->mSession.mDirectControl;
5588 }
5589
5590 /* ignore calls made after #OnSessionEnd() is called */
5591 if (!directControl)
5592 return S_OK;
5593
5594 BOOL dummy;
5595 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5596}
5597
5598#ifdef VBOX_WITH_GUEST_PROPS
5599/**
5600 * Look up a guest property in VBoxSVC's internal structures.
5601 */
5602HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5603 com::Utf8Str &aValue,
5604 LONG64 *aTimestamp,
5605 com::Utf8Str &aFlags) const
5606{
5607 using namespace guestProp;
5608
5609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5610 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5611
5612 if (it != mHWData->mGuestProperties.end())
5613 {
5614 char szFlags[MAX_FLAGS_LEN + 1];
5615 aValue = it->second.strValue;
5616 *aTimestamp = it->second.mTimestamp;
5617 writeFlags(it->second.mFlags, szFlags);
5618 aFlags = Utf8Str(szFlags);
5619 }
5620
5621 return S_OK;
5622}
5623
5624/**
5625 * Query the VM that a guest property belongs to for the property.
5626 * @returns E_ACCESSDENIED if the VM process is not available or not
5627 * currently handling queries and the lookup should then be done in
5628 * VBoxSVC.
5629 */
5630HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5631 com::Utf8Str &aValue,
5632 LONG64 *aTimestamp,
5633 com::Utf8Str &aFlags) const
5634{
5635 HRESULT rc = S_OK;
5636 BSTR bValue = NULL;
5637 BSTR bFlags = NULL;
5638
5639 ComPtr<IInternalSessionControl> directControl;
5640 {
5641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5642 if (mData->mSession.mLockType == LockType_VM)
5643 directControl = mData->mSession.mDirectControl;
5644 }
5645
5646 /* ignore calls made after #OnSessionEnd() is called */
5647 if (!directControl)
5648 rc = E_ACCESSDENIED;
5649 else
5650 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5651 0 /* accessMode */,
5652 &bValue, aTimestamp, &bFlags);
5653
5654 aValue = bValue;
5655 aFlags = bFlags;
5656
5657 return rc;
5658}
5659#endif // VBOX_WITH_GUEST_PROPS
5660
5661HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5662 com::Utf8Str &aValue,
5663 LONG64 *aTimestamp,
5664 com::Utf8Str &aFlags)
5665{
5666#ifndef VBOX_WITH_GUEST_PROPS
5667 ReturnComNotImplemented();
5668#else // VBOX_WITH_GUEST_PROPS
5669
5670 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5671
5672 if (rc == E_ACCESSDENIED)
5673 /* The VM is not running or the service is not (yet) accessible */
5674 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5675 return rc;
5676#endif // VBOX_WITH_GUEST_PROPS
5677}
5678
5679HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5680{
5681 LONG64 dummyTimestamp;
5682 com::Utf8Str dummyFlags;
5683 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5684 return rc;
5685
5686}
5687HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5688{
5689 com::Utf8Str dummyFlags;
5690 com::Utf8Str dummyValue;
5691 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5692 return rc;
5693}
5694
5695#ifdef VBOX_WITH_GUEST_PROPS
5696/**
5697 * Set a guest property in VBoxSVC's internal structures.
5698 */
5699HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5700 const com::Utf8Str &aFlags, bool fDelete)
5701{
5702 using namespace guestProp;
5703
5704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5705 HRESULT rc = S_OK;
5706
5707 rc = i_checkStateDependency(MutableOrSavedStateDep);
5708 if (FAILED(rc)) return rc;
5709
5710 try
5711 {
5712 uint32_t fFlags = NILFLAG;
5713 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5714 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5715
5716 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5717 if (it == mHWData->mGuestProperties.end())
5718 {
5719 if (!fDelete)
5720 {
5721 i_setModified(IsModified_MachineData);
5722 mHWData.backupEx();
5723
5724 RTTIMESPEC time;
5725 HWData::GuestProperty prop;
5726 prop.strValue = Bstr(aValue).raw();
5727 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5728 prop.mFlags = fFlags;
5729 mHWData->mGuestProperties[aName] = prop;
5730 }
5731 }
5732 else
5733 {
5734 if (it->second.mFlags & (RDONLYHOST))
5735 {
5736 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5737 }
5738 else
5739 {
5740 i_setModified(IsModified_MachineData);
5741 mHWData.backupEx();
5742
5743 /* The backupEx() operation invalidates our iterator,
5744 * so get a new one. */
5745 it = mHWData->mGuestProperties.find(aName);
5746 Assert(it != mHWData->mGuestProperties.end());
5747
5748 if (!fDelete)
5749 {
5750 RTTIMESPEC time;
5751 it->second.strValue = aValue;
5752 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5753 it->second.mFlags = fFlags;
5754 }
5755 else
5756 mHWData->mGuestProperties.erase(it);
5757 }
5758 }
5759
5760 if ( SUCCEEDED(rc)
5761 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5762 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5763 RTSTR_MAX,
5764 aName.c_str(),
5765 RTSTR_MAX,
5766 NULL)
5767 )
5768 )
5769 {
5770 alock.release();
5771
5772 mParent->i_onGuestPropertyChange(mData->mUuid,
5773 Bstr(aName).raw(),
5774 Bstr(aValue).raw(),
5775 Bstr(aFlags).raw());
5776 }
5777 }
5778 catch (std::bad_alloc &)
5779 {
5780 rc = E_OUTOFMEMORY;
5781 }
5782
5783 return rc;
5784}
5785
5786/**
5787 * Set a property on the VM that that property belongs to.
5788 * @returns E_ACCESSDENIED if the VM process is not available or not
5789 * currently handling queries and the setting should then be done in
5790 * VBoxSVC.
5791 */
5792HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5793 const com::Utf8Str &aFlags, bool fDelete)
5794{
5795 HRESULT rc;
5796
5797 try
5798 {
5799 ComPtr<IInternalSessionControl> directControl;
5800 {
5801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5802 if (mData->mSession.mLockType == LockType_VM)
5803 directControl = mData->mSession.mDirectControl;
5804 }
5805
5806 BSTR dummy = NULL; /* will not be changed (setter) */
5807 LONG64 dummy64;
5808 if (!directControl)
5809 rc = E_ACCESSDENIED;
5810 else
5811 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5812 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5813 fDelete? 2: 1 /* accessMode */,
5814 &dummy, &dummy64, &dummy);
5815 }
5816 catch (std::bad_alloc &)
5817 {
5818 rc = E_OUTOFMEMORY;
5819 }
5820
5821 return rc;
5822}
5823#endif // VBOX_WITH_GUEST_PROPS
5824
5825HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5826 const com::Utf8Str &aFlags)
5827{
5828#ifndef VBOX_WITH_GUEST_PROPS
5829 ReturnComNotImplemented();
5830#else // VBOX_WITH_GUEST_PROPS
5831 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5832 if (rc == E_ACCESSDENIED)
5833 /* The VM is not running or the service is not (yet) accessible */
5834 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5835 return rc;
5836#endif // VBOX_WITH_GUEST_PROPS
5837}
5838
5839HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5840{
5841 return setGuestProperty(aProperty, aValue, "");
5842}
5843
5844HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5845{
5846#ifndef VBOX_WITH_GUEST_PROPS
5847 ReturnComNotImplemented();
5848#else // VBOX_WITH_GUEST_PROPS
5849 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5850 if (rc == E_ACCESSDENIED)
5851 /* The VM is not running or the service is not (yet) accessible */
5852 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5853 return rc;
5854#endif // VBOX_WITH_GUEST_PROPS
5855}
5856
5857#ifdef VBOX_WITH_GUEST_PROPS
5858/**
5859 * Enumerate the guest properties in VBoxSVC's internal structures.
5860 */
5861HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5862 std::vector<com::Utf8Str> &aNames,
5863 std::vector<com::Utf8Str> &aValues,
5864 std::vector<LONG64> &aTimestamps,
5865 std::vector<com::Utf8Str> &aFlags)
5866{
5867 using namespace guestProp;
5868
5869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5870 Utf8Str strPatterns(aPatterns);
5871
5872 HWData::GuestPropertyMap propMap;
5873
5874 /*
5875 * Look for matching patterns and build up a list.
5876 */
5877 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5878 while (it != mHWData->mGuestProperties.end())
5879 {
5880 if ( strPatterns.isEmpty()
5881 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5882 RTSTR_MAX,
5883 it->first.c_str(),
5884 RTSTR_MAX,
5885 NULL)
5886 )
5887 propMap.insert(*it);
5888 ++it;
5889 }
5890
5891 alock.release();
5892
5893 /*
5894 * And build up the arrays for returning the property information.
5895 */
5896 size_t cEntries = propMap.size();
5897
5898 aNames.resize(cEntries);
5899 aValues.resize(cEntries);
5900 aTimestamps.resize(cEntries);
5901 aFlags.resize(cEntries);
5902
5903 char szFlags[MAX_FLAGS_LEN + 1];
5904 size_t i= 0;
5905 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5906 {
5907 aNames[i] = it->first;
5908 aValues[i] = it->second.strValue;
5909 aTimestamps[i] = it->second.mTimestamp;
5910 writeFlags(it->second.mFlags, szFlags);
5911 aFlags[i] = Utf8Str(szFlags);
5912 }
5913
5914 return S_OK;
5915}
5916
5917/**
5918 * Enumerate the properties managed by a VM.
5919 * @returns E_ACCESSDENIED if the VM process is not available or not
5920 * currently handling queries and the setting should then be done in
5921 * VBoxSVC.
5922 */
5923HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5924 std::vector<com::Utf8Str> &aNames,
5925 std::vector<com::Utf8Str> &aValues,
5926 std::vector<LONG64> &aTimestamps,
5927 std::vector<com::Utf8Str> &aFlags)
5928{
5929 HRESULT rc;
5930 ComPtr<IInternalSessionControl> directControl;
5931 {
5932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5933 if (mData->mSession.mLockType == LockType_VM)
5934 directControl = mData->mSession.mDirectControl;
5935 }
5936
5937 com::SafeArray<BSTR> bNames;
5938 com::SafeArray<BSTR> bValues;
5939 com::SafeArray<LONG64> bTimestamps;
5940 com::SafeArray<BSTR> bFlags;
5941
5942 if (!directControl)
5943 rc = E_ACCESSDENIED;
5944 else
5945 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5946 ComSafeArrayAsOutParam(bNames),
5947 ComSafeArrayAsOutParam(bValues),
5948 ComSafeArrayAsOutParam(bTimestamps),
5949 ComSafeArrayAsOutParam(bFlags));
5950 size_t i;
5951 aNames.resize(bNames.size());
5952 for (i = 0; i < bNames.size(); ++i)
5953 aNames[i] = Utf8Str(bNames[i]);
5954 aValues.resize(bValues.size());
5955 for (i = 0; i < bValues.size(); ++i)
5956 aValues[i] = Utf8Str(bValues[i]);
5957 aTimestamps.resize(bTimestamps.size());
5958 for (i = 0; i < bTimestamps.size(); ++i)
5959 aTimestamps[i] = bTimestamps[i];
5960 aFlags.resize(bFlags.size());
5961 for (i = 0; i < bFlags.size(); ++i)
5962 aFlags[i] = Utf8Str(bFlags[i]);
5963
5964 return rc;
5965}
5966#endif // VBOX_WITH_GUEST_PROPS
5967HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5968 std::vector<com::Utf8Str> &aNames,
5969 std::vector<com::Utf8Str> &aValues,
5970 std::vector<LONG64> &aTimestamps,
5971 std::vector<com::Utf8Str> &aFlags)
5972{
5973#ifndef VBOX_WITH_GUEST_PROPS
5974 ReturnComNotImplemented();
5975#else // VBOX_WITH_GUEST_PROPS
5976
5977 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5978
5979 if (rc == E_ACCESSDENIED)
5980 /* The VM is not running or the service is not (yet) accessible */
5981 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5982 return rc;
5983#endif // VBOX_WITH_GUEST_PROPS
5984}
5985
5986HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5987 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5988{
5989 MediaData::AttachmentList atts;
5990
5991 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5992 if (FAILED(rc)) return rc;
5993
5994 size_t i = 0;
5995 aMediumAttachments.resize(atts.size());
5996 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5997 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5998
5999 return S_OK;
6000}
6001
6002HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6003 LONG aControllerPort,
6004 LONG aDevice,
6005 ComPtr<IMediumAttachment> &aAttachment)
6006{
6007 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6008 aName.c_str(), aControllerPort, aDevice));
6009
6010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6011
6012 aAttachment = NULL;
6013
6014 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6015 Bstr(aName).raw(),
6016 aControllerPort,
6017 aDevice);
6018 if (pAttach.isNull())
6019 return setError(VBOX_E_OBJECT_NOT_FOUND,
6020 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6021 aDevice, aControllerPort, aName.c_str());
6022
6023 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6024
6025 return S_OK;
6026}
6027
6028
6029HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6030 StorageBus_T aConnectionType,
6031 ComPtr<IStorageController> &aController)
6032{
6033 if ( (aConnectionType <= StorageBus_Null)
6034 || (aConnectionType > StorageBus_USB))
6035 return setError(E_INVALIDARG,
6036 tr("Invalid connection type: %d"),
6037 aConnectionType);
6038
6039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6040
6041 HRESULT rc = i_checkStateDependency(MutableStateDep);
6042 if (FAILED(rc)) return rc;
6043
6044 /* try to find one with the name first. */
6045 ComObjPtr<StorageController> ctrl;
6046
6047 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6048 if (SUCCEEDED(rc))
6049 return setError(VBOX_E_OBJECT_IN_USE,
6050 tr("Storage controller named '%s' already exists"),
6051 aName.c_str());
6052
6053 ctrl.createObject();
6054
6055 /* get a new instance number for the storage controller */
6056 ULONG ulInstance = 0;
6057 bool fBootable = true;
6058 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6059 it != mStorageControllers->end();
6060 ++it)
6061 {
6062 if ((*it)->i_getStorageBus() == aConnectionType)
6063 {
6064 ULONG ulCurInst = (*it)->i_getInstance();
6065
6066 if (ulCurInst >= ulInstance)
6067 ulInstance = ulCurInst + 1;
6068
6069 /* Only one controller of each type can be marked as bootable. */
6070 if ((*it)->i_getBootable())
6071 fBootable = false;
6072 }
6073 }
6074
6075 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6076 if (FAILED(rc)) return rc;
6077
6078 i_setModified(IsModified_Storage);
6079 mStorageControllers.backup();
6080 mStorageControllers->push_back(ctrl);
6081
6082 ctrl.queryInterfaceTo(aController.asOutParam());
6083
6084 /* inform the direct session if any */
6085 alock.release();
6086 i_onStorageControllerChange();
6087
6088 return S_OK;
6089}
6090
6091HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6092 ComPtr<IStorageController> &aStorageController)
6093{
6094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6095
6096 ComObjPtr<StorageController> ctrl;
6097
6098 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6099 if (SUCCEEDED(rc))
6100 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6101
6102 return rc;
6103}
6104
6105HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6106 ComPtr<IStorageController> &aStorageController)
6107{
6108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6109
6110 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6111 it != mStorageControllers->end();
6112 ++it)
6113 {
6114 if ((*it)->i_getInstance() == aInstance)
6115 {
6116 (*it).queryInterfaceTo(aStorageController.asOutParam());
6117 return S_OK;
6118 }
6119 }
6120
6121 return setError(VBOX_E_OBJECT_NOT_FOUND,
6122 tr("Could not find a storage controller with instance number '%lu'"),
6123 aInstance);
6124}
6125
6126HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6127{
6128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6129
6130 HRESULT rc = i_checkStateDependency(MutableStateDep);
6131 if (FAILED(rc)) return rc;
6132
6133 ComObjPtr<StorageController> ctrl;
6134
6135 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6136 if (SUCCEEDED(rc))
6137 {
6138 /* Ensure that only one controller of each type is marked as bootable. */
6139 if (aBootable == TRUE)
6140 {
6141 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6142 it != mStorageControllers->end();
6143 ++it)
6144 {
6145 ComObjPtr<StorageController> aCtrl = (*it);
6146
6147 if ( (aCtrl->i_getName() != aName)
6148 && aCtrl->i_getBootable() == TRUE
6149 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6150 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6151 {
6152 aCtrl->i_setBootable(FALSE);
6153 break;
6154 }
6155 }
6156 }
6157
6158 if (SUCCEEDED(rc))
6159 {
6160 ctrl->i_setBootable(aBootable);
6161 i_setModified(IsModified_Storage);
6162 }
6163 }
6164
6165 if (SUCCEEDED(rc))
6166 {
6167 /* inform the direct session if any */
6168 alock.release();
6169 i_onStorageControllerChange();
6170 }
6171
6172 return rc;
6173}
6174
6175HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6176{
6177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6178
6179 HRESULT rc = i_checkStateDependency(MutableStateDep);
6180 if (FAILED(rc)) return rc;
6181
6182 ComObjPtr<StorageController> ctrl;
6183 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6184 if (FAILED(rc)) return rc;
6185
6186 {
6187 /* find all attached devices to the appropriate storage controller and detach them all */
6188 // make a temporary list because detachDevice invalidates iterators into
6189 // mMediaData->mAttachments
6190 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6191
6192 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6193 it != llAttachments2.end();
6194 ++it)
6195 {
6196 MediumAttachment *pAttachTemp = *it;
6197
6198 AutoCaller localAutoCaller(pAttachTemp);
6199 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6200
6201 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6202
6203 if (pAttachTemp->i_getControllerName() == aName)
6204 {
6205 rc = i_detachDevice(pAttachTemp, alock, NULL);
6206 if (FAILED(rc)) return rc;
6207 }
6208 }
6209 }
6210
6211 /* We can remove it now. */
6212 i_setModified(IsModified_Storage);
6213 mStorageControllers.backup();
6214
6215 ctrl->i_unshare();
6216
6217 mStorageControllers->remove(ctrl);
6218
6219 /* inform the direct session if any */
6220 alock.release();
6221 i_onStorageControllerChange();
6222
6223 return S_OK;
6224}
6225
6226HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6227 ComPtr<IUSBController> &aController)
6228{
6229 if ( (aType <= USBControllerType_Null)
6230 || (aType >= USBControllerType_Last))
6231 return setError(E_INVALIDARG,
6232 tr("Invalid USB controller type: %d"),
6233 aType);
6234
6235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6236
6237 HRESULT rc = i_checkStateDependency(MutableStateDep);
6238 if (FAILED(rc)) return rc;
6239
6240 /* try to find one with the same type first. */
6241 ComObjPtr<USBController> ctrl;
6242
6243 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6244 if (SUCCEEDED(rc))
6245 return setError(VBOX_E_OBJECT_IN_USE,
6246 tr("USB controller named '%s' already exists"),
6247 aName.c_str());
6248
6249 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6250 ULONG maxInstances;
6251 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6252 if (FAILED(rc))
6253 return rc;
6254
6255 ULONG cInstances = i_getUSBControllerCountByType(aType);
6256 if (cInstances >= maxInstances)
6257 return setError(E_INVALIDARG,
6258 tr("Too many USB controllers of this type"));
6259
6260 ctrl.createObject();
6261
6262 rc = ctrl->init(this, aName, aType);
6263 if (FAILED(rc)) return rc;
6264
6265 i_setModified(IsModified_USB);
6266 mUSBControllers.backup();
6267 mUSBControllers->push_back(ctrl);
6268
6269 ctrl.queryInterfaceTo(aController.asOutParam());
6270
6271 /* inform the direct session if any */
6272 alock.release();
6273 i_onUSBControllerChange();
6274
6275 return S_OK;
6276}
6277
6278HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6279{
6280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6281
6282 ComObjPtr<USBController> ctrl;
6283
6284 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6285 if (SUCCEEDED(rc))
6286 ctrl.queryInterfaceTo(aController.asOutParam());
6287
6288 return rc;
6289}
6290
6291HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6292 ULONG *aControllers)
6293{
6294 if ( (aType <= USBControllerType_Null)
6295 || (aType >= USBControllerType_Last))
6296 return setError(E_INVALIDARG,
6297 tr("Invalid USB controller type: %d"),
6298 aType);
6299
6300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 ComObjPtr<USBController> ctrl;
6303
6304 *aControllers = i_getUSBControllerCountByType(aType);
6305
6306 return S_OK;
6307}
6308
6309HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6310{
6311
6312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 HRESULT rc = i_checkStateDependency(MutableStateDep);
6315 if (FAILED(rc)) return rc;
6316
6317 ComObjPtr<USBController> ctrl;
6318 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6319 if (FAILED(rc)) return rc;
6320
6321 i_setModified(IsModified_USB);
6322 mUSBControllers.backup();
6323
6324 ctrl->i_unshare();
6325
6326 mUSBControllers->remove(ctrl);
6327
6328 /* inform the direct session if any */
6329 alock.release();
6330 i_onUSBControllerChange();
6331
6332 return S_OK;
6333}
6334
6335HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6336 ULONG *aOriginX,
6337 ULONG *aOriginY,
6338 ULONG *aWidth,
6339 ULONG *aHeight,
6340 BOOL *aEnabled)
6341{
6342 uint32_t u32OriginX= 0;
6343 uint32_t u32OriginY= 0;
6344 uint32_t u32Width = 0;
6345 uint32_t u32Height = 0;
6346 uint16_t u16Flags = 0;
6347
6348 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6349 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6350 if (RT_FAILURE(vrc))
6351 {
6352#ifdef RT_OS_WINDOWS
6353 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6354 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6355 * So just assign fEnable to TRUE again.
6356 * The right fix would be to change GUI API wrappers to make sure that parameters
6357 * are changed only if API succeeds.
6358 */
6359 *aEnabled = TRUE;
6360#endif
6361 return setError(VBOX_E_IPRT_ERROR,
6362 tr("Saved guest size is not available (%Rrc)"),
6363 vrc);
6364 }
6365
6366 *aOriginX = u32OriginX;
6367 *aOriginY = u32OriginY;
6368 *aWidth = u32Width;
6369 *aHeight = u32Height;
6370 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6371
6372 return S_OK;
6373}
6374
6375HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6376{
6377 if (aScreenId != 0)
6378 return E_NOTIMPL;
6379
6380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 uint8_t *pu8Data = NULL;
6383 uint32_t cbData = 0;
6384 uint32_t u32Width = 0;
6385 uint32_t u32Height = 0;
6386
6387 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6388
6389 if (RT_FAILURE(vrc))
6390 return setError(VBOX_E_IPRT_ERROR,
6391 tr("Saved screenshot data is not available (%Rrc)"),
6392 vrc);
6393
6394 *aSize = cbData;
6395 *aWidth = u32Width;
6396 *aHeight = u32Height;
6397
6398 freeSavedDisplayScreenshot(pu8Data);
6399
6400 return S_OK;
6401}
6402
6403HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6404 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6405{
6406 if (aScreenId != 0)
6407 return E_NOTIMPL;
6408
6409 if ( aBitmapFormat != BitmapFormat_BGR0
6410 && aBitmapFormat != BitmapFormat_BGRA
6411 && aBitmapFormat != BitmapFormat_RGBA
6412 && aBitmapFormat != BitmapFormat_PNG)
6413 return setError(E_NOTIMPL,
6414 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6415
6416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 uint8_t *pu8Data = NULL;
6419 uint32_t cbData = 0;
6420 uint32_t u32Width = 0;
6421 uint32_t u32Height = 0;
6422
6423 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6424
6425 if (RT_FAILURE(vrc))
6426 return setError(VBOX_E_IPRT_ERROR,
6427 tr("Saved thumbnail data is not available (%Rrc)"),
6428 vrc);
6429
6430 HRESULT hr = S_OK;
6431
6432 *aWidth = u32Width;
6433 *aHeight = u32Height;
6434
6435 if (cbData > 0)
6436 {
6437 /* Convert pixels to the format expected by the API caller. */
6438 if (aBitmapFormat == BitmapFormat_BGR0)
6439 {
6440 /* [0] B, [1] G, [2] R, [3] 0. */
6441 aData.resize(cbData);
6442 memcpy(&aData.front(), pu8Data, cbData);
6443 }
6444 else if (aBitmapFormat == BitmapFormat_BGRA)
6445 {
6446 /* [0] B, [1] G, [2] R, [3] A. */
6447 aData.resize(cbData);
6448 for (uint32_t i = 0; i < cbData; i += 4)
6449 {
6450 aData[i] = pu8Data[i];
6451 aData[i + 1] = pu8Data[i + 1];
6452 aData[i + 2] = pu8Data[i + 2];
6453 aData[i + 3] = 0xff;
6454 }
6455 }
6456 else if (aBitmapFormat == BitmapFormat_RGBA)
6457 {
6458 /* [0] R, [1] G, [2] B, [3] A. */
6459 aData.resize(cbData);
6460 for (uint32_t i = 0; i < cbData; i += 4)
6461 {
6462 aData[i] = pu8Data[i + 2];
6463 aData[i + 1] = pu8Data[i + 1];
6464 aData[i + 2] = pu8Data[i];
6465 aData[i + 3] = 0xff;
6466 }
6467 }
6468 else if (aBitmapFormat == BitmapFormat_PNG)
6469 {
6470 uint8_t *pu8PNG = NULL;
6471 uint32_t cbPNG = 0;
6472 uint32_t cxPNG = 0;
6473 uint32_t cyPNG = 0;
6474
6475 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6476
6477 if (RT_SUCCESS(vrc))
6478 {
6479 aData.resize(cbPNG);
6480 if (cbPNG)
6481 memcpy(&aData.front(), pu8PNG, cbPNG);
6482 }
6483 else
6484 hr = setError(VBOX_E_IPRT_ERROR,
6485 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6486 vrc);
6487
6488 RTMemFree(pu8PNG);
6489 }
6490 }
6491
6492 freeSavedDisplayScreenshot(pu8Data);
6493
6494 return hr;
6495}
6496
6497HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6498{
6499 if (aScreenId != 0)
6500 return E_NOTIMPL;
6501
6502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6503
6504 uint8_t *pu8Data = NULL;
6505 uint32_t cbData = 0;
6506 uint32_t u32Width = 0;
6507 uint32_t u32Height = 0;
6508
6509 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6510
6511 if (RT_FAILURE(vrc))
6512 return setError(VBOX_E_IPRT_ERROR,
6513 tr("Saved screenshot data is not available (%Rrc)"),
6514 vrc);
6515
6516 *aSize = cbData;
6517 *aWidth = u32Width;
6518 *aHeight = u32Height;
6519
6520 freeSavedDisplayScreenshot(pu8Data);
6521
6522 return S_OK;
6523}
6524
6525HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6526{
6527 if (aScreenId != 0)
6528 return E_NOTIMPL;
6529
6530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 uint8_t *pu8Data = NULL;
6533 uint32_t cbData = 0;
6534 uint32_t u32Width = 0;
6535 uint32_t u32Height = 0;
6536
6537 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6538
6539 if (RT_FAILURE(vrc))
6540 return setError(VBOX_E_IPRT_ERROR,
6541 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6542 vrc);
6543
6544 *aWidth = u32Width;
6545 *aHeight = u32Height;
6546
6547 aData.resize(cbData);
6548 if (cbData)
6549 memcpy(&aData.front(), pu8Data, cbData);
6550
6551 freeSavedDisplayScreenshot(pu8Data);
6552
6553 return S_OK;
6554}
6555
6556HRESULT Machine::hotPlugCPU(ULONG aCpu)
6557{
6558 HRESULT rc = S_OK;
6559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6560
6561 if (!mHWData->mCPUHotPlugEnabled)
6562 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6563
6564 if (aCpu >= mHWData->mCPUCount)
6565 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6566
6567 if (mHWData->mCPUAttached[aCpu])
6568 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6569
6570 alock.release();
6571 rc = i_onCPUChange(aCpu, false);
6572 alock.acquire();
6573 if (FAILED(rc)) return rc;
6574
6575 i_setModified(IsModified_MachineData);
6576 mHWData.backup();
6577 mHWData->mCPUAttached[aCpu] = true;
6578
6579 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6580 if (Global::IsOnline(mData->mMachineState))
6581 i_saveSettings(NULL);
6582
6583 return S_OK;
6584}
6585
6586HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6587{
6588 HRESULT rc = S_OK;
6589
6590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6591
6592 if (!mHWData->mCPUHotPlugEnabled)
6593 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6594
6595 if (aCpu >= SchemaDefs::MaxCPUCount)
6596 return setError(E_INVALIDARG,
6597 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6598 SchemaDefs::MaxCPUCount);
6599
6600 if (!mHWData->mCPUAttached[aCpu])
6601 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6602
6603 /* CPU 0 can't be detached */
6604 if (aCpu == 0)
6605 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6606
6607 alock.release();
6608 rc = i_onCPUChange(aCpu, true);
6609 alock.acquire();
6610 if (FAILED(rc)) return rc;
6611
6612 i_setModified(IsModified_MachineData);
6613 mHWData.backup();
6614 mHWData->mCPUAttached[aCpu] = false;
6615
6616 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6617 if (Global::IsOnline(mData->mMachineState))
6618 i_saveSettings(NULL);
6619
6620 return S_OK;
6621}
6622
6623HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6624{
6625 *aAttached = false;
6626
6627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6628
6629 /* If hotplug is enabled the CPU is always enabled. */
6630 if (!mHWData->mCPUHotPlugEnabled)
6631 {
6632 if (aCpu < mHWData->mCPUCount)
6633 *aAttached = true;
6634 }
6635 else
6636 {
6637 if (aCpu < SchemaDefs::MaxCPUCount)
6638 *aAttached = mHWData->mCPUAttached[aCpu];
6639 }
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6645{
6646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6647
6648 Utf8Str log = i_queryLogFilename(aIdx);
6649 if (!RTFileExists(log.c_str()))
6650 log.setNull();
6651 aFilename = log;
6652
6653 return S_OK;
6654}
6655
6656HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6657{
6658 if (aSize < 0)
6659 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6660
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662
6663 HRESULT rc = S_OK;
6664 Utf8Str log = i_queryLogFilename(aIdx);
6665
6666 /* do not unnecessarily hold the lock while doing something which does
6667 * not need the lock and potentially takes a long time. */
6668 alock.release();
6669
6670 /* Limit the chunk size to 32K for now, as that gives better performance
6671 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6672 * One byte expands to approx. 25 bytes of breathtaking XML. */
6673 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6674 aData.resize(cbData);
6675
6676 RTFILE LogFile;
6677 int vrc = RTFileOpen(&LogFile, log.c_str(),
6678 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6679 if (RT_SUCCESS(vrc))
6680 {
6681 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6682 if (RT_SUCCESS(vrc))
6683 aData.resize(cbData);
6684 else
6685 rc = setError(VBOX_E_IPRT_ERROR,
6686 tr("Could not read log file '%s' (%Rrc)"),
6687 log.c_str(), vrc);
6688 RTFileClose(LogFile);
6689 }
6690 else
6691 rc = setError(VBOX_E_IPRT_ERROR,
6692 tr("Could not open log file '%s' (%Rrc)"),
6693 log.c_str(), vrc);
6694
6695 if (FAILED(rc))
6696 aData.resize(0);
6697
6698 return rc;
6699}
6700
6701
6702/**
6703 * Currently this method doesn't attach device to the running VM,
6704 * just makes sure it's plugged on next VM start.
6705 */
6706HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6707{
6708 // lock scope
6709 {
6710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6711
6712 HRESULT rc = i_checkStateDependency(MutableStateDep);
6713 if (FAILED(rc)) return rc;
6714
6715 ChipsetType_T aChipset = ChipsetType_PIIX3;
6716 COMGETTER(ChipsetType)(&aChipset);
6717
6718 if (aChipset != ChipsetType_ICH9)
6719 {
6720 return setError(E_INVALIDARG,
6721 tr("Host PCI attachment only supported with ICH9 chipset"));
6722 }
6723
6724 // check if device with this host PCI address already attached
6725 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6726 it != mHWData->mPCIDeviceAssignments.end();
6727 ++it)
6728 {
6729 LONG iHostAddress = -1;
6730 ComPtr<PCIDeviceAttachment> pAttach;
6731 pAttach = *it;
6732 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6733 if (iHostAddress == aHostAddress)
6734 return setError(E_INVALIDARG,
6735 tr("Device with host PCI address already attached to this VM"));
6736 }
6737
6738 ComObjPtr<PCIDeviceAttachment> pda;
6739 char name[32];
6740
6741 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6742 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6743 Bstr bname(name);
6744 pda.createObject();
6745 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6746 i_setModified(IsModified_MachineData);
6747 mHWData.backup();
6748 mHWData->mPCIDeviceAssignments.push_back(pda);
6749 }
6750
6751 return S_OK;
6752}
6753
6754/**
6755 * Currently this method doesn't detach device from the running VM,
6756 * just makes sure it's not plugged on next VM start.
6757 */
6758HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6759{
6760 ComObjPtr<PCIDeviceAttachment> pAttach;
6761 bool fRemoved = false;
6762 HRESULT rc;
6763
6764 // lock scope
6765 {
6766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6767
6768 rc = i_checkStateDependency(MutableStateDep);
6769 if (FAILED(rc)) return rc;
6770
6771 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6772 it != mHWData->mPCIDeviceAssignments.end();
6773 ++it)
6774 {
6775 LONG iHostAddress = -1;
6776 pAttach = *it;
6777 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6778 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6779 {
6780 i_setModified(IsModified_MachineData);
6781 mHWData.backup();
6782 mHWData->mPCIDeviceAssignments.remove(pAttach);
6783 fRemoved = true;
6784 break;
6785 }
6786 }
6787 }
6788
6789
6790 /* Fire event outside of the lock */
6791 if (fRemoved)
6792 {
6793 Assert(!pAttach.isNull());
6794 ComPtr<IEventSource> es;
6795 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6796 Assert(SUCCEEDED(rc));
6797 Bstr mid;
6798 rc = this->COMGETTER(Id)(mid.asOutParam());
6799 Assert(SUCCEEDED(rc));
6800 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6801 }
6802
6803 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6804 tr("No host PCI device %08x attached"),
6805 aHostAddress
6806 );
6807}
6808
6809HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6810{
6811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6812
6813 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6814
6815 size_t i = 0;
6816 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6817 it != mHWData->mPCIDeviceAssignments.end();
6818 ++i, ++it)
6819 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6820
6821 return S_OK;
6822}
6823
6824HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6825{
6826 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6827
6828 return S_OK;
6829}
6830
6831HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6832{
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6836
6837 return S_OK;
6838}
6839
6840HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6841{
6842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6843 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6844 if (SUCCEEDED(hrc))
6845 {
6846 hrc = mHWData.backupEx();
6847 if (SUCCEEDED(hrc))
6848 {
6849 i_setModified(IsModified_MachineData);
6850 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6851 }
6852 }
6853 return hrc;
6854}
6855
6856HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6857{
6858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6859 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6860 return S_OK;
6861}
6862
6863HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6864{
6865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6866 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6867 if (SUCCEEDED(hrc))
6868 {
6869 hrc = mHWData.backupEx();
6870 if (SUCCEEDED(hrc))
6871 {
6872 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6873 if (SUCCEEDED(hrc))
6874 i_setModified(IsModified_MachineData);
6875 }
6876 }
6877 return hrc;
6878}
6879
6880HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6881{
6882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6883
6884 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6885
6886 return S_OK;
6887}
6888
6889HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6890{
6891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6892 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6893 if (SUCCEEDED(hrc))
6894 {
6895 hrc = mHWData.backupEx();
6896 if (SUCCEEDED(hrc))
6897 {
6898 i_setModified(IsModified_MachineData);
6899 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6900 }
6901 }
6902 return hrc;
6903}
6904
6905HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6906{
6907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6908
6909 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6910
6911 return S_OK;
6912}
6913
6914HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6915{
6916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6917
6918 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6919 if ( SUCCEEDED(hrc)
6920 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6921 {
6922 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6923 int vrc;
6924
6925 if (aAutostartEnabled)
6926 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6927 else
6928 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6929
6930 if (RT_SUCCESS(vrc))
6931 {
6932 hrc = mHWData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6937 }
6938 }
6939 else if (vrc == VERR_NOT_SUPPORTED)
6940 hrc = setError(VBOX_E_NOT_SUPPORTED,
6941 tr("The VM autostart feature is not supported on this platform"));
6942 else if (vrc == VERR_PATH_NOT_FOUND)
6943 hrc = setError(E_FAIL,
6944 tr("The path to the autostart database is not set"));
6945 else
6946 hrc = setError(E_UNEXPECTED,
6947 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6948 aAutostartEnabled ? "Adding" : "Removing",
6949 mUserData->s.strName.c_str(), vrc);
6950 }
6951 return hrc;
6952}
6953
6954HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6955{
6956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6957
6958 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6959
6960 return S_OK;
6961}
6962
6963HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6964{
6965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6966 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6967 if (SUCCEEDED(hrc))
6968 {
6969 hrc = mHWData.backupEx();
6970 if (SUCCEEDED(hrc))
6971 {
6972 i_setModified(IsModified_MachineData);
6973 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6974 }
6975 }
6976 return hrc;
6977}
6978
6979HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6980{
6981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6982
6983 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6984
6985 return S_OK;
6986}
6987
6988HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6989{
6990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6991 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6992 if ( SUCCEEDED(hrc)
6993 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6994 {
6995 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6996 int vrc;
6997
6998 if (aAutostopType != AutostopType_Disabled)
6999 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7000 else
7001 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7002
7003 if (RT_SUCCESS(vrc))
7004 {
7005 hrc = mHWData.backupEx();
7006 if (SUCCEEDED(hrc))
7007 {
7008 i_setModified(IsModified_MachineData);
7009 mHWData->mAutostart.enmAutostopType = aAutostopType;
7010 }
7011 }
7012 else if (vrc == VERR_NOT_SUPPORTED)
7013 hrc = setError(VBOX_E_NOT_SUPPORTED,
7014 tr("The VM autostop feature is not supported on this platform"));
7015 else if (vrc == VERR_PATH_NOT_FOUND)
7016 hrc = setError(E_FAIL,
7017 tr("The path to the autostart database is not set"));
7018 else
7019 hrc = setError(E_UNEXPECTED,
7020 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7021 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7022 mUserData->s.strName.c_str(), vrc);
7023 }
7024 return hrc;
7025}
7026
7027HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7028{
7029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7030
7031 aDefaultFrontend = mHWData->mDefaultFrontend;
7032
7033 return S_OK;
7034}
7035
7036HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7037{
7038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7039 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7040 if (SUCCEEDED(hrc))
7041 {
7042 hrc = mHWData.backupEx();
7043 if (SUCCEEDED(hrc))
7044 {
7045 i_setModified(IsModified_MachineData);
7046 mHWData->mDefaultFrontend = aDefaultFrontend;
7047 }
7048 }
7049 return hrc;
7050}
7051
7052HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7053{
7054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7055 size_t cbIcon = mUserData->mIcon.size();
7056 aIcon.resize(cbIcon);
7057 if (cbIcon)
7058 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7059 return S_OK;
7060}
7061
7062HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7063{
7064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7065 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7066 if (SUCCEEDED(hrc))
7067 {
7068 i_setModified(IsModified_MachineData);
7069 mUserData.backup();
7070 size_t cbIcon = aIcon.size();
7071 mUserData->mIcon.resize(cbIcon);
7072 if (cbIcon)
7073 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7074 }
7075 return hrc;
7076}
7077
7078HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7079{
7080#ifdef VBOX_WITH_USB
7081 *aUSBProxyAvailable = true;
7082#else
7083 *aUSBProxyAvailable = false;
7084#endif
7085 return S_OK;
7086}
7087
7088HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7089 ComPtr<IProgress> &aProgress)
7090{
7091 ComObjPtr<Progress> pP;
7092 Progress *ppP = pP;
7093 IProgress *iP = static_cast<IProgress *>(ppP);
7094 IProgress **pProgress = &iP;
7095
7096 IMachine *pTarget = aTarget;
7097
7098 /* Convert the options. */
7099 RTCList<CloneOptions_T> optList;
7100 if (aOptions.size())
7101 for (size_t i = 0; i < aOptions.size(); ++i)
7102 optList.append(aOptions[i]);
7103
7104 if (optList.contains(CloneOptions_Link))
7105 {
7106 if (!i_isSnapshotMachine())
7107 return setError(E_INVALIDARG,
7108 tr("Linked clone can only be created from a snapshot"));
7109 if (aMode != CloneMode_MachineState)
7110 return setError(E_INVALIDARG,
7111 tr("Linked clone can only be created for a single machine state"));
7112 }
7113 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7114
7115 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7116
7117 HRESULT rc = pWorker->start(pProgress);
7118
7119 pP = static_cast<Progress *>(*pProgress);
7120 pP.queryInterfaceTo(aProgress.asOutParam());
7121
7122 return rc;
7123
7124}
7125
7126HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7127{
7128 NOREF(aProgress);
7129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 // This check should always fail.
7132 HRESULT rc = i_checkStateDependency(MutableStateDep);
7133 if (FAILED(rc)) return rc;
7134
7135 AssertFailedReturn(E_NOTIMPL);
7136}
7137
7138HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7139{
7140 NOREF(aSavedStateFile);
7141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7142
7143 // This check should always fail.
7144 HRESULT rc = i_checkStateDependency(MutableStateDep);
7145 if (FAILED(rc)) return rc;
7146
7147 AssertFailedReturn(E_NOTIMPL);
7148}
7149
7150HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7151{
7152 NOREF(aFRemoveFile);
7153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 // This check should always fail.
7156 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7157 if (FAILED(rc)) return rc;
7158
7159 AssertFailedReturn(E_NOTIMPL);
7160}
7161
7162// public methods for internal purposes
7163/////////////////////////////////////////////////////////////////////////////
7164
7165/**
7166 * Adds the given IsModified_* flag to the dirty flags of the machine.
7167 * This must be called either during i_loadSettings or under the machine write lock.
7168 * @param fl
7169 */
7170void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7171{
7172 mData->flModifications |= fl;
7173 if (fAllowStateModification && i_isStateModificationAllowed())
7174 mData->mCurrentStateModified = true;
7175}
7176
7177/**
7178 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7179 * care of the write locking.
7180 *
7181 * @param fModifications The flag to add.
7182 */
7183void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7184{
7185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7186 i_setModified(fModification, fAllowStateModification);
7187}
7188
7189/**
7190 * Saves the registry entry of this machine to the given configuration node.
7191 *
7192 * @param aEntryNode Node to save the registry entry to.
7193 *
7194 * @note locks this object for reading.
7195 */
7196HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7197{
7198 AutoLimitedCaller autoCaller(this);
7199 AssertComRCReturnRC(autoCaller.rc());
7200
7201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 data.uuid = mData->mUuid;
7204 data.strSettingsFile = mData->m_strConfigFile;
7205
7206 return S_OK;
7207}
7208
7209/**
7210 * Calculates the absolute path of the given path taking the directory of the
7211 * machine settings file as the current directory.
7212 *
7213 * @param aPath Path to calculate the absolute path for.
7214 * @param aResult Where to put the result (used only on success, can be the
7215 * same Utf8Str instance as passed in @a aPath).
7216 * @return IPRT result.
7217 *
7218 * @note Locks this object for reading.
7219 */
7220int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7221{
7222 AutoCaller autoCaller(this);
7223 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7228
7229 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7230
7231 strSettingsDir.stripFilename();
7232 char folder[RTPATH_MAX];
7233 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7234 if (RT_SUCCESS(vrc))
7235 aResult = folder;
7236
7237 return vrc;
7238}
7239
7240/**
7241 * Copies strSource to strTarget, making it relative to the machine folder
7242 * if it is a subdirectory thereof, or simply copying it otherwise.
7243 *
7244 * @param strSource Path to evaluate and copy.
7245 * @param strTarget Buffer to receive target path.
7246 *
7247 * @note Locks this object for reading.
7248 */
7249void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7250 Utf8Str &strTarget)
7251{
7252 AutoCaller autoCaller(this);
7253 AssertComRCReturn(autoCaller.rc(), (void)0);
7254
7255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7258 // use strTarget as a temporary buffer to hold the machine settings dir
7259 strTarget = mData->m_strConfigFileFull;
7260 strTarget.stripFilename();
7261 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7262 {
7263 // is relative: then append what's left
7264 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7265 // for empty paths (only possible for subdirs) use "." to avoid
7266 // triggering default settings for not present config attributes.
7267 if (strTarget.isEmpty())
7268 strTarget = ".";
7269 }
7270 else
7271 // is not relative: then overwrite
7272 strTarget = strSource;
7273}
7274
7275/**
7276 * Returns the full path to the machine's log folder in the
7277 * \a aLogFolder argument.
7278 */
7279void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7280{
7281 AutoCaller autoCaller(this);
7282 AssertComRCReturnVoid(autoCaller.rc());
7283
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 char szTmp[RTPATH_MAX];
7287 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7288 if (RT_SUCCESS(vrc))
7289 {
7290 if (szTmp[0] && !mUserData.isNull())
7291 {
7292 char szTmp2[RTPATH_MAX];
7293 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7294 if (RT_SUCCESS(vrc))
7295 aLogFolder = BstrFmt("%s%c%s",
7296 szTmp2,
7297 RTPATH_DELIMITER,
7298 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7299 }
7300 else
7301 vrc = VERR_PATH_IS_RELATIVE;
7302 }
7303
7304 if (RT_FAILURE(vrc))
7305 {
7306 // fallback if VBOX_USER_LOGHOME is not set or invalid
7307 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7308 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7309 aLogFolder.append(RTPATH_DELIMITER);
7310 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7311 }
7312}
7313
7314/**
7315 * Returns the full path to the machine's log file for an given index.
7316 */
7317Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7318 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7319{
7320 Utf8Str logFolder;
7321 getLogFolder(logFolder);
7322 Assert(logFolder.length());
7323 Utf8Str log;
7324 if (idx == 0)
7325 log = Utf8StrFmt("%s%cVBox.log",
7326 logFolder.c_str(), RTPATH_DELIMITER);
7327 else
7328 log = Utf8StrFmt("%s%cVBox.log.%d",
7329 logFolder.c_str(), RTPATH_DELIMITER, idx);
7330 return log;
7331}
7332
7333/**
7334 * Returns the full path to the machine's (hardened) startup log file.
7335 */
7336Utf8Str Machine::i_getStartupLogFilename(void)
7337{
7338 Utf8Str strFilename;
7339 getLogFolder(strFilename);
7340 Assert(strFilename.length());
7341 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7342 return strFilename;
7343}
7344
7345
7346/**
7347 * Composes a unique saved state filename based on the current system time. The filename is
7348 * granular to the second so this will work so long as no more than one snapshot is taken on
7349 * a machine per second.
7350 *
7351 * Before version 4.1, we used this formula for saved state files:
7352 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7353 * which no longer works because saved state files can now be shared between the saved state of the
7354 * "saved" machine and an online snapshot, and the following would cause problems:
7355 * 1) save machine
7356 * 2) create online snapshot from that machine state --> reusing saved state file
7357 * 3) save machine again --> filename would be reused, breaking the online snapshot
7358 *
7359 * So instead we now use a timestamp.
7360 *
7361 * @param str
7362 */
7363
7364void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7365{
7366 AutoCaller autoCaller(this);
7367 AssertComRCReturnVoid(autoCaller.rc());
7368
7369 {
7370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7371 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7372 }
7373
7374 RTTIMESPEC ts;
7375 RTTimeNow(&ts);
7376 RTTIME time;
7377 RTTimeExplode(&time, &ts);
7378
7379 strStateFilePath += RTPATH_DELIMITER;
7380 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7381 time.i32Year, time.u8Month, time.u8MonthDay,
7382 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7383}
7384
7385/**
7386 * Returns the full path to the default video capture file.
7387 */
7388void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7389{
7390 AutoCaller autoCaller(this);
7391 AssertComRCReturnVoid(autoCaller.rc());
7392
7393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7394
7395 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7396 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7397 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7398}
7399
7400/**
7401 * Returns whether at least one USB controller is present for the VM.
7402 */
7403bool Machine::i_isUSBControllerPresent()
7404{
7405 AutoCaller autoCaller(this);
7406 AssertComRCReturn(autoCaller.rc(), false);
7407
7408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7409
7410 return (mUSBControllers->size() > 0);
7411}
7412
7413/**
7414 * @note Locks this object for writing, calls the client process
7415 * (inside the lock).
7416 */
7417HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7418 const Utf8Str &strFrontend,
7419 const Utf8Str &strEnvironment,
7420 ProgressProxy *aProgress)
7421{
7422 LogFlowThisFuncEnter();
7423
7424 AssertReturn(aControl, E_FAIL);
7425 AssertReturn(aProgress, E_FAIL);
7426 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7427
7428 AutoCaller autoCaller(this);
7429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7430
7431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7432
7433 if (!mData->mRegistered)
7434 return setError(E_UNEXPECTED,
7435 tr("The machine '%s' is not registered"),
7436 mUserData->s.strName.c_str());
7437
7438 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7439
7440 /* The process started when launching a VM with separate UI/VM processes is always
7441 * the UI process, i.e. needs special handling as it won't claim the session. */
7442 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7443
7444 if (fSeparate)
7445 {
7446 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName == "headless")
7447 return setError(VBOX_E_INVALID_OBJECT_STATE,
7448 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7449 mUserData->s.strName.c_str());
7450 }
7451 else
7452 {
7453 if ( mData->mSession.mState == SessionState_Locked
7454 || mData->mSession.mState == SessionState_Spawning
7455 || mData->mSession.mState == SessionState_Unlocking)
7456 return setError(VBOX_E_INVALID_OBJECT_STATE,
7457 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7458 mUserData->s.strName.c_str());
7459
7460 /* may not be busy */
7461 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7462 }
7463
7464 /* get the path to the executable */
7465 char szPath[RTPATH_MAX];
7466 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7467 size_t cchBufLeft = strlen(szPath);
7468 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7469 szPath[cchBufLeft] = 0;
7470 char *pszNamePart = szPath + cchBufLeft;
7471 cchBufLeft = sizeof(szPath) - cchBufLeft;
7472
7473 int vrc = VINF_SUCCESS;
7474 RTPROCESS pid = NIL_RTPROCESS;
7475
7476 RTENV env = RTENV_DEFAULT;
7477
7478 if (!strEnvironment.isEmpty())
7479 {
7480 char *newEnvStr = NULL;
7481
7482 do
7483 {
7484 /* clone the current environment */
7485 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7486 AssertRCBreakStmt(vrc2, vrc = vrc2);
7487
7488 newEnvStr = RTStrDup(strEnvironment.c_str());
7489 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7490
7491 /* put new variables to the environment
7492 * (ignore empty variable names here since RTEnv API
7493 * intentionally doesn't do that) */
7494 char *var = newEnvStr;
7495 for (char *p = newEnvStr; *p; ++p)
7496 {
7497 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7498 {
7499 *p = '\0';
7500 if (*var)
7501 {
7502 char *val = strchr(var, '=');
7503 if (val)
7504 {
7505 *val++ = '\0';
7506 vrc2 = RTEnvSetEx(env, var, val);
7507 }
7508 else
7509 vrc2 = RTEnvUnsetEx(env, var);
7510 if (RT_FAILURE(vrc2))
7511 break;
7512 }
7513 var = p + 1;
7514 }
7515 }
7516 if (RT_SUCCESS(vrc2) && *var)
7517 vrc2 = RTEnvPutEx(env, var);
7518
7519 AssertRCBreakStmt(vrc2, vrc = vrc2);
7520 }
7521 while (0);
7522
7523 if (newEnvStr != NULL)
7524 RTStrFree(newEnvStr);
7525 }
7526
7527 /* Hardened startup logging */
7528#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7529 Utf8Str strSupStartLogArg("--sup-startup-log=");
7530 {
7531 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7532 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7533 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7534 {
7535 Utf8Str strStartupLogDir = strStartupLogFile;
7536 strStartupLogDir.stripFilename();
7537 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7538 file without stripping the file. */
7539 }
7540 strSupStartLogArg.append(strStartupLogFile);
7541 }
7542 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7543#else
7544 const char *pszSupStartupLogArg = NULL;
7545#endif
7546
7547 Utf8Str strCanonicalName;
7548
7549#ifdef VBOX_WITH_QTGUI
7550 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7551 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7552 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7553 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7554 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7555 {
7556 strCanonicalName = "GUI/Qt";
7557# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7558 /* Modify the base path so that we don't need to use ".." below. */
7559 RTPathStripTrailingSlash(szPath);
7560 RTPathStripFilename(szPath);
7561 cchBufLeft = strlen(szPath);
7562 pszNamePart = szPath + cchBufLeft;
7563 cchBufLeft = sizeof(szPath) - cchBufLeft;
7564
7565# define OSX_APP_NAME "VirtualBoxVM"
7566# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7567
7568 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7569 if ( strAppOverride.contains(".")
7570 || strAppOverride.contains("/")
7571 || strAppOverride.contains("\\")
7572 || strAppOverride.contains(":"))
7573 strAppOverride.setNull();
7574 Utf8Str strAppPath;
7575 if (!strAppOverride.isEmpty())
7576 {
7577 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7578 Utf8Str strFullPath(szPath);
7579 strFullPath.append(strAppPath);
7580 /* there is a race, but people using this deserve the failure */
7581 if (!RTFileExists(strFullPath.c_str()))
7582 strAppOverride.setNull();
7583 }
7584 if (strAppOverride.isEmpty())
7585 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7586 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7587 strcpy(pszNamePart, strAppPath.c_str());
7588# else
7589 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7590 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7591 strcpy(pszNamePart, s_szVirtualBox_exe);
7592# endif
7593
7594 Utf8Str idStr = mData->mUuid.toString();
7595 const char *apszArgs[] =
7596 {
7597 szPath,
7598 "--comment", mUserData->s.strName.c_str(),
7599 "--startvm", idStr.c_str(),
7600 "--no-startvm-errormsgbox",
7601 NULL, /* For "--separate". */
7602 NULL, /* For "--sup-startup-log". */
7603 NULL
7604 };
7605 unsigned iArg = 6;
7606 if (fSeparate)
7607 apszArgs[iArg++] = "--separate";
7608 apszArgs[iArg++] = pszSupStartupLogArg;
7609
7610 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7611 }
7612#else /* !VBOX_WITH_QTGUI */
7613 if (0)
7614 ;
7615#endif /* VBOX_WITH_QTGUI */
7616
7617 else
7618
7619#ifdef VBOX_WITH_VBOXSDL
7620 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7621 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7622 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7623 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7624 {
7625 strCanonicalName = "GUI/SDL";
7626 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7627 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7628 strcpy(pszNamePart, s_szVBoxSDL_exe);
7629
7630 Utf8Str idStr = mData->mUuid.toString();
7631 const char *apszArgs[] =
7632 {
7633 szPath,
7634 "--comment", mUserData->s.strName.c_str(),
7635 "--startvm", idStr.c_str(),
7636 NULL, /* For "--separate". */
7637 NULL, /* For "--sup-startup-log". */
7638 NULL
7639 };
7640 unsigned iArg = 5;
7641 if (fSeparate)
7642 apszArgs[iArg++] = "--separate";
7643 apszArgs[iArg++] = pszSupStartupLogArg;
7644
7645 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7646 }
7647#else /* !VBOX_WITH_VBOXSDL */
7648 if (0)
7649 ;
7650#endif /* !VBOX_WITH_VBOXSDL */
7651
7652 else
7653
7654#ifdef VBOX_WITH_HEADLESS
7655 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7656 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7657 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7658 )
7659 {
7660 strCanonicalName = "headless";
7661 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7662 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7663 * and a VM works even if the server has not been installed.
7664 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7665 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7666 * differently in 4.0 and 3.x.
7667 */
7668 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7669 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7670 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7671
7672 Utf8Str idStr = mData->mUuid.toString();
7673 const char *apszArgs[] =
7674 {
7675 szPath,
7676 "--comment", mUserData->s.strName.c_str(),
7677 "--startvm", idStr.c_str(),
7678 "--vrde", "config",
7679 NULL, /* For "--capture". */
7680 NULL, /* For "--sup-startup-log". */
7681 NULL
7682 };
7683 unsigned iArg = 7;
7684 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7685 apszArgs[iArg++] = "--capture";
7686 apszArgs[iArg++] = pszSupStartupLogArg;
7687
7688# ifdef RT_OS_WINDOWS
7689 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7690# else
7691 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7692# endif
7693 }
7694#else /* !VBOX_WITH_HEADLESS */
7695 if (0)
7696 ;
7697#endif /* !VBOX_WITH_HEADLESS */
7698 else
7699 {
7700 RTEnvDestroy(env);
7701 return setError(E_INVALIDARG,
7702 tr("Invalid frontend name: '%s'"),
7703 strFrontend.c_str());
7704 }
7705
7706 RTEnvDestroy(env);
7707
7708 if (RT_FAILURE(vrc))
7709 return setError(VBOX_E_IPRT_ERROR,
7710 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7711 mUserData->s.strName.c_str(), vrc);
7712
7713 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7714
7715 if (!fSeparate)
7716 {
7717 /*
7718 * Note that we don't release the lock here before calling the client,
7719 * because it doesn't need to call us back if called with a NULL argument.
7720 * Releasing the lock here is dangerous because we didn't prepare the
7721 * launch data yet, but the client we've just started may happen to be
7722 * too fast and call LockMachine() that will fail (because of PID, etc.),
7723 * so that the Machine will never get out of the Spawning session state.
7724 */
7725
7726 /* inform the session that it will be a remote one */
7727 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7728#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7729 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7730#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7731 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7732#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7733 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7734
7735 if (FAILED(rc))
7736 {
7737 /* restore the session state */
7738 mData->mSession.mState = SessionState_Unlocked;
7739 alock.release();
7740 mParent->i_addProcessToReap(pid);
7741 /* The failure may occur w/o any error info (from RPC), so provide one */
7742 return setError(VBOX_E_VM_ERROR,
7743 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7744 }
7745
7746 /* attach launch data to the machine */
7747 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7748 mData->mSession.mRemoteControls.push_back(aControl);
7749 mData->mSession.mProgress = aProgress;
7750 mData->mSession.mPID = pid;
7751 mData->mSession.mState = SessionState_Spawning;
7752 Assert(strCanonicalName.isNotEmpty());
7753 mData->mSession.mName = strCanonicalName;
7754 }
7755 else
7756 {
7757 /* For separate UI process we declare the launch as completed instantly, as the
7758 * actual headless VM start may or may not come. No point in remembering anything
7759 * yet, as what matters for us is when the headless VM gets started. */
7760 aProgress->i_notifyComplete(S_OK);
7761 }
7762
7763 alock.release();
7764 mParent->i_addProcessToReap(pid);
7765
7766 LogFlowThisFuncLeave();
7767 return S_OK;
7768}
7769
7770/**
7771 * Returns @c true if the given session machine instance has an open direct
7772 * session (and optionally also for direct sessions which are closing) and
7773 * returns the session control machine instance if so.
7774 *
7775 * Note that when the method returns @c false, the arguments remain unchanged.
7776 *
7777 * @param aMachine Session machine object.
7778 * @param aControl Direct session control object (optional).
7779 * @param aAllowClosing If true then additionally a session which is currently
7780 * being closed will also be allowed.
7781 *
7782 * @note locks this object for reading.
7783 */
7784bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7785 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7786 bool aAllowClosing /*= false*/)
7787{
7788 AutoLimitedCaller autoCaller(this);
7789 AssertComRCReturn(autoCaller.rc(), false);
7790
7791 /* just return false for inaccessible machines */
7792 if (getObjectState().getState() != ObjectState::Ready)
7793 return false;
7794
7795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7796
7797 if ( ( mData->mSession.mState == SessionState_Locked
7798 && mData->mSession.mLockType == LockType_VM)
7799 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7800 )
7801 {
7802 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7803
7804 aMachine = mData->mSession.mMachine;
7805
7806 if (aControl != NULL)
7807 *aControl = mData->mSession.mDirectControl;
7808
7809 return true;
7810 }
7811
7812 return false;
7813}
7814
7815/**
7816 * Returns @c true if the given machine has an spawning direct session.
7817 *
7818 * @note locks this object for reading.
7819 */
7820bool Machine::i_isSessionSpawning()
7821{
7822 AutoLimitedCaller autoCaller(this);
7823 AssertComRCReturn(autoCaller.rc(), false);
7824
7825 /* just return false for inaccessible machines */
7826 if (getObjectState().getState() != ObjectState::Ready)
7827 return false;
7828
7829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7830
7831 if (mData->mSession.mState == SessionState_Spawning)
7832 return true;
7833
7834 return false;
7835}
7836
7837/**
7838 * Called from the client watcher thread to check for unexpected client process
7839 * death during Session_Spawning state (e.g. before it successfully opened a
7840 * direct session).
7841 *
7842 * On Win32 and on OS/2, this method is called only when we've got the
7843 * direct client's process termination notification, so it always returns @c
7844 * true.
7845 *
7846 * On other platforms, this method returns @c true if the client process is
7847 * terminated and @c false if it's still alive.
7848 *
7849 * @note Locks this object for writing.
7850 */
7851bool Machine::i_checkForSpawnFailure()
7852{
7853 AutoCaller autoCaller(this);
7854 if (!autoCaller.isOk())
7855 {
7856 /* nothing to do */
7857 LogFlowThisFunc(("Already uninitialized!\n"));
7858 return true;
7859 }
7860
7861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7862
7863 if (mData->mSession.mState != SessionState_Spawning)
7864 {
7865 /* nothing to do */
7866 LogFlowThisFunc(("Not spawning any more!\n"));
7867 return true;
7868 }
7869
7870 HRESULT rc = S_OK;
7871
7872 /* PID not yet initialized, skip check. */
7873 if (mData->mSession.mPID == NIL_RTPROCESS)
7874 return false;
7875
7876 RTPROCSTATUS status;
7877 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7878
7879 if (vrc != VERR_PROCESS_RUNNING)
7880 {
7881 Utf8Str strExtraInfo;
7882
7883#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7884 /* If the startup logfile exists and is of non-zero length, tell the
7885 user to look there for more details to encourage them to attach it
7886 when reporting startup issues. */
7887 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7888 uint64_t cbStartupLogFile = 0;
7889 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7890 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7891 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7892#endif
7893
7894 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7895 rc = setError(E_FAIL,
7896 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7897 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7898 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7899 rc = setError(E_FAIL,
7900 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7901 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7902 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7903 rc = setError(E_FAIL,
7904 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7905 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7906 else
7907 rc = setError(E_FAIL,
7908 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7909 i_getName().c_str(), vrc, strExtraInfo.c_str());
7910 }
7911
7912 if (FAILED(rc))
7913 {
7914 /* Close the remote session, remove the remote control from the list
7915 * and reset session state to Closed (@note keep the code in sync with
7916 * the relevant part in LockMachine()). */
7917
7918 Assert(mData->mSession.mRemoteControls.size() == 1);
7919 if (mData->mSession.mRemoteControls.size() == 1)
7920 {
7921 ErrorInfoKeeper eik;
7922 mData->mSession.mRemoteControls.front()->Uninitialize();
7923 }
7924
7925 mData->mSession.mRemoteControls.clear();
7926 mData->mSession.mState = SessionState_Unlocked;
7927
7928 /* finalize the progress after setting the state */
7929 if (!mData->mSession.mProgress.isNull())
7930 {
7931 mData->mSession.mProgress->notifyComplete(rc);
7932 mData->mSession.mProgress.setNull();
7933 }
7934
7935 mData->mSession.mPID = NIL_RTPROCESS;
7936
7937 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7938 return true;
7939 }
7940
7941 return false;
7942}
7943
7944/**
7945 * Checks whether the machine can be registered. If so, commits and saves
7946 * all settings.
7947 *
7948 * @note Must be called from mParent's write lock. Locks this object and
7949 * children for writing.
7950 */
7951HRESULT Machine::i_prepareRegister()
7952{
7953 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7954
7955 AutoLimitedCaller autoCaller(this);
7956 AssertComRCReturnRC(autoCaller.rc());
7957
7958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7959
7960 /* wait for state dependents to drop to zero */
7961 i_ensureNoStateDependencies();
7962
7963 if (!mData->mAccessible)
7964 return setError(VBOX_E_INVALID_OBJECT_STATE,
7965 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7966 mUserData->s.strName.c_str(),
7967 mData->mUuid.toString().c_str());
7968
7969 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7970
7971 if (mData->mRegistered)
7972 return setError(VBOX_E_INVALID_OBJECT_STATE,
7973 tr("The machine '%s' with UUID {%s} is already registered"),
7974 mUserData->s.strName.c_str(),
7975 mData->mUuid.toString().c_str());
7976
7977 HRESULT rc = S_OK;
7978
7979 // Ensure the settings are saved. If we are going to be registered and
7980 // no config file exists yet, create it by calling i_saveSettings() too.
7981 if ( (mData->flModifications)
7982 || (!mData->pMachineConfigFile->fileExists())
7983 )
7984 {
7985 rc = i_saveSettings(NULL);
7986 // no need to check whether VirtualBox.xml needs saving too since
7987 // we can't have a machine XML file rename pending
7988 if (FAILED(rc)) return rc;
7989 }
7990
7991 /* more config checking goes here */
7992
7993 if (SUCCEEDED(rc))
7994 {
7995 /* we may have had implicit modifications we want to fix on success */
7996 i_commit();
7997
7998 mData->mRegistered = true;
7999 }
8000 else
8001 {
8002 /* we may have had implicit modifications we want to cancel on failure*/
8003 i_rollback(false /* aNotify */);
8004 }
8005
8006 return rc;
8007}
8008
8009/**
8010 * Increases the number of objects dependent on the machine state or on the
8011 * registered state. Guarantees that these two states will not change at least
8012 * until #releaseStateDependency() is called.
8013 *
8014 * Depending on the @a aDepType value, additional state checks may be made.
8015 * These checks will set extended error info on failure. See
8016 * #checkStateDependency() for more info.
8017 *
8018 * If this method returns a failure, the dependency is not added and the caller
8019 * is not allowed to rely on any particular machine state or registration state
8020 * value and may return the failed result code to the upper level.
8021 *
8022 * @param aDepType Dependency type to add.
8023 * @param aState Current machine state (NULL if not interested).
8024 * @param aRegistered Current registered state (NULL if not interested).
8025 *
8026 * @note Locks this object for writing.
8027 */
8028HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8029 MachineState_T *aState /* = NULL */,
8030 BOOL *aRegistered /* = NULL */)
8031{
8032 AutoCaller autoCaller(this);
8033 AssertComRCReturnRC(autoCaller.rc());
8034
8035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8036
8037 HRESULT rc = i_checkStateDependency(aDepType);
8038 if (FAILED(rc)) return rc;
8039
8040 {
8041 if (mData->mMachineStateChangePending != 0)
8042 {
8043 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8044 * drop to zero so don't add more. It may make sense to wait a bit
8045 * and retry before reporting an error (since the pending state
8046 * transition should be really quick) but let's just assert for
8047 * now to see if it ever happens on practice. */
8048
8049 AssertFailed();
8050
8051 return setError(E_ACCESSDENIED,
8052 tr("Machine state change is in progress. Please retry the operation later."));
8053 }
8054
8055 ++mData->mMachineStateDeps;
8056 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8057 }
8058
8059 if (aState)
8060 *aState = mData->mMachineState;
8061 if (aRegistered)
8062 *aRegistered = mData->mRegistered;
8063
8064 return S_OK;
8065}
8066
8067/**
8068 * Decreases the number of objects dependent on the machine state.
8069 * Must always complete the #addStateDependency() call after the state
8070 * dependency is no more necessary.
8071 */
8072void Machine::i_releaseStateDependency()
8073{
8074 AutoCaller autoCaller(this);
8075 AssertComRCReturnVoid(autoCaller.rc());
8076
8077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8078
8079 /* releaseStateDependency() w/o addStateDependency()? */
8080 AssertReturnVoid(mData->mMachineStateDeps != 0);
8081 -- mData->mMachineStateDeps;
8082
8083 if (mData->mMachineStateDeps == 0)
8084 {
8085 /* inform i_ensureNoStateDependencies() that there are no more deps */
8086 if (mData->mMachineStateChangePending != 0)
8087 {
8088 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8089 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8090 }
8091 }
8092}
8093
8094Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8095{
8096 /* start with nothing found */
8097 Utf8Str strResult("");
8098
8099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8100
8101 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8102 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8103 // found:
8104 strResult = it->second; // source is a Utf8Str
8105
8106 return strResult;
8107}
8108
8109// protected methods
8110/////////////////////////////////////////////////////////////////////////////
8111
8112/**
8113 * Performs machine state checks based on the @a aDepType value. If a check
8114 * fails, this method will set extended error info, otherwise it will return
8115 * S_OK. It is supposed, that on failure, the caller will immediately return
8116 * the return value of this method to the upper level.
8117 *
8118 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8119 *
8120 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8121 * current state of this machine object allows to change settings of the
8122 * machine (i.e. the machine is not registered, or registered but not running
8123 * and not saved). It is useful to call this method from Machine setters
8124 * before performing any change.
8125 *
8126 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8127 * as for MutableStateDep except that if the machine is saved, S_OK is also
8128 * returned. This is useful in setters which allow changing machine
8129 * properties when it is in the saved state.
8130 *
8131 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8132 * if the current state of this machine object allows to change runtime
8133 * changeable settings of the machine (i.e. the machine is not registered, or
8134 * registered but either running or not running and not saved). It is useful
8135 * to call this method from Machine setters before performing any changes to
8136 * runtime changeable settings.
8137 *
8138 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8139 * the same as for MutableOrRunningStateDep except that if the machine is
8140 * saved, S_OK is also returned. This is useful in setters which allow
8141 * changing runtime and saved state changeable machine properties.
8142 *
8143 * @param aDepType Dependency type to check.
8144 *
8145 * @note Non Machine based classes should use #addStateDependency() and
8146 * #releaseStateDependency() methods or the smart AutoStateDependency
8147 * template.
8148 *
8149 * @note This method must be called from under this object's read or write
8150 * lock.
8151 */
8152HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8153{
8154 switch (aDepType)
8155 {
8156 case AnyStateDep:
8157 {
8158 break;
8159 }
8160 case MutableStateDep:
8161 {
8162 if ( mData->mRegistered
8163 && ( !i_isSessionMachine()
8164 || ( mData->mMachineState != MachineState_Aborted
8165 && mData->mMachineState != MachineState_Teleported
8166 && mData->mMachineState != MachineState_PoweredOff
8167 )
8168 )
8169 )
8170 return setError(VBOX_E_INVALID_VM_STATE,
8171 tr("The machine is not mutable (state is %s)"),
8172 Global::stringifyMachineState(mData->mMachineState));
8173 break;
8174 }
8175 case MutableOrSavedStateDep:
8176 {
8177 if ( mData->mRegistered
8178 && ( !i_isSessionMachine()
8179 || ( mData->mMachineState != MachineState_Aborted
8180 && mData->mMachineState != MachineState_Teleported
8181 && mData->mMachineState != MachineState_Saved
8182 && mData->mMachineState != MachineState_PoweredOff
8183 )
8184 )
8185 )
8186 return setError(VBOX_E_INVALID_VM_STATE,
8187 tr("The machine is not mutable (state is %s)"),
8188 Global::stringifyMachineState(mData->mMachineState));
8189 break;
8190 }
8191 case MutableOrRunningStateDep:
8192 {
8193 if ( mData->mRegistered
8194 && ( !i_isSessionMachine()
8195 || ( mData->mMachineState != MachineState_Aborted
8196 && mData->mMachineState != MachineState_Teleported
8197 && mData->mMachineState != MachineState_PoweredOff
8198 && !Global::IsOnline(mData->mMachineState)
8199 )
8200 )
8201 )
8202 return setError(VBOX_E_INVALID_VM_STATE,
8203 tr("The machine is not mutable (state is %s)"),
8204 Global::stringifyMachineState(mData->mMachineState));
8205 break;
8206 }
8207 case MutableOrSavedOrRunningStateDep:
8208 {
8209 if ( mData->mRegistered
8210 && ( !i_isSessionMachine()
8211 || ( mData->mMachineState != MachineState_Aborted
8212 && mData->mMachineState != MachineState_Teleported
8213 && mData->mMachineState != MachineState_Saved
8214 && mData->mMachineState != MachineState_PoweredOff
8215 && !Global::IsOnline(mData->mMachineState)
8216 )
8217 )
8218 )
8219 return setError(VBOX_E_INVALID_VM_STATE,
8220 tr("The machine is not mutable (state is %s)"),
8221 Global::stringifyMachineState(mData->mMachineState));
8222 break;
8223 }
8224 }
8225
8226 return S_OK;
8227}
8228
8229/**
8230 * Helper to initialize all associated child objects and allocate data
8231 * structures.
8232 *
8233 * This method must be called as a part of the object's initialization procedure
8234 * (usually done in the #init() method).
8235 *
8236 * @note Must be called only from #init() or from #registeredInit().
8237 */
8238HRESULT Machine::initDataAndChildObjects()
8239{
8240 AutoCaller autoCaller(this);
8241 AssertComRCReturnRC(autoCaller.rc());
8242 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8243 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8244
8245 AssertReturn(!mData->mAccessible, E_FAIL);
8246
8247 /* allocate data structures */
8248 mSSData.allocate();
8249 mUserData.allocate();
8250 mHWData.allocate();
8251 mMediaData.allocate();
8252 mStorageControllers.allocate();
8253 mUSBControllers.allocate();
8254
8255 /* initialize mOSTypeId */
8256 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8257
8258 /* create associated BIOS settings object */
8259 unconst(mBIOSSettings).createObject();
8260 mBIOSSettings->init(this);
8261
8262 /* create an associated VRDE object (default is disabled) */
8263 unconst(mVRDEServer).createObject();
8264 mVRDEServer->init(this);
8265
8266 /* create associated serial port objects */
8267 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8268 {
8269 unconst(mSerialPorts[slot]).createObject();
8270 mSerialPorts[slot]->init(this, slot);
8271 }
8272
8273 /* create associated parallel port objects */
8274 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8275 {
8276 unconst(mParallelPorts[slot]).createObject();
8277 mParallelPorts[slot]->init(this, slot);
8278 }
8279
8280 /* create the audio adapter object (always present, default is disabled) */
8281 unconst(mAudioAdapter).createObject();
8282 mAudioAdapter->init(this);
8283
8284 /* create the USB device filters object (always present) */
8285 unconst(mUSBDeviceFilters).createObject();
8286 mUSBDeviceFilters->init(this);
8287
8288 /* create associated network adapter objects */
8289 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8290 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8291 {
8292 unconst(mNetworkAdapters[slot]).createObject();
8293 mNetworkAdapters[slot]->init(this, slot);
8294 }
8295
8296 /* create the bandwidth control */
8297 unconst(mBandwidthControl).createObject();
8298 mBandwidthControl->init(this);
8299
8300 return S_OK;
8301}
8302
8303/**
8304 * Helper to uninitialize all associated child objects and to free all data
8305 * structures.
8306 *
8307 * This method must be called as a part of the object's uninitialization
8308 * procedure (usually done in the #uninit() method).
8309 *
8310 * @note Must be called only from #uninit() or from #registeredInit().
8311 */
8312void Machine::uninitDataAndChildObjects()
8313{
8314 AutoCaller autoCaller(this);
8315 AssertComRCReturnVoid(autoCaller.rc());
8316 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8317 || getObjectState().getState() == ObjectState::Limited);
8318
8319 /* tell all our other child objects we've been uninitialized */
8320 if (mBandwidthControl)
8321 {
8322 mBandwidthControl->uninit();
8323 unconst(mBandwidthControl).setNull();
8324 }
8325
8326 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8327 {
8328 if (mNetworkAdapters[slot])
8329 {
8330 mNetworkAdapters[slot]->uninit();
8331 unconst(mNetworkAdapters[slot]).setNull();
8332 }
8333 }
8334
8335 if (mUSBDeviceFilters)
8336 {
8337 mUSBDeviceFilters->uninit();
8338 unconst(mUSBDeviceFilters).setNull();
8339 }
8340
8341 if (mAudioAdapter)
8342 {
8343 mAudioAdapter->uninit();
8344 unconst(mAudioAdapter).setNull();
8345 }
8346
8347 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8348 {
8349 if (mParallelPorts[slot])
8350 {
8351 mParallelPorts[slot]->uninit();
8352 unconst(mParallelPorts[slot]).setNull();
8353 }
8354 }
8355
8356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8357 {
8358 if (mSerialPorts[slot])
8359 {
8360 mSerialPorts[slot]->uninit();
8361 unconst(mSerialPorts[slot]).setNull();
8362 }
8363 }
8364
8365 if (mVRDEServer)
8366 {
8367 mVRDEServer->uninit();
8368 unconst(mVRDEServer).setNull();
8369 }
8370
8371 if (mBIOSSettings)
8372 {
8373 mBIOSSettings->uninit();
8374 unconst(mBIOSSettings).setNull();
8375 }
8376
8377 /* Deassociate media (only when a real Machine or a SnapshotMachine
8378 * instance is uninitialized; SessionMachine instances refer to real
8379 * Machine media). This is necessary for a clean re-initialization of
8380 * the VM after successfully re-checking the accessibility state. Note
8381 * that in case of normal Machine or SnapshotMachine uninitialization (as
8382 * a result of unregistering or deleting the snapshot), outdated media
8383 * attachments will already be uninitialized and deleted, so this
8384 * code will not affect them. */
8385 if ( !!mMediaData
8386 && (!i_isSessionMachine())
8387 )
8388 {
8389 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8390 it != mMediaData->mAttachments.end();
8391 ++it)
8392 {
8393 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8394 if (pMedium.isNull())
8395 continue;
8396 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8397 AssertComRC(rc);
8398 }
8399 }
8400
8401 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8402 {
8403 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8404 if (mData->mFirstSnapshot)
8405 {
8406 // snapshots tree is protected by machine write lock; strictly
8407 // this isn't necessary here since we're deleting the entire
8408 // machine, but otherwise we assert in Snapshot::uninit()
8409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8410 mData->mFirstSnapshot->uninit();
8411 mData->mFirstSnapshot.setNull();
8412 }
8413
8414 mData->mCurrentSnapshot.setNull();
8415 }
8416
8417 /* free data structures (the essential mData structure is not freed here
8418 * since it may be still in use) */
8419 mMediaData.free();
8420 mStorageControllers.free();
8421 mUSBControllers.free();
8422 mHWData.free();
8423 mUserData.free();
8424 mSSData.free();
8425}
8426
8427/**
8428 * Returns a pointer to the Machine object for this machine that acts like a
8429 * parent for complex machine data objects such as shared folders, etc.
8430 *
8431 * For primary Machine objects and for SnapshotMachine objects, returns this
8432 * object's pointer itself. For SessionMachine objects, returns the peer
8433 * (primary) machine pointer.
8434 */
8435Machine* Machine::i_getMachine()
8436{
8437 if (i_isSessionMachine())
8438 return (Machine*)mPeer;
8439 return this;
8440}
8441
8442/**
8443 * Makes sure that there are no machine state dependents. If necessary, waits
8444 * for the number of dependents to drop to zero.
8445 *
8446 * Make sure this method is called from under this object's write lock to
8447 * guarantee that no new dependents may be added when this method returns
8448 * control to the caller.
8449 *
8450 * @note Locks this object for writing. The lock will be released while waiting
8451 * (if necessary).
8452 *
8453 * @warning To be used only in methods that change the machine state!
8454 */
8455void Machine::i_ensureNoStateDependencies()
8456{
8457 AssertReturnVoid(isWriteLockOnCurrentThread());
8458
8459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8460
8461 /* Wait for all state dependents if necessary */
8462 if (mData->mMachineStateDeps != 0)
8463 {
8464 /* lazy semaphore creation */
8465 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8466 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8467
8468 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8469 mData->mMachineStateDeps));
8470
8471 ++mData->mMachineStateChangePending;
8472
8473 /* reset the semaphore before waiting, the last dependent will signal
8474 * it */
8475 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8476
8477 alock.release();
8478
8479 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8480
8481 alock.acquire();
8482
8483 -- mData->mMachineStateChangePending;
8484 }
8485}
8486
8487/**
8488 * Changes the machine state and informs callbacks.
8489 *
8490 * This method is not intended to fail so it either returns S_OK or asserts (and
8491 * returns a failure).
8492 *
8493 * @note Locks this object for writing.
8494 */
8495HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8496{
8497 LogFlowThisFuncEnter();
8498 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8499 Assert(aMachineState != MachineState_Null);
8500
8501 AutoCaller autoCaller(this);
8502 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8503
8504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8505
8506 /* wait for state dependents to drop to zero */
8507 i_ensureNoStateDependencies();
8508
8509 MachineState_T const enmOldState = mData->mMachineState;
8510 if (enmOldState != aMachineState)
8511 {
8512 mData->mMachineState = aMachineState;
8513 RTTimeNow(&mData->mLastStateChange);
8514
8515#ifdef VBOX_WITH_DTRACE_R3_MAIN
8516 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8517#endif
8518 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8519 }
8520
8521 LogFlowThisFuncLeave();
8522 return S_OK;
8523}
8524
8525/**
8526 * Searches for a shared folder with the given logical name
8527 * in the collection of shared folders.
8528 *
8529 * @param aName logical name of the shared folder
8530 * @param aSharedFolder where to return the found object
8531 * @param aSetError whether to set the error info if the folder is
8532 * not found
8533 * @return
8534 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8535 *
8536 * @note
8537 * must be called from under the object's lock!
8538 */
8539HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8540 ComObjPtr<SharedFolder> &aSharedFolder,
8541 bool aSetError /* = false */)
8542{
8543 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8544 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8545 it != mHWData->mSharedFolders.end();
8546 ++it)
8547 {
8548 SharedFolder *pSF = *it;
8549 AutoCaller autoCaller(pSF);
8550 if (pSF->i_getName() == aName)
8551 {
8552 aSharedFolder = pSF;
8553 rc = S_OK;
8554 break;
8555 }
8556 }
8557
8558 if (aSetError && FAILED(rc))
8559 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8560
8561 return rc;
8562}
8563
8564/**
8565 * Initializes all machine instance data from the given settings structures
8566 * from XML. The exception is the machine UUID which needs special handling
8567 * depending on the caller's use case, so the caller needs to set that herself.
8568 *
8569 * This gets called in several contexts during machine initialization:
8570 *
8571 * -- When machine XML exists on disk already and needs to be loaded into memory,
8572 * for example, from registeredInit() to load all registered machines on
8573 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8574 * attached to the machine should be part of some media registry already.
8575 *
8576 * -- During OVF import, when a machine config has been constructed from an
8577 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8578 * ensure that the media listed as attachments in the config (which have
8579 * been imported from the OVF) receive the correct registry ID.
8580 *
8581 * -- During VM cloning.
8582 *
8583 * @param config Machine settings from XML.
8584 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8585 * for each attached medium in the config.
8586 * @return
8587 */
8588HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8589 const Guid *puuidRegistry)
8590{
8591 // copy name, description, OS type, teleporter, UTC etc.
8592 mUserData->s = config.machineUserData;
8593
8594 // Decode the Icon overide data from config userdata and set onto Machine.
8595 #define DECODE_STR_MAX _1M
8596 const char* pszStr = config.machineUserData.ovIcon.c_str();
8597 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8598 if (cbOut > DECODE_STR_MAX)
8599 return setError(E_FAIL,
8600 tr("Icon Data too long.'%d' > '%d'"),
8601 cbOut,
8602 DECODE_STR_MAX);
8603 mUserData->mIcon.resize(cbOut);
8604 int vrc = VINF_SUCCESS;
8605 if (cbOut)
8606 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8607 if (RT_FAILURE(vrc))
8608 {
8609 mUserData->mIcon.resize(0);
8610 return setError(E_FAIL,
8611 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8612 pszStr,
8613 vrc);
8614 }
8615
8616 // look up the object by Id to check it is valid
8617 ComPtr<IGuestOSType> guestOSType;
8618 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8619 guestOSType.asOutParam());
8620 if (FAILED(rc)) return rc;
8621
8622 // stateFile (optional)
8623 if (config.strStateFile.isEmpty())
8624 mSSData->strStateFilePath.setNull();
8625 else
8626 {
8627 Utf8Str stateFilePathFull(config.strStateFile);
8628 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8629 if (RT_FAILURE(vrc))
8630 return setError(E_FAIL,
8631 tr("Invalid saved state file path '%s' (%Rrc)"),
8632 config.strStateFile.c_str(),
8633 vrc);
8634 mSSData->strStateFilePath = stateFilePathFull;
8635 }
8636
8637 // snapshot folder needs special processing so set it again
8638 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8639 if (FAILED(rc)) return rc;
8640
8641 /* Copy the extra data items (Not in any case config is already the same as
8642 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8643 * make sure the extra data map is copied). */
8644 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8645
8646 /* currentStateModified (optional, default is true) */
8647 mData->mCurrentStateModified = config.fCurrentStateModified;
8648
8649 mData->mLastStateChange = config.timeLastStateChange;
8650
8651 /*
8652 * note: all mUserData members must be assigned prior this point because
8653 * we need to commit changes in order to let mUserData be shared by all
8654 * snapshot machine instances.
8655 */
8656 mUserData.commitCopy();
8657
8658 // machine registry, if present (must be loaded before snapshots)
8659 if (config.canHaveOwnMediaRegistry())
8660 {
8661 // determine machine folder
8662 Utf8Str strMachineFolder = i_getSettingsFileFull();
8663 strMachineFolder.stripFilename();
8664 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8665 config.mediaRegistry,
8666 strMachineFolder);
8667 if (FAILED(rc)) return rc;
8668 }
8669
8670 /* Snapshot node (optional) */
8671 size_t cRootSnapshots;
8672 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8673 {
8674 // there must be only one root snapshot
8675 Assert(cRootSnapshots == 1);
8676
8677 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8678
8679 rc = i_loadSnapshot(snap,
8680 config.uuidCurrentSnapshot,
8681 NULL); // no parent == first snapshot
8682 if (FAILED(rc)) return rc;
8683 }
8684
8685 // hardware data
8686 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8687 if (FAILED(rc)) return rc;
8688
8689 // load storage controllers
8690 rc = i_loadStorageControllers(config.storageMachine,
8691 puuidRegistry,
8692 NULL /* puuidSnapshot */);
8693 if (FAILED(rc)) return rc;
8694
8695 /*
8696 * NOTE: the assignment below must be the last thing to do,
8697 * otherwise it will be not possible to change the settings
8698 * somewhere in the code above because all setters will be
8699 * blocked by i_checkStateDependency(MutableStateDep).
8700 */
8701
8702 /* set the machine state to Aborted or Saved when appropriate */
8703 if (config.fAborted)
8704 {
8705 mSSData->strStateFilePath.setNull();
8706
8707 /* no need to use i_setMachineState() during init() */
8708 mData->mMachineState = MachineState_Aborted;
8709 }
8710 else if (!mSSData->strStateFilePath.isEmpty())
8711 {
8712 /* no need to use i_setMachineState() during init() */
8713 mData->mMachineState = MachineState_Saved;
8714 }
8715
8716 // after loading settings, we are no longer different from the XML on disk
8717 mData->flModifications = 0;
8718
8719 return S_OK;
8720}
8721
8722/**
8723 * Recursively loads all snapshots starting from the given.
8724 *
8725 * @param aNode <Snapshot> node.
8726 * @param aCurSnapshotId Current snapshot ID from the settings file.
8727 * @param aParentSnapshot Parent snapshot.
8728 */
8729HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8730 const Guid &aCurSnapshotId,
8731 Snapshot *aParentSnapshot)
8732{
8733 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8734 AssertReturn(!i_isSessionMachine(), E_FAIL);
8735
8736 HRESULT rc = S_OK;
8737
8738 Utf8Str strStateFile;
8739 if (!data.strStateFile.isEmpty())
8740 {
8741 /* optional */
8742 strStateFile = data.strStateFile;
8743 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8744 if (RT_FAILURE(vrc))
8745 return setError(E_FAIL,
8746 tr("Invalid saved state file path '%s' (%Rrc)"),
8747 strStateFile.c_str(),
8748 vrc);
8749 }
8750
8751 /* create a snapshot machine object */
8752 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8753 pSnapshotMachine.createObject();
8754 rc = pSnapshotMachine->initFromSettings(this,
8755 data.hardware,
8756 &data.debugging,
8757 &data.autostart,
8758 data.storage,
8759 data.uuid.ref(),
8760 strStateFile);
8761 if (FAILED(rc)) return rc;
8762
8763 /* create a snapshot object */
8764 ComObjPtr<Snapshot> pSnapshot;
8765 pSnapshot.createObject();
8766 /* initialize the snapshot */
8767 rc = pSnapshot->init(mParent, // VirtualBox object
8768 data.uuid,
8769 data.strName,
8770 data.strDescription,
8771 data.timestamp,
8772 pSnapshotMachine,
8773 aParentSnapshot);
8774 if (FAILED(rc)) return rc;
8775
8776 /* memorize the first snapshot if necessary */
8777 if (!mData->mFirstSnapshot)
8778 mData->mFirstSnapshot = pSnapshot;
8779
8780 /* memorize the current snapshot when appropriate */
8781 if ( !mData->mCurrentSnapshot
8782 && pSnapshot->i_getId() == aCurSnapshotId
8783 )
8784 mData->mCurrentSnapshot = pSnapshot;
8785
8786 // now create the children
8787 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8788 it != data.llChildSnapshots.end();
8789 ++it)
8790 {
8791 const settings::Snapshot &childData = *it;
8792 // recurse
8793 rc = i_loadSnapshot(childData,
8794 aCurSnapshotId,
8795 pSnapshot); // parent = the one we created above
8796 if (FAILED(rc)) return rc;
8797 }
8798
8799 return rc;
8800}
8801
8802/**
8803 * Loads settings into mHWData.
8804 *
8805 * @param data Reference to the hardware settings.
8806 * @param pDbg Pointer to the debugging settings.
8807 * @param pAutostart Pointer to the autostart settings.
8808 */
8809HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8810 const settings::Autostart *pAutostart)
8811{
8812 AssertReturn(!i_isSessionMachine(), E_FAIL);
8813
8814 HRESULT rc = S_OK;
8815
8816 try
8817 {
8818 /* The hardware version attribute (optional). */
8819 mHWData->mHWVersion = data.strVersion;
8820 mHWData->mHardwareUUID = data.uuid;
8821
8822 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8823 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8824 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8825 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8826 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8827 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8828 mHWData->mPAEEnabled = data.fPAE;
8829 mHWData->mLongMode = data.enmLongMode;
8830 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8831 mHWData->mCPUCount = data.cCPUs;
8832 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8833 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8834 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8835
8836 // cpu
8837 if (mHWData->mCPUHotPlugEnabled)
8838 {
8839 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8840 it != data.llCpus.end();
8841 ++it)
8842 {
8843 const settings::Cpu &cpu = *it;
8844
8845 mHWData->mCPUAttached[cpu.ulId] = true;
8846 }
8847 }
8848
8849 // cpuid leafs
8850 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8851 it != data.llCpuIdLeafs.end();
8852 ++it)
8853 {
8854 const settings::CpuIdLeaf &leaf = *it;
8855
8856 switch (leaf.ulId)
8857 {
8858 case 0x0:
8859 case 0x1:
8860 case 0x2:
8861 case 0x3:
8862 case 0x4:
8863 case 0x5:
8864 case 0x6:
8865 case 0x7:
8866 case 0x8:
8867 case 0x9:
8868 case 0xA:
8869 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8870 break;
8871
8872 case 0x80000000:
8873 case 0x80000001:
8874 case 0x80000002:
8875 case 0x80000003:
8876 case 0x80000004:
8877 case 0x80000005:
8878 case 0x80000006:
8879 case 0x80000007:
8880 case 0x80000008:
8881 case 0x80000009:
8882 case 0x8000000A:
8883 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8884 break;
8885
8886 default:
8887 /* just ignore */
8888 break;
8889 }
8890 }
8891
8892 mHWData->mMemorySize = data.ulMemorySizeMB;
8893 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8894
8895 // boot order
8896 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8897 {
8898 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8899 if (it == data.mapBootOrder.end())
8900 mHWData->mBootOrder[i] = DeviceType_Null;
8901 else
8902 mHWData->mBootOrder[i] = it->second;
8903 }
8904
8905 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8906 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8907 mHWData->mMonitorCount = data.cMonitors;
8908 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8909 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8910 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8911 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8912 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8913 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8914 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8915 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8916 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8917 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8918 if (!data.strVideoCaptureFile.isEmpty())
8919 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8920 else
8921 mHWData->mVideoCaptureFile.setNull();
8922 mHWData->mFirmwareType = data.firmwareType;
8923 mHWData->mPointingHIDType = data.pointingHIDType;
8924 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8925 mHWData->mChipsetType = data.chipsetType;
8926 mHWData->mParavirtProvider = data.paravirtProvider;
8927 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8928 mHWData->mHPETEnabled = data.fHPETEnabled;
8929
8930 /* VRDEServer */
8931 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8932 if (FAILED(rc)) return rc;
8933
8934 /* BIOS */
8935 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8936 if (FAILED(rc)) return rc;
8937
8938 // Bandwidth control (must come before network adapters)
8939 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8940 if (FAILED(rc)) return rc;
8941
8942 /* Shared folders */
8943 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8944 it != data.usbSettings.llUSBControllers.end();
8945 ++it)
8946 {
8947 const settings::USBController &settingsCtrl = *it;
8948 ComObjPtr<USBController> newCtrl;
8949
8950 newCtrl.createObject();
8951 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8952 mUSBControllers->push_back(newCtrl);
8953 }
8954
8955 /* USB device filters */
8956 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8957 if (FAILED(rc)) return rc;
8958
8959 // network adapters
8960 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8961 size_t oldCount = mNetworkAdapters.size();
8962 if (newCount > oldCount)
8963 {
8964 mNetworkAdapters.resize(newCount);
8965 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8966 {
8967 unconst(mNetworkAdapters[slot]).createObject();
8968 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8969 }
8970 }
8971 else if (newCount < oldCount)
8972 mNetworkAdapters.resize(newCount);
8973 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8974 it != data.llNetworkAdapters.end();
8975 ++it)
8976 {
8977 const settings::NetworkAdapter &nic = *it;
8978
8979 /* slot unicity is guaranteed by XML Schema */
8980 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8981 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8982 if (FAILED(rc)) return rc;
8983 }
8984
8985 // serial ports
8986 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8987 it != data.llSerialPorts.end();
8988 ++it)
8989 {
8990 const settings::SerialPort &s = *it;
8991
8992 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8993 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8994 if (FAILED(rc)) return rc;
8995 }
8996
8997 // parallel ports (optional)
8998 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8999 it != data.llParallelPorts.end();
9000 ++it)
9001 {
9002 const settings::ParallelPort &p = *it;
9003
9004 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9005 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9006 if (FAILED(rc)) return rc;
9007 }
9008
9009 /* AudioAdapter */
9010 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9011 if (FAILED(rc)) return rc;
9012
9013 /* Shared folders */
9014 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9015 it != data.llSharedFolders.end();
9016 ++it)
9017 {
9018 const settings::SharedFolder &sf = *it;
9019
9020 ComObjPtr<SharedFolder> sharedFolder;
9021 /* Check for double entries. Not allowed! */
9022 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9023 if (SUCCEEDED(rc))
9024 return setError(VBOX_E_OBJECT_IN_USE,
9025 tr("Shared folder named '%s' already exists"),
9026 sf.strName.c_str());
9027
9028 /* Create the new shared folder. Don't break on error. This will be
9029 * reported when the machine starts. */
9030 sharedFolder.createObject();
9031 rc = sharedFolder->init(i_getMachine(),
9032 sf.strName,
9033 sf.strHostPath,
9034 RT_BOOL(sf.fWritable),
9035 RT_BOOL(sf.fAutoMount),
9036 false /* fFailOnError */);
9037 if (FAILED(rc)) return rc;
9038 mHWData->mSharedFolders.push_back(sharedFolder);
9039 }
9040
9041 // Clipboard
9042 mHWData->mClipboardMode = data.clipboardMode;
9043
9044 // drag'n'drop
9045 mHWData->mDnDMode = data.dndMode;
9046
9047 // guest settings
9048 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9049
9050 // IO settings
9051 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9052 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9053
9054 // Host PCI devices
9055 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9056 it != data.pciAttachments.end();
9057 ++it)
9058 {
9059 const settings::HostPCIDeviceAttachment &hpda = *it;
9060 ComObjPtr<PCIDeviceAttachment> pda;
9061
9062 pda.createObject();
9063 pda->i_loadSettings(this, hpda);
9064 mHWData->mPCIDeviceAssignments.push_back(pda);
9065 }
9066
9067 /*
9068 * (The following isn't really real hardware, but it lives in HWData
9069 * for reasons of convenience.)
9070 */
9071
9072#ifdef VBOX_WITH_GUEST_PROPS
9073 /* Guest properties (optional) */
9074 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9075 it != data.llGuestProperties.end();
9076 ++it)
9077 {
9078 const settings::GuestProperty &prop = *it;
9079 uint32_t fFlags = guestProp::NILFLAG;
9080 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9081 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9082 mHWData->mGuestProperties[prop.strName] = property;
9083 }
9084
9085 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9086#endif /* VBOX_WITH_GUEST_PROPS defined */
9087
9088 rc = i_loadDebugging(pDbg);
9089 if (FAILED(rc))
9090 return rc;
9091
9092 mHWData->mAutostart = *pAutostart;
9093
9094 /* default frontend */
9095 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9096 }
9097 catch(std::bad_alloc &)
9098 {
9099 return E_OUTOFMEMORY;
9100 }
9101
9102 AssertComRC(rc);
9103 return rc;
9104}
9105
9106/**
9107 * Called from Machine::loadHardware() to load the debugging settings of the
9108 * machine.
9109 *
9110 * @param pDbg Pointer to the settings.
9111 */
9112HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9113{
9114 mHWData->mDebugging = *pDbg;
9115 /* no more processing currently required, this will probably change. */
9116 return S_OK;
9117}
9118
9119/**
9120 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9121 *
9122 * @param data
9123 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9124 * @param puuidSnapshot
9125 * @return
9126 */
9127HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9128 const Guid *puuidRegistry,
9129 const Guid *puuidSnapshot)
9130{
9131 AssertReturn(!i_isSessionMachine(), E_FAIL);
9132
9133 HRESULT rc = S_OK;
9134
9135 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9136 it != data.llStorageControllers.end();
9137 ++it)
9138 {
9139 const settings::StorageController &ctlData = *it;
9140
9141 ComObjPtr<StorageController> pCtl;
9142 /* Try to find one with the name first. */
9143 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9144 if (SUCCEEDED(rc))
9145 return setError(VBOX_E_OBJECT_IN_USE,
9146 tr("Storage controller named '%s' already exists"),
9147 ctlData.strName.c_str());
9148
9149 pCtl.createObject();
9150 rc = pCtl->init(this,
9151 ctlData.strName,
9152 ctlData.storageBus,
9153 ctlData.ulInstance,
9154 ctlData.fBootable);
9155 if (FAILED(rc)) return rc;
9156
9157 mStorageControllers->push_back(pCtl);
9158
9159 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9160 if (FAILED(rc)) return rc;
9161
9162 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9163 if (FAILED(rc)) return rc;
9164
9165 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9166 if (FAILED(rc)) return rc;
9167
9168 /* Set IDE emulation settings (only for AHCI controller). */
9169 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9170 {
9171 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9172 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9173 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9174 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9175 )
9176 return rc;
9177 }
9178
9179 /* Load the attached devices now. */
9180 rc = i_loadStorageDevices(pCtl,
9181 ctlData,
9182 puuidRegistry,
9183 puuidSnapshot);
9184 if (FAILED(rc)) return rc;
9185 }
9186
9187 return S_OK;
9188}
9189
9190/**
9191 * Called from i_loadStorageControllers for a controller's devices.
9192 *
9193 * @param aStorageController
9194 * @param data
9195 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9196 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9197 * @return
9198 */
9199HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9200 const settings::StorageController &data,
9201 const Guid *puuidRegistry,
9202 const Guid *puuidSnapshot)
9203{
9204 HRESULT rc = S_OK;
9205
9206 /* paranoia: detect duplicate attachments */
9207 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9208 it != data.llAttachedDevices.end();
9209 ++it)
9210 {
9211 const settings::AttachedDevice &ad = *it;
9212
9213 for (settings::AttachedDevicesList::const_iterator it2 = it;
9214 it2 != data.llAttachedDevices.end();
9215 ++it2)
9216 {
9217 if (it == it2)
9218 continue;
9219
9220 const settings::AttachedDevice &ad2 = *it2;
9221
9222 if ( ad.lPort == ad2.lPort
9223 && ad.lDevice == ad2.lDevice)
9224 {
9225 return setError(E_FAIL,
9226 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9227 aStorageController->i_getName().c_str(),
9228 ad.lPort,
9229 ad.lDevice,
9230 mUserData->s.strName.c_str());
9231 }
9232 }
9233 }
9234
9235 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9236 it != data.llAttachedDevices.end();
9237 ++it)
9238 {
9239 const settings::AttachedDevice &dev = *it;
9240 ComObjPtr<Medium> medium;
9241
9242 switch (dev.deviceType)
9243 {
9244 case DeviceType_Floppy:
9245 case DeviceType_DVD:
9246 if (dev.strHostDriveSrc.isNotEmpty())
9247 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9248 false /* fRefresh */, medium);
9249 else
9250 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9251 dev.uuid,
9252 false /* fRefresh */,
9253 false /* aSetError */,
9254 medium);
9255 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9256 // This is not an error. The host drive or UUID might have vanished, so just go
9257 // ahead without this removeable medium attachment
9258 rc = S_OK;
9259 break;
9260
9261 case DeviceType_HardDisk:
9262 {
9263 /* find a hard disk by UUID */
9264 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9265 if (FAILED(rc))
9266 {
9267 if (i_isSnapshotMachine())
9268 {
9269 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9270 // so the user knows that the bad disk is in a snapshot somewhere
9271 com::ErrorInfo info;
9272 return setError(E_FAIL,
9273 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9274 puuidSnapshot->raw(),
9275 info.getText().raw());
9276 }
9277 else
9278 return rc;
9279 }
9280
9281 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9282
9283 if (medium->i_getType() == MediumType_Immutable)
9284 {
9285 if (i_isSnapshotMachine())
9286 return setError(E_FAIL,
9287 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9288 "of the virtual machine '%s' ('%s')"),
9289 medium->i_getLocationFull().c_str(),
9290 dev.uuid.raw(),
9291 puuidSnapshot->raw(),
9292 mUserData->s.strName.c_str(),
9293 mData->m_strConfigFileFull.c_str());
9294
9295 return setError(E_FAIL,
9296 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9297 medium->i_getLocationFull().c_str(),
9298 dev.uuid.raw(),
9299 mUserData->s.strName.c_str(),
9300 mData->m_strConfigFileFull.c_str());
9301 }
9302
9303 if (medium->i_getType() == MediumType_MultiAttach)
9304 {
9305 if (i_isSnapshotMachine())
9306 return setError(E_FAIL,
9307 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9308 "of the virtual machine '%s' ('%s')"),
9309 medium->i_getLocationFull().c_str(),
9310 dev.uuid.raw(),
9311 puuidSnapshot->raw(),
9312 mUserData->s.strName.c_str(),
9313 mData->m_strConfigFileFull.c_str());
9314
9315 return setError(E_FAIL,
9316 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9317 medium->i_getLocationFull().c_str(),
9318 dev.uuid.raw(),
9319 mUserData->s.strName.c_str(),
9320 mData->m_strConfigFileFull.c_str());
9321 }
9322
9323 if ( !i_isSnapshotMachine()
9324 && medium->i_getChildren().size() != 0
9325 )
9326 return setError(E_FAIL,
9327 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9328 "because it has %d differencing child hard disks"),
9329 medium->i_getLocationFull().c_str(),
9330 dev.uuid.raw(),
9331 mUserData->s.strName.c_str(),
9332 mData->m_strConfigFileFull.c_str(),
9333 medium->i_getChildren().size());
9334
9335 if (i_findAttachment(mMediaData->mAttachments,
9336 medium))
9337 return setError(E_FAIL,
9338 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9339 medium->i_getLocationFull().c_str(),
9340 dev.uuid.raw(),
9341 mUserData->s.strName.c_str(),
9342 mData->m_strConfigFileFull.c_str());
9343
9344 break;
9345 }
9346
9347 default:
9348 return setError(E_FAIL,
9349 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9350 medium->i_getLocationFull().c_str(),
9351 mUserData->s.strName.c_str(),
9352 mData->m_strConfigFileFull.c_str());
9353 }
9354
9355 if (FAILED(rc))
9356 break;
9357
9358 /* Bandwidth groups are loaded at this point. */
9359 ComObjPtr<BandwidthGroup> pBwGroup;
9360
9361 if (!dev.strBwGroup.isEmpty())
9362 {
9363 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9364 if (FAILED(rc))
9365 return setError(E_FAIL,
9366 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9367 medium->i_getLocationFull().c_str(),
9368 dev.strBwGroup.c_str(),
9369 mUserData->s.strName.c_str(),
9370 mData->m_strConfigFileFull.c_str());
9371 pBwGroup->i_reference();
9372 }
9373
9374 const Bstr controllerName = aStorageController->i_getName();
9375 ComObjPtr<MediumAttachment> pAttachment;
9376 pAttachment.createObject();
9377 rc = pAttachment->init(this,
9378 medium,
9379 controllerName,
9380 dev.lPort,
9381 dev.lDevice,
9382 dev.deviceType,
9383 false,
9384 dev.fPassThrough,
9385 dev.fTempEject,
9386 dev.fNonRotational,
9387 dev.fDiscard,
9388 dev.fHotPluggable,
9389 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9390 if (FAILED(rc)) break;
9391
9392 /* associate the medium with this machine and snapshot */
9393 if (!medium.isNull())
9394 {
9395 AutoCaller medCaller(medium);
9396 if (FAILED(medCaller.rc())) return medCaller.rc();
9397 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9398
9399 if (i_isSnapshotMachine())
9400 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9401 else
9402 rc = medium->i_addBackReference(mData->mUuid);
9403 /* If the medium->addBackReference fails it sets an appropriate
9404 * error message, so no need to do any guesswork here. */
9405
9406 if (puuidRegistry)
9407 // caller wants registry ID to be set on all attached media (OVF import case)
9408 medium->i_addRegistry(*puuidRegistry);
9409 }
9410
9411 if (FAILED(rc))
9412 break;
9413
9414 /* back up mMediaData to let registeredInit() properly rollback on failure
9415 * (= limited accessibility) */
9416 i_setModified(IsModified_Storage);
9417 mMediaData.backup();
9418 mMediaData->mAttachments.push_back(pAttachment);
9419 }
9420
9421 return rc;
9422}
9423
9424/**
9425 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9426 *
9427 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9428 * @param aSnapshot where to return the found snapshot
9429 * @param aSetError true to set extended error info on failure
9430 */
9431HRESULT Machine::i_findSnapshotById(const Guid &aId,
9432 ComObjPtr<Snapshot> &aSnapshot,
9433 bool aSetError /* = false */)
9434{
9435 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9436
9437 if (!mData->mFirstSnapshot)
9438 {
9439 if (aSetError)
9440 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9441 return E_FAIL;
9442 }
9443
9444 if (aId.isZero())
9445 aSnapshot = mData->mFirstSnapshot;
9446 else
9447 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9448
9449 if (!aSnapshot)
9450 {
9451 if (aSetError)
9452 return setError(E_FAIL,
9453 tr("Could not find a snapshot with UUID {%s}"),
9454 aId.toString().c_str());
9455 return E_FAIL;
9456 }
9457
9458 return S_OK;
9459}
9460
9461/**
9462 * Returns the snapshot with the given name or fails of no such snapshot.
9463 *
9464 * @param aName snapshot name to find
9465 * @param aSnapshot where to return the found snapshot
9466 * @param aSetError true to set extended error info on failure
9467 */
9468HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9469 ComObjPtr<Snapshot> &aSnapshot,
9470 bool aSetError /* = false */)
9471{
9472 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9473
9474 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9475
9476 if (!mData->mFirstSnapshot)
9477 {
9478 if (aSetError)
9479 return setError(VBOX_E_OBJECT_NOT_FOUND,
9480 tr("This machine does not have any snapshots"));
9481 return VBOX_E_OBJECT_NOT_FOUND;
9482 }
9483
9484 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9485
9486 if (!aSnapshot)
9487 {
9488 if (aSetError)
9489 return setError(VBOX_E_OBJECT_NOT_FOUND,
9490 tr("Could not find a snapshot named '%s'"), strName.c_str());
9491 return VBOX_E_OBJECT_NOT_FOUND;
9492 }
9493
9494 return S_OK;
9495}
9496
9497/**
9498 * Returns a storage controller object with the given name.
9499 *
9500 * @param aName storage controller name to find
9501 * @param aStorageController where to return the found storage controller
9502 * @param aSetError true to set extended error info on failure
9503 */
9504HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9505 ComObjPtr<StorageController> &aStorageController,
9506 bool aSetError /* = false */)
9507{
9508 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9509
9510 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9511 it != mStorageControllers->end();
9512 ++it)
9513 {
9514 if ((*it)->i_getName() == aName)
9515 {
9516 aStorageController = (*it);
9517 return S_OK;
9518 }
9519 }
9520
9521 if (aSetError)
9522 return setError(VBOX_E_OBJECT_NOT_FOUND,
9523 tr("Could not find a storage controller named '%s'"),
9524 aName.c_str());
9525 return VBOX_E_OBJECT_NOT_FOUND;
9526}
9527
9528/**
9529 * Returns a USB controller object with the given name.
9530 *
9531 * @param aName USB controller name to find
9532 * @param aUSBController where to return the found USB controller
9533 * @param aSetError true to set extended error info on failure
9534 */
9535HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9536 ComObjPtr<USBController> &aUSBController,
9537 bool aSetError /* = false */)
9538{
9539 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9540
9541 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9542 it != mUSBControllers->end();
9543 ++it)
9544 {
9545 if ((*it)->i_getName() == aName)
9546 {
9547 aUSBController = (*it);
9548 return S_OK;
9549 }
9550 }
9551
9552 if (aSetError)
9553 return setError(VBOX_E_OBJECT_NOT_FOUND,
9554 tr("Could not find a storage controller named '%s'"),
9555 aName.c_str());
9556 return VBOX_E_OBJECT_NOT_FOUND;
9557}
9558
9559/**
9560 * Returns the number of USB controller instance of the given type.
9561 *
9562 * @param enmType USB controller type.
9563 */
9564ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9565{
9566 ULONG cCtrls = 0;
9567
9568 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9569 it != mUSBControllers->end();
9570 ++it)
9571 {
9572 if ((*it)->i_getControllerType() == enmType)
9573 cCtrls++;
9574 }
9575
9576 return cCtrls;
9577}
9578
9579HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9580 MediaData::AttachmentList &atts)
9581{
9582 AutoCaller autoCaller(this);
9583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9584
9585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9586
9587 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9588 it != mMediaData->mAttachments.end();
9589 ++it)
9590 {
9591 const ComObjPtr<MediumAttachment> &pAtt = *it;
9592 // should never happen, but deal with NULL pointers in the list.
9593 AssertStmt(!pAtt.isNull(), continue);
9594
9595 // getControllerName() needs caller+read lock
9596 AutoCaller autoAttCaller(pAtt);
9597 if (FAILED(autoAttCaller.rc()))
9598 {
9599 atts.clear();
9600 return autoAttCaller.rc();
9601 }
9602 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9603
9604 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9605 atts.push_back(pAtt);
9606 }
9607
9608 return S_OK;
9609}
9610
9611
9612/**
9613 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9614 * file if the machine name was changed and about creating a new settings file
9615 * if this is a new machine.
9616 *
9617 * @note Must be never called directly but only from #saveSettings().
9618 */
9619HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9620{
9621 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9622
9623 HRESULT rc = S_OK;
9624
9625 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9626
9627 /// @todo need to handle primary group change, too
9628
9629 /* attempt to rename the settings file if machine name is changed */
9630 if ( mUserData->s.fNameSync
9631 && mUserData.isBackedUp()
9632 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9633 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9634 )
9635 {
9636 bool dirRenamed = false;
9637 bool fileRenamed = false;
9638
9639 Utf8Str configFile, newConfigFile;
9640 Utf8Str configFilePrev, newConfigFilePrev;
9641 Utf8Str configDir, newConfigDir;
9642
9643 do
9644 {
9645 int vrc = VINF_SUCCESS;
9646
9647 Utf8Str name = mUserData.backedUpData()->s.strName;
9648 Utf8Str newName = mUserData->s.strName;
9649 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9650 if (group == "/")
9651 group.setNull();
9652 Utf8Str newGroup = mUserData->s.llGroups.front();
9653 if (newGroup == "/")
9654 newGroup.setNull();
9655
9656 configFile = mData->m_strConfigFileFull;
9657
9658 /* first, rename the directory if it matches the group and machine name */
9659 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9660 group.c_str(), RTPATH_DELIMITER, name.c_str());
9661 /** @todo hack, make somehow use of ComposeMachineFilename */
9662 if (mUserData->s.fDirectoryIncludesUUID)
9663 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9664 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9665 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9666 /** @todo hack, make somehow use of ComposeMachineFilename */
9667 if (mUserData->s.fDirectoryIncludesUUID)
9668 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9669 configDir = configFile;
9670 configDir.stripFilename();
9671 newConfigDir = configDir;
9672 if ( configDir.length() >= groupPlusName.length()
9673 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9674 groupPlusName.c_str()))
9675 {
9676 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9677 Utf8Str newConfigBaseDir(newConfigDir);
9678 newConfigDir.append(newGroupPlusName);
9679 /* consistency: use \ if appropriate on the platform */
9680 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9681 /* new dir and old dir cannot be equal here because of 'if'
9682 * above and because name != newName */
9683 Assert(configDir != newConfigDir);
9684 if (!fSettingsFileIsNew)
9685 {
9686 /* perform real rename only if the machine is not new */
9687 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9688 if ( vrc == VERR_FILE_NOT_FOUND
9689 || vrc == VERR_PATH_NOT_FOUND)
9690 {
9691 /* create the parent directory, then retry renaming */
9692 Utf8Str parent(newConfigDir);
9693 parent.stripFilename();
9694 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9695 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9696 }
9697 if (RT_FAILURE(vrc))
9698 {
9699 rc = setError(E_FAIL,
9700 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9701 configDir.c_str(),
9702 newConfigDir.c_str(),
9703 vrc);
9704 break;
9705 }
9706 /* delete subdirectories which are no longer needed */
9707 Utf8Str dir(configDir);
9708 dir.stripFilename();
9709 while (dir != newConfigBaseDir && dir != ".")
9710 {
9711 vrc = RTDirRemove(dir.c_str());
9712 if (RT_FAILURE(vrc))
9713 break;
9714 dir.stripFilename();
9715 }
9716 dirRenamed = true;
9717 }
9718 }
9719
9720 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9721 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9722
9723 /* then try to rename the settings file itself */
9724 if (newConfigFile != configFile)
9725 {
9726 /* get the path to old settings file in renamed directory */
9727 configFile = Utf8StrFmt("%s%c%s",
9728 newConfigDir.c_str(),
9729 RTPATH_DELIMITER,
9730 RTPathFilename(configFile.c_str()));
9731 if (!fSettingsFileIsNew)
9732 {
9733 /* perform real rename only if the machine is not new */
9734 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9735 if (RT_FAILURE(vrc))
9736 {
9737 rc = setError(E_FAIL,
9738 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9739 configFile.c_str(),
9740 newConfigFile.c_str(),
9741 vrc);
9742 break;
9743 }
9744 fileRenamed = true;
9745 configFilePrev = configFile;
9746 configFilePrev += "-prev";
9747 newConfigFilePrev = newConfigFile;
9748 newConfigFilePrev += "-prev";
9749 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9750 }
9751 }
9752
9753 // update m_strConfigFileFull amd mConfigFile
9754 mData->m_strConfigFileFull = newConfigFile;
9755 // compute the relative path too
9756 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9757
9758 // store the old and new so that VirtualBox::i_saveSettings() can update
9759 // the media registry
9760 if ( mData->mRegistered
9761 && (configDir != newConfigDir || configFile != newConfigFile))
9762 {
9763 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9764
9765 if (pfNeedsGlobalSaveSettings)
9766 *pfNeedsGlobalSaveSettings = true;
9767 }
9768
9769 // in the saved state file path, replace the old directory with the new directory
9770 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9771 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9772
9773 // and do the same thing for the saved state file paths of all the online snapshots
9774 if (mData->mFirstSnapshot)
9775 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9776 newConfigDir.c_str());
9777 }
9778 while (0);
9779
9780 if (FAILED(rc))
9781 {
9782 /* silently try to rename everything back */
9783 if (fileRenamed)
9784 {
9785 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9786 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9787 }
9788 if (dirRenamed)
9789 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9790 }
9791
9792 if (FAILED(rc)) return rc;
9793 }
9794
9795 if (fSettingsFileIsNew)
9796 {
9797 /* create a virgin config file */
9798 int vrc = VINF_SUCCESS;
9799
9800 /* ensure the settings directory exists */
9801 Utf8Str path(mData->m_strConfigFileFull);
9802 path.stripFilename();
9803 if (!RTDirExists(path.c_str()))
9804 {
9805 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9806 if (RT_FAILURE(vrc))
9807 {
9808 return setError(E_FAIL,
9809 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9810 path.c_str(),
9811 vrc);
9812 }
9813 }
9814
9815 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9816 path = Utf8Str(mData->m_strConfigFileFull);
9817 RTFILE f = NIL_RTFILE;
9818 vrc = RTFileOpen(&f, path.c_str(),
9819 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9820 if (RT_FAILURE(vrc))
9821 return setError(E_FAIL,
9822 tr("Could not create the settings file '%s' (%Rrc)"),
9823 path.c_str(),
9824 vrc);
9825 RTFileClose(f);
9826 }
9827
9828 return rc;
9829}
9830
9831/**
9832 * Saves and commits machine data, user data and hardware data.
9833 *
9834 * Note that on failure, the data remains uncommitted.
9835 *
9836 * @a aFlags may combine the following flags:
9837 *
9838 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9839 * Used when saving settings after an operation that makes them 100%
9840 * correspond to the settings from the current snapshot.
9841 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9842 * #isReallyModified() returns false. This is necessary for cases when we
9843 * change machine data directly, not through the backup()/commit() mechanism.
9844 * - SaveS_Force: settings will be saved without doing a deep compare of the
9845 * settings structures. This is used when this is called because snapshots
9846 * have changed to avoid the overhead of the deep compare.
9847 *
9848 * @note Must be called from under this object's write lock. Locks children for
9849 * writing.
9850 *
9851 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9852 * initialized to false and that will be set to true by this function if
9853 * the caller must invoke VirtualBox::i_saveSettings() because the global
9854 * settings have changed. This will happen if a machine rename has been
9855 * saved and the global machine and media registries will therefore need
9856 * updating.
9857 */
9858HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9859 int aFlags /*= 0*/)
9860{
9861 LogFlowThisFuncEnter();
9862
9863 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9864
9865 /* make sure child objects are unable to modify the settings while we are
9866 * saving them */
9867 i_ensureNoStateDependencies();
9868
9869 AssertReturn(!i_isSnapshotMachine(),
9870 E_FAIL);
9871
9872 HRESULT rc = S_OK;
9873 bool fNeedsWrite = false;
9874
9875 /* First, prepare to save settings. It will care about renaming the
9876 * settings directory and file if the machine name was changed and about
9877 * creating a new settings file if this is a new machine. */
9878 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9879 if (FAILED(rc)) return rc;
9880
9881 // keep a pointer to the current settings structures
9882 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9883 settings::MachineConfigFile *pNewConfig = NULL;
9884
9885 try
9886 {
9887 // make a fresh one to have everyone write stuff into
9888 pNewConfig = new settings::MachineConfigFile(NULL);
9889 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9890
9891 // now go and copy all the settings data from COM to the settings structures
9892 // (this calles i_saveSettings() on all the COM objects in the machine)
9893 i_copyMachineDataToSettings(*pNewConfig);
9894
9895 if (aFlags & SaveS_ResetCurStateModified)
9896 {
9897 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9898 mData->mCurrentStateModified = FALSE;
9899 fNeedsWrite = true; // always, no need to compare
9900 }
9901 else if (aFlags & SaveS_Force)
9902 {
9903 fNeedsWrite = true; // always, no need to compare
9904 }
9905 else
9906 {
9907 if (!mData->mCurrentStateModified)
9908 {
9909 // do a deep compare of the settings that we just saved with the settings
9910 // previously stored in the config file; this invokes MachineConfigFile::operator==
9911 // which does a deep compare of all the settings, which is expensive but less expensive
9912 // than writing out XML in vain
9913 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9914
9915 // could still be modified if any settings changed
9916 mData->mCurrentStateModified = fAnySettingsChanged;
9917
9918 fNeedsWrite = fAnySettingsChanged;
9919 }
9920 else
9921 fNeedsWrite = true;
9922 }
9923
9924 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9925
9926 if (fNeedsWrite)
9927 // now spit it all out!
9928 pNewConfig->write(mData->m_strConfigFileFull);
9929
9930 mData->pMachineConfigFile = pNewConfig;
9931 delete pOldConfig;
9932 i_commit();
9933
9934 // after saving settings, we are no longer different from the XML on disk
9935 mData->flModifications = 0;
9936 }
9937 catch (HRESULT err)
9938 {
9939 // we assume that error info is set by the thrower
9940 rc = err;
9941
9942 // restore old config
9943 delete pNewConfig;
9944 mData->pMachineConfigFile = pOldConfig;
9945 }
9946 catch (...)
9947 {
9948 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9949 }
9950
9951 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9952 {
9953 /* Fire the data change event, even on failure (since we've already
9954 * committed all data). This is done only for SessionMachines because
9955 * mutable Machine instances are always not registered (i.e. private
9956 * to the client process that creates them) and thus don't need to
9957 * inform callbacks. */
9958 if (i_isSessionMachine())
9959 mParent->i_onMachineDataChange(mData->mUuid);
9960 }
9961
9962 LogFlowThisFunc(("rc=%08X\n", rc));
9963 LogFlowThisFuncLeave();
9964 return rc;
9965}
9966
9967/**
9968 * Implementation for saving the machine settings into the given
9969 * settings::MachineConfigFile instance. This copies machine extradata
9970 * from the previous machine config file in the instance data, if any.
9971 *
9972 * This gets called from two locations:
9973 *
9974 * -- Machine::i_saveSettings(), during the regular XML writing;
9975 *
9976 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9977 * exported to OVF and we write the VirtualBox proprietary XML
9978 * into a <vbox:Machine> tag.
9979 *
9980 * This routine fills all the fields in there, including snapshots, *except*
9981 * for the following:
9982 *
9983 * -- fCurrentStateModified. There is some special logic associated with that.
9984 *
9985 * The caller can then call MachineConfigFile::write() or do something else
9986 * with it.
9987 *
9988 * Caller must hold the machine lock!
9989 *
9990 * This throws XML errors and HRESULT, so the caller must have a catch block!
9991 */
9992void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9993{
9994 // deep copy extradata
9995 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9996
9997 config.uuid = mData->mUuid;
9998
9999 // copy name, description, OS type, teleport, UTC etc.
10000 config.machineUserData = mUserData->s;
10001
10002 // Encode the Icon Override data from Machine and store on config userdata.
10003 std::vector<BYTE> iconByte;
10004 getIcon(iconByte);
10005 ssize_t cbData = iconByte.size();
10006 if (cbData > 0)
10007 {
10008 ssize_t cchOut = RTBase64EncodedLength(cbData);
10009 Utf8Str strIconData;
10010 strIconData.reserve(cchOut+1);
10011 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10012 strIconData.mutableRaw(), strIconData.capacity(),
10013 NULL);
10014 if (RT_FAILURE(vrc))
10015 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10016 strIconData.jolt();
10017 config.machineUserData.ovIcon = strIconData;
10018 }
10019 else
10020 config.machineUserData.ovIcon.setNull();
10021
10022 if ( mData->mMachineState == MachineState_Saved
10023 || mData->mMachineState == MachineState_Restoring
10024 // when doing certain snapshot operations we may or may not have
10025 // a saved state in the current state, so keep everything as is
10026 || ( ( mData->mMachineState == MachineState_Snapshotting
10027 || mData->mMachineState == MachineState_DeletingSnapshot
10028 || mData->mMachineState == MachineState_RestoringSnapshot)
10029 && (!mSSData->strStateFilePath.isEmpty())
10030 )
10031 )
10032 {
10033 Assert(!mSSData->strStateFilePath.isEmpty());
10034 /* try to make the file name relative to the settings file dir */
10035 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10036 }
10037 else
10038 {
10039 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10040 config.strStateFile.setNull();
10041 }
10042
10043 if (mData->mCurrentSnapshot)
10044 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10045 else
10046 config.uuidCurrentSnapshot.clear();
10047
10048 config.timeLastStateChange = mData->mLastStateChange;
10049 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10050 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10051
10052 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10053 if (FAILED(rc)) throw rc;
10054
10055 rc = i_saveStorageControllers(config.storageMachine);
10056 if (FAILED(rc)) throw rc;
10057
10058 // save machine's media registry if this is VirtualBox 4.0 or later
10059 if (config.canHaveOwnMediaRegistry())
10060 {
10061 // determine machine folder
10062 Utf8Str strMachineFolder = i_getSettingsFileFull();
10063 strMachineFolder.stripFilename();
10064 mParent->i_saveMediaRegistry(config.mediaRegistry,
10065 i_getId(), // only media with registry ID == machine UUID
10066 strMachineFolder);
10067 // this throws HRESULT
10068 }
10069
10070 // save snapshots
10071 rc = i_saveAllSnapshots(config);
10072 if (FAILED(rc)) throw rc;
10073}
10074
10075/**
10076 * Saves all snapshots of the machine into the given machine config file. Called
10077 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10078 * @param config
10079 * @return
10080 */
10081HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10082{
10083 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10084
10085 HRESULT rc = S_OK;
10086
10087 try
10088 {
10089 config.llFirstSnapshot.clear();
10090
10091 if (mData->mFirstSnapshot)
10092 {
10093 // the settings use a list for "the first snapshot"
10094 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10095
10096 // get reference to the snapshot on the list and work on that
10097 // element straight in the list to avoid excessive copying later
10098 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10099 if (FAILED(rc)) throw rc;
10100 }
10101
10102// if (mType == IsSessionMachine)
10103// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10104
10105 }
10106 catch (HRESULT err)
10107 {
10108 /* we assume that error info is set by the thrower */
10109 rc = err;
10110 }
10111 catch (...)
10112 {
10113 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10114 }
10115
10116 return rc;
10117}
10118
10119/**
10120 * Saves the VM hardware configuration. It is assumed that the
10121 * given node is empty.
10122 *
10123 * @param data Reference to the settings object for the hardware config.
10124 * @param pDbg Pointer to the settings object for the debugging config
10125 * which happens to live in mHWData.
10126 * @param pAutostart Pointer to the settings object for the autostart config
10127 * which happens to live in mHWData.
10128 */
10129HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10130 settings::Autostart *pAutostart)
10131{
10132 HRESULT rc = S_OK;
10133
10134 try
10135 {
10136 /* The hardware version attribute (optional).
10137 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10138 if ( mHWData->mHWVersion == "1"
10139 && mSSData->strStateFilePath.isEmpty()
10140 )
10141 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10142 other point needs to be found where this can be done. */
10143
10144 data.strVersion = mHWData->mHWVersion;
10145 data.uuid = mHWData->mHardwareUUID;
10146
10147 // CPU
10148 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10149 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10150 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10151 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10152 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10153 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10154 data.fPAE = !!mHWData->mPAEEnabled;
10155 data.enmLongMode = mHWData->mLongMode;
10156 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10157 data.cCPUs = mHWData->mCPUCount;
10158 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10159 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10160 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10161
10162 data.llCpus.clear();
10163 if (data.fCpuHotPlug)
10164 {
10165 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10166 {
10167 if (mHWData->mCPUAttached[idx])
10168 {
10169 settings::Cpu cpu;
10170 cpu.ulId = idx;
10171 data.llCpus.push_back(cpu);
10172 }
10173 }
10174 }
10175
10176 /* Standard and Extended CPUID leafs. */
10177 data.llCpuIdLeafs.clear();
10178 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10179 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10180 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10181 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10182 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10183 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10184
10185 // memory
10186 data.ulMemorySizeMB = mHWData->mMemorySize;
10187 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10188
10189 // firmware
10190 data.firmwareType = mHWData->mFirmwareType;
10191
10192 // HID
10193 data.pointingHIDType = mHWData->mPointingHIDType;
10194 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10195
10196 // chipset
10197 data.chipsetType = mHWData->mChipsetType;
10198
10199 // paravirt
10200 data.paravirtProvider = mHWData->mParavirtProvider;
10201
10202
10203 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10204
10205 // HPET
10206 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10207
10208 // boot order
10209 data.mapBootOrder.clear();
10210 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10211 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10212
10213 // display
10214 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10215 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10216 data.cMonitors = mHWData->mMonitorCount;
10217 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10218 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10219 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10220 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10221 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10222 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10223 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10224 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10225 {
10226 if (mHWData->maVideoCaptureScreens[i])
10227 ASMBitSet(&data.u64VideoCaptureScreens, i);
10228 else
10229 ASMBitClear(&data.u64VideoCaptureScreens, i);
10230 }
10231 /* store relative video capture file if possible */
10232 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10233
10234 /* VRDEServer settings (optional) */
10235 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10236 if (FAILED(rc)) throw rc;
10237
10238 /* BIOS (required) */
10239 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10240 if (FAILED(rc)) throw rc;
10241
10242 /* USB Controller (required) */
10243 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10244 {
10245 ComObjPtr<USBController> ctrl = *it;
10246 settings::USBController settingsCtrl;
10247
10248 settingsCtrl.strName = ctrl->i_getName();
10249 settingsCtrl.enmType = ctrl->i_getControllerType();
10250
10251 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10252 }
10253
10254 /* USB device filters (required) */
10255 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10256 if (FAILED(rc)) throw rc;
10257
10258 /* Network adapters (required) */
10259 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10260 data.llNetworkAdapters.clear();
10261 /* Write out only the nominal number of network adapters for this
10262 * chipset type. Since Machine::commit() hasn't been called there
10263 * may be extra NIC settings in the vector. */
10264 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10265 {
10266 settings::NetworkAdapter nic;
10267 nic.ulSlot = (uint32_t)slot;
10268 /* paranoia check... must not be NULL, but must not crash either. */
10269 if (mNetworkAdapters[slot])
10270 {
10271 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10272 if (FAILED(rc)) throw rc;
10273
10274 data.llNetworkAdapters.push_back(nic);
10275 }
10276 }
10277
10278 /* Serial ports */
10279 data.llSerialPorts.clear();
10280 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10281 {
10282 settings::SerialPort s;
10283 s.ulSlot = slot;
10284 rc = mSerialPorts[slot]->i_saveSettings(s);
10285 if (FAILED(rc)) return rc;
10286
10287 data.llSerialPorts.push_back(s);
10288 }
10289
10290 /* Parallel ports */
10291 data.llParallelPorts.clear();
10292 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10293 {
10294 settings::ParallelPort p;
10295 p.ulSlot = slot;
10296 rc = mParallelPorts[slot]->i_saveSettings(p);
10297 if (FAILED(rc)) return rc;
10298
10299 data.llParallelPorts.push_back(p);
10300 }
10301
10302 /* Audio adapter */
10303 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10304 if (FAILED(rc)) return rc;
10305
10306 /* Shared folders */
10307 data.llSharedFolders.clear();
10308 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10309 it != mHWData->mSharedFolders.end();
10310 ++it)
10311 {
10312 SharedFolder *pSF = *it;
10313 AutoCaller sfCaller(pSF);
10314 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10315 settings::SharedFolder sf;
10316 sf.strName = pSF->i_getName();
10317 sf.strHostPath = pSF->i_getHostPath();
10318 sf.fWritable = !!pSF->i_isWritable();
10319 sf.fAutoMount = !!pSF->i_isAutoMounted();
10320
10321 data.llSharedFolders.push_back(sf);
10322 }
10323
10324 // clipboard
10325 data.clipboardMode = mHWData->mClipboardMode;
10326
10327 // drag'n'drop
10328 data.dndMode = mHWData->mDnDMode;
10329
10330 /* Guest */
10331 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10332
10333 // IO settings
10334 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10335 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10336
10337 /* BandwidthControl (required) */
10338 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10339 if (FAILED(rc)) throw rc;
10340
10341 /* Host PCI devices */
10342 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10343 it != mHWData->mPCIDeviceAssignments.end();
10344 ++it)
10345 {
10346 ComObjPtr<PCIDeviceAttachment> pda = *it;
10347 settings::HostPCIDeviceAttachment hpda;
10348
10349 rc = pda->i_saveSettings(hpda);
10350 if (FAILED(rc)) throw rc;
10351
10352 data.pciAttachments.push_back(hpda);
10353 }
10354
10355
10356 // guest properties
10357 data.llGuestProperties.clear();
10358#ifdef VBOX_WITH_GUEST_PROPS
10359 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10360 it != mHWData->mGuestProperties.end();
10361 ++it)
10362 {
10363 HWData::GuestProperty property = it->second;
10364
10365 /* Remove transient guest properties at shutdown unless we
10366 * are saving state */
10367 if ( ( mData->mMachineState == MachineState_PoweredOff
10368 || mData->mMachineState == MachineState_Aborted
10369 || mData->mMachineState == MachineState_Teleported)
10370 && ( property.mFlags & guestProp::TRANSIENT
10371 || property.mFlags & guestProp::TRANSRESET))
10372 continue;
10373 settings::GuestProperty prop;
10374 prop.strName = it->first;
10375 prop.strValue = property.strValue;
10376 prop.timestamp = property.mTimestamp;
10377 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10378 guestProp::writeFlags(property.mFlags, szFlags);
10379 prop.strFlags = szFlags;
10380
10381 data.llGuestProperties.push_back(prop);
10382 }
10383
10384 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10385 /* I presume this doesn't require a backup(). */
10386 mData->mGuestPropertiesModified = FALSE;
10387#endif /* VBOX_WITH_GUEST_PROPS defined */
10388
10389 *pDbg = mHWData->mDebugging;
10390 *pAutostart = mHWData->mAutostart;
10391
10392 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10393 }
10394 catch(std::bad_alloc &)
10395 {
10396 return E_OUTOFMEMORY;
10397 }
10398
10399 AssertComRC(rc);
10400 return rc;
10401}
10402
10403/**
10404 * Saves the storage controller configuration.
10405 *
10406 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10407 */
10408HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10409{
10410 data.llStorageControllers.clear();
10411
10412 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10413 it != mStorageControllers->end();
10414 ++it)
10415 {
10416 HRESULT rc;
10417 ComObjPtr<StorageController> pCtl = *it;
10418
10419 settings::StorageController ctl;
10420 ctl.strName = pCtl->i_getName();
10421 ctl.controllerType = pCtl->i_getControllerType();
10422 ctl.storageBus = pCtl->i_getStorageBus();
10423 ctl.ulInstance = pCtl->i_getInstance();
10424 ctl.fBootable = pCtl->i_getBootable();
10425
10426 /* Save the port count. */
10427 ULONG portCount;
10428 rc = pCtl->COMGETTER(PortCount)(&portCount);
10429 ComAssertComRCRet(rc, rc);
10430 ctl.ulPortCount = portCount;
10431
10432 /* Save fUseHostIOCache */
10433 BOOL fUseHostIOCache;
10434 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10435 ComAssertComRCRet(rc, rc);
10436 ctl.fUseHostIOCache = !!fUseHostIOCache;
10437
10438 /* Save IDE emulation settings. */
10439 if (ctl.controllerType == StorageControllerType_IntelAhci)
10440 {
10441 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10442 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10443 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10444 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10445 )
10446 ComAssertComRCRet(rc, rc);
10447 }
10448
10449 /* save the devices now. */
10450 rc = i_saveStorageDevices(pCtl, ctl);
10451 ComAssertComRCRet(rc, rc);
10452
10453 data.llStorageControllers.push_back(ctl);
10454 }
10455
10456 return S_OK;
10457}
10458
10459/**
10460 * Saves the hard disk configuration.
10461 */
10462HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10463 settings::StorageController &data)
10464{
10465 MediaData::AttachmentList atts;
10466
10467 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10468 if (FAILED(rc)) return rc;
10469
10470 data.llAttachedDevices.clear();
10471 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10472 it != atts.end();
10473 ++it)
10474 {
10475 settings::AttachedDevice dev;
10476 IMediumAttachment *iA = *it;
10477 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10478 Medium *pMedium = pAttach->i_getMedium();
10479
10480 dev.deviceType = pAttach->i_getType();
10481 dev.lPort = pAttach->i_getPort();
10482 dev.lDevice = pAttach->i_getDevice();
10483 dev.fPassThrough = pAttach->i_getPassthrough();
10484 dev.fHotPluggable = pAttach->i_getHotPluggable();
10485 if (pMedium)
10486 {
10487 if (pMedium->i_isHostDrive())
10488 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10489 else
10490 dev.uuid = pMedium->i_getId();
10491 dev.fTempEject = pAttach->i_getTempEject();
10492 dev.fNonRotational = pAttach->i_getNonRotational();
10493 dev.fDiscard = pAttach->i_getDiscard();
10494 }
10495
10496 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10497
10498 data.llAttachedDevices.push_back(dev);
10499 }
10500
10501 return S_OK;
10502}
10503
10504/**
10505 * Saves machine state settings as defined by aFlags
10506 * (SaveSTS_* values).
10507 *
10508 * @param aFlags Combination of SaveSTS_* flags.
10509 *
10510 * @note Locks objects for writing.
10511 */
10512HRESULT Machine::i_saveStateSettings(int aFlags)
10513{
10514 if (aFlags == 0)
10515 return S_OK;
10516
10517 AutoCaller autoCaller(this);
10518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10519
10520 /* This object's write lock is also necessary to serialize file access
10521 * (prevent concurrent reads and writes) */
10522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10523
10524 HRESULT rc = S_OK;
10525
10526 Assert(mData->pMachineConfigFile);
10527
10528 try
10529 {
10530 if (aFlags & SaveSTS_CurStateModified)
10531 mData->pMachineConfigFile->fCurrentStateModified = true;
10532
10533 if (aFlags & SaveSTS_StateFilePath)
10534 {
10535 if (!mSSData->strStateFilePath.isEmpty())
10536 /* try to make the file name relative to the settings file dir */
10537 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10538 else
10539 mData->pMachineConfigFile->strStateFile.setNull();
10540 }
10541
10542 if (aFlags & SaveSTS_StateTimeStamp)
10543 {
10544 Assert( mData->mMachineState != MachineState_Aborted
10545 || mSSData->strStateFilePath.isEmpty());
10546
10547 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10548
10549 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10550//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10551 }
10552
10553 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10554 }
10555 catch (...)
10556 {
10557 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10558 }
10559
10560 return rc;
10561}
10562
10563/**
10564 * Ensures that the given medium is added to a media registry. If this machine
10565 * was created with 4.0 or later, then the machine registry is used. Otherwise
10566 * the global VirtualBox media registry is used.
10567 *
10568 * Caller must NOT hold machine lock, media tree or any medium locks!
10569 *
10570 * @param pMedium
10571 */
10572void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10573{
10574 /* Paranoia checks: do not hold machine or media tree locks. */
10575 AssertReturnVoid(!isWriteLockOnCurrentThread());
10576 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10577
10578 ComObjPtr<Medium> pBase;
10579 {
10580 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10581 pBase = pMedium->i_getBase();
10582 }
10583
10584 /* Paranoia checks: do not hold medium locks. */
10585 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10586 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10587
10588 // decide which medium registry to use now that the medium is attached:
10589 Guid uuid;
10590 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10591 // machine XML is VirtualBox 4.0 or higher:
10592 uuid = i_getId(); // machine UUID
10593 else
10594 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10595
10596 if (pMedium->i_addRegistry(uuid))
10597 mParent->i_markRegistryModified(uuid);
10598
10599 /* For more complex hard disk structures it can happen that the base
10600 * medium isn't yet associated with any medium registry. Do that now. */
10601 if (pMedium != pBase)
10602 {
10603 /* Tree lock needed by Medium::addRegistry when recursing. */
10604 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10605 if (pBase->i_addRegistryRecursive(uuid))
10606 {
10607 treeLock.release();
10608 mParent->i_markRegistryModified(uuid);
10609 }
10610 }
10611}
10612
10613/**
10614 * Creates differencing hard disks for all normal hard disks attached to this
10615 * machine and a new set of attachments to refer to created disks.
10616 *
10617 * Used when taking a snapshot or when deleting the current state. Gets called
10618 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10619 *
10620 * This method assumes that mMediaData contains the original hard disk attachments
10621 * it needs to create diffs for. On success, these attachments will be replaced
10622 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10623 * called to delete created diffs which will also rollback mMediaData and restore
10624 * whatever was backed up before calling this method.
10625 *
10626 * Attachments with non-normal hard disks are left as is.
10627 *
10628 * If @a aOnline is @c false then the original hard disks that require implicit
10629 * diffs will be locked for reading. Otherwise it is assumed that they are
10630 * already locked for writing (when the VM was started). Note that in the latter
10631 * case it is responsibility of the caller to lock the newly created diffs for
10632 * writing if this method succeeds.
10633 *
10634 * @param aProgress Progress object to run (must contain at least as
10635 * many operations left as the number of hard disks
10636 * attached).
10637 * @param aOnline Whether the VM was online prior to this operation.
10638 *
10639 * @note The progress object is not marked as completed, neither on success nor
10640 * on failure. This is a responsibility of the caller.
10641 *
10642 * @note Locks this object and the media tree for writing.
10643 */
10644HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10645 ULONG aWeight,
10646 bool aOnline)
10647{
10648 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10649
10650 AutoCaller autoCaller(this);
10651 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10652
10653 AutoMultiWriteLock2 alock(this->lockHandle(),
10654 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10655
10656 /* must be in a protective state because we release the lock below */
10657 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10658 || mData->mMachineState == MachineState_OnlineSnapshotting
10659 || mData->mMachineState == MachineState_LiveSnapshotting
10660 || mData->mMachineState == MachineState_RestoringSnapshot
10661 || mData->mMachineState == MachineState_DeletingSnapshot
10662 , E_FAIL);
10663
10664 HRESULT rc = S_OK;
10665
10666 // use appropriate locked media map (online or offline)
10667 MediumLockListMap lockedMediaOffline;
10668 MediumLockListMap *lockedMediaMap;
10669 if (aOnline)
10670 lockedMediaMap = &mData->mSession.mLockedMedia;
10671 else
10672 lockedMediaMap = &lockedMediaOffline;
10673
10674 try
10675 {
10676 if (!aOnline)
10677 {
10678 /* lock all attached hard disks early to detect "in use"
10679 * situations before creating actual diffs */
10680 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10681 it != mMediaData->mAttachments.end();
10682 ++it)
10683 {
10684 MediumAttachment* pAtt = *it;
10685 if (pAtt->i_getType() == DeviceType_HardDisk)
10686 {
10687 Medium* pMedium = pAtt->i_getMedium();
10688 Assert(pMedium);
10689
10690 MediumLockList *pMediumLockList(new MediumLockList());
10691 alock.release();
10692 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10693 false /* fMediumLockWrite */,
10694 false /* fMediumLockWriteAll */,
10695 NULL,
10696 *pMediumLockList);
10697 alock.acquire();
10698 if (FAILED(rc))
10699 {
10700 delete pMediumLockList;
10701 throw rc;
10702 }
10703 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10704 if (FAILED(rc))
10705 {
10706 throw setError(rc,
10707 tr("Collecting locking information for all attached media failed"));
10708 }
10709 }
10710 }
10711
10712 /* Now lock all media. If this fails, nothing is locked. */
10713 alock.release();
10714 rc = lockedMediaMap->Lock();
10715 alock.acquire();
10716 if (FAILED(rc))
10717 {
10718 throw setError(rc,
10719 tr("Locking of attached media failed"));
10720 }
10721 }
10722
10723 /* remember the current list (note that we don't use backup() since
10724 * mMediaData may be already backed up) */
10725 MediaData::AttachmentList atts = mMediaData->mAttachments;
10726
10727 /* start from scratch */
10728 mMediaData->mAttachments.clear();
10729
10730 /* go through remembered attachments and create diffs for normal hard
10731 * disks and attach them */
10732 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10733 it != atts.end();
10734 ++it)
10735 {
10736 MediumAttachment* pAtt = *it;
10737
10738 DeviceType_T devType = pAtt->i_getType();
10739 Medium* pMedium = pAtt->i_getMedium();
10740
10741 if ( devType != DeviceType_HardDisk
10742 || pMedium == NULL
10743 || pMedium->i_getType() != MediumType_Normal)
10744 {
10745 /* copy the attachment as is */
10746
10747 /** @todo the progress object created in SessionMachine::TakeSnaphot
10748 * only expects operations for hard disks. Later other
10749 * device types need to show up in the progress as well. */
10750 if (devType == DeviceType_HardDisk)
10751 {
10752 if (pMedium == NULL)
10753 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10754 aWeight); // weight
10755 else
10756 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10757 pMedium->i_getBase()->i_getName().c_str()).raw(),
10758 aWeight); // weight
10759 }
10760
10761 mMediaData->mAttachments.push_back(pAtt);
10762 continue;
10763 }
10764
10765 /* need a diff */
10766 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10767 pMedium->i_getBase()->i_getName().c_str()).raw(),
10768 aWeight); // weight
10769
10770 Utf8Str strFullSnapshotFolder;
10771 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10772
10773 ComObjPtr<Medium> diff;
10774 diff.createObject();
10775 // store the diff in the same registry as the parent
10776 // (this cannot fail here because we can't create implicit diffs for
10777 // unregistered images)
10778 Guid uuidRegistryParent;
10779 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10780 Assert(fInRegistry); NOREF(fInRegistry);
10781 rc = diff->init(mParent,
10782 pMedium->i_getPreferredDiffFormat(),
10783 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10784 uuidRegistryParent,
10785 DeviceType_HardDisk);
10786 if (FAILED(rc)) throw rc;
10787
10788 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10789 * the push_back? Looks like we're going to release medium with the
10790 * wrong kind of lock (general issue with if we fail anywhere at all)
10791 * and an orphaned VDI in the snapshots folder. */
10792
10793 /* update the appropriate lock list */
10794 MediumLockList *pMediumLockList;
10795 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10796 AssertComRCThrowRC(rc);
10797 if (aOnline)
10798 {
10799 alock.release();
10800 /* The currently attached medium will be read-only, change
10801 * the lock type to read. */
10802 rc = pMediumLockList->Update(pMedium, false);
10803 alock.acquire();
10804 AssertComRCThrowRC(rc);
10805 }
10806
10807 /* release the locks before the potentially lengthy operation */
10808 alock.release();
10809 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10810 pMediumLockList,
10811 NULL /* aProgress */,
10812 true /* aWait */);
10813 alock.acquire();
10814 if (FAILED(rc)) throw rc;
10815
10816 /* actual lock list update is done in Medium::commitMedia */
10817
10818 rc = diff->i_addBackReference(mData->mUuid);
10819 AssertComRCThrowRC(rc);
10820
10821 /* add a new attachment */
10822 ComObjPtr<MediumAttachment> attachment;
10823 attachment.createObject();
10824 rc = attachment->init(this,
10825 diff,
10826 pAtt->i_getControllerName(),
10827 pAtt->i_getPort(),
10828 pAtt->i_getDevice(),
10829 DeviceType_HardDisk,
10830 true /* aImplicit */,
10831 false /* aPassthrough */,
10832 false /* aTempEject */,
10833 pAtt->i_getNonRotational(),
10834 pAtt->i_getDiscard(),
10835 pAtt->i_getHotPluggable(),
10836 pAtt->i_getBandwidthGroup());
10837 if (FAILED(rc)) throw rc;
10838
10839 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10840 AssertComRCThrowRC(rc);
10841 mMediaData->mAttachments.push_back(attachment);
10842 }
10843 }
10844 catch (HRESULT aRC) { rc = aRC; }
10845
10846 /* unlock all hard disks we locked when there is no VM */
10847 if (!aOnline)
10848 {
10849 ErrorInfoKeeper eik;
10850
10851 HRESULT rc1 = lockedMediaMap->Clear();
10852 AssertComRC(rc1);
10853 }
10854
10855 return rc;
10856}
10857
10858/**
10859 * Deletes implicit differencing hard disks created either by
10860 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10861 *
10862 * Note that to delete hard disks created by #AttachDevice() this method is
10863 * called from #fixupMedia() when the changes are rolled back.
10864 *
10865 * @note Locks this object and the media tree for writing.
10866 */
10867HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10868{
10869 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10870
10871 AutoCaller autoCaller(this);
10872 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10873
10874 AutoMultiWriteLock2 alock(this->lockHandle(),
10875 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10876
10877 /* We absolutely must have backed up state. */
10878 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10879
10880 /* Check if there are any implicitly created diff images. */
10881 bool fImplicitDiffs = false;
10882 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10883 it != mMediaData->mAttachments.end();
10884 ++it)
10885 {
10886 const ComObjPtr<MediumAttachment> &pAtt = *it;
10887 if (pAtt->i_isImplicit())
10888 {
10889 fImplicitDiffs = true;
10890 break;
10891 }
10892 }
10893 /* If there is nothing to do, leave early. This saves lots of image locking
10894 * effort. It also avoids a MachineStateChanged event without real reason.
10895 * This is important e.g. when loading a VM config, because there should be
10896 * no events. Otherwise API clients can become thoroughly confused for
10897 * inaccessible VMs (the code for loading VM configs uses this method for
10898 * cleanup if the config makes no sense), as they take such events as an
10899 * indication that the VM is alive, and they would force the VM config to
10900 * be reread, leading to an endless loop. */
10901 if (!fImplicitDiffs)
10902 return S_OK;
10903
10904 HRESULT rc = S_OK;
10905 MachineState_T oldState = mData->mMachineState;
10906
10907 /* will release the lock before the potentially lengthy operation,
10908 * so protect with the special state (unless already protected) */
10909 if ( oldState != MachineState_Snapshotting
10910 && oldState != MachineState_OnlineSnapshotting
10911 && oldState != MachineState_LiveSnapshotting
10912 && oldState != MachineState_RestoringSnapshot
10913 && oldState != MachineState_DeletingSnapshot
10914 && oldState != MachineState_DeletingSnapshotOnline
10915 && oldState != MachineState_DeletingSnapshotPaused
10916 )
10917 i_setMachineState(MachineState_SettingUp);
10918
10919 // use appropriate locked media map (online or offline)
10920 MediumLockListMap lockedMediaOffline;
10921 MediumLockListMap *lockedMediaMap;
10922 if (aOnline)
10923 lockedMediaMap = &mData->mSession.mLockedMedia;
10924 else
10925 lockedMediaMap = &lockedMediaOffline;
10926
10927 try
10928 {
10929 if (!aOnline)
10930 {
10931 /* lock all attached hard disks early to detect "in use"
10932 * situations before deleting actual diffs */
10933 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10934 it != mMediaData->mAttachments.end();
10935 ++it)
10936 {
10937 MediumAttachment* pAtt = *it;
10938 if (pAtt->i_getType() == DeviceType_HardDisk)
10939 {
10940 Medium* pMedium = pAtt->i_getMedium();
10941 Assert(pMedium);
10942
10943 MediumLockList *pMediumLockList(new MediumLockList());
10944 alock.release();
10945 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10946 false /* fMediumLockWrite */,
10947 false /* fMediumLockWriteAll */,
10948 NULL,
10949 *pMediumLockList);
10950 alock.acquire();
10951
10952 if (FAILED(rc))
10953 {
10954 delete pMediumLockList;
10955 throw rc;
10956 }
10957
10958 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10959 if (FAILED(rc))
10960 throw rc;
10961 }
10962 }
10963
10964 if (FAILED(rc))
10965 throw rc;
10966 } // end of offline
10967
10968 /* Lock lists are now up to date and include implicitly created media */
10969
10970 /* Go through remembered attachments and delete all implicitly created
10971 * diffs and fix up the attachment information */
10972 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10973 MediaData::AttachmentList implicitAtts;
10974 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10975 it != mMediaData->mAttachments.end();
10976 ++it)
10977 {
10978 ComObjPtr<MediumAttachment> pAtt = *it;
10979 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10980 if (pMedium.isNull())
10981 continue;
10982
10983 // Implicit attachments go on the list for deletion and back references are removed.
10984 if (pAtt->i_isImplicit())
10985 {
10986 /* Deassociate and mark for deletion */
10987 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10988 rc = pMedium->i_removeBackReference(mData->mUuid);
10989 if (FAILED(rc))
10990 throw rc;
10991 implicitAtts.push_back(pAtt);
10992 continue;
10993 }
10994
10995 /* Was this medium attached before? */
10996 if (!i_findAttachment(oldAtts, pMedium))
10997 {
10998 /* no: de-associate */
10999 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11000 rc = pMedium->i_removeBackReference(mData->mUuid);
11001 if (FAILED(rc))
11002 throw rc;
11003 continue;
11004 }
11005 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11006 }
11007
11008 /* If there are implicit attachments to delete, throw away the lock
11009 * map contents (which will unlock all media) since the medium
11010 * attachments will be rolled back. Below we need to completely
11011 * recreate the lock map anyway since it is infinitely complex to
11012 * do this incrementally (would need reconstructing each attachment
11013 * change, which would be extremely hairy). */
11014 if (implicitAtts.size() != 0)
11015 {
11016 ErrorInfoKeeper eik;
11017
11018 HRESULT rc1 = lockedMediaMap->Clear();
11019 AssertComRC(rc1);
11020 }
11021
11022 /* rollback hard disk changes */
11023 mMediaData.rollback();
11024
11025 MultiResult mrc(S_OK);
11026
11027 // Delete unused implicit diffs.
11028 if (implicitAtts.size() != 0)
11029 {
11030 alock.release();
11031
11032 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11033 {
11034 // Remove medium associated with this attachment.
11035 ComObjPtr<MediumAttachment> pAtt = *it;
11036 Assert(pAtt);
11037 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11038 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11039 Assert(pMedium);
11040
11041 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11042 // continue on delete failure, just collect error messages
11043 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11044 pMedium->i_getLocationFull().c_str() ));
11045 mrc = rc;
11046 }
11047 // Clear the list of deleted implicit attachments now, while not
11048 // holding the lock, as it will ultimately trigger Medium::uninit()
11049 // calls which assume that the media tree lock isn't held.
11050 implicitAtts.clear();
11051
11052 alock.acquire();
11053
11054 /* if there is a VM recreate media lock map as mentioned above,
11055 * otherwise it is a waste of time and we leave things unlocked */
11056 if (aOnline)
11057 {
11058 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11059 /* must never be NULL, but better safe than sorry */
11060 if (!pMachine.isNull())
11061 {
11062 alock.release();
11063 rc = mData->mSession.mMachine->i_lockMedia();
11064 alock.acquire();
11065 if (FAILED(rc))
11066 throw rc;
11067 }
11068 }
11069 }
11070 }
11071 catch (HRESULT aRC) {rc = aRC;}
11072
11073 if (mData->mMachineState == MachineState_SettingUp)
11074 i_setMachineState(oldState);
11075
11076 /* unlock all hard disks we locked when there is no VM */
11077 if (!aOnline)
11078 {
11079 ErrorInfoKeeper eik;
11080
11081 HRESULT rc1 = lockedMediaMap->Clear();
11082 AssertComRC(rc1);
11083 }
11084
11085 return rc;
11086}
11087
11088
11089/**
11090 * Looks through the given list of media attachments for one with the given parameters
11091 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11092 * can be searched as well if needed.
11093 *
11094 * @param list
11095 * @param aControllerName
11096 * @param aControllerPort
11097 * @param aDevice
11098 * @return
11099 */
11100MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11101 IN_BSTR aControllerName,
11102 LONG aControllerPort,
11103 LONG aDevice)
11104{
11105 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11106 {
11107 MediumAttachment *pAttach = *it;
11108 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11109 return pAttach;
11110 }
11111
11112 return NULL;
11113}
11114
11115/**
11116 * Looks through the given list of media attachments for one with the given parameters
11117 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11118 * can be searched as well if needed.
11119 *
11120 * @param list
11121 * @param aControllerName
11122 * @param aControllerPort
11123 * @param aDevice
11124 * @return
11125 */
11126MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11127 ComObjPtr<Medium> pMedium)
11128{
11129 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11130 {
11131 MediumAttachment *pAttach = *it;
11132 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11133 if (pMediumThis == pMedium)
11134 return pAttach;
11135 }
11136
11137 return NULL;
11138}
11139
11140/**
11141 * Looks through the given list of media attachments for one with the given parameters
11142 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11143 * can be searched as well if needed.
11144 *
11145 * @param list
11146 * @param aControllerName
11147 * @param aControllerPort
11148 * @param aDevice
11149 * @return
11150 */
11151MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11152 Guid &id)
11153{
11154 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11155 {
11156 MediumAttachment *pAttach = *it;
11157 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11158 if (pMediumThis->i_getId() == id)
11159 return pAttach;
11160 }
11161
11162 return NULL;
11163}
11164
11165/**
11166 * Main implementation for Machine::DetachDevice. This also gets called
11167 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11168 *
11169 * @param pAttach Medium attachment to detach.
11170 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11171 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11172 * SnapshotMachine, and this must be its snapshot.
11173 * @return
11174 */
11175HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11176 AutoWriteLock &writeLock,
11177 Snapshot *pSnapshot)
11178{
11179 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11180 DeviceType_T mediumType = pAttach->i_getType();
11181
11182 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11183
11184 if (pAttach->i_isImplicit())
11185 {
11186 /* attempt to implicitly delete the implicitly created diff */
11187
11188 /// @todo move the implicit flag from MediumAttachment to Medium
11189 /// and forbid any hard disk operation when it is implicit. Or maybe
11190 /// a special media state for it to make it even more simple.
11191
11192 Assert(mMediaData.isBackedUp());
11193
11194 /* will release the lock before the potentially lengthy operation, so
11195 * protect with the special state */
11196 MachineState_T oldState = mData->mMachineState;
11197 i_setMachineState(MachineState_SettingUp);
11198
11199 writeLock.release();
11200
11201 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11202 true /*aWait*/);
11203
11204 writeLock.acquire();
11205
11206 i_setMachineState(oldState);
11207
11208 if (FAILED(rc)) return rc;
11209 }
11210
11211 i_setModified(IsModified_Storage);
11212 mMediaData.backup();
11213 mMediaData->mAttachments.remove(pAttach);
11214
11215 if (!oldmedium.isNull())
11216 {
11217 // if this is from a snapshot, do not defer detachment to commitMedia()
11218 if (pSnapshot)
11219 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11220 // else if non-hard disk media, do not defer detachment to commitMedia() either
11221 else if (mediumType != DeviceType_HardDisk)
11222 oldmedium->i_removeBackReference(mData->mUuid);
11223 }
11224
11225 return S_OK;
11226}
11227
11228/**
11229 * Goes thru all media of the given list and
11230 *
11231 * 1) calls i_detachDevice() on each of them for this machine and
11232 * 2) adds all Medium objects found in the process to the given list,
11233 * depending on cleanupMode.
11234 *
11235 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11236 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11237 * media to the list.
11238 *
11239 * This gets called from Machine::Unregister, both for the actual Machine and
11240 * the SnapshotMachine objects that might be found in the snapshots.
11241 *
11242 * Requires caller and locking. The machine lock must be passed in because it
11243 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11244 *
11245 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11246 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11247 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11248 * Full, then all media get added;
11249 * otherwise no media get added.
11250 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11251 * @return
11252 */
11253HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11254 Snapshot *pSnapshot,
11255 CleanupMode_T cleanupMode,
11256 MediaList &llMedia)
11257{
11258 Assert(isWriteLockOnCurrentThread());
11259
11260 HRESULT rc;
11261
11262 // make a temporary list because i_detachDevice invalidates iterators into
11263 // mMediaData->mAttachments
11264 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11265
11266 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11267 {
11268 ComObjPtr<MediumAttachment> &pAttach = *it;
11269 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11270
11271 if (!pMedium.isNull())
11272 {
11273 AutoCaller mac(pMedium);
11274 if (FAILED(mac.rc())) return mac.rc();
11275 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11276 DeviceType_T devType = pMedium->i_getDeviceType();
11277 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11278 && devType == DeviceType_HardDisk)
11279 || (cleanupMode == CleanupMode_Full)
11280 )
11281 {
11282 llMedia.push_back(pMedium);
11283 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11284 /* Not allowed to keep this lock as below we need the parent
11285 * medium lock, and the lock order is parent to child. */
11286 lock.release();
11287 /*
11288 * Search for medias which are not attached to any machine, but
11289 * in the chain to an attached disk. Mediums are only consided
11290 * if they are:
11291 * - have only one child
11292 * - no references to any machines
11293 * - are of normal medium type
11294 */
11295 while (!pParent.isNull())
11296 {
11297 AutoCaller mac1(pParent);
11298 if (FAILED(mac1.rc())) return mac1.rc();
11299 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11300 if (pParent->i_getChildren().size() == 1)
11301 {
11302 if ( pParent->i_getMachineBackRefCount() == 0
11303 && pParent->i_getType() == MediumType_Normal
11304 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11305 llMedia.push_back(pParent);
11306 }
11307 else
11308 break;
11309 pParent = pParent->i_getParent();
11310 }
11311 }
11312 }
11313
11314 // real machine: then we need to use the proper method
11315 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11316
11317 if (FAILED(rc))
11318 return rc;
11319 }
11320
11321 return S_OK;
11322}
11323
11324/**
11325 * Perform deferred hard disk detachments.
11326 *
11327 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11328 * backed up).
11329 *
11330 * If @a aOnline is @c true then this method will also unlock the old hard disks
11331 * for which the new implicit diffs were created and will lock these new diffs for
11332 * writing.
11333 *
11334 * @param aOnline Whether the VM was online prior to this operation.
11335 *
11336 * @note Locks this object for writing!
11337 */
11338void Machine::i_commitMedia(bool aOnline /*= false*/)
11339{
11340 AutoCaller autoCaller(this);
11341 AssertComRCReturnVoid(autoCaller.rc());
11342
11343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11344
11345 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11346
11347 HRESULT rc = S_OK;
11348
11349 /* no attach/detach operations -- nothing to do */
11350 if (!mMediaData.isBackedUp())
11351 return;
11352
11353 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11354 bool fMediaNeedsLocking = false;
11355
11356 /* enumerate new attachments */
11357 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11358 it != mMediaData->mAttachments.end();
11359 ++it)
11360 {
11361 MediumAttachment *pAttach = *it;
11362
11363 pAttach->i_commit();
11364
11365 Medium* pMedium = pAttach->i_getMedium();
11366 bool fImplicit = pAttach->i_isImplicit();
11367
11368 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11369 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11370 fImplicit));
11371
11372 /** @todo convert all this Machine-based voodoo to MediumAttachment
11373 * based commit logic. */
11374 if (fImplicit)
11375 {
11376 /* convert implicit attachment to normal */
11377 pAttach->i_setImplicit(false);
11378
11379 if ( aOnline
11380 && pMedium
11381 && pAttach->i_getType() == DeviceType_HardDisk
11382 )
11383 {
11384 /* update the appropriate lock list */
11385 MediumLockList *pMediumLockList;
11386 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11387 AssertComRC(rc);
11388 if (pMediumLockList)
11389 {
11390 /* unlock if there's a need to change the locking */
11391 if (!fMediaNeedsLocking)
11392 {
11393 rc = mData->mSession.mLockedMedia.Unlock();
11394 AssertComRC(rc);
11395 fMediaNeedsLocking = true;
11396 }
11397 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11398 AssertComRC(rc);
11399 rc = pMediumLockList->Append(pMedium, true);
11400 AssertComRC(rc);
11401 }
11402 }
11403
11404 continue;
11405 }
11406
11407 if (pMedium)
11408 {
11409 /* was this medium attached before? */
11410 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11411 {
11412 MediumAttachment *pOldAttach = *oldIt;
11413 if (pOldAttach->i_getMedium() == pMedium)
11414 {
11415 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11416
11417 /* yes: remove from old to avoid de-association */
11418 oldAtts.erase(oldIt);
11419 break;
11420 }
11421 }
11422 }
11423 }
11424
11425 /* enumerate remaining old attachments and de-associate from the
11426 * current machine state */
11427 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11428 {
11429 MediumAttachment *pAttach = *it;
11430 Medium* pMedium = pAttach->i_getMedium();
11431
11432 /* Detach only hard disks, since DVD/floppy media is detached
11433 * instantly in MountMedium. */
11434 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11435 {
11436 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11437
11438 /* now de-associate from the current machine state */
11439 rc = pMedium->i_removeBackReference(mData->mUuid);
11440 AssertComRC(rc);
11441
11442 if (aOnline)
11443 {
11444 /* unlock since medium is not used anymore */
11445 MediumLockList *pMediumLockList;
11446 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11447 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11448 {
11449 /* this happens for online snapshots, there the attachment
11450 * is changing, but only to a diff image created under
11451 * the old one, so there is no separate lock list */
11452 Assert(!pMediumLockList);
11453 }
11454 else
11455 {
11456 AssertComRC(rc);
11457 if (pMediumLockList)
11458 {
11459 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11460 AssertComRC(rc);
11461 }
11462 }
11463 }
11464 }
11465 }
11466
11467 /* take media locks again so that the locking state is consistent */
11468 if (fMediaNeedsLocking)
11469 {
11470 Assert(aOnline);
11471 rc = mData->mSession.mLockedMedia.Lock();
11472 AssertComRC(rc);
11473 }
11474
11475 /* commit the hard disk changes */
11476 mMediaData.commit();
11477
11478 if (i_isSessionMachine())
11479 {
11480 /*
11481 * Update the parent machine to point to the new owner.
11482 * This is necessary because the stored parent will point to the
11483 * session machine otherwise and cause crashes or errors later
11484 * when the session machine gets invalid.
11485 */
11486 /** @todo Change the MediumAttachment class to behave like any other
11487 * class in this regard by creating peer MediumAttachment
11488 * objects for session machines and share the data with the peer
11489 * machine.
11490 */
11491 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11492 it != mMediaData->mAttachments.end();
11493 ++it)
11494 (*it)->i_updateParentMachine(mPeer);
11495
11496 /* attach new data to the primary machine and reshare it */
11497 mPeer->mMediaData.attach(mMediaData);
11498 }
11499
11500 return;
11501}
11502
11503/**
11504 * Perform deferred deletion of implicitly created diffs.
11505 *
11506 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11507 * backed up).
11508 *
11509 * @note Locks this object for writing!
11510 */
11511void Machine::i_rollbackMedia()
11512{
11513 AutoCaller autoCaller(this);
11514 AssertComRCReturnVoid(autoCaller.rc());
11515
11516 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11517 LogFlowThisFunc(("Entering rollbackMedia\n"));
11518
11519 HRESULT rc = S_OK;
11520
11521 /* no attach/detach operations -- nothing to do */
11522 if (!mMediaData.isBackedUp())
11523 return;
11524
11525 /* enumerate new attachments */
11526 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11527 it != mMediaData->mAttachments.end();
11528 ++it)
11529 {
11530 MediumAttachment *pAttach = *it;
11531 /* Fix up the backrefs for DVD/floppy media. */
11532 if (pAttach->i_getType() != DeviceType_HardDisk)
11533 {
11534 Medium* pMedium = pAttach->i_getMedium();
11535 if (pMedium)
11536 {
11537 rc = pMedium->i_removeBackReference(mData->mUuid);
11538 AssertComRC(rc);
11539 }
11540 }
11541
11542 (*it)->i_rollback();
11543
11544 pAttach = *it;
11545 /* Fix up the backrefs for DVD/floppy media. */
11546 if (pAttach->i_getType() != DeviceType_HardDisk)
11547 {
11548 Medium* pMedium = pAttach->i_getMedium();
11549 if (pMedium)
11550 {
11551 rc = pMedium->i_addBackReference(mData->mUuid);
11552 AssertComRC(rc);
11553 }
11554 }
11555 }
11556
11557 /** @todo convert all this Machine-based voodoo to MediumAttachment
11558 * based rollback logic. */
11559 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11560
11561 return;
11562}
11563
11564/**
11565 * Returns true if the settings file is located in the directory named exactly
11566 * as the machine; this means, among other things, that the machine directory
11567 * should be auto-renamed.
11568 *
11569 * @param aSettingsDir if not NULL, the full machine settings file directory
11570 * name will be assigned there.
11571 *
11572 * @note Doesn't lock anything.
11573 * @note Not thread safe (must be called from this object's lock).
11574 */
11575bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11576{
11577 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11578 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11579 if (aSettingsDir)
11580 *aSettingsDir = strMachineDirName;
11581 strMachineDirName.stripPath(); // vmname
11582 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11583 strConfigFileOnly.stripPath() // vmname.vbox
11584 .stripSuffix(); // vmname
11585 /** @todo hack, make somehow use of ComposeMachineFilename */
11586 if (mUserData->s.fDirectoryIncludesUUID)
11587 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11588
11589 AssertReturn(!strMachineDirName.isEmpty(), false);
11590 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11591
11592 return strMachineDirName == strConfigFileOnly;
11593}
11594
11595/**
11596 * Discards all changes to machine settings.
11597 *
11598 * @param aNotify Whether to notify the direct session about changes or not.
11599 *
11600 * @note Locks objects for writing!
11601 */
11602void Machine::i_rollback(bool aNotify)
11603{
11604 AutoCaller autoCaller(this);
11605 AssertComRCReturn(autoCaller.rc(), (void)0);
11606
11607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11608
11609 if (!mStorageControllers.isNull())
11610 {
11611 if (mStorageControllers.isBackedUp())
11612 {
11613 /* unitialize all new devices (absent in the backed up list). */
11614 StorageControllerList::const_iterator it = mStorageControllers->begin();
11615 StorageControllerList *backedList = mStorageControllers.backedUpData();
11616 while (it != mStorageControllers->end())
11617 {
11618 if ( std::find(backedList->begin(), backedList->end(), *it)
11619 == backedList->end()
11620 )
11621 {
11622 (*it)->uninit();
11623 }
11624 ++it;
11625 }
11626
11627 /* restore the list */
11628 mStorageControllers.rollback();
11629 }
11630
11631 /* rollback any changes to devices after restoring the list */
11632 if (mData->flModifications & IsModified_Storage)
11633 {
11634 StorageControllerList::const_iterator it = mStorageControllers->begin();
11635 while (it != mStorageControllers->end())
11636 {
11637 (*it)->i_rollback();
11638 ++it;
11639 }
11640 }
11641 }
11642
11643 if (!mUSBControllers.isNull())
11644 {
11645 if (mUSBControllers.isBackedUp())
11646 {
11647 /* unitialize all new devices (absent in the backed up list). */
11648 USBControllerList::const_iterator it = mUSBControllers->begin();
11649 USBControllerList *backedList = mUSBControllers.backedUpData();
11650 while (it != mUSBControllers->end())
11651 {
11652 if ( std::find(backedList->begin(), backedList->end(), *it)
11653 == backedList->end()
11654 )
11655 {
11656 (*it)->uninit();
11657 }
11658 ++it;
11659 }
11660
11661 /* restore the list */
11662 mUSBControllers.rollback();
11663 }
11664
11665 /* rollback any changes to devices after restoring the list */
11666 if (mData->flModifications & IsModified_USB)
11667 {
11668 USBControllerList::const_iterator it = mUSBControllers->begin();
11669 while (it != mUSBControllers->end())
11670 {
11671 (*it)->i_rollback();
11672 ++it;
11673 }
11674 }
11675 }
11676
11677 mUserData.rollback();
11678
11679 mHWData.rollback();
11680
11681 if (mData->flModifications & IsModified_Storage)
11682 i_rollbackMedia();
11683
11684 if (mBIOSSettings)
11685 mBIOSSettings->i_rollback();
11686
11687 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11688 mVRDEServer->i_rollback();
11689
11690 if (mAudioAdapter)
11691 mAudioAdapter->i_rollback();
11692
11693 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11694 mUSBDeviceFilters->i_rollback();
11695
11696 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11697 mBandwidthControl->i_rollback();
11698
11699 if (!mHWData.isNull())
11700 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11701 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11702 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11703 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11704
11705 if (mData->flModifications & IsModified_NetworkAdapters)
11706 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11707 if ( mNetworkAdapters[slot]
11708 && mNetworkAdapters[slot]->i_isModified())
11709 {
11710 mNetworkAdapters[slot]->i_rollback();
11711 networkAdapters[slot] = mNetworkAdapters[slot];
11712 }
11713
11714 if (mData->flModifications & IsModified_SerialPorts)
11715 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11716 if ( mSerialPorts[slot]
11717 && mSerialPorts[slot]->i_isModified())
11718 {
11719 mSerialPorts[slot]->i_rollback();
11720 serialPorts[slot] = mSerialPorts[slot];
11721 }
11722
11723 if (mData->flModifications & IsModified_ParallelPorts)
11724 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11725 if ( mParallelPorts[slot]
11726 && mParallelPorts[slot]->i_isModified())
11727 {
11728 mParallelPorts[slot]->i_rollback();
11729 parallelPorts[slot] = mParallelPorts[slot];
11730 }
11731
11732 if (aNotify)
11733 {
11734 /* inform the direct session about changes */
11735
11736 ComObjPtr<Machine> that = this;
11737 uint32_t flModifications = mData->flModifications;
11738 alock.release();
11739
11740 if (flModifications & IsModified_SharedFolders)
11741 that->i_onSharedFolderChange();
11742
11743 if (flModifications & IsModified_VRDEServer)
11744 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11745 if (flModifications & IsModified_USB)
11746 that->i_onUSBControllerChange();
11747
11748 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11749 if (networkAdapters[slot])
11750 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11751 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11752 if (serialPorts[slot])
11753 that->i_onSerialPortChange(serialPorts[slot]);
11754 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11755 if (parallelPorts[slot])
11756 that->i_onParallelPortChange(parallelPorts[slot]);
11757
11758 if (flModifications & IsModified_Storage)
11759 that->i_onStorageControllerChange();
11760
11761#if 0
11762 if (flModifications & IsModified_BandwidthControl)
11763 that->onBandwidthControlChange();
11764#endif
11765 }
11766}
11767
11768/**
11769 * Commits all the changes to machine settings.
11770 *
11771 * Note that this operation is supposed to never fail.
11772 *
11773 * @note Locks this object and children for writing.
11774 */
11775void Machine::i_commit()
11776{
11777 AutoCaller autoCaller(this);
11778 AssertComRCReturnVoid(autoCaller.rc());
11779
11780 AutoCaller peerCaller(mPeer);
11781 AssertComRCReturnVoid(peerCaller.rc());
11782
11783 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11784
11785 /*
11786 * use safe commit to ensure Snapshot machines (that share mUserData)
11787 * will still refer to a valid memory location
11788 */
11789 mUserData.commitCopy();
11790
11791 mHWData.commit();
11792
11793 if (mMediaData.isBackedUp())
11794 i_commitMedia(Global::IsOnline(mData->mMachineState));
11795
11796 mBIOSSettings->i_commit();
11797 mVRDEServer->i_commit();
11798 mAudioAdapter->i_commit();
11799 mUSBDeviceFilters->i_commit();
11800 mBandwidthControl->i_commit();
11801
11802 /* Since mNetworkAdapters is a list which might have been changed (resized)
11803 * without using the Backupable<> template we need to handle the copying
11804 * of the list entries manually, including the creation of peers for the
11805 * new objects. */
11806 bool commitNetworkAdapters = false;
11807 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11808 if (mPeer)
11809 {
11810 /* commit everything, even the ones which will go away */
11811 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11812 mNetworkAdapters[slot]->i_commit();
11813 /* copy over the new entries, creating a peer and uninit the original */
11814 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11815 for (size_t slot = 0; slot < newSize; slot++)
11816 {
11817 /* look if this adapter has a peer device */
11818 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11819 if (!peer)
11820 {
11821 /* no peer means the adapter is a newly created one;
11822 * create a peer owning data this data share it with */
11823 peer.createObject();
11824 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11825 }
11826 mPeer->mNetworkAdapters[slot] = peer;
11827 }
11828 /* uninit any no longer needed network adapters */
11829 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11830 mNetworkAdapters[slot]->uninit();
11831 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11832 {
11833 if (mPeer->mNetworkAdapters[slot])
11834 mPeer->mNetworkAdapters[slot]->uninit();
11835 }
11836 /* Keep the original network adapter count until this point, so that
11837 * discarding a chipset type change will not lose settings. */
11838 mNetworkAdapters.resize(newSize);
11839 mPeer->mNetworkAdapters.resize(newSize);
11840 }
11841 else
11842 {
11843 /* we have no peer (our parent is the newly created machine);
11844 * just commit changes to the network adapters */
11845 commitNetworkAdapters = true;
11846 }
11847 if (commitNetworkAdapters)
11848 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11849 mNetworkAdapters[slot]->i_commit();
11850
11851 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11852 mSerialPorts[slot]->i_commit();
11853 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11854 mParallelPorts[slot]->i_commit();
11855
11856 bool commitStorageControllers = false;
11857
11858 if (mStorageControllers.isBackedUp())
11859 {
11860 mStorageControllers.commit();
11861
11862 if (mPeer)
11863 {
11864 /* Commit all changes to new controllers (this will reshare data with
11865 * peers for those who have peers) */
11866 StorageControllerList *newList = new StorageControllerList();
11867 StorageControllerList::const_iterator it = mStorageControllers->begin();
11868 while (it != mStorageControllers->end())
11869 {
11870 (*it)->i_commit();
11871
11872 /* look if this controller has a peer device */
11873 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11874 if (!peer)
11875 {
11876 /* no peer means the device is a newly created one;
11877 * create a peer owning data this device share it with */
11878 peer.createObject();
11879 peer->init(mPeer, *it, true /* aReshare */);
11880 }
11881 else
11882 {
11883 /* remove peer from the old list */
11884 mPeer->mStorageControllers->remove(peer);
11885 }
11886 /* and add it to the new list */
11887 newList->push_back(peer);
11888
11889 ++it;
11890 }
11891
11892 /* uninit old peer's controllers that are left */
11893 it = mPeer->mStorageControllers->begin();
11894 while (it != mPeer->mStorageControllers->end())
11895 {
11896 (*it)->uninit();
11897 ++it;
11898 }
11899
11900 /* attach new list of controllers to our peer */
11901 mPeer->mStorageControllers.attach(newList);
11902 }
11903 else
11904 {
11905 /* we have no peer (our parent is the newly created machine);
11906 * just commit changes to devices */
11907 commitStorageControllers = true;
11908 }
11909 }
11910 else
11911 {
11912 /* the list of controllers itself is not changed,
11913 * just commit changes to controllers themselves */
11914 commitStorageControllers = true;
11915 }
11916
11917 if (commitStorageControllers)
11918 {
11919 StorageControllerList::const_iterator it = mStorageControllers->begin();
11920 while (it != mStorageControllers->end())
11921 {
11922 (*it)->i_commit();
11923 ++it;
11924 }
11925 }
11926
11927 bool commitUSBControllers = false;
11928
11929 if (mUSBControllers.isBackedUp())
11930 {
11931 mUSBControllers.commit();
11932
11933 if (mPeer)
11934 {
11935 /* Commit all changes to new controllers (this will reshare data with
11936 * peers for those who have peers) */
11937 USBControllerList *newList = new USBControllerList();
11938 USBControllerList::const_iterator it = mUSBControllers->begin();
11939 while (it != mUSBControllers->end())
11940 {
11941 (*it)->i_commit();
11942
11943 /* look if this controller has a peer device */
11944 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11945 if (!peer)
11946 {
11947 /* no peer means the device is a newly created one;
11948 * create a peer owning data this device share it with */
11949 peer.createObject();
11950 peer->init(mPeer, *it, true /* aReshare */);
11951 }
11952 else
11953 {
11954 /* remove peer from the old list */
11955 mPeer->mUSBControllers->remove(peer);
11956 }
11957 /* and add it to the new list */
11958 newList->push_back(peer);
11959
11960 ++it;
11961 }
11962
11963 /* uninit old peer's controllers that are left */
11964 it = mPeer->mUSBControllers->begin();
11965 while (it != mPeer->mUSBControllers->end())
11966 {
11967 (*it)->uninit();
11968 ++it;
11969 }
11970
11971 /* attach new list of controllers to our peer */
11972 mPeer->mUSBControllers.attach(newList);
11973 }
11974 else
11975 {
11976 /* we have no peer (our parent is the newly created machine);
11977 * just commit changes to devices */
11978 commitUSBControllers = true;
11979 }
11980 }
11981 else
11982 {
11983 /* the list of controllers itself is not changed,
11984 * just commit changes to controllers themselves */
11985 commitUSBControllers = true;
11986 }
11987
11988 if (commitUSBControllers)
11989 {
11990 USBControllerList::const_iterator it = mUSBControllers->begin();
11991 while (it != mUSBControllers->end())
11992 {
11993 (*it)->i_commit();
11994 ++it;
11995 }
11996 }
11997
11998 if (i_isSessionMachine())
11999 {
12000 /* attach new data to the primary machine and reshare it */
12001 mPeer->mUserData.attach(mUserData);
12002 mPeer->mHWData.attach(mHWData);
12003 /* mMediaData is reshared by fixupMedia */
12004 // mPeer->mMediaData.attach(mMediaData);
12005 Assert(mPeer->mMediaData.data() == mMediaData.data());
12006 }
12007}
12008
12009/**
12010 * Copies all the hardware data from the given machine.
12011 *
12012 * Currently, only called when the VM is being restored from a snapshot. In
12013 * particular, this implies that the VM is not running during this method's
12014 * call.
12015 *
12016 * @note This method must be called from under this object's lock.
12017 *
12018 * @note This method doesn't call #commit(), so all data remains backed up and
12019 * unsaved.
12020 */
12021void Machine::i_copyFrom(Machine *aThat)
12022{
12023 AssertReturnVoid(!i_isSnapshotMachine());
12024 AssertReturnVoid(aThat->i_isSnapshotMachine());
12025
12026 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12027
12028 mHWData.assignCopy(aThat->mHWData);
12029
12030 // create copies of all shared folders (mHWData after attaching a copy
12031 // contains just references to original objects)
12032 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12033 it != mHWData->mSharedFolders.end();
12034 ++it)
12035 {
12036 ComObjPtr<SharedFolder> folder;
12037 folder.createObject();
12038 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12039 AssertComRC(rc);
12040 *it = folder;
12041 }
12042
12043 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12044 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12045 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12046 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12047 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12048
12049 /* create private copies of all controllers */
12050 mStorageControllers.backup();
12051 mStorageControllers->clear();
12052 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12053 it != aThat->mStorageControllers->end();
12054 ++it)
12055 {
12056 ComObjPtr<StorageController> ctrl;
12057 ctrl.createObject();
12058 ctrl->initCopy(this, *it);
12059 mStorageControllers->push_back(ctrl);
12060 }
12061
12062 /* create private copies of all USB controllers */
12063 mUSBControllers.backup();
12064 mUSBControllers->clear();
12065 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12066 it != aThat->mUSBControllers->end();
12067 ++it)
12068 {
12069 ComObjPtr<USBController> ctrl;
12070 ctrl.createObject();
12071 ctrl->initCopy(this, *it);
12072 mUSBControllers->push_back(ctrl);
12073 }
12074
12075 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12076 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12077 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12078 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12079 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12080 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12081 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12082}
12083
12084/**
12085 * Returns whether the given storage controller is hotplug capable.
12086 *
12087 * @returns true if the controller supports hotplugging
12088 * false otherwise.
12089 * @param enmCtrlType The controller type to check for.
12090 */
12091bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12092{
12093 ComPtr<ISystemProperties> systemProperties;
12094 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12095 if (FAILED(rc))
12096 return false;
12097
12098 BOOL aHotplugCapable = FALSE;
12099 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12100
12101 return RT_BOOL(aHotplugCapable);
12102}
12103
12104#ifdef VBOX_WITH_RESOURCE_USAGE_API
12105
12106void Machine::i_getDiskList(MediaList &list)
12107{
12108 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12109 it != mMediaData->mAttachments.end();
12110 ++it)
12111 {
12112 MediumAttachment* pAttach = *it;
12113 /* just in case */
12114 AssertStmt(pAttach, continue);
12115
12116 AutoCaller localAutoCallerA(pAttach);
12117 if (FAILED(localAutoCallerA.rc())) continue;
12118
12119 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12120
12121 if (pAttach->i_getType() == DeviceType_HardDisk)
12122 list.push_back(pAttach->i_getMedium());
12123 }
12124}
12125
12126void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12127{
12128 AssertReturnVoid(isWriteLockOnCurrentThread());
12129 AssertPtrReturnVoid(aCollector);
12130
12131 pm::CollectorHAL *hal = aCollector->getHAL();
12132 /* Create sub metrics */
12133 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12134 "Percentage of processor time spent in user mode by the VM process.");
12135 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12136 "Percentage of processor time spent in kernel mode by the VM process.");
12137 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12138 "Size of resident portion of VM process in memory.");
12139 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12140 "Actual size of all VM disks combined.");
12141 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12142 "Network receive rate.");
12143 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12144 "Network transmit rate.");
12145 /* Create and register base metrics */
12146 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12147 cpuLoadUser, cpuLoadKernel);
12148 aCollector->registerBaseMetric(cpuLoad);
12149 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12150 ramUsageUsed);
12151 aCollector->registerBaseMetric(ramUsage);
12152 MediaList disks;
12153 i_getDiskList(disks);
12154 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12155 diskUsageUsed);
12156 aCollector->registerBaseMetric(diskUsage);
12157
12158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12159 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12160 new pm::AggregateAvg()));
12161 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12162 new pm::AggregateMin()));
12163 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12164 new pm::AggregateMax()));
12165 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12166 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12167 new pm::AggregateAvg()));
12168 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12169 new pm::AggregateMin()));
12170 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12171 new pm::AggregateMax()));
12172
12173 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12174 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12175 new pm::AggregateAvg()));
12176 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12177 new pm::AggregateMin()));
12178 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12179 new pm::AggregateMax()));
12180
12181 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12182 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12183 new pm::AggregateAvg()));
12184 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12185 new pm::AggregateMin()));
12186 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12187 new pm::AggregateMax()));
12188
12189
12190 /* Guest metrics collector */
12191 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12192 aCollector->registerGuest(mCollectorGuest);
12193 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12194 this, __PRETTY_FUNCTION__, mCollectorGuest));
12195
12196 /* Create sub metrics */
12197 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12198 "Percentage of processor time spent in user mode as seen by the guest.");
12199 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12200 "Percentage of processor time spent in kernel mode as seen by the guest.");
12201 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12202 "Percentage of processor time spent idling as seen by the guest.");
12203
12204 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12205 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12206 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12207 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12208 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12209 pm::SubMetric *guestMemCache = new pm::SubMetric(
12210 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12211
12212 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12213 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12214
12215 /* Create and register base metrics */
12216 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12217 machineNetRx, machineNetTx);
12218 aCollector->registerBaseMetric(machineNetRate);
12219
12220 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12221 guestLoadUser, guestLoadKernel, guestLoadIdle);
12222 aCollector->registerBaseMetric(guestCpuLoad);
12223
12224 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12225 guestMemTotal, guestMemFree,
12226 guestMemBalloon, guestMemShared,
12227 guestMemCache, guestPagedTotal);
12228 aCollector->registerBaseMetric(guestCpuMem);
12229
12230 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12231 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12232 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12233 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12234
12235 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12236 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12237 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12238 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12239
12240 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12241 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12243 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12244
12245 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12246 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12247 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12249
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12253 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12254
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12259
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12264
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12269
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12274
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12284}
12285
12286void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12287{
12288 AssertReturnVoid(isWriteLockOnCurrentThread());
12289
12290 if (aCollector)
12291 {
12292 aCollector->unregisterMetricsFor(aMachine);
12293 aCollector->unregisterBaseMetricsFor(aMachine);
12294 }
12295}
12296
12297#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12298
12299
12300////////////////////////////////////////////////////////////////////////////////
12301
12302DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12303
12304HRESULT SessionMachine::FinalConstruct()
12305{
12306 LogFlowThisFunc(("\n"));
12307
12308 mClientToken = NULL;
12309
12310 return BaseFinalConstruct();
12311}
12312
12313void SessionMachine::FinalRelease()
12314{
12315 LogFlowThisFunc(("\n"));
12316
12317 Assert(!mClientToken);
12318 /* paranoia, should not hang around any more */
12319 if (mClientToken)
12320 {
12321 delete mClientToken;
12322 mClientToken = NULL;
12323 }
12324
12325 uninit(Uninit::Unexpected);
12326
12327 BaseFinalRelease();
12328}
12329
12330/**
12331 * @note Must be called only by Machine::LockMachine() from its own write lock.
12332 */
12333HRESULT SessionMachine::init(Machine *aMachine)
12334{
12335 LogFlowThisFuncEnter();
12336 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12337
12338 AssertReturn(aMachine, E_INVALIDARG);
12339
12340 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12341
12342 /* Enclose the state transition NotReady->InInit->Ready */
12343 AutoInitSpan autoInitSpan(this);
12344 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12345
12346 HRESULT rc = S_OK;
12347
12348 /* create the machine client token */
12349 try
12350 {
12351 mClientToken = new ClientToken(aMachine, this);
12352 if (!mClientToken->isReady())
12353 {
12354 delete mClientToken;
12355 mClientToken = NULL;
12356 rc = E_FAIL;
12357 }
12358 }
12359 catch (std::bad_alloc &)
12360 {
12361 rc = E_OUTOFMEMORY;
12362 }
12363 if (FAILED(rc))
12364 return rc;
12365
12366 /* memorize the peer Machine */
12367 unconst(mPeer) = aMachine;
12368 /* share the parent pointer */
12369 unconst(mParent) = aMachine->mParent;
12370
12371 /* take the pointers to data to share */
12372 mData.share(aMachine->mData);
12373 mSSData.share(aMachine->mSSData);
12374
12375 mUserData.share(aMachine->mUserData);
12376 mHWData.share(aMachine->mHWData);
12377 mMediaData.share(aMachine->mMediaData);
12378
12379 mStorageControllers.allocate();
12380 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12381 it != aMachine->mStorageControllers->end();
12382 ++it)
12383 {
12384 ComObjPtr<StorageController> ctl;
12385 ctl.createObject();
12386 ctl->init(this, *it);
12387 mStorageControllers->push_back(ctl);
12388 }
12389
12390 mUSBControllers.allocate();
12391 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12392 it != aMachine->mUSBControllers->end();
12393 ++it)
12394 {
12395 ComObjPtr<USBController> ctl;
12396 ctl.createObject();
12397 ctl->init(this, *it);
12398 mUSBControllers->push_back(ctl);
12399 }
12400
12401 unconst(mBIOSSettings).createObject();
12402 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12403 /* create another VRDEServer object that will be mutable */
12404 unconst(mVRDEServer).createObject();
12405 mVRDEServer->init(this, aMachine->mVRDEServer);
12406 /* create another audio adapter object that will be mutable */
12407 unconst(mAudioAdapter).createObject();
12408 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12409 /* create a list of serial ports that will be mutable */
12410 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12411 {
12412 unconst(mSerialPorts[slot]).createObject();
12413 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12414 }
12415 /* create a list of parallel ports that will be mutable */
12416 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12417 {
12418 unconst(mParallelPorts[slot]).createObject();
12419 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12420 }
12421
12422 /* create another USB device filters object that will be mutable */
12423 unconst(mUSBDeviceFilters).createObject();
12424 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12425
12426 /* create a list of network adapters that will be mutable */
12427 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12428 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12429 {
12430 unconst(mNetworkAdapters[slot]).createObject();
12431 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12432 }
12433
12434 /* create another bandwidth control object that will be mutable */
12435 unconst(mBandwidthControl).createObject();
12436 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12437
12438 /* default is to delete saved state on Saved -> PoweredOff transition */
12439 mRemoveSavedState = true;
12440
12441 /* Confirm a successful initialization when it's the case */
12442 autoInitSpan.setSucceeded();
12443
12444 miNATNetworksStarted = 0;
12445
12446 LogFlowThisFuncLeave();
12447 return rc;
12448}
12449
12450/**
12451 * Uninitializes this session object. If the reason is other than
12452 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12453 * or the client watcher code.
12454 *
12455 * @param aReason uninitialization reason
12456 *
12457 * @note Locks mParent + this object for writing.
12458 */
12459void SessionMachine::uninit(Uninit::Reason aReason)
12460{
12461 LogFlowThisFuncEnter();
12462 LogFlowThisFunc(("reason=%d\n", aReason));
12463
12464 /*
12465 * Strongly reference ourselves to prevent this object deletion after
12466 * mData->mSession.mMachine.setNull() below (which can release the last
12467 * reference and call the destructor). Important: this must be done before
12468 * accessing any members (and before AutoUninitSpan that does it as well).
12469 * This self reference will be released as the very last step on return.
12470 */
12471 ComObjPtr<SessionMachine> selfRef = this;
12472
12473 /* Enclose the state transition Ready->InUninit->NotReady */
12474 AutoUninitSpan autoUninitSpan(this);
12475 if (autoUninitSpan.uninitDone())
12476 {
12477 LogFlowThisFunc(("Already uninitialized\n"));
12478 LogFlowThisFuncLeave();
12479 return;
12480 }
12481
12482 if (autoUninitSpan.initFailed())
12483 {
12484 /* We've been called by init() because it's failed. It's not really
12485 * necessary (nor it's safe) to perform the regular uninit sequence
12486 * below, the following is enough.
12487 */
12488 LogFlowThisFunc(("Initialization failed.\n"));
12489 /* destroy the machine client token */
12490 if (mClientToken)
12491 {
12492 delete mClientToken;
12493 mClientToken = NULL;
12494 }
12495 uninitDataAndChildObjects();
12496 mData.free();
12497 unconst(mParent) = NULL;
12498 unconst(mPeer) = NULL;
12499 LogFlowThisFuncLeave();
12500 return;
12501 }
12502
12503 MachineState_T lastState;
12504 {
12505 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12506 lastState = mData->mMachineState;
12507 }
12508 NOREF(lastState);
12509
12510#ifdef VBOX_WITH_USB
12511 // release all captured USB devices, but do this before requesting the locks below
12512 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12513 {
12514 /* Console::captureUSBDevices() is called in the VM process only after
12515 * setting the machine state to Starting or Restoring.
12516 * Console::detachAllUSBDevices() will be called upon successful
12517 * termination. So, we need to release USB devices only if there was
12518 * an abnormal termination of a running VM.
12519 *
12520 * This is identical to SessionMachine::DetachAllUSBDevices except
12521 * for the aAbnormal argument. */
12522 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12523 AssertComRC(rc);
12524 NOREF(rc);
12525
12526 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12527 if (service)
12528 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12529 }
12530#endif /* VBOX_WITH_USB */
12531
12532 // we need to lock this object in uninit() because the lock is shared
12533 // with mPeer (as well as data we modify below). mParent lock is needed
12534 // by several calls to it, and USB needs host lock.
12535 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12536
12537#ifdef VBOX_WITH_RESOURCE_USAGE_API
12538 /*
12539 * It is safe to call Machine::i_unregisterMetrics() here because
12540 * PerformanceCollector::samplerCallback no longer accesses guest methods
12541 * holding the lock.
12542 */
12543 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12544 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12545 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12546 this, __PRETTY_FUNCTION__, mCollectorGuest));
12547 if (mCollectorGuest)
12548 {
12549 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12550 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12551 mCollectorGuest = NULL;
12552 }
12553#endif
12554
12555 if (aReason == Uninit::Abnormal)
12556 {
12557 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12558 Global::IsOnlineOrTransient(lastState)));
12559
12560 /* reset the state to Aborted */
12561 if (mData->mMachineState != MachineState_Aborted)
12562 i_setMachineState(MachineState_Aborted);
12563 }
12564
12565 // any machine settings modified?
12566 if (mData->flModifications)
12567 {
12568 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12569 i_rollback(false /* aNotify */);
12570 }
12571
12572 mData->mSession.mPID = NIL_RTPROCESS;
12573
12574 if (aReason == Uninit::Unexpected)
12575 {
12576 /* Uninitialization didn't come from #checkForDeath(), so tell the
12577 * client watcher thread to update the set of machines that have open
12578 * sessions. */
12579 mParent->i_updateClientWatcher();
12580 }
12581
12582 /* uninitialize all remote controls */
12583 if (mData->mSession.mRemoteControls.size())
12584 {
12585 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12586 mData->mSession.mRemoteControls.size()));
12587
12588 Data::Session::RemoteControlList::iterator it =
12589 mData->mSession.mRemoteControls.begin();
12590 while (it != mData->mSession.mRemoteControls.end())
12591 {
12592 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12593 HRESULT rc = (*it)->Uninitialize();
12594 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12595 if (FAILED(rc))
12596 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12597 ++it;
12598 }
12599 mData->mSession.mRemoteControls.clear();
12600 }
12601
12602 /* Remove all references to the NAT network service. The service will stop
12603 * if all references (also from other VMs) are removed. */
12604 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12605 {
12606 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12607 {
12608 NetworkAttachmentType_T type;
12609 HRESULT hrc;
12610
12611 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12612 if ( SUCCEEDED(hrc)
12613 && type == NetworkAttachmentType_NATNetwork)
12614 {
12615 Bstr name;
12616 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12617 if (SUCCEEDED(hrc))
12618 {
12619 multilock.release();
12620 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12621 mUserData->s.strName.c_str(), name.raw()));
12622 mParent->i_natNetworkRefDec(name.raw());
12623 multilock.acquire();
12624 }
12625 }
12626 }
12627 }
12628
12629 /*
12630 * An expected uninitialization can come only from #checkForDeath().
12631 * Otherwise it means that something's gone really wrong (for example,
12632 * the Session implementation has released the VirtualBox reference
12633 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12634 * etc). However, it's also possible, that the client releases the IPC
12635 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12636 * but the VirtualBox release event comes first to the server process.
12637 * This case is practically possible, so we should not assert on an
12638 * unexpected uninit, just log a warning.
12639 */
12640
12641 if ((aReason == Uninit::Unexpected))
12642 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12643
12644 if (aReason != Uninit::Normal)
12645 {
12646 mData->mSession.mDirectControl.setNull();
12647 }
12648 else
12649 {
12650 /* this must be null here (see #OnSessionEnd()) */
12651 Assert(mData->mSession.mDirectControl.isNull());
12652 Assert(mData->mSession.mState == SessionState_Unlocking);
12653 Assert(!mData->mSession.mProgress.isNull());
12654 }
12655 if (mData->mSession.mProgress)
12656 {
12657 if (aReason == Uninit::Normal)
12658 mData->mSession.mProgress->i_notifyComplete(S_OK);
12659 else
12660 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12661 COM_IIDOF(ISession),
12662 getComponentName(),
12663 tr("The VM session was aborted"));
12664 mData->mSession.mProgress.setNull();
12665 }
12666
12667 /* remove the association between the peer machine and this session machine */
12668 Assert( (SessionMachine*)mData->mSession.mMachine == this
12669 || aReason == Uninit::Unexpected);
12670
12671 /* reset the rest of session data */
12672 mData->mSession.mLockType = LockType_Null;
12673 mData->mSession.mMachine.setNull();
12674 mData->mSession.mState = SessionState_Unlocked;
12675 mData->mSession.mName.setNull();
12676
12677 /* destroy the machine client token before leaving the exclusive lock */
12678 if (mClientToken)
12679 {
12680 delete mClientToken;
12681 mClientToken = NULL;
12682 }
12683
12684 /* fire an event */
12685 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12686
12687 uninitDataAndChildObjects();
12688
12689 /* free the essential data structure last */
12690 mData.free();
12691
12692 /* release the exclusive lock before setting the below two to NULL */
12693 multilock.release();
12694
12695 unconst(mParent) = NULL;
12696 unconst(mPeer) = NULL;
12697
12698 LogFlowThisFuncLeave();
12699}
12700
12701// util::Lockable interface
12702////////////////////////////////////////////////////////////////////////////////
12703
12704/**
12705 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12706 * with the primary Machine instance (mPeer).
12707 */
12708RWLockHandle *SessionMachine::lockHandle() const
12709{
12710 AssertReturn(mPeer != NULL, NULL);
12711 return mPeer->lockHandle();
12712}
12713
12714// IInternalMachineControl methods
12715////////////////////////////////////////////////////////////////////////////////
12716
12717/**
12718 * Passes collected guest statistics to performance collector object
12719 */
12720HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12721 ULONG aCpuKernel, ULONG aCpuIdle,
12722 ULONG aMemTotal, ULONG aMemFree,
12723 ULONG aMemBalloon, ULONG aMemShared,
12724 ULONG aMemCache, ULONG aPageTotal,
12725 ULONG aAllocVMM, ULONG aFreeVMM,
12726 ULONG aBalloonedVMM, ULONG aSharedVMM,
12727 ULONG aVmNetRx, ULONG aVmNetTx)
12728{
12729#ifdef VBOX_WITH_RESOURCE_USAGE_API
12730 if (mCollectorGuest)
12731 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12732 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12733 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12734 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12735
12736 return S_OK;
12737#else
12738 NOREF(aValidStats);
12739 NOREF(aCpuUser);
12740 NOREF(aCpuKernel);
12741 NOREF(aCpuIdle);
12742 NOREF(aMemTotal);
12743 NOREF(aMemFree);
12744 NOREF(aMemBalloon);
12745 NOREF(aMemShared);
12746 NOREF(aMemCache);
12747 NOREF(aPageTotal);
12748 NOREF(aAllocVMM);
12749 NOREF(aFreeVMM);
12750 NOREF(aBalloonedVMM);
12751 NOREF(aSharedVMM);
12752 NOREF(aVmNetRx);
12753 NOREF(aVmNetTx);
12754 return E_NOTIMPL;
12755#endif
12756}
12757
12758////////////////////////////////////////////////////////////////////////////////
12759//
12760// SessionMachine task records
12761//
12762////////////////////////////////////////////////////////////////////////////////
12763
12764/**
12765 * Task record for saving the machine state.
12766 */
12767struct SessionMachine::SaveStateTask
12768 : public Machine::Task
12769{
12770 SaveStateTask(SessionMachine *m,
12771 Progress *p,
12772 const Utf8Str &t,
12773 Reason_T enmReason,
12774 const Utf8Str &strStateFilePath)
12775 : Task(m, p, t),
12776 m_enmReason(enmReason),
12777 m_strStateFilePath(strStateFilePath)
12778 {}
12779
12780 void handler()
12781 {
12782 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12783 }
12784
12785 Reason_T m_enmReason;
12786 Utf8Str m_strStateFilePath;
12787};
12788
12789/**
12790 * Task thread implementation for SessionMachine::SaveState(), called from
12791 * SessionMachine::taskHandler().
12792 *
12793 * @note Locks this object for writing.
12794 *
12795 * @param task
12796 * @return
12797 */
12798void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12799{
12800 LogFlowThisFuncEnter();
12801
12802 AutoCaller autoCaller(this);
12803 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12804 if (FAILED(autoCaller.rc()))
12805 {
12806 /* we might have been uninitialized because the session was accidentally
12807 * closed by the client, so don't assert */
12808 HRESULT rc = setError(E_FAIL,
12809 tr("The session has been accidentally closed"));
12810 task.m_pProgress->i_notifyComplete(rc);
12811 LogFlowThisFuncLeave();
12812 return;
12813 }
12814
12815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12816
12817 HRESULT rc = S_OK;
12818
12819 try
12820 {
12821 ComPtr<IInternalSessionControl> directControl;
12822 if (mData->mSession.mLockType == LockType_VM)
12823 directControl = mData->mSession.mDirectControl;
12824 if (directControl.isNull())
12825 throw setError(VBOX_E_INVALID_VM_STATE,
12826 tr("Trying to save state without a running VM"));
12827 alock.release();
12828 BOOL fSuspendedBySave;
12829 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12830 Assert(!fSuspendedBySave);
12831 alock.acquire();
12832
12833 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12834 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12835 throw E_FAIL);
12836
12837 if (SUCCEEDED(rc))
12838 {
12839 mSSData->strStateFilePath = task.m_strStateFilePath;
12840
12841 /* save all VM settings */
12842 rc = i_saveSettings(NULL);
12843 // no need to check whether VirtualBox.xml needs saving also since
12844 // we can't have a name change pending at this point
12845 }
12846 else
12847 {
12848 // On failure, set the state to the state we had at the beginning.
12849 i_setMachineState(task.m_machineStateBackup);
12850 i_updateMachineStateOnClient();
12851
12852 // Delete the saved state file (might have been already created).
12853 // No need to check whether this is shared with a snapshot here
12854 // because we certainly created a fresh saved state file here.
12855 RTFileDelete(task.m_strStateFilePath.c_str());
12856 }
12857 }
12858 catch (HRESULT aRC) { rc = aRC; }
12859
12860 task.m_pProgress->i_notifyComplete(rc);
12861
12862 LogFlowThisFuncLeave();
12863}
12864
12865/**
12866 * @note Locks this object for writing.
12867 */
12868HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12869{
12870 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12871}
12872
12873HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12874{
12875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12876
12877 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12878 if (FAILED(rc)) return rc;
12879
12880 if ( mData->mMachineState != MachineState_Running
12881 && mData->mMachineState != MachineState_Paused
12882 )
12883 return setError(VBOX_E_INVALID_VM_STATE,
12884 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12885 Global::stringifyMachineState(mData->mMachineState));
12886
12887 ComObjPtr<Progress> pProgress;
12888 pProgress.createObject();
12889 rc = pProgress->init(i_getVirtualBox(),
12890 static_cast<IMachine *>(this) /* aInitiator */,
12891 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12892 FALSE /* aCancelable */);
12893 if (FAILED(rc))
12894 return rc;
12895
12896 Utf8Str strStateFilePath;
12897 i_composeSavedStateFilename(strStateFilePath);
12898
12899 /* create and start the task on a separate thread (note that it will not
12900 * start working until we release alock) */
12901 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12902 rc = pTask->createThread();
12903 if (FAILED(rc))
12904 return rc;
12905
12906 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12907 i_setMachineState(MachineState_Saving);
12908 i_updateMachineStateOnClient();
12909
12910 pProgress.queryInterfaceTo(aProgress.asOutParam());
12911
12912 return S_OK;
12913}
12914
12915/**
12916 * @note Locks this object for writing.
12917 */
12918HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12919{
12920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12921
12922 HRESULT rc = i_checkStateDependency(MutableStateDep);
12923 if (FAILED(rc)) return rc;
12924
12925 if ( mData->mMachineState != MachineState_PoweredOff
12926 && mData->mMachineState != MachineState_Teleported
12927 && mData->mMachineState != MachineState_Aborted
12928 )
12929 return setError(VBOX_E_INVALID_VM_STATE,
12930 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12931 Global::stringifyMachineState(mData->mMachineState));
12932
12933 com::Utf8Str stateFilePathFull;
12934 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12935 if (RT_FAILURE(vrc))
12936 return setError(VBOX_E_FILE_ERROR,
12937 tr("Invalid saved state file path '%s' (%Rrc)"),
12938 aSavedStateFile.c_str(),
12939 vrc);
12940
12941 mSSData->strStateFilePath = stateFilePathFull;
12942
12943 /* The below i_setMachineState() will detect the state transition and will
12944 * update the settings file */
12945
12946 return i_setMachineState(MachineState_Saved);
12947}
12948
12949/**
12950 * @note Locks this object for writing.
12951 */
12952HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12953{
12954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12955
12956 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12957 if (FAILED(rc)) return rc;
12958
12959 if (mData->mMachineState != MachineState_Saved)
12960 return setError(VBOX_E_INVALID_VM_STATE,
12961 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12962 Global::stringifyMachineState(mData->mMachineState));
12963
12964 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12965
12966 /*
12967 * Saved -> PoweredOff transition will be detected in the SessionMachine
12968 * and properly handled.
12969 */
12970 rc = i_setMachineState(MachineState_PoweredOff);
12971 return rc;
12972}
12973
12974
12975/**
12976 * @note Locks the same as #i_setMachineState() does.
12977 */
12978HRESULT SessionMachine::updateState(MachineState_T aState)
12979{
12980 return i_setMachineState(aState);
12981}
12982
12983/**
12984 * @note Locks this object for writing.
12985 */
12986HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12987{
12988 IProgress* pProgress(aProgress);
12989
12990 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12991
12992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12993
12994 if (mData->mSession.mState != SessionState_Locked)
12995 return VBOX_E_INVALID_OBJECT_STATE;
12996
12997 if (!mData->mSession.mProgress.isNull())
12998 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12999
13000 /* If we didn't reference the NAT network service yet, add a reference to
13001 * force a start */
13002 if (miNATNetworksStarted < 1)
13003 {
13004 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13005 {
13006 NetworkAttachmentType_T type;
13007 HRESULT hrc;
13008 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13009 if ( SUCCEEDED(hrc)
13010 && type == NetworkAttachmentType_NATNetwork)
13011 {
13012 Bstr name;
13013 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13014 if (SUCCEEDED(hrc))
13015 {
13016 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13017 mUserData->s.strName.c_str(), name.raw()));
13018 mPeer->lockHandle()->unlockWrite();
13019 mParent->i_natNetworkRefInc(name.raw());
13020#ifdef RT_LOCK_STRICT
13021 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13022#else
13023 mPeer->lockHandle()->lockWrite();
13024#endif
13025 }
13026 }
13027 }
13028 miNATNetworksStarted++;
13029 }
13030
13031 LogFlowThisFunc(("returns S_OK.\n"));
13032 return S_OK;
13033}
13034
13035/**
13036 * @note Locks this object for writing.
13037 */
13038HRESULT SessionMachine::endPowerUp(LONG aResult)
13039{
13040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13041
13042 if (mData->mSession.mState != SessionState_Locked)
13043 return VBOX_E_INVALID_OBJECT_STATE;
13044
13045 /* Finalize the LaunchVMProcess progress object. */
13046 if (mData->mSession.mProgress)
13047 {
13048 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13049 mData->mSession.mProgress.setNull();
13050 }
13051
13052 if (SUCCEEDED((HRESULT)aResult))
13053 {
13054#ifdef VBOX_WITH_RESOURCE_USAGE_API
13055 /* The VM has been powered up successfully, so it makes sense
13056 * now to offer the performance metrics for a running machine
13057 * object. Doing it earlier wouldn't be safe. */
13058 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13059 mData->mSession.mPID);
13060#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13061 }
13062
13063 return S_OK;
13064}
13065
13066/**
13067 * @note Locks this object for writing.
13068 */
13069HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13070{
13071 LogFlowThisFuncEnter();
13072
13073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13074
13075 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13076 E_FAIL);
13077
13078 /* create a progress object to track operation completion */
13079 ComObjPtr<Progress> pProgress;
13080 pProgress.createObject();
13081 pProgress->init(i_getVirtualBox(),
13082 static_cast<IMachine *>(this) /* aInitiator */,
13083 Bstr(tr("Stopping the virtual machine")).raw(),
13084 FALSE /* aCancelable */);
13085
13086 /* fill in the console task data */
13087 mConsoleTaskData.mLastState = mData->mMachineState;
13088 mConsoleTaskData.mProgress = pProgress;
13089
13090 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13091 i_setMachineState(MachineState_Stopping);
13092
13093 pProgress.queryInterfaceTo(aProgress.asOutParam());
13094
13095 return S_OK;
13096}
13097
13098/**
13099 * @note Locks this object for writing.
13100 */
13101HRESULT SessionMachine::endPoweringDown(LONG aResult,
13102 const com::Utf8Str &aErrMsg)
13103{
13104 LogFlowThisFuncEnter();
13105
13106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13107
13108 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13109 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13110 && mConsoleTaskData.mLastState != MachineState_Null,
13111 E_FAIL);
13112
13113 /*
13114 * On failure, set the state to the state we had when BeginPoweringDown()
13115 * was called (this is expected by Console::PowerDown() and the associated
13116 * task). On success the VM process already changed the state to
13117 * MachineState_PoweredOff, so no need to do anything.
13118 */
13119 if (FAILED(aResult))
13120 i_setMachineState(mConsoleTaskData.mLastState);
13121
13122 /* notify the progress object about operation completion */
13123 Assert(mConsoleTaskData.mProgress);
13124 if (SUCCEEDED(aResult))
13125 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13126 else
13127 {
13128 if (aErrMsg.length())
13129 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13130 COM_IIDOF(ISession),
13131 getComponentName(),
13132 aErrMsg.c_str());
13133 else
13134 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13135 }
13136
13137 /* clear out the temporary saved state data */
13138 mConsoleTaskData.mLastState = MachineState_Null;
13139 mConsoleTaskData.mProgress.setNull();
13140
13141 LogFlowThisFuncLeave();
13142 return S_OK;
13143}
13144
13145
13146/**
13147 * Goes through the USB filters of the given machine to see if the given
13148 * device matches any filter or not.
13149 *
13150 * @note Locks the same as USBController::hasMatchingFilter() does.
13151 */
13152HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13153 BOOL *aMatched,
13154 ULONG *aMaskedInterfaces)
13155{
13156 LogFlowThisFunc(("\n"));
13157
13158#ifdef VBOX_WITH_USB
13159 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13160#else
13161 NOREF(aDevice);
13162 NOREF(aMaskedInterfaces);
13163 *aMatched = FALSE;
13164#endif
13165
13166 return S_OK;
13167}
13168
13169/**
13170 * @note Locks the same as Host::captureUSBDevice() does.
13171 */
13172HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13173{
13174 LogFlowThisFunc(("\n"));
13175
13176#ifdef VBOX_WITH_USB
13177 /* if captureDeviceForVM() fails, it must have set extended error info */
13178 clearError();
13179 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13180 if (FAILED(rc)) return rc;
13181
13182 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13183 AssertReturn(service, E_FAIL);
13184 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13185#else
13186 NOREF(aId);
13187 return E_NOTIMPL;
13188#endif
13189}
13190
13191/**
13192 * @note Locks the same as Host::detachUSBDevice() does.
13193 */
13194HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13195 BOOL aDone)
13196{
13197 LogFlowThisFunc(("\n"));
13198
13199#ifdef VBOX_WITH_USB
13200 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13201 AssertReturn(service, E_FAIL);
13202 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13203#else
13204 NOREF(aId);
13205 NOREF(aDone);
13206 return E_NOTIMPL;
13207#endif
13208}
13209
13210/**
13211 * Inserts all machine filters to the USB proxy service and then calls
13212 * Host::autoCaptureUSBDevices().
13213 *
13214 * Called by Console from the VM process upon VM startup.
13215 *
13216 * @note Locks what called methods lock.
13217 */
13218HRESULT SessionMachine::autoCaptureUSBDevices()
13219{
13220 LogFlowThisFunc(("\n"));
13221
13222#ifdef VBOX_WITH_USB
13223 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13224 AssertComRC(rc);
13225 NOREF(rc);
13226
13227 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13228 AssertReturn(service, E_FAIL);
13229 return service->autoCaptureDevicesForVM(this);
13230#else
13231 return S_OK;
13232#endif
13233}
13234
13235/**
13236 * Removes all machine filters from the USB proxy service and then calls
13237 * Host::detachAllUSBDevices().
13238 *
13239 * Called by Console from the VM process upon normal VM termination or by
13240 * SessionMachine::uninit() upon abnormal VM termination (from under the
13241 * Machine/SessionMachine lock).
13242 *
13243 * @note Locks what called methods lock.
13244 */
13245HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13246{
13247 LogFlowThisFunc(("\n"));
13248
13249#ifdef VBOX_WITH_USB
13250 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13251 AssertComRC(rc);
13252 NOREF(rc);
13253
13254 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13255 AssertReturn(service, E_FAIL);
13256 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13257#else
13258 NOREF(aDone);
13259 return S_OK;
13260#endif
13261}
13262
13263/**
13264 * @note Locks this object for writing.
13265 */
13266HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13267 ComPtr<IProgress> &aProgress)
13268{
13269 LogFlowThisFuncEnter();
13270
13271 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13272 /*
13273 * We don't assert below because it might happen that a non-direct session
13274 * informs us it is closed right after we've been uninitialized -- it's ok.
13275 */
13276
13277 /* get IInternalSessionControl interface */
13278 ComPtr<IInternalSessionControl> control(aSession);
13279
13280 ComAssertRet(!control.isNull(), E_INVALIDARG);
13281
13282 /* Creating a Progress object requires the VirtualBox lock, and
13283 * thus locking it here is required by the lock order rules. */
13284 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13285
13286 if (control == mData->mSession.mDirectControl)
13287 {
13288 /* The direct session is being normally closed by the client process
13289 * ----------------------------------------------------------------- */
13290
13291 /* go to the closing state (essential for all open*Session() calls and
13292 * for #checkForDeath()) */
13293 Assert(mData->mSession.mState == SessionState_Locked);
13294 mData->mSession.mState = SessionState_Unlocking;
13295
13296 /* set direct control to NULL to release the remote instance */
13297 mData->mSession.mDirectControl.setNull();
13298 LogFlowThisFunc(("Direct control is set to NULL\n"));
13299
13300 if (mData->mSession.mProgress)
13301 {
13302 /* finalize the progress, someone might wait if a frontend
13303 * closes the session before powering on the VM. */
13304 mData->mSession.mProgress->notifyComplete(E_FAIL,
13305 COM_IIDOF(ISession),
13306 getComponentName(),
13307 tr("The VM session was closed before any attempt to power it on"));
13308 mData->mSession.mProgress.setNull();
13309 }
13310
13311 /* Create the progress object the client will use to wait until
13312 * #checkForDeath() is called to uninitialize this session object after
13313 * it releases the IPC semaphore.
13314 * Note! Because we're "reusing" mProgress here, this must be a proxy
13315 * object just like for LaunchVMProcess. */
13316 Assert(mData->mSession.mProgress.isNull());
13317 ComObjPtr<ProgressProxy> progress;
13318 progress.createObject();
13319 ComPtr<IUnknown> pPeer(mPeer);
13320 progress->init(mParent, pPeer,
13321 Bstr(tr("Closing session")).raw(),
13322 FALSE /* aCancelable */);
13323 progress.queryInterfaceTo(aProgress.asOutParam());
13324 mData->mSession.mProgress = progress;
13325 }
13326 else
13327 {
13328 /* the remote session is being normally closed */
13329 Data::Session::RemoteControlList::iterator it =
13330 mData->mSession.mRemoteControls.begin();
13331 while (it != mData->mSession.mRemoteControls.end())
13332 {
13333 if (control == *it)
13334 break;
13335 ++it;
13336 }
13337 BOOL found = it != mData->mSession.mRemoteControls.end();
13338 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13339 E_INVALIDARG);
13340 // This MUST be erase(it), not remove(*it) as the latter triggers a
13341 // very nasty use after free due to the place where the value "lives".
13342 mData->mSession.mRemoteControls.erase(it);
13343 }
13344
13345 /* signal the client watcher thread, because the client is going away */
13346 mParent->i_updateClientWatcher();
13347
13348 LogFlowThisFuncLeave();
13349 return S_OK;
13350}
13351
13352HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13353 std::vector<com::Utf8Str> &aValues,
13354 std::vector<LONG64> &aTimestamps,
13355 std::vector<com::Utf8Str> &aFlags)
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359#ifdef VBOX_WITH_GUEST_PROPS
13360 using namespace guestProp;
13361
13362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13363
13364 size_t cEntries = mHWData->mGuestProperties.size();
13365 aNames.resize(cEntries);
13366 aValues.resize(cEntries);
13367 aTimestamps.resize(cEntries);
13368 aFlags.resize(cEntries);
13369
13370 size_t i = 0;
13371 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13372 it != mHWData->mGuestProperties.end();
13373 ++it, ++i)
13374 {
13375 char szFlags[MAX_FLAGS_LEN + 1];
13376 aNames[i] = it->first;
13377 aValues[i] = it->second.strValue;
13378 aTimestamps[i] = it->second.mTimestamp;
13379
13380 /* If it is NULL, keep it NULL. */
13381 if (it->second.mFlags)
13382 {
13383 writeFlags(it->second.mFlags, szFlags);
13384 aFlags[i] = szFlags;
13385 }
13386 else
13387 aFlags[i] = "";
13388 }
13389 return S_OK;
13390#else
13391 ReturnComNotImplemented();
13392#endif
13393}
13394
13395HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13396 const com::Utf8Str &aValue,
13397 LONG64 aTimestamp,
13398 const com::Utf8Str &aFlags,
13399 BOOL *aNotify)
13400{
13401 LogFlowThisFunc(("\n"));
13402
13403#ifdef VBOX_WITH_GUEST_PROPS
13404 using namespace guestProp;
13405
13406 *aNotify = FALSE;
13407
13408 try
13409 {
13410 /*
13411 * Convert input up front.
13412 */
13413 uint32_t fFlags = NILFLAG;
13414 if (aFlags.length())
13415 {
13416 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13417 AssertRCReturn(vrc, E_INVALIDARG);
13418 }
13419
13420 /*
13421 * Now grab the object lock, validate the state and do the update.
13422 */
13423
13424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13425
13426 switch (mData->mMachineState)
13427 {
13428 case MachineState_Paused:
13429 case MachineState_Running:
13430 case MachineState_Teleporting:
13431 case MachineState_TeleportingPausedVM:
13432 case MachineState_OnlineSnapshotting:
13433 case MachineState_LiveSnapshotting:
13434 case MachineState_DeletingSnapshotOnline:
13435 case MachineState_DeletingSnapshotPaused:
13436 case MachineState_Saving:
13437 case MachineState_Stopping:
13438 break;
13439
13440 default:
13441 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13442 VBOX_E_INVALID_VM_STATE);
13443 }
13444
13445 i_setModified(IsModified_MachineData);
13446 mHWData.backup();
13447
13448 bool fDelete = !aValue.length();
13449 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13450 if (it != mHWData->mGuestProperties.end())
13451 {
13452 if (!fDelete)
13453 {
13454 it->second.strValue = aValue;
13455 it->second.mTimestamp = aTimestamp;
13456 it->second.mFlags = fFlags;
13457 }
13458 else
13459 mHWData->mGuestProperties.erase(it);
13460
13461 mData->mGuestPropertiesModified = TRUE;
13462 }
13463 else if (!fDelete)
13464 {
13465 HWData::GuestProperty prop;
13466 prop.strValue = aValue;
13467 prop.mTimestamp = aTimestamp;
13468 prop.mFlags = fFlags;
13469
13470 mHWData->mGuestProperties[aName] = prop;
13471 mData->mGuestPropertiesModified = TRUE;
13472 }
13473
13474 /*
13475 * Send a callback notification if appropriate
13476 */
13477 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13478 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13479 RTSTR_MAX,
13480 aName.c_str(),
13481 RTSTR_MAX, NULL)
13482 )
13483 {
13484 alock.release();
13485
13486 mParent->i_onGuestPropertyChange(mData->mUuid,
13487 Bstr(aName).raw(),
13488 Bstr(aValue).raw(),
13489 Bstr(aFlags).raw());
13490 *aNotify = TRUE;
13491 }
13492 }
13493 catch (...)
13494 {
13495 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13496 }
13497 return S_OK;
13498#else
13499 ReturnComNotImplemented();
13500#endif
13501}
13502
13503
13504HRESULT SessionMachine::lockMedia()
13505{
13506 AutoMultiWriteLock2 alock(this->lockHandle(),
13507 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13508
13509 AssertReturn( mData->mMachineState == MachineState_Starting
13510 || mData->mMachineState == MachineState_Restoring
13511 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13512
13513 clearError();
13514 alock.release();
13515 return i_lockMedia();
13516}
13517
13518HRESULT SessionMachine::unlockMedia()
13519{
13520 HRESULT hrc = i_unlockMedia();
13521 return hrc;
13522}
13523
13524HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13525 ComPtr<IMediumAttachment> &aNewAttachment)
13526{
13527 // request the host lock first, since might be calling Host methods for getting host drives;
13528 // next, protect the media tree all the while we're in here, as well as our member variables
13529 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13530 this->lockHandle(),
13531 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13532
13533 IMediumAttachment *iAttach = aAttachment;
13534 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13535
13536 Bstr ctrlName;
13537 LONG lPort;
13538 LONG lDevice;
13539 bool fTempEject;
13540 {
13541 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13542
13543 /* Need to query the details first, as the IMediumAttachment reference
13544 * might be to the original settings, which we are going to change. */
13545 ctrlName = pAttach->i_getControllerName();
13546 lPort = pAttach->i_getPort();
13547 lDevice = pAttach->i_getDevice();
13548 fTempEject = pAttach->i_getTempEject();
13549 }
13550
13551 if (!fTempEject)
13552 {
13553 /* Remember previously mounted medium. The medium before taking the
13554 * backup is not necessarily the same thing. */
13555 ComObjPtr<Medium> oldmedium;
13556 oldmedium = pAttach->i_getMedium();
13557
13558 i_setModified(IsModified_Storage);
13559 mMediaData.backup();
13560
13561 // The backup operation makes the pAttach reference point to the
13562 // old settings. Re-get the correct reference.
13563 pAttach = i_findAttachment(mMediaData->mAttachments,
13564 ctrlName.raw(),
13565 lPort,
13566 lDevice);
13567
13568 {
13569 AutoCaller autoAttachCaller(this);
13570 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13571
13572 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13573 if (!oldmedium.isNull())
13574 oldmedium->i_removeBackReference(mData->mUuid);
13575
13576 pAttach->i_updateMedium(NULL);
13577 pAttach->i_updateEjected();
13578 }
13579
13580 i_setModified(IsModified_Storage);
13581 }
13582 else
13583 {
13584 {
13585 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13586 pAttach->i_updateEjected();
13587 }
13588 }
13589
13590 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13591
13592 return S_OK;
13593}
13594
13595// public methods only for internal purposes
13596/////////////////////////////////////////////////////////////////////////////
13597
13598#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13599/**
13600 * Called from the client watcher thread to check for expected or unexpected
13601 * death of the client process that has a direct session to this machine.
13602 *
13603 * On Win32 and on OS/2, this method is called only when we've got the
13604 * mutex (i.e. the client has either died or terminated normally) so it always
13605 * returns @c true (the client is terminated, the session machine is
13606 * uninitialized).
13607 *
13608 * On other platforms, the method returns @c true if the client process has
13609 * terminated normally or abnormally and the session machine was uninitialized,
13610 * and @c false if the client process is still alive.
13611 *
13612 * @note Locks this object for writing.
13613 */
13614bool SessionMachine::i_checkForDeath()
13615{
13616 Uninit::Reason reason;
13617 bool terminated = false;
13618
13619 /* Enclose autoCaller with a block because calling uninit() from under it
13620 * will deadlock. */
13621 {
13622 AutoCaller autoCaller(this);
13623 if (!autoCaller.isOk())
13624 {
13625 /* return true if not ready, to cause the client watcher to exclude
13626 * the corresponding session from watching */
13627 LogFlowThisFunc(("Already uninitialized!\n"));
13628 return true;
13629 }
13630
13631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13632
13633 /* Determine the reason of death: if the session state is Closing here,
13634 * everything is fine. Otherwise it means that the client did not call
13635 * OnSessionEnd() before it released the IPC semaphore. This may happen
13636 * either because the client process has abnormally terminated, or
13637 * because it simply forgot to call ISession::Close() before exiting. We
13638 * threat the latter also as an abnormal termination (see
13639 * Session::uninit() for details). */
13640 reason = mData->mSession.mState == SessionState_Unlocking ?
13641 Uninit::Normal :
13642 Uninit::Abnormal;
13643
13644 if (mClientToken)
13645 terminated = mClientToken->release();
13646 } /* AutoCaller block */
13647
13648 if (terminated)
13649 uninit(reason);
13650
13651 return terminated;
13652}
13653
13654void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13655{
13656 LogFlowThisFunc(("\n"));
13657
13658 strTokenId.setNull();
13659
13660 AutoCaller autoCaller(this);
13661 AssertComRCReturnVoid(autoCaller.rc());
13662
13663 Assert(mClientToken);
13664 if (mClientToken)
13665 mClientToken->getId(strTokenId);
13666}
13667#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13668IToken *SessionMachine::i_getToken()
13669{
13670 LogFlowThisFunc(("\n"));
13671
13672 AutoCaller autoCaller(this);
13673 AssertComRCReturn(autoCaller.rc(), NULL);
13674
13675 Assert(mClientToken);
13676 if (mClientToken)
13677 return mClientToken->getToken();
13678 else
13679 return NULL;
13680}
13681#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13682
13683Machine::ClientToken *SessionMachine::i_getClientToken()
13684{
13685 LogFlowThisFunc(("\n"));
13686
13687 AutoCaller autoCaller(this);
13688 AssertComRCReturn(autoCaller.rc(), NULL);
13689
13690 return mClientToken;
13691}
13692
13693
13694/**
13695 * @note Locks this object for reading.
13696 */
13697HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13698{
13699 LogFlowThisFunc(("\n"));
13700
13701 AutoCaller autoCaller(this);
13702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13703
13704 ComPtr<IInternalSessionControl> directControl;
13705 {
13706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13707 if (mData->mSession.mLockType == LockType_VM)
13708 directControl = mData->mSession.mDirectControl;
13709 }
13710
13711 /* ignore notifications sent after #OnSessionEnd() is called */
13712 if (!directControl)
13713 return S_OK;
13714
13715 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13716}
13717
13718/**
13719 * @note Locks this object for reading.
13720 */
13721HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13722 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13723 IN_BSTR aGuestIp, LONG aGuestPort)
13724{
13725 LogFlowThisFunc(("\n"));
13726
13727 AutoCaller autoCaller(this);
13728 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13729
13730 ComPtr<IInternalSessionControl> directControl;
13731 {
13732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13733 if (mData->mSession.mLockType == LockType_VM)
13734 directControl = mData->mSession.mDirectControl;
13735 }
13736
13737 /* ignore notifications sent after #OnSessionEnd() is called */
13738 if (!directControl)
13739 return S_OK;
13740 /*
13741 * instead acting like callback we ask IVirtualBox deliver corresponding event
13742 */
13743
13744 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13745 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13746 return S_OK;
13747}
13748
13749/**
13750 * @note Locks this object for reading.
13751 */
13752HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13753{
13754 LogFlowThisFunc(("\n"));
13755
13756 AutoCaller autoCaller(this);
13757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13758
13759 ComPtr<IInternalSessionControl> directControl;
13760 {
13761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13762 if (mData->mSession.mLockType == LockType_VM)
13763 directControl = mData->mSession.mDirectControl;
13764 }
13765
13766 /* ignore notifications sent after #OnSessionEnd() is called */
13767 if (!directControl)
13768 return S_OK;
13769
13770 return directControl->OnSerialPortChange(serialPort);
13771}
13772
13773/**
13774 * @note Locks this object for reading.
13775 */
13776HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13777{
13778 LogFlowThisFunc(("\n"));
13779
13780 AutoCaller autoCaller(this);
13781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13782
13783 ComPtr<IInternalSessionControl> directControl;
13784 {
13785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13786 if (mData->mSession.mLockType == LockType_VM)
13787 directControl = mData->mSession.mDirectControl;
13788 }
13789
13790 /* ignore notifications sent after #OnSessionEnd() is called */
13791 if (!directControl)
13792 return S_OK;
13793
13794 return directControl->OnParallelPortChange(parallelPort);
13795}
13796
13797/**
13798 * @note Locks this object for reading.
13799 */
13800HRESULT SessionMachine::i_onStorageControllerChange()
13801{
13802 LogFlowThisFunc(("\n"));
13803
13804 AutoCaller autoCaller(this);
13805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13806
13807 ComPtr<IInternalSessionControl> directControl;
13808 {
13809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13810 if (mData->mSession.mLockType == LockType_VM)
13811 directControl = mData->mSession.mDirectControl;
13812 }
13813
13814 /* ignore notifications sent after #OnSessionEnd() is called */
13815 if (!directControl)
13816 return S_OK;
13817
13818 return directControl->OnStorageControllerChange();
13819}
13820
13821/**
13822 * @note Locks this object for reading.
13823 */
13824HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13825{
13826 LogFlowThisFunc(("\n"));
13827
13828 AutoCaller autoCaller(this);
13829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13830
13831 ComPtr<IInternalSessionControl> directControl;
13832 {
13833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13834 if (mData->mSession.mLockType == LockType_VM)
13835 directControl = mData->mSession.mDirectControl;
13836 }
13837
13838 /* ignore notifications sent after #OnSessionEnd() is called */
13839 if (!directControl)
13840 return S_OK;
13841
13842 return directControl->OnMediumChange(aAttachment, aForce);
13843}
13844
13845/**
13846 * @note Locks this object for reading.
13847 */
13848HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13849{
13850 LogFlowThisFunc(("\n"));
13851
13852 AutoCaller autoCaller(this);
13853 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13854
13855 ComPtr<IInternalSessionControl> directControl;
13856 {
13857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13858 if (mData->mSession.mLockType == LockType_VM)
13859 directControl = mData->mSession.mDirectControl;
13860 }
13861
13862 /* ignore notifications sent after #OnSessionEnd() is called */
13863 if (!directControl)
13864 return S_OK;
13865
13866 return directControl->OnCPUChange(aCPU, aRemove);
13867}
13868
13869HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13870{
13871 LogFlowThisFunc(("\n"));
13872
13873 AutoCaller autoCaller(this);
13874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13875
13876 ComPtr<IInternalSessionControl> directControl;
13877 {
13878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13879 if (mData->mSession.mLockType == LockType_VM)
13880 directControl = mData->mSession.mDirectControl;
13881 }
13882
13883 /* ignore notifications sent after #OnSessionEnd() is called */
13884 if (!directControl)
13885 return S_OK;
13886
13887 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13888}
13889
13890/**
13891 * @note Locks this object for reading.
13892 */
13893HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13894{
13895 LogFlowThisFunc(("\n"));
13896
13897 AutoCaller autoCaller(this);
13898 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13899
13900 ComPtr<IInternalSessionControl> directControl;
13901 {
13902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13903 if (mData->mSession.mLockType == LockType_VM)
13904 directControl = mData->mSession.mDirectControl;
13905 }
13906
13907 /* ignore notifications sent after #OnSessionEnd() is called */
13908 if (!directControl)
13909 return S_OK;
13910
13911 return directControl->OnVRDEServerChange(aRestart);
13912}
13913
13914/**
13915 * @note Locks this object for reading.
13916 */
13917HRESULT SessionMachine::i_onVideoCaptureChange()
13918{
13919 LogFlowThisFunc(("\n"));
13920
13921 AutoCaller autoCaller(this);
13922 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13923
13924 ComPtr<IInternalSessionControl> directControl;
13925 {
13926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13927 if (mData->mSession.mLockType == LockType_VM)
13928 directControl = mData->mSession.mDirectControl;
13929 }
13930
13931 /* ignore notifications sent after #OnSessionEnd() is called */
13932 if (!directControl)
13933 return S_OK;
13934
13935 return directControl->OnVideoCaptureChange();
13936}
13937
13938/**
13939 * @note Locks this object for reading.
13940 */
13941HRESULT SessionMachine::i_onUSBControllerChange()
13942{
13943 LogFlowThisFunc(("\n"));
13944
13945 AutoCaller autoCaller(this);
13946 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13947
13948 ComPtr<IInternalSessionControl> directControl;
13949 {
13950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13951 if (mData->mSession.mLockType == LockType_VM)
13952 directControl = mData->mSession.mDirectControl;
13953 }
13954
13955 /* ignore notifications sent after #OnSessionEnd() is called */
13956 if (!directControl)
13957 return S_OK;
13958
13959 return directControl->OnUSBControllerChange();
13960}
13961
13962/**
13963 * @note Locks this object for reading.
13964 */
13965HRESULT SessionMachine::i_onSharedFolderChange()
13966{
13967 LogFlowThisFunc(("\n"));
13968
13969 AutoCaller autoCaller(this);
13970 AssertComRCReturnRC(autoCaller.rc());
13971
13972 ComPtr<IInternalSessionControl> directControl;
13973 {
13974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13975 if (mData->mSession.mLockType == LockType_VM)
13976 directControl = mData->mSession.mDirectControl;
13977 }
13978
13979 /* ignore notifications sent after #OnSessionEnd() is called */
13980 if (!directControl)
13981 return S_OK;
13982
13983 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13984}
13985
13986/**
13987 * @note Locks this object for reading.
13988 */
13989HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13990{
13991 LogFlowThisFunc(("\n"));
13992
13993 AutoCaller autoCaller(this);
13994 AssertComRCReturnRC(autoCaller.rc());
13995
13996 ComPtr<IInternalSessionControl> directControl;
13997 {
13998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13999 if (mData->mSession.mLockType == LockType_VM)
14000 directControl = mData->mSession.mDirectControl;
14001 }
14002
14003 /* ignore notifications sent after #OnSessionEnd() is called */
14004 if (!directControl)
14005 return S_OK;
14006
14007 return directControl->OnClipboardModeChange(aClipboardMode);
14008}
14009
14010/**
14011 * @note Locks this object for reading.
14012 */
14013HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14014{
14015 LogFlowThisFunc(("\n"));
14016
14017 AutoCaller autoCaller(this);
14018 AssertComRCReturnRC(autoCaller.rc());
14019
14020 ComPtr<IInternalSessionControl> directControl;
14021 {
14022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14023 if (mData->mSession.mLockType == LockType_VM)
14024 directControl = mData->mSession.mDirectControl;
14025 }
14026
14027 /* ignore notifications sent after #OnSessionEnd() is called */
14028 if (!directControl)
14029 return S_OK;
14030
14031 return directControl->OnDnDModeChange(aDnDMode);
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 if (mData->mSession.mLockType == LockType_VM)
14048 directControl = mData->mSession.mDirectControl;
14049 }
14050
14051 /* ignore notifications sent after #OnSessionEnd() is called */
14052 if (!directControl)
14053 return S_OK;
14054
14055 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14056}
14057
14058/**
14059 * @note Locks this object for reading.
14060 */
14061HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14062{
14063 LogFlowThisFunc(("\n"));
14064
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14067
14068 ComPtr<IInternalSessionControl> directControl;
14069 {
14070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14071 if (mData->mSession.mLockType == LockType_VM)
14072 directControl = mData->mSession.mDirectControl;
14073 }
14074
14075 /* ignore notifications sent after #OnSessionEnd() is called */
14076 if (!directControl)
14077 return S_OK;
14078
14079 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14080}
14081
14082/**
14083 * Returns @c true if this machine's USB controller reports it has a matching
14084 * filter for the given USB device and @c false otherwise.
14085 *
14086 * @note locks this object for reading.
14087 */
14088bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14089{
14090 AutoCaller autoCaller(this);
14091 /* silently return if not ready -- this method may be called after the
14092 * direct machine session has been called */
14093 if (!autoCaller.isOk())
14094 return false;
14095
14096#ifdef VBOX_WITH_USB
14097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14098
14099 switch (mData->mMachineState)
14100 {
14101 case MachineState_Starting:
14102 case MachineState_Restoring:
14103 case MachineState_TeleportingIn:
14104 case MachineState_Paused:
14105 case MachineState_Running:
14106 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14107 * elsewhere... */
14108 alock.release();
14109 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14110 default: break;
14111 }
14112#else
14113 NOREF(aDevice);
14114 NOREF(aMaskedIfs);
14115#endif
14116 return false;
14117}
14118
14119/**
14120 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14121 */
14122HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14123 IVirtualBoxErrorInfo *aError,
14124 ULONG aMaskedIfs,
14125 const com::Utf8Str &aCaptureFilename)
14126{
14127 LogFlowThisFunc(("\n"));
14128
14129 AutoCaller autoCaller(this);
14130
14131 /* This notification may happen after the machine object has been
14132 * uninitialized (the session was closed), so don't assert. */
14133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14134
14135 ComPtr<IInternalSessionControl> directControl;
14136 {
14137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14138 if (mData->mSession.mLockType == LockType_VM)
14139 directControl = mData->mSession.mDirectControl;
14140 }
14141
14142 /* fail on notifications sent after #OnSessionEnd() is called, it is
14143 * expected by the caller */
14144 if (!directControl)
14145 return E_FAIL;
14146
14147 /* No locks should be held at this point. */
14148 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14149 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14150
14151 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14152}
14153
14154/**
14155 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14156 */
14157HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14158 IVirtualBoxErrorInfo *aError)
14159{
14160 LogFlowThisFunc(("\n"));
14161
14162 AutoCaller autoCaller(this);
14163
14164 /* This notification may happen after the machine object has been
14165 * uninitialized (the session was closed), so don't assert. */
14166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* fail on notifications sent after #OnSessionEnd() is called, it is
14176 * expected by the caller */
14177 if (!directControl)
14178 return E_FAIL;
14179
14180 /* No locks should be held at this point. */
14181 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14182 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14183
14184 return directControl->OnUSBDeviceDetach(aId, aError);
14185}
14186
14187// protected methods
14188/////////////////////////////////////////////////////////////////////////////
14189
14190/**
14191 * Deletes the given file if it is no longer in use by either the current machine state
14192 * (if the machine is "saved") or any of the machine's snapshots.
14193 *
14194 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14195 * but is different for each SnapshotMachine. When calling this, the order of calling this
14196 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14197 * is therefore critical. I know, it's all rather messy.
14198 *
14199 * @param strStateFile
14200 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14201 * the test for whether the saved state file is in use.
14202 */
14203void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14204 Snapshot *pSnapshotToIgnore)
14205{
14206 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14207 if ( (strStateFile.isNotEmpty())
14208 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14209 )
14210 // ... and it must also not be shared with other snapshots
14211 if ( !mData->mFirstSnapshot
14212 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14213 // this checks the SnapshotMachine's state file paths
14214 )
14215 RTFileDelete(strStateFile.c_str());
14216}
14217
14218/**
14219 * Locks the attached media.
14220 *
14221 * All attached hard disks are locked for writing and DVD/floppy are locked for
14222 * reading. Parents of attached hard disks (if any) are locked for reading.
14223 *
14224 * This method also performs accessibility check of all media it locks: if some
14225 * media is inaccessible, the method will return a failure and a bunch of
14226 * extended error info objects per each inaccessible medium.
14227 *
14228 * Note that this method is atomic: if it returns a success, all media are
14229 * locked as described above; on failure no media is locked at all (all
14230 * succeeded individual locks will be undone).
14231 *
14232 * The caller is responsible for doing the necessary state sanity checks.
14233 *
14234 * The locks made by this method must be undone by calling #unlockMedia() when
14235 * no more needed.
14236 */
14237HRESULT SessionMachine::i_lockMedia()
14238{
14239 AutoCaller autoCaller(this);
14240 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14241
14242 AutoMultiWriteLock2 alock(this->lockHandle(),
14243 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14244
14245 /* bail out if trying to lock things with already set up locking */
14246 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14247
14248 MultiResult mrc(S_OK);
14249
14250 /* Collect locking information for all medium objects attached to the VM. */
14251 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14252 it != mMediaData->mAttachments.end();
14253 ++it)
14254 {
14255 MediumAttachment* pAtt = *it;
14256 DeviceType_T devType = pAtt->i_getType();
14257 Medium *pMedium = pAtt->i_getMedium();
14258
14259 MediumLockList *pMediumLockList(new MediumLockList());
14260 // There can be attachments without a medium (floppy/dvd), and thus
14261 // it's impossible to create a medium lock list. It still makes sense
14262 // to have the empty medium lock list in the map in case a medium is
14263 // attached later.
14264 if (pMedium != NULL)
14265 {
14266 MediumType_T mediumType = pMedium->i_getType();
14267 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14268 || mediumType == MediumType_Shareable;
14269 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14270
14271 alock.release();
14272 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14273 !fIsReadOnlyLock /* fMediumLockWrite */,
14274 false /* fMediumLockWriteAll */,
14275 NULL,
14276 *pMediumLockList);
14277 alock.acquire();
14278 if (FAILED(mrc))
14279 {
14280 delete pMediumLockList;
14281 mData->mSession.mLockedMedia.Clear();
14282 break;
14283 }
14284 }
14285
14286 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14287 if (FAILED(rc))
14288 {
14289 mData->mSession.mLockedMedia.Clear();
14290 mrc = setError(rc,
14291 tr("Collecting locking information for all attached media failed"));
14292 break;
14293 }
14294 }
14295
14296 if (SUCCEEDED(mrc))
14297 {
14298 /* Now lock all media. If this fails, nothing is locked. */
14299 alock.release();
14300 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14301 alock.acquire();
14302 if (FAILED(rc))
14303 {
14304 mrc = setError(rc,
14305 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14306 }
14307 }
14308
14309 return mrc;
14310}
14311
14312/**
14313 * Undoes the locks made by by #lockMedia().
14314 */
14315HRESULT SessionMachine::i_unlockMedia()
14316{
14317 AutoCaller autoCaller(this);
14318 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14319
14320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14321
14322 /* we may be holding important error info on the current thread;
14323 * preserve it */
14324 ErrorInfoKeeper eik;
14325
14326 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14327 AssertComRC(rc);
14328 return rc;
14329}
14330
14331/**
14332 * Helper to change the machine state (reimplementation).
14333 *
14334 * @note Locks this object for writing.
14335 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14336 * it can cause crashes in random places due to unexpectedly committing
14337 * the current settings. The caller is responsible for that. The call
14338 * to saveStateSettings is fine, because this method does not commit.
14339 */
14340HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14341{
14342 LogFlowThisFuncEnter();
14343 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14344
14345 AutoCaller autoCaller(this);
14346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14347
14348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14349
14350 MachineState_T oldMachineState = mData->mMachineState;
14351
14352 AssertMsgReturn(oldMachineState != aMachineState,
14353 ("oldMachineState=%s, aMachineState=%s\n",
14354 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14355 E_FAIL);
14356
14357 HRESULT rc = S_OK;
14358
14359 int stsFlags = 0;
14360 bool deleteSavedState = false;
14361
14362 /* detect some state transitions */
14363
14364 if ( ( oldMachineState == MachineState_Saved
14365 && aMachineState == MachineState_Restoring)
14366 || ( ( oldMachineState == MachineState_PoweredOff
14367 || oldMachineState == MachineState_Teleported
14368 || oldMachineState == MachineState_Aborted
14369 )
14370 && ( aMachineState == MachineState_TeleportingIn
14371 || aMachineState == MachineState_Starting
14372 )
14373 )
14374 )
14375 {
14376 /* The EMT thread is about to start */
14377
14378 /* Nothing to do here for now... */
14379
14380 /// @todo NEWMEDIA don't let mDVDDrive and other children
14381 /// change anything when in the Starting/Restoring state
14382 }
14383 else if ( ( oldMachineState == MachineState_Running
14384 || oldMachineState == MachineState_Paused
14385 || oldMachineState == MachineState_Teleporting
14386 || oldMachineState == MachineState_OnlineSnapshotting
14387 || oldMachineState == MachineState_LiveSnapshotting
14388 || oldMachineState == MachineState_Stuck
14389 || oldMachineState == MachineState_Starting
14390 || oldMachineState == MachineState_Stopping
14391 || oldMachineState == MachineState_Saving
14392 || oldMachineState == MachineState_Restoring
14393 || oldMachineState == MachineState_TeleportingPausedVM
14394 || oldMachineState == MachineState_TeleportingIn
14395 )
14396 && ( aMachineState == MachineState_PoweredOff
14397 || aMachineState == MachineState_Saved
14398 || aMachineState == MachineState_Teleported
14399 || aMachineState == MachineState_Aborted
14400 )
14401 )
14402 {
14403 /* The EMT thread has just stopped, unlock attached media. Note that as
14404 * opposed to locking that is done from Console, we do unlocking here
14405 * because the VM process may have aborted before having a chance to
14406 * properly unlock all media it locked. */
14407
14408 unlockMedia();
14409 }
14410
14411 if (oldMachineState == MachineState_Restoring)
14412 {
14413 if (aMachineState != MachineState_Saved)
14414 {
14415 /*
14416 * delete the saved state file once the machine has finished
14417 * restoring from it (note that Console sets the state from
14418 * Restoring to Saved if the VM couldn't restore successfully,
14419 * to give the user an ability to fix an error and retry --
14420 * we keep the saved state file in this case)
14421 */
14422 deleteSavedState = true;
14423 }
14424 }
14425 else if ( oldMachineState == MachineState_Saved
14426 && ( aMachineState == MachineState_PoweredOff
14427 || aMachineState == MachineState_Aborted
14428 || aMachineState == MachineState_Teleported
14429 )
14430 )
14431 {
14432 /*
14433 * delete the saved state after SessionMachine::ForgetSavedState() is called
14434 * or if the VM process (owning a direct VM session) crashed while the
14435 * VM was Saved
14436 */
14437
14438 /// @todo (dmik)
14439 // Not sure that deleting the saved state file just because of the
14440 // client death before it attempted to restore the VM is a good
14441 // thing. But when it crashes we need to go to the Aborted state
14442 // which cannot have the saved state file associated... The only
14443 // way to fix this is to make the Aborted condition not a VM state
14444 // but a bool flag: i.e., when a crash occurs, set it to true and
14445 // change the state to PoweredOff or Saved depending on the
14446 // saved state presence.
14447
14448 deleteSavedState = true;
14449 mData->mCurrentStateModified = TRUE;
14450 stsFlags |= SaveSTS_CurStateModified;
14451 }
14452
14453 if ( aMachineState == MachineState_Starting
14454 || aMachineState == MachineState_Restoring
14455 || aMachineState == MachineState_TeleportingIn
14456 )
14457 {
14458 /* set the current state modified flag to indicate that the current
14459 * state is no more identical to the state in the
14460 * current snapshot */
14461 if (!mData->mCurrentSnapshot.isNull())
14462 {
14463 mData->mCurrentStateModified = TRUE;
14464 stsFlags |= SaveSTS_CurStateModified;
14465 }
14466 }
14467
14468 if (deleteSavedState)
14469 {
14470 if (mRemoveSavedState)
14471 {
14472 Assert(!mSSData->strStateFilePath.isEmpty());
14473
14474 // it is safe to delete the saved state file if ...
14475 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14476 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14477 // ... none of the snapshots share the saved state file
14478 )
14479 RTFileDelete(mSSData->strStateFilePath.c_str());
14480 }
14481
14482 mSSData->strStateFilePath.setNull();
14483 stsFlags |= SaveSTS_StateFilePath;
14484 }
14485
14486 /* redirect to the underlying peer machine */
14487 mPeer->i_setMachineState(aMachineState);
14488
14489 if ( aMachineState == MachineState_PoweredOff
14490 || aMachineState == MachineState_Teleported
14491 || aMachineState == MachineState_Aborted
14492 || aMachineState == MachineState_Saved)
14493 {
14494 /* the machine has stopped execution
14495 * (or the saved state file was adopted) */
14496 stsFlags |= SaveSTS_StateTimeStamp;
14497 }
14498
14499 if ( ( oldMachineState == MachineState_PoweredOff
14500 || oldMachineState == MachineState_Aborted
14501 || oldMachineState == MachineState_Teleported
14502 )
14503 && aMachineState == MachineState_Saved)
14504 {
14505 /* the saved state file was adopted */
14506 Assert(!mSSData->strStateFilePath.isEmpty());
14507 stsFlags |= SaveSTS_StateFilePath;
14508 }
14509
14510#ifdef VBOX_WITH_GUEST_PROPS
14511 if ( aMachineState == MachineState_PoweredOff
14512 || aMachineState == MachineState_Aborted
14513 || aMachineState == MachineState_Teleported)
14514 {
14515 /* Make sure any transient guest properties get removed from the
14516 * property store on shutdown. */
14517 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14518
14519 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14520 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14521 while (it != llGuestProperties.end())
14522 {
14523 const settings::GuestProperty &prop = *it;
14524 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14525 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14526 {
14527 it = llGuestProperties.erase(it);
14528 fNeedsSaving = true;
14529 }
14530 else
14531 {
14532 ++it;
14533 }
14534 }
14535
14536 if (fNeedsSaving)
14537 {
14538 mData->mCurrentStateModified = TRUE;
14539 stsFlags |= SaveSTS_CurStateModified;
14540 }
14541 }
14542#endif /* VBOX_WITH_GUEST_PROPS */
14543
14544 rc = i_saveStateSettings(stsFlags);
14545
14546 if ( ( oldMachineState != MachineState_PoweredOff
14547 && oldMachineState != MachineState_Aborted
14548 && oldMachineState != MachineState_Teleported
14549 )
14550 && ( aMachineState == MachineState_PoweredOff
14551 || aMachineState == MachineState_Aborted
14552 || aMachineState == MachineState_Teleported
14553 )
14554 )
14555 {
14556 /* we've been shut down for any reason */
14557 /* no special action so far */
14558 }
14559
14560 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14561 LogFlowThisFuncLeave();
14562 return rc;
14563}
14564
14565/**
14566 * Sends the current machine state value to the VM process.
14567 *
14568 * @note Locks this object for reading, then calls a client process.
14569 */
14570HRESULT SessionMachine::i_updateMachineStateOnClient()
14571{
14572 AutoCaller autoCaller(this);
14573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14574
14575 ComPtr<IInternalSessionControl> directControl;
14576 {
14577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14578 AssertReturn(!!mData, E_FAIL);
14579 if (mData->mSession.mLockType == LockType_VM)
14580 directControl = mData->mSession.mDirectControl;
14581
14582 /* directControl may be already set to NULL here in #OnSessionEnd()
14583 * called too early by the direct session process while there is still
14584 * some operation (like deleting the snapshot) in progress. The client
14585 * process in this case is waiting inside Session::close() for the
14586 * "end session" process object to complete, while #uninit() called by
14587 * #checkForDeath() on the Watcher thread is waiting for the pending
14588 * operation to complete. For now, we accept this inconsistent behavior
14589 * and simply do nothing here. */
14590
14591 if (mData->mSession.mState == SessionState_Unlocking)
14592 return S_OK;
14593 }
14594
14595 /* ignore notifications sent after #OnSessionEnd() is called */
14596 if (!directControl)
14597 return S_OK;
14598
14599 return directControl->UpdateMachineState(mData->mMachineState);
14600}
14601
14602
14603/**
14604 * Static Machine method that can get passed to RTThreadCreate to
14605 * have a thread started for a Task. See Machine::Task.
14606 */
14607/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14608{
14609 AssertReturn(pvUser, VERR_INVALID_POINTER);
14610
14611 Task *pTask = static_cast<Task *>(pvUser);
14612 pTask->handler();
14613 /** @todo r=klaus it would be safer to update the progress object here,
14614 * as it avoids possible races due to scoping issues/tricks in the handler */
14615 // it's our responsibility to delete the task
14616 delete pTask;
14617
14618 return 0;
14619}
14620
14621/*static*/
14622HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14623{
14624 va_list args;
14625 va_start(args, pcszMsg);
14626 HRESULT rc = setErrorInternal(aResultCode,
14627 getStaticClassIID(),
14628 getStaticComponentName(),
14629 Utf8Str(pcszMsg, args),
14630 false /* aWarning */,
14631 true /* aLogIt */);
14632 va_end(args);
14633 return rc;
14634}
14635
14636
14637HRESULT Machine::updateState(MachineState_T aState)
14638{
14639 NOREF(aState);
14640 ReturnComNotImplemented();
14641}
14642
14643HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14644{
14645 NOREF(aProgress);
14646 ReturnComNotImplemented();
14647}
14648
14649HRESULT Machine::endPowerUp(LONG aResult)
14650{
14651 NOREF(aResult);
14652 ReturnComNotImplemented();
14653}
14654
14655HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14656{
14657 NOREF(aProgress);
14658 ReturnComNotImplemented();
14659}
14660
14661HRESULT Machine::endPoweringDown(LONG aResult,
14662 const com::Utf8Str &aErrMsg)
14663{
14664 NOREF(aResult);
14665 NOREF(aErrMsg);
14666 ReturnComNotImplemented();
14667}
14668
14669HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14670 BOOL *aMatched,
14671 ULONG *aMaskedInterfaces)
14672{
14673 NOREF(aDevice);
14674 NOREF(aMatched);
14675 NOREF(aMaskedInterfaces);
14676 ReturnComNotImplemented();
14677
14678}
14679
14680HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14681{
14682 NOREF(aId); NOREF(aCaptureFilename);
14683 ReturnComNotImplemented();
14684}
14685
14686HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14687 BOOL aDone)
14688{
14689 NOREF(aId);
14690 NOREF(aDone);
14691 ReturnComNotImplemented();
14692}
14693
14694HRESULT Machine::autoCaptureUSBDevices()
14695{
14696 ReturnComNotImplemented();
14697}
14698
14699HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14700{
14701 NOREF(aDone);
14702 ReturnComNotImplemented();
14703}
14704
14705HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14706 ComPtr<IProgress> &aProgress)
14707{
14708 NOREF(aSession);
14709 NOREF(aProgress);
14710 ReturnComNotImplemented();
14711}
14712
14713HRESULT Machine::finishOnlineMergeMedium()
14714{
14715 ReturnComNotImplemented();
14716}
14717
14718HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14719 std::vector<com::Utf8Str> &aValues,
14720 std::vector<LONG64> &aTimestamps,
14721 std::vector<com::Utf8Str> &aFlags)
14722{
14723 NOREF(aNames);
14724 NOREF(aValues);
14725 NOREF(aTimestamps);
14726 NOREF(aFlags);
14727 ReturnComNotImplemented();
14728}
14729
14730HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14731 const com::Utf8Str &aValue,
14732 LONG64 aTimestamp,
14733 const com::Utf8Str &aFlags,
14734 BOOL *aNotify)
14735{
14736 NOREF(aName);
14737 NOREF(aValue);
14738 NOREF(aTimestamp);
14739 NOREF(aFlags);
14740 NOREF(aNotify);
14741 ReturnComNotImplemented();
14742}
14743
14744HRESULT Machine::lockMedia()
14745{
14746 ReturnComNotImplemented();
14747}
14748
14749HRESULT Machine::unlockMedia()
14750{
14751 ReturnComNotImplemented();
14752}
14753
14754HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14755 ComPtr<IMediumAttachment> &aNewAttachment)
14756{
14757 NOREF(aAttachment);
14758 NOREF(aNewAttachment);
14759 ReturnComNotImplemented();
14760}
14761
14762HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14763 ULONG aCpuUser,
14764 ULONG aCpuKernel,
14765 ULONG aCpuIdle,
14766 ULONG aMemTotal,
14767 ULONG aMemFree,
14768 ULONG aMemBalloon,
14769 ULONG aMemShared,
14770 ULONG aMemCache,
14771 ULONG aPagedTotal,
14772 ULONG aMemAllocTotal,
14773 ULONG aMemFreeTotal,
14774 ULONG aMemBalloonTotal,
14775 ULONG aMemSharedTotal,
14776 ULONG aVmNetRx,
14777 ULONG aVmNetTx)
14778{
14779 NOREF(aValidStats);
14780 NOREF(aCpuUser);
14781 NOREF(aCpuKernel);
14782 NOREF(aCpuIdle);
14783 NOREF(aMemTotal);
14784 NOREF(aMemFree);
14785 NOREF(aMemBalloon);
14786 NOREF(aMemShared);
14787 NOREF(aMemCache);
14788 NOREF(aPagedTotal);
14789 NOREF(aMemAllocTotal);
14790 NOREF(aMemFreeTotal);
14791 NOREF(aMemBalloonTotal);
14792 NOREF(aMemSharedTotal);
14793 NOREF(aVmNetRx);
14794 NOREF(aVmNetTx);
14795 ReturnComNotImplemented();
14796}
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