VirtualBox

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

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

Main/Machine+Snapshot+Medium: Remove bogus assertion when locking a machine for shared access. Fix a race between querying medium information and deleting the medium, which was triggered by the GUI medium thread querying the same medium which restoring a snapshot wanted to delete (timing sensitive, happened most with poweroff/restore snapshot). Additionally move the machine state update for restoring a snapshot to the end, when the operation is pretty much done. For taking a snapshot, make sure that the corresponding event is signalled after the operation completed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 508.9 KB
Line 
1/* $Id: MachineImpl.cpp 55728 2015-05-07 13:58:38Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209 mGuestPropertyNotificationPatterns = "";
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine::HDData structure
231/////////////////////////////////////////////////////////////////////////////
232
233Machine::MediaData::MediaData()
234{
235}
236
237Machine::MediaData::~MediaData()
238{
239}
240
241/////////////////////////////////////////////////////////////////////////////
242// Machine class
243/////////////////////////////////////////////////////////////////////////////
244
245// constructor / destructor
246/////////////////////////////////////////////////////////////////////////////
247
248Machine::Machine() :
249#ifdef VBOX_WITH_RESOURCE_USAGE_API
250 mCollectorGuest(NULL),
251#endif
252 mPeer(NULL),
253 mParent(NULL),
254 mSerialPorts(),
255 mParallelPorts(),
256 uRegistryNeedsSaving(0)
257{}
258
259Machine::~Machine()
260{}
261
262HRESULT Machine::FinalConstruct()
263{
264 LogFlowThisFunc(("\n"));
265 return BaseFinalConstruct();
266}
267
268void Machine::FinalRelease()
269{
270 LogFlowThisFunc(("\n"));
271 uninit();
272 BaseFinalRelease();
273}
274
275/**
276 * Initializes a new machine instance; this init() variant creates a new, empty machine.
277 * This gets called from VirtualBox::CreateMachine().
278 *
279 * @param aParent Associated parent object
280 * @param strConfigFile Local file system path to the VM settings file (can
281 * be relative to the VirtualBox config directory).
282 * @param strName name for the machine
283 * @param llGroups list of groups for the machine
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->i_applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->i_applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->i_is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360 }
361
362 /* At this point the changing of the current state modification
363 * flag is allowed. */
364 i_allowStateModification();
365
366 /* commit all changes made during the initialization */
367 i_commit();
368 }
369
370 /* Confirm a successful initialization when it's the case */
371 if (SUCCEEDED(rc))
372 {
373 if (mData->mAccessible)
374 autoInitSpan.setSucceeded();
375 else
376 autoInitSpan.setLimited();
377 }
378
379 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
380 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
381 mData->mRegistered,
382 mData->mAccessible,
383 rc));
384
385 LogFlowThisFuncLeave();
386
387 return rc;
388}
389
390/**
391 * Initializes a new instance with data from machine XML (formerly Init_Registered).
392 * Gets called in two modes:
393 *
394 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
395 * UUID is specified and we mark the machine as "registered";
396 *
397 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
398 * and the machine remains unregistered until RegisterMachine() is called.
399 *
400 * @param aParent Associated parent object
401 * @param aConfigFile Local file system path to the VM settings file (can
402 * be relative to the VirtualBox config directory).
403 * @param aId UUID of the machine or NULL (see above).
404 *
405 * @return Success indicator. if not S_OK, the machine object is invalid
406 */
407HRESULT Machine::initFromSettings(VirtualBox *aParent,
408 const Utf8Str &strConfigFile,
409 const Guid *aId)
410{
411 LogFlowThisFuncEnter();
412 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
413
414 /* Enclose the state transition NotReady->InInit->Ready */
415 AutoInitSpan autoInitSpan(this);
416 AssertReturn(autoInitSpan.isOk(), E_FAIL);
417
418 HRESULT rc = initImpl(aParent, strConfigFile);
419 if (FAILED(rc)) return rc;
420
421 if (aId)
422 {
423 // loading a registered VM:
424 unconst(mData->mUuid) = *aId;
425 mData->mRegistered = TRUE;
426 // now load the settings from XML:
427 rc = i_registeredInit();
428 // this calls initDataAndChildObjects() and loadSettings()
429 }
430 else
431 {
432 // opening an unregistered VM (VirtualBox::OpenMachine()):
433 rc = initDataAndChildObjects();
434
435 if (SUCCEEDED(rc))
436 {
437 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
438 mData->mAccessible = TRUE;
439
440 try
441 {
442 // load and parse machine XML; this will throw on XML or logic errors
443 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
444
445 // reject VM UUID duplicates, they can happen if someone
446 // tries to register an already known VM config again
447 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
448 true /* fPermitInaccessible */,
449 false /* aDoSetError */,
450 NULL) != VBOX_E_OBJECT_NOT_FOUND)
451 {
452 throw setError(E_FAIL,
453 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
454 mData->m_strConfigFile.c_str());
455 }
456
457 // use UUID from machine config
458 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
459
460 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
461 NULL /* puuidRegistry */);
462 if (FAILED(rc)) throw rc;
463
464 /* At this point the changing of the current state modification
465 * flag is allowed. */
466 i_allowStateModification();
467
468 i_commit();
469 }
470 catch (HRESULT err)
471 {
472 /* we assume that error info is set by the thrower */
473 rc = err;
474 }
475 catch (...)
476 {
477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
478 }
479 }
480 }
481
482 /* Confirm a successful initialization when it's the case */
483 if (SUCCEEDED(rc))
484 {
485 if (mData->mAccessible)
486 autoInitSpan.setSucceeded();
487 else
488 {
489 autoInitSpan.setLimited();
490
491 // uninit media from this machine's media registry, or else
492 // reloading the settings will fail
493 mParent->i_unregisterMachineMedia(i_getId());
494 }
495 }
496
497 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
498 "rc=%08X\n",
499 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
500 mData->mRegistered, mData->mAccessible, rc));
501
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507/**
508 * Initializes a new instance from a machine config that is already in memory
509 * (import OVF case). Since we are importing, the UUID in the machine
510 * config is ignored and we always generate a fresh one.
511 *
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->i_getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = i_loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 i_allowStateModification();
566
567 /* commit all changes made during the initialization */
568 i_commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 /* Ignore all errors from unregistering, they would destroy
580- * the more interesting error information we already have,
581- * pinpointing the issue with the VM config. */
582 ErrorInfoKeeper eik;
583
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->i_unregisterMachineMedia(i_getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 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
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 *aSessionState = mData->mSession.mState;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 aSessionType = mData->mSession.mType;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 *aSessionPID = mData->mSession.mPID;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getState(MachineState_T *aState)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aState = mData->mMachineState;
2707 Assert(mData->mMachineState != MachineState_Null);
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2713{
2714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2717
2718 return S_OK;
2719}
2720
2721HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2722{
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 aStateFilePath = mSSData->strStateFilePath;
2726
2727 return S_OK;
2728}
2729
2730HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2731{
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 i_getLogFolder(aLogFolder);
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aCurrentSnapshot = mData->mCurrentSnapshot;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2749{
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2753 ? 0
2754 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 /* Note: for machines with no snapshots, we always return FALSE
2764 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2765 * reasons :) */
2766
2767 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2768 ? FALSE
2769 : mData->mCurrentStateModified;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 aSharedFolders.resize(mHWData->mSharedFolders.size());
2779 size_t i = 0;
2780 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2781 it != mHWData->mSharedFolders.end(); ++i, ++it)
2782 aSharedFolders[i] = *it;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aClipboardMode = mHWData->mClipboardMode;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2797{
2798 HRESULT rc = S_OK;
2799
2800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 alock.release();
2803 rc = i_onClipboardModeChange(aClipboardMode);
2804 alock.acquire();
2805 if (FAILED(rc)) return rc;
2806
2807 i_setModified(IsModified_MachineData);
2808 mHWData.backup();
2809 mHWData->mClipboardMode = aClipboardMode;
2810
2811 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2812 if (Global::IsOnline(mData->mMachineState))
2813 i_saveSettings(NULL);
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aDnDMode = mHWData->mDnDMode;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2828{
2829 HRESULT rc = S_OK;
2830
2831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 alock.release();
2834 rc = i_onDnDModeChange(aDnDMode);
2835
2836 alock.acquire();
2837 if (FAILED(rc)) return rc;
2838
2839 i_setModified(IsModified_MachineData);
2840 mHWData.backup();
2841 mHWData->mDnDMode = aDnDMode;
2842
2843 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2844 if (Global::IsOnline(mData->mMachineState))
2845 i_saveSettings(NULL);
2846
2847 return S_OK;
2848}
2849
2850HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2851{
2852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
2854 try
2855 {
2856 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2857 }
2858 catch (...)
2859 {
2860 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2861 }
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2867{
2868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2871 if (FAILED(rc)) return rc;
2872
2873 i_setModified(IsModified_MachineData);
2874 mHWData.backup();
2875 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2876 return rc;
2877}
2878
2879HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882 StorageControllerList data = *mStorageControllers.data();
2883 size_t i = 0;
2884 aStorageControllers.resize(data.size());
2885 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2886 aStorageControllers[i] = *it;
2887 return S_OK;
2888}
2889
2890HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2891{
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aEnabled = mUserData->s.fTeleporterEnabled;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2900{
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 /* Only allow it to be set to true when PoweredOff or Aborted.
2904 (Clearing it is always permitted.) */
2905 if ( aTeleporterEnabled
2906 && mData->mRegistered
2907 && ( !i_isSessionMachine()
2908 || ( mData->mMachineState != MachineState_PoweredOff
2909 && mData->mMachineState != MachineState_Teleported
2910 && mData->mMachineState != MachineState_Aborted
2911 )
2912 )
2913 )
2914 return setError(VBOX_E_INVALID_VM_STATE,
2915 tr("The machine is not powered off (state is %s)"),
2916 Global::stringifyMachineState(mData->mMachineState));
2917
2918 i_setModified(IsModified_MachineData);
2919 mUserData.backup();
2920 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2935{
2936 if (aTeleporterPort >= _64K)
2937 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2938
2939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2942 if (FAILED(rc)) return rc;
2943
2944 i_setModified(IsModified_MachineData);
2945 mUserData.backup();
2946 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2961{
2962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2963
2964 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2965 if (FAILED(rc)) return rc;
2966
2967 i_setModified(IsModified_MachineData);
2968 mUserData.backup();
2969 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2970
2971 return S_OK;
2972}
2973
2974HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2975{
2976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2977 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2978
2979 return S_OK;
2980}
2981
2982HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2983{
2984 /*
2985 * Hash the password first.
2986 */
2987 com::Utf8Str aT = aTeleporterPassword;
2988
2989 if (!aT.isEmpty())
2990 {
2991 if (VBoxIsPasswordHashed(&aT))
2992 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2993 VBoxHashPassword(&aT);
2994 }
2995
2996 /*
2997 * Do the update.
2998 */
2999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3000 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3001 if (SUCCEEDED(hrc))
3002 {
3003 i_setModified(IsModified_MachineData);
3004 mUserData.backup();
3005 mUserData->s.strTeleporterPassword = aT;
3006 }
3007
3008 return hrc;
3009}
3010
3011HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3012{
3013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3016 return S_OK;
3017}
3018
3019HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3020{
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 /* @todo deal with running state change. */
3024 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3025 if (FAILED(rc)) return rc;
3026
3027 i_setModified(IsModified_MachineData);
3028 mUserData.backup();
3029 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3038 return S_OK;
3039}
3040
3041HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3042{
3043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 /* @todo deal with running state change. */
3046 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3047 if (FAILED(rc)) return rc;
3048
3049 i_setModified(IsModified_MachineData);
3050 mUserData.backup();
3051 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3064{
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 /* @todo deal with running state change. */
3068 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 i_setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3074 return S_OK;
3075}
3076
3077HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3078{
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3082
3083 return S_OK;
3084}
3085
3086HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3087{
3088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 /* @todo deal with running state change. */
3091 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3092 if (FAILED(rc)) return rc;
3093
3094 i_setModified(IsModified_MachineData);
3095 mUserData.backup();
3096 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3097
3098 return S_OK;
3099}
3100
3101HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3102{
3103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3106 return S_OK;
3107}
3108
3109HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3110{
3111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3112
3113 /* @todo deal with running state change. */
3114 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3115 if (FAILED(rc)) return rc;
3116
3117 i_setModified(IsModified_MachineData);
3118 mUserData.backup();
3119 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3124{
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3128
3129 return S_OK;
3130}
3131
3132HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3133{
3134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3135
3136 /* Only allow it to be set to true when PoweredOff or Aborted.
3137 (Clearing it is always permitted.) */
3138 if ( aRTCUseUTC
3139 && mData->mRegistered
3140 && ( !i_isSessionMachine()
3141 || ( mData->mMachineState != MachineState_PoweredOff
3142 && mData->mMachineState != MachineState_Teleported
3143 && mData->mMachineState != MachineState_Aborted
3144 )
3145 )
3146 )
3147 return setError(VBOX_E_INVALID_VM_STATE,
3148 tr("The machine is not powered off (state is %s)"),
3149 Global::stringifyMachineState(mData->mMachineState));
3150
3151 i_setModified(IsModified_MachineData);
3152 mUserData.backup();
3153 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3154
3155 return S_OK;
3156}
3157
3158HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3159{
3160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3163
3164 return S_OK;
3165}
3166
3167HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3168{
3169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3170
3171 HRESULT rc = i_checkStateDependency(MutableStateDep);
3172 if (FAILED(rc)) return rc;
3173
3174 i_setModified(IsModified_MachineData);
3175 mHWData.backup();
3176 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3177
3178 return S_OK;
3179}
3180
3181HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3182{
3183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 *aIOCacheSize = mHWData->mIOCacheSize;
3186
3187 return S_OK;
3188}
3189
3190HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3191{
3192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3193
3194 HRESULT rc = i_checkStateDependency(MutableStateDep);
3195 if (FAILED(rc)) return rc;
3196
3197 i_setModified(IsModified_MachineData);
3198 mHWData.backup();
3199 mHWData->mIOCacheSize = aIOCacheSize;
3200
3201 return S_OK;
3202}
3203
3204
3205/**
3206 * @note Locks objects!
3207 */
3208HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3209 LockType_T aLockType)
3210{
3211 /* check the session state */
3212 SessionState_T state;
3213 HRESULT rc = aSession->COMGETTER(State)(&state);
3214 if (FAILED(rc)) return rc;
3215
3216 if (state != SessionState_Unlocked)
3217 return setError(VBOX_E_INVALID_OBJECT_STATE,
3218 tr("The given session is busy"));
3219
3220 // get the client's IInternalSessionControl interface
3221 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3222 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3223 E_INVALIDARG);
3224
3225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3226
3227 if (!mData->mRegistered)
3228 return setError(E_UNEXPECTED,
3229 tr("The machine '%s' is not registered"),
3230 mUserData->s.strName.c_str());
3231
3232 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3233
3234 SessionState_T oldState = mData->mSession.mState;
3235 /* Hack: in case the session is closing and there is a progress object
3236 * which allows waiting for the session to be closed, take the opportunity
3237 * and do a limited wait (max. 1 second). This helps a lot when the system
3238 * is busy and thus session closing can take a little while. */
3239 if ( mData->mSession.mState == SessionState_Unlocking
3240 && mData->mSession.mProgress)
3241 {
3242 alock.release();
3243 mData->mSession.mProgress->WaitForCompletion(1000);
3244 alock.acquire();
3245 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3246 }
3247
3248 // try again now
3249 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3250 // (i.e. session machine exists)
3251 && (aLockType == LockType_Shared) // caller wants a shared link to the
3252 // existing session that holds the write lock:
3253 )
3254 {
3255 // OK, share the session... we are now dealing with three processes:
3256 // 1) VBoxSVC (where this code runs);
3257 // 2) process C: the caller's client process (who wants a shared session);
3258 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3259
3260 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3261 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3262 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3263 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3264 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3265
3266 /*
3267 * Release the lock before calling the client process. It's safe here
3268 * since the only thing to do after we get the lock again is to add
3269 * the remote control to the list (which doesn't directly influence
3270 * anything).
3271 */
3272 alock.release();
3273
3274 // get the console of the session holding the write lock (this is a remote call)
3275 ComPtr<IConsole> pConsoleW;
3276 if (mData->mSession.mLockType == LockType_VM)
3277 {
3278 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3279 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3280 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3281 if (FAILED(rc))
3282 // the failure may occur w/o any error info (from RPC), so provide one
3283 return setError(VBOX_E_VM_ERROR,
3284 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3285 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3286 }
3287
3288 // share the session machine and W's console with the caller's session
3289 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3290 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3291 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3292
3293 if (FAILED(rc))
3294 // the failure may occur w/o any error info (from RPC), so provide one
3295 return setError(VBOX_E_VM_ERROR,
3296 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3297 alock.acquire();
3298
3299 // need to revalidate the state after acquiring the lock again
3300 if (mData->mSession.mState != SessionState_Locked)
3301 {
3302 pSessionControl->Uninitialize();
3303 return setError(VBOX_E_INVALID_SESSION_STATE,
3304 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3305 mUserData->s.strName.c_str());
3306 }
3307
3308 // add the caller's session to the list
3309 mData->mSession.mRemoteControls.push_back(pSessionControl);
3310 }
3311 else if ( mData->mSession.mState == SessionState_Locked
3312 || mData->mSession.mState == SessionState_Unlocking
3313 )
3314 {
3315 // sharing not permitted, or machine still unlocking:
3316 return setError(VBOX_E_INVALID_OBJECT_STATE,
3317 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3318 mUserData->s.strName.c_str());
3319 }
3320 else
3321 {
3322 // machine is not locked: then write-lock the machine (create the session machine)
3323
3324 // must not be busy
3325 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3326
3327 // get the caller's session PID
3328 RTPROCESS pid = NIL_RTPROCESS;
3329 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3330 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3331 Assert(pid != NIL_RTPROCESS);
3332
3333 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3334
3335 if (fLaunchingVMProcess)
3336 {
3337 if (mData->mSession.mPID == NIL_RTPROCESS)
3338 {
3339 // two or more clients racing for a lock, the one which set the
3340 // session state to Spawning will win, the others will get an
3341 // error as we can't decide here if waiting a little would help
3342 // (only for shared locks this would avoid an error)
3343 return setError(VBOX_E_INVALID_OBJECT_STATE,
3344 tr("The machine '%s' already has a lock request pending"),
3345 mUserData->s.strName.c_str());
3346 }
3347
3348 // this machine is awaiting for a spawning session to be opened:
3349 // then the calling process must be the one that got started by
3350 // LaunchVMProcess()
3351
3352 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3353 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3354
3355#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3356 /* Hardened windows builds spawns three processes when a VM is
3357 launched, the 3rd one is the one that will end up here. */
3358 RTPROCESS ppid;
3359 int rc = RTProcQueryParent(pid, &ppid);
3360 if (RT_SUCCESS(rc))
3361 rc = RTProcQueryParent(ppid, &ppid);
3362 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3363 || rc == VERR_ACCESS_DENIED)
3364 {
3365 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3366 mData->mSession.mPID = pid;
3367 }
3368#endif
3369
3370 if (mData->mSession.mPID != pid)
3371 return setError(E_ACCESSDENIED,
3372 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3373 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3374 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3375 }
3376
3377 // create the mutable SessionMachine from the current machine
3378 ComObjPtr<SessionMachine> sessionMachine;
3379 sessionMachine.createObject();
3380 rc = sessionMachine->init(this);
3381 AssertComRC(rc);
3382
3383 /* NOTE: doing return from this function after this point but
3384 * before the end is forbidden since it may call SessionMachine::uninit()
3385 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3386 * lock while still holding the Machine lock in alock so that a deadlock
3387 * is possible due to the wrong lock order. */
3388
3389 if (SUCCEEDED(rc))
3390 {
3391 /*
3392 * Set the session state to Spawning to protect against subsequent
3393 * attempts to open a session and to unregister the machine after
3394 * we release the lock.
3395 */
3396 SessionState_T origState = mData->mSession.mState;
3397 mData->mSession.mState = SessionState_Spawning;
3398
3399#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3400 /* Get the client token ID to be passed to the client process */
3401 Utf8Str strTokenId;
3402 sessionMachine->i_getTokenId(strTokenId);
3403 Assert(!strTokenId.isEmpty());
3404#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3405 /* Get the client token to be passed to the client process */
3406 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3407 /* The token is now "owned" by pToken, fix refcount */
3408 if (!pToken.isNull())
3409 pToken->Release();
3410#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3411
3412 /*
3413 * Release the lock before calling the client process -- it will call
3414 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3415 * because the state is Spawning, so that LaunchVMProcess() and
3416 * LockMachine() calls will fail. This method, called before we
3417 * acquire the lock again, will fail because of the wrong PID.
3418 *
3419 * Note that mData->mSession.mRemoteControls accessed outside
3420 * the lock may not be modified when state is Spawning, so it's safe.
3421 */
3422 alock.release();
3423
3424 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3425#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3426 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3427#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3428 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3429 /* Now the token is owned by the client process. */
3430 pToken.setNull();
3431#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3432 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3433
3434 /* The failure may occur w/o any error info (from RPC), so provide one */
3435 if (FAILED(rc))
3436 setError(VBOX_E_VM_ERROR,
3437 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3438
3439 if ( SUCCEEDED(rc)
3440 && fLaunchingVMProcess
3441 )
3442 {
3443 /* complete the remote session initialization */
3444
3445 /* get the console from the direct session */
3446 ComPtr<IConsole> console;
3447 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3448 ComAssertComRC(rc);
3449
3450 if (SUCCEEDED(rc) && !console)
3451 {
3452 ComAssert(!!console);
3453 rc = E_FAIL;
3454 }
3455
3456 /* assign machine & console to the remote session */
3457 if (SUCCEEDED(rc))
3458 {
3459 /*
3460 * after LaunchVMProcess(), the first and the only
3461 * entry in remoteControls is that remote session
3462 */
3463 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3464 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3465 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3466
3467 /* The failure may occur w/o any error info (from RPC), so provide one */
3468 if (FAILED(rc))
3469 setError(VBOX_E_VM_ERROR,
3470 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3471 }
3472
3473 if (FAILED(rc))
3474 pSessionControl->Uninitialize();
3475 }
3476
3477 /* acquire the lock again */
3478 alock.acquire();
3479
3480 /* Restore the session state */
3481 mData->mSession.mState = origState;
3482 }
3483
3484 // finalize spawning anyway (this is why we don't return on errors above)
3485 if (fLaunchingVMProcess)
3486 {
3487 /* Note that the progress object is finalized later */
3488 /** @todo Consider checking mData->mSession.mProgress for cancellation
3489 * around here. */
3490
3491 /* We don't reset mSession.mPID here because it is necessary for
3492 * SessionMachine::uninit() to reap the child process later. */
3493
3494 if (FAILED(rc))
3495 {
3496 /* Close the remote session, remove the remote control from the list
3497 * and reset session state to Closed (@note keep the code in sync
3498 * with the relevant part in checkForSpawnFailure()). */
3499
3500 Assert(mData->mSession.mRemoteControls.size() == 1);
3501 if (mData->mSession.mRemoteControls.size() == 1)
3502 {
3503 ErrorInfoKeeper eik;
3504 mData->mSession.mRemoteControls.front()->Uninitialize();
3505 }
3506
3507 mData->mSession.mRemoteControls.clear();
3508 mData->mSession.mState = SessionState_Unlocked;
3509 }
3510 }
3511 else
3512 {
3513 /* memorize PID of the directly opened session */
3514 if (SUCCEEDED(rc))
3515 mData->mSession.mPID = pid;
3516 }
3517
3518 if (SUCCEEDED(rc))
3519 {
3520 mData->mSession.mLockType = aLockType;
3521 /* memorize the direct session control and cache IUnknown for it */
3522 mData->mSession.mDirectControl = pSessionControl;
3523 mData->mSession.mState = SessionState_Locked;
3524 /* associate the SessionMachine with this Machine */
3525 mData->mSession.mMachine = sessionMachine;
3526
3527 /* request an IUnknown pointer early from the remote party for later
3528 * identity checks (it will be internally cached within mDirectControl
3529 * at least on XPCOM) */
3530 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3531 NOREF(unk);
3532 }
3533
3534 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3535 * would break the lock order */
3536 alock.release();
3537
3538 /* uninitialize the created session machine on failure */
3539 if (FAILED(rc))
3540 sessionMachine->uninit();
3541 }
3542
3543 if (SUCCEEDED(rc))
3544 {
3545 /*
3546 * tell the client watcher thread to update the set of
3547 * machines that have open sessions
3548 */
3549 mParent->i_updateClientWatcher();
3550
3551 if (oldState != SessionState_Locked)
3552 /* fire an event */
3553 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3554 }
3555
3556 return rc;
3557}
3558
3559/**
3560 * @note Locks objects!
3561 */
3562HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3563 const com::Utf8Str &aType,
3564 const com::Utf8Str &aEnvironment,
3565 ComPtr<IProgress> &aProgress)
3566{
3567 Utf8Str strFrontend(aType);
3568 /* "emergencystop" doesn't need the session, so skip the checks/interface
3569 * retrieval. This code doesn't quite fit in here, but introducing a
3570 * special API method would be even more effort, and would require explicit
3571 * support by every API client. It's better to hide the feature a bit. */
3572 if (strFrontend != "emergencystop")
3573 CheckComArgNotNull(aSession);
3574
3575 HRESULT rc = S_OK;
3576 if (strFrontend.isEmpty())
3577 {
3578 Bstr bstrFrontend;
3579 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3580 if (FAILED(rc))
3581 return rc;
3582 strFrontend = bstrFrontend;
3583 if (strFrontend.isEmpty())
3584 {
3585 ComPtr<ISystemProperties> systemProperties;
3586 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3587 if (FAILED(rc))
3588 return rc;
3589 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3590 if (FAILED(rc))
3591 return rc;
3592 strFrontend = bstrFrontend;
3593 }
3594 /* paranoia - emergencystop is not a valid default */
3595 if (strFrontend == "emergencystop")
3596 strFrontend = Utf8Str::Empty;
3597 }
3598 /* default frontend: Qt GUI */
3599 if (strFrontend.isEmpty())
3600 strFrontend = "GUI/Qt";
3601
3602 if (strFrontend != "emergencystop")
3603 {
3604 /* check the session state */
3605 SessionState_T state;
3606 rc = aSession->COMGETTER(State)(&state);
3607 if (FAILED(rc))
3608 return rc;
3609
3610 if (state != SessionState_Unlocked)
3611 return setError(VBOX_E_INVALID_OBJECT_STATE,
3612 tr("The given session is busy"));
3613
3614 /* get the IInternalSessionControl interface */
3615 ComPtr<IInternalSessionControl> control(aSession);
3616 ComAssertMsgRet(!control.isNull(),
3617 ("No IInternalSessionControl interface"),
3618 E_INVALIDARG);
3619
3620 /* get the teleporter enable state for the progress object init. */
3621 BOOL fTeleporterEnabled;
3622 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3623 if (FAILED(rc))
3624 return rc;
3625
3626 /* create a progress object */
3627 ComObjPtr<ProgressProxy> progress;
3628 progress.createObject();
3629 rc = progress->init(mParent,
3630 static_cast<IMachine*>(this),
3631 Bstr(tr("Starting VM")).raw(),
3632 TRUE /* aCancelable */,
3633 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3634 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3635 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3636 2 /* uFirstOperationWeight */,
3637 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3638
3639 if (SUCCEEDED(rc))
3640 {
3641 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3642 if (SUCCEEDED(rc))
3643 {
3644 aProgress = progress;
3645
3646 /* signal the client watcher thread */
3647 mParent->i_updateClientWatcher();
3648
3649 /* fire an event */
3650 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3651 }
3652 }
3653 }
3654 else
3655 {
3656 /* no progress object - either instant success or failure */
3657 aProgress = NULL;
3658
3659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3660
3661 if (mData->mSession.mState != SessionState_Locked)
3662 return setError(VBOX_E_INVALID_OBJECT_STATE,
3663 tr("The machine '%s' is not locked by a session"),
3664 mUserData->s.strName.c_str());
3665
3666 /* must have a VM process associated - do not kill normal API clients
3667 * with an open session */
3668 if (!Global::IsOnline(mData->mMachineState))
3669 return setError(VBOX_E_INVALID_OBJECT_STATE,
3670 tr("The machine '%s' does not have a VM process"),
3671 mUserData->s.strName.c_str());
3672
3673 /* forcibly terminate the VM process */
3674 if (mData->mSession.mPID != NIL_RTPROCESS)
3675 RTProcTerminate(mData->mSession.mPID);
3676
3677 /* signal the client watcher thread, as most likely the client has
3678 * been terminated */
3679 mParent->i_updateClientWatcher();
3680 }
3681
3682 return rc;
3683}
3684
3685HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3686{
3687 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3688 return setError(E_INVALIDARG,
3689 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3690 aPosition, SchemaDefs::MaxBootPosition);
3691
3692 if (aDevice == DeviceType_USB)
3693 return setError(E_NOTIMPL,
3694 tr("Booting from USB device is currently not supported"));
3695
3696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3697
3698 HRESULT rc = i_checkStateDependency(MutableStateDep);
3699 if (FAILED(rc)) return rc;
3700
3701 i_setModified(IsModified_MachineData);
3702 mHWData.backup();
3703 mHWData->mBootOrder[aPosition - 1] = aDevice;
3704
3705 return S_OK;
3706}
3707
3708HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3709{
3710 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3711 return setError(E_INVALIDARG,
3712 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3713 aPosition, SchemaDefs::MaxBootPosition);
3714
3715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3716
3717 *aDevice = mHWData->mBootOrder[aPosition - 1];
3718
3719 return S_OK;
3720}
3721
3722HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3723 LONG aControllerPort,
3724 LONG aDevice,
3725 DeviceType_T aType,
3726 const ComPtr<IMedium> &aMedium)
3727{
3728 IMedium *aM = aMedium;
3729 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3730 aName.c_str(), aControllerPort, aDevice, aType, aM));
3731
3732 // request the host lock first, since might be calling Host methods for getting host drives;
3733 // next, protect the media tree all the while we're in here, as well as our member variables
3734 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3735 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3736
3737 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3738 if (FAILED(rc)) return rc;
3739
3740 /// @todo NEWMEDIA implicit machine registration
3741 if (!mData->mRegistered)
3742 return setError(VBOX_E_INVALID_OBJECT_STATE,
3743 tr("Cannot attach storage devices to an unregistered machine"));
3744
3745 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3746
3747 /* Check for an existing controller. */
3748 ComObjPtr<StorageController> ctl;
3749 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3750 if (FAILED(rc)) return rc;
3751
3752 StorageControllerType_T ctrlType;
3753 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3754 if (FAILED(rc))
3755 return setError(E_FAIL,
3756 tr("Could not get type of controller '%s'"),
3757 aName.c_str());
3758
3759 bool fSilent = false;
3760 Utf8Str strReconfig;
3761
3762 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3763 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3764 if ( mData->mMachineState == MachineState_Paused
3765 && strReconfig == "1")
3766 fSilent = true;
3767
3768 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3769 bool fHotplug = false;
3770 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3771 fHotplug = true;
3772
3773 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3774 return setError(VBOX_E_INVALID_VM_STATE,
3775 tr("Controller '%s' does not support hotplugging"),
3776 aName.c_str());
3777
3778 // check that the port and device are not out of range
3779 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3780 if (FAILED(rc)) return rc;
3781
3782 /* check if the device slot is already busy */
3783 MediumAttachment *pAttachTemp;
3784 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3785 Bstr(aName).raw(),
3786 aControllerPort,
3787 aDevice)))
3788 {
3789 Medium *pMedium = pAttachTemp->i_getMedium();
3790 if (pMedium)
3791 {
3792 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3793 return setError(VBOX_E_OBJECT_IN_USE,
3794 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3795 pMedium->i_getLocationFull().c_str(),
3796 aControllerPort,
3797 aDevice,
3798 aName.c_str());
3799 }
3800 else
3801 return setError(VBOX_E_OBJECT_IN_USE,
3802 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3803 aControllerPort, aDevice, aName.c_str());
3804 }
3805
3806 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3807 if (aMedium && medium.isNull())
3808 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3809
3810 AutoCaller mediumCaller(medium);
3811 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3812
3813 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3814
3815 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3816 && !medium.isNull()
3817 )
3818 return setError(VBOX_E_OBJECT_IN_USE,
3819 tr("Medium '%s' is already attached to this virtual machine"),
3820 medium->i_getLocationFull().c_str());
3821
3822 if (!medium.isNull())
3823 {
3824 MediumType_T mtype = medium->i_getType();
3825 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3826 // For DVDs it's not written to the config file, so needs no global config
3827 // version bump. For floppies it's a new attribute "type", which is ignored
3828 // by older VirtualBox version, so needs no global config version bump either.
3829 // For hard disks this type is not accepted.
3830 if (mtype == MediumType_MultiAttach)
3831 {
3832 // This type is new with VirtualBox 4.0 and therefore requires settings
3833 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3834 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3835 // two reasons: The medium type is a property of the media registry tree, which
3836 // can reside in the global config file (for pre-4.0 media); we would therefore
3837 // possibly need to bump the global config version. We don't want to do that though
3838 // because that might make downgrading to pre-4.0 impossible.
3839 // As a result, we can only use these two new types if the medium is NOT in the
3840 // global registry:
3841 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3842 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3843 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3844 )
3845 return setError(VBOX_E_INVALID_OBJECT_STATE,
3846 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3847 "to machines that were created with VirtualBox 4.0 or later"),
3848 medium->i_getLocationFull().c_str());
3849 }
3850 }
3851
3852 bool fIndirect = false;
3853 if (!medium.isNull())
3854 fIndirect = medium->i_isReadOnly();
3855 bool associate = true;
3856
3857 do
3858 {
3859 if ( aType == DeviceType_HardDisk
3860 && mMediaData.isBackedUp())
3861 {
3862 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3863
3864 /* check if the medium was attached to the VM before we started
3865 * changing attachments in which case the attachment just needs to
3866 * be restored */
3867 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3868 {
3869 AssertReturn(!fIndirect, E_FAIL);
3870
3871 /* see if it's the same bus/channel/device */
3872 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3873 {
3874 /* the simplest case: restore the whole attachment
3875 * and return, nothing else to do */
3876 mMediaData->mAttachments.push_back(pAttachTemp);
3877
3878 /* Reattach the medium to the VM. */
3879 if (fHotplug || fSilent)
3880 {
3881 mediumLock.release();
3882 treeLock.release();
3883 alock.release();
3884
3885 MediumLockList *pMediumLockList(new MediumLockList());
3886
3887 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3888 true /* fMediumLockWrite */,
3889 false /* fMediumLockWriteAll */,
3890 NULL,
3891 *pMediumLockList);
3892 alock.acquire();
3893 if (FAILED(rc))
3894 delete pMediumLockList;
3895 else
3896 {
3897 mData->mSession.mLockedMedia.Unlock();
3898 alock.release();
3899 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3900 mData->mSession.mLockedMedia.Lock();
3901 alock.acquire();
3902 }
3903 alock.release();
3904
3905 if (SUCCEEDED(rc))
3906 {
3907 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3908 /* Remove lock list in case of error. */
3909 if (FAILED(rc))
3910 {
3911 mData->mSession.mLockedMedia.Unlock();
3912 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3913 mData->mSession.mLockedMedia.Lock();
3914 }
3915 }
3916 }
3917
3918 return S_OK;
3919 }
3920
3921 /* bus/channel/device differ; we need a new attachment object,
3922 * but don't try to associate it again */
3923 associate = false;
3924 break;
3925 }
3926 }
3927
3928 /* go further only if the attachment is to be indirect */
3929 if (!fIndirect)
3930 break;
3931
3932 /* perform the so called smart attachment logic for indirect
3933 * attachments. Note that smart attachment is only applicable to base
3934 * hard disks. */
3935
3936 if (medium->i_getParent().isNull())
3937 {
3938 /* first, investigate the backup copy of the current hard disk
3939 * attachments to make it possible to re-attach existing diffs to
3940 * another device slot w/o losing their contents */
3941 if (mMediaData.isBackedUp())
3942 {
3943 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3944
3945 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3946 uint32_t foundLevel = 0;
3947
3948 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3949 {
3950 uint32_t level = 0;
3951 MediumAttachment *pAttach = *it;
3952 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3953 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3954 if (pMedium.isNull())
3955 continue;
3956
3957 if (pMedium->i_getBase(&level) == medium)
3958 {
3959 /* skip the hard disk if its currently attached (we
3960 * cannot attach the same hard disk twice) */
3961 if (i_findAttachment(mMediaData->mAttachments,
3962 pMedium))
3963 continue;
3964
3965 /* matched device, channel and bus (i.e. attached to the
3966 * same place) will win and immediately stop the search;
3967 * otherwise the attachment that has the youngest
3968 * descendant of medium will be used
3969 */
3970 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3971 {
3972 /* the simplest case: restore the whole attachment
3973 * and return, nothing else to do */
3974 mMediaData->mAttachments.push_back(*it);
3975
3976 /* Reattach the medium to the VM. */
3977 if (fHotplug || fSilent)
3978 {
3979 mediumLock.release();
3980 treeLock.release();
3981 alock.release();
3982
3983 MediumLockList *pMediumLockList(new MediumLockList());
3984
3985 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3986 true /* fMediumLockWrite */,
3987 false /* fMediumLockWriteAll */,
3988 NULL,
3989 *pMediumLockList);
3990 alock.acquire();
3991 if (FAILED(rc))
3992 delete pMediumLockList;
3993 else
3994 {
3995 mData->mSession.mLockedMedia.Unlock();
3996 alock.release();
3997 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3998 mData->mSession.mLockedMedia.Lock();
3999 alock.acquire();
4000 }
4001 alock.release();
4002
4003 if (SUCCEEDED(rc))
4004 {
4005 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4006 /* Remove lock list in case of error. */
4007 if (FAILED(rc))
4008 {
4009 mData->mSession.mLockedMedia.Unlock();
4010 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4011 mData->mSession.mLockedMedia.Lock();
4012 }
4013 }
4014 }
4015
4016 return S_OK;
4017 }
4018 else if ( foundIt == oldAtts.end()
4019 || level > foundLevel /* prefer younger */
4020 )
4021 {
4022 foundIt = it;
4023 foundLevel = level;
4024 }
4025 }
4026 }
4027
4028 if (foundIt != oldAtts.end())
4029 {
4030 /* use the previously attached hard disk */
4031 medium = (*foundIt)->i_getMedium();
4032 mediumCaller.attach(medium);
4033 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4034 mediumLock.attach(medium);
4035 /* not implicit, doesn't require association with this VM */
4036 fIndirect = false;
4037 associate = false;
4038 /* go right to the MediumAttachment creation */
4039 break;
4040 }
4041 }
4042
4043 /* must give up the medium lock and medium tree lock as below we
4044 * go over snapshots, which needs a lock with higher lock order. */
4045 mediumLock.release();
4046 treeLock.release();
4047
4048 /* then, search through snapshots for the best diff in the given
4049 * hard disk's chain to base the new diff on */
4050
4051 ComObjPtr<Medium> base;
4052 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4053 while (snap)
4054 {
4055 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4056
4057 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4058
4059 MediumAttachment *pAttachFound = NULL;
4060 uint32_t foundLevel = 0;
4061
4062 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4063 {
4064 MediumAttachment *pAttach = *it;
4065 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4066 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4067 if (pMedium.isNull())
4068 continue;
4069
4070 uint32_t level = 0;
4071 if (pMedium->i_getBase(&level) == medium)
4072 {
4073 /* matched device, channel and bus (i.e. attached to the
4074 * same place) will win and immediately stop the search;
4075 * otherwise the attachment that has the youngest
4076 * descendant of medium will be used
4077 */
4078 if ( pAttach->i_getDevice() == aDevice
4079 && pAttach->i_getPort() == aControllerPort
4080 && pAttach->i_getControllerName() == aName
4081 )
4082 {
4083 pAttachFound = pAttach;
4084 break;
4085 }
4086 else if ( !pAttachFound
4087 || level > foundLevel /* prefer younger */
4088 )
4089 {
4090 pAttachFound = pAttach;
4091 foundLevel = level;
4092 }
4093 }
4094 }
4095
4096 if (pAttachFound)
4097 {
4098 base = pAttachFound->i_getMedium();
4099 break;
4100 }
4101
4102 snap = snap->i_getParent();
4103 }
4104
4105 /* re-lock medium tree and the medium, as we need it below */
4106 treeLock.acquire();
4107 mediumLock.acquire();
4108
4109 /* found a suitable diff, use it as a base */
4110 if (!base.isNull())
4111 {
4112 medium = base;
4113 mediumCaller.attach(medium);
4114 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4115 mediumLock.attach(medium);
4116 }
4117 }
4118
4119 Utf8Str strFullSnapshotFolder;
4120 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4121
4122 ComObjPtr<Medium> diff;
4123 diff.createObject();
4124 // store this diff in the same registry as the parent
4125 Guid uuidRegistryParent;
4126 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4127 {
4128 // parent image has no registry: this can happen if we're attaching a new immutable
4129 // image that has not yet been attached (medium then points to the base and we're
4130 // creating the diff image for the immutable, and the parent is not yet registered);
4131 // put the parent in the machine registry then
4132 mediumLock.release();
4133 treeLock.release();
4134 alock.release();
4135 i_addMediumToRegistry(medium);
4136 alock.acquire();
4137 treeLock.acquire();
4138 mediumLock.acquire();
4139 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4140 }
4141 rc = diff->init(mParent,
4142 medium->i_getPreferredDiffFormat(),
4143 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4144 uuidRegistryParent,
4145 DeviceType_HardDisk);
4146 if (FAILED(rc)) return rc;
4147
4148 /* Apply the normal locking logic to the entire chain. */
4149 MediumLockList *pMediumLockList(new MediumLockList());
4150 mediumLock.release();
4151 treeLock.release();
4152 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4153 true /* fMediumLockWrite */,
4154 false /* fMediumLockWriteAll */,
4155 medium,
4156 *pMediumLockList);
4157 treeLock.acquire();
4158 mediumLock.acquire();
4159 if (SUCCEEDED(rc))
4160 {
4161 mediumLock.release();
4162 treeLock.release();
4163 rc = pMediumLockList->Lock();
4164 treeLock.acquire();
4165 mediumLock.acquire();
4166 if (FAILED(rc))
4167 setError(rc,
4168 tr("Could not lock medium when creating diff '%s'"),
4169 diff->i_getLocationFull().c_str());
4170 else
4171 {
4172 /* will release the lock before the potentially lengthy
4173 * operation, so protect with the special state */
4174 MachineState_T oldState = mData->mMachineState;
4175 i_setMachineState(MachineState_SettingUp);
4176
4177 mediumLock.release();
4178 treeLock.release();
4179 alock.release();
4180
4181 rc = medium->i_createDiffStorage(diff,
4182 MediumVariant_Standard,
4183 pMediumLockList,
4184 NULL /* aProgress */,
4185 true /* aWait */);
4186
4187 alock.acquire();
4188 treeLock.acquire();
4189 mediumLock.acquire();
4190
4191 i_setMachineState(oldState);
4192 }
4193 }
4194
4195 /* Unlock the media and free the associated memory. */
4196 delete pMediumLockList;
4197
4198 if (FAILED(rc)) return rc;
4199
4200 /* use the created diff for the actual attachment */
4201 medium = diff;
4202 mediumCaller.attach(medium);
4203 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4204 mediumLock.attach(medium);
4205 }
4206 while (0);
4207
4208 ComObjPtr<MediumAttachment> attachment;
4209 attachment.createObject();
4210 rc = attachment->init(this,
4211 medium,
4212 aName,
4213 aControllerPort,
4214 aDevice,
4215 aType,
4216 fIndirect,
4217 false /* fPassthrough */,
4218 false /* fTempEject */,
4219 false /* fNonRotational */,
4220 false /* fDiscard */,
4221 fHotplug /* fHotPluggable */,
4222 Utf8Str::Empty);
4223 if (FAILED(rc)) return rc;
4224
4225 if (associate && !medium.isNull())
4226 {
4227 // as the last step, associate the medium to the VM
4228 rc = medium->i_addBackReference(mData->mUuid);
4229 // here we can fail because of Deleting, or being in process of creating a Diff
4230 if (FAILED(rc)) return rc;
4231
4232 mediumLock.release();
4233 treeLock.release();
4234 alock.release();
4235 i_addMediumToRegistry(medium);
4236 alock.acquire();
4237 treeLock.acquire();
4238 mediumLock.acquire();
4239 }
4240
4241 /* success: finally remember the attachment */
4242 i_setModified(IsModified_Storage);
4243 mMediaData.backup();
4244 mMediaData->mAttachments.push_back(attachment);
4245
4246 mediumLock.release();
4247 treeLock.release();
4248 alock.release();
4249
4250 if (fHotplug || fSilent)
4251 {
4252 if (!medium.isNull())
4253 {
4254 MediumLockList *pMediumLockList(new MediumLockList());
4255
4256 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4257 true /* fMediumLockWrite */,
4258 false /* fMediumLockWriteAll */,
4259 NULL,
4260 *pMediumLockList);
4261 alock.acquire();
4262 if (FAILED(rc))
4263 delete pMediumLockList;
4264 else
4265 {
4266 mData->mSession.mLockedMedia.Unlock();
4267 alock.release();
4268 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4269 mData->mSession.mLockedMedia.Lock();
4270 alock.acquire();
4271 }
4272 alock.release();
4273 }
4274
4275 if (SUCCEEDED(rc))
4276 {
4277 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4278 /* Remove lock list in case of error. */
4279 if (FAILED(rc))
4280 {
4281 mData->mSession.mLockedMedia.Unlock();
4282 mData->mSession.mLockedMedia.Remove(attachment);
4283 mData->mSession.mLockedMedia.Lock();
4284 }
4285 }
4286 }
4287
4288 /* Save modified registries, but skip this machine as it's the caller's
4289 * job to save its settings like all other settings changes. */
4290 mParent->i_unmarkRegistryModified(i_getId());
4291 mParent->i_saveModifiedRegistries();
4292
4293 return rc;
4294}
4295
4296HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4297 LONG aDevice)
4298{
4299 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4300 aName.c_str(), aControllerPort, aDevice));
4301
4302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4303
4304 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4305 if (FAILED(rc)) return rc;
4306
4307 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4308
4309 /* Check for an existing controller. */
4310 ComObjPtr<StorageController> ctl;
4311 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4312 if (FAILED(rc)) return rc;
4313
4314 StorageControllerType_T ctrlType;
4315 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4316 if (FAILED(rc))
4317 return setError(E_FAIL,
4318 tr("Could not get type of controller '%s'"),
4319 aName.c_str());
4320
4321 bool fSilent = false;
4322 Utf8Str strReconfig;
4323
4324 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4325 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4326 if ( mData->mMachineState == MachineState_Paused
4327 && strReconfig == "1")
4328 fSilent = true;
4329
4330 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4331 bool fHotplug = false;
4332 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4333 fHotplug = true;
4334
4335 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4336 return setError(VBOX_E_INVALID_VM_STATE,
4337 tr("Controller '%s' does not support hotplugging"),
4338 aName.c_str());
4339
4340 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4341 Bstr(aName).raw(),
4342 aControllerPort,
4343 aDevice);
4344 if (!pAttach)
4345 return setError(VBOX_E_OBJECT_NOT_FOUND,
4346 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4347 aDevice, aControllerPort, aName.c_str());
4348
4349 if (fHotplug && !pAttach->i_getHotPluggable())
4350 return setError(VBOX_E_NOT_SUPPORTED,
4351 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4352 aDevice, aControllerPort, aName.c_str());
4353
4354 /*
4355 * The VM has to detach the device before we delete any implicit diffs.
4356 * If this fails we can roll back without loosing data.
4357 */
4358 if (fHotplug || fSilent)
4359 {
4360 alock.release();
4361 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4362 alock.acquire();
4363 }
4364 if (FAILED(rc)) return rc;
4365
4366 /* If we are here everything went well and we can delete the implicit now. */
4367 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4368
4369 alock.release();
4370
4371 /* Save modified registries, but skip this machine as it's the caller's
4372 * job to save its settings like all other settings changes. */
4373 mParent->i_unmarkRegistryModified(i_getId());
4374 mParent->i_saveModifiedRegistries();
4375
4376 return rc;
4377}
4378
4379HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4380 LONG aDevice, BOOL aPassthrough)
4381{
4382 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4383 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4384
4385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4386
4387 HRESULT rc = i_checkStateDependency(MutableStateDep);
4388 if (FAILED(rc)) return rc;
4389
4390 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4391
4392 if (Global::IsOnlineOrTransient(mData->mMachineState))
4393 return setError(VBOX_E_INVALID_VM_STATE,
4394 tr("Invalid machine state: %s"),
4395 Global::stringifyMachineState(mData->mMachineState));
4396
4397 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4398 Bstr(aName).raw(),
4399 aControllerPort,
4400 aDevice);
4401 if (!pAttach)
4402 return setError(VBOX_E_OBJECT_NOT_FOUND,
4403 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4404 aDevice, aControllerPort, aName.c_str());
4405
4406
4407 i_setModified(IsModified_Storage);
4408 mMediaData.backup();
4409
4410 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4411
4412 if (pAttach->i_getType() != DeviceType_DVD)
4413 return setError(E_INVALIDARG,
4414 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4415 aDevice, aControllerPort, aName.c_str());
4416 pAttach->i_updatePassthrough(!!aPassthrough);
4417
4418 return S_OK;
4419}
4420
4421HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4422 LONG aDevice, BOOL aTemporaryEject)
4423{
4424
4425 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4426 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4427
4428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4429
4430 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4431 if (FAILED(rc)) return rc;
4432
4433 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4434 Bstr(aName).raw(),
4435 aControllerPort,
4436 aDevice);
4437 if (!pAttach)
4438 return setError(VBOX_E_OBJECT_NOT_FOUND,
4439 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4440 aDevice, aControllerPort, aName.c_str());
4441
4442
4443 i_setModified(IsModified_Storage);
4444 mMediaData.backup();
4445
4446 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4447
4448 if (pAttach->i_getType() != DeviceType_DVD)
4449 return setError(E_INVALIDARG,
4450 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4451 aDevice, aControllerPort, aName.c_str());
4452 pAttach->i_updateTempEject(!!aTemporaryEject);
4453
4454 return S_OK;
4455}
4456
4457HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4458 LONG aDevice, BOOL aNonRotational)
4459{
4460
4461 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4462 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4463
4464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4465
4466 HRESULT rc = i_checkStateDependency(MutableStateDep);
4467 if (FAILED(rc)) return rc;
4468
4469 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4470
4471 if (Global::IsOnlineOrTransient(mData->mMachineState))
4472 return setError(VBOX_E_INVALID_VM_STATE,
4473 tr("Invalid machine state: %s"),
4474 Global::stringifyMachineState(mData->mMachineState));
4475
4476 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4477 Bstr(aName).raw(),
4478 aControllerPort,
4479 aDevice);
4480 if (!pAttach)
4481 return setError(VBOX_E_OBJECT_NOT_FOUND,
4482 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4483 aDevice, aControllerPort, aName.c_str());
4484
4485
4486 i_setModified(IsModified_Storage);
4487 mMediaData.backup();
4488
4489 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4490
4491 if (pAttach->i_getType() != DeviceType_HardDisk)
4492 return setError(E_INVALIDARG,
4493 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"),
4494 aDevice, aControllerPort, aName.c_str());
4495 pAttach->i_updateNonRotational(!!aNonRotational);
4496
4497 return S_OK;
4498}
4499
4500HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4501 LONG aDevice, BOOL aDiscard)
4502{
4503
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4505 aName.c_str(), aControllerPort, aDevice, aDiscard));
4506
4507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4508
4509 HRESULT rc = i_checkStateDependency(MutableStateDep);
4510 if (FAILED(rc)) return rc;
4511
4512 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4513
4514 if (Global::IsOnlineOrTransient(mData->mMachineState))
4515 return setError(VBOX_E_INVALID_VM_STATE,
4516 tr("Invalid machine state: %s"),
4517 Global::stringifyMachineState(mData->mMachineState));
4518
4519 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4520 Bstr(aName).raw(),
4521 aControllerPort,
4522 aDevice);
4523 if (!pAttach)
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528
4529 i_setModified(IsModified_Storage);
4530 mMediaData.backup();
4531
4532 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4533
4534 if (pAttach->i_getType() != DeviceType_HardDisk)
4535 return setError(E_INVALIDARG,
4536 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"),
4537 aDevice, aControllerPort, aName.c_str());
4538 pAttach->i_updateDiscard(!!aDiscard);
4539
4540 return S_OK;
4541}
4542
4543HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4544 LONG aDevice, BOOL aHotPluggable)
4545{
4546 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4547 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4548
4549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4550
4551 HRESULT rc = i_checkStateDependency(MutableStateDep);
4552 if (FAILED(rc)) return rc;
4553
4554 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4555
4556 if (Global::IsOnlineOrTransient(mData->mMachineState))
4557 return setError(VBOX_E_INVALID_VM_STATE,
4558 tr("Invalid machine state: %s"),
4559 Global::stringifyMachineState(mData->mMachineState));
4560
4561 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4562 Bstr(aName).raw(),
4563 aControllerPort,
4564 aDevice);
4565 if (!pAttach)
4566 return setError(VBOX_E_OBJECT_NOT_FOUND,
4567 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4568 aDevice, aControllerPort, aName.c_str());
4569
4570 /* Check for an existing controller. */
4571 ComObjPtr<StorageController> ctl;
4572 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4573 if (FAILED(rc)) return rc;
4574
4575 StorageControllerType_T ctrlType;
4576 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4577 if (FAILED(rc))
4578 return setError(E_FAIL,
4579 tr("Could not get type of controller '%s'"),
4580 aName.c_str());
4581
4582 if (!i_isControllerHotplugCapable(ctrlType))
4583 return setError(VBOX_E_NOT_SUPPORTED,
4584 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4585 aName.c_str());
4586
4587 i_setModified(IsModified_Storage);
4588 mMediaData.backup();
4589
4590 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4591
4592 if (pAttach->i_getType() == DeviceType_Floppy)
4593 return setError(E_INVALIDARG,
4594 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"),
4595 aDevice, aControllerPort, aName.c_str());
4596 pAttach->i_updateHotPluggable(!!aHotPluggable);
4597
4598 return S_OK;
4599}
4600
4601HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4602 LONG aDevice)
4603{
4604 int rc = S_OK;
4605 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4606 aName.c_str(), aControllerPort, aDevice));
4607
4608 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4609
4610 return rc;
4611}
4612
4613HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4614 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4615{
4616 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4617 aName.c_str(), aControllerPort, aDevice));
4618
4619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4620
4621 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4622 if (FAILED(rc)) return rc;
4623
4624 if (Global::IsOnlineOrTransient(mData->mMachineState))
4625 return setError(VBOX_E_INVALID_VM_STATE,
4626 tr("Invalid machine state: %s"),
4627 Global::stringifyMachineState(mData->mMachineState));
4628
4629 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4630 Bstr(aName).raw(),
4631 aControllerPort,
4632 aDevice);
4633 if (!pAttach)
4634 return setError(VBOX_E_OBJECT_NOT_FOUND,
4635 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4636 aDevice, aControllerPort, aName.c_str());
4637
4638
4639 i_setModified(IsModified_Storage);
4640 mMediaData.backup();
4641
4642 IBandwidthGroup *iB = aBandwidthGroup;
4643 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4644 if (aBandwidthGroup && group.isNull())
4645 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4646
4647 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4648
4649 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4650 if (strBandwidthGroupOld.isNotEmpty())
4651 {
4652 /* Get the bandwidth group object and release it - this must not fail. */
4653 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4654 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4655 Assert(SUCCEEDED(rc));
4656
4657 pBandwidthGroupOld->i_release();
4658 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4659 }
4660
4661 if (!group.isNull())
4662 {
4663 group->i_reference();
4664 pAttach->i_updateBandwidthGroup(group->i_getName());
4665 }
4666
4667 return S_OK;
4668}
4669
4670HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4671 LONG aControllerPort,
4672 LONG aDevice,
4673 DeviceType_T aType)
4674{
4675 HRESULT rc = S_OK;
4676
4677 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4678 aName.c_str(), aControllerPort, aDevice, aType));
4679
4680 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4681
4682 return rc;
4683}
4684
4685
4686HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4687 LONG aControllerPort,
4688 LONG aDevice,
4689 BOOL aForce)
4690{
4691 int rc = S_OK;
4692 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4693 aName.c_str(), aControllerPort, aForce));
4694
4695 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4696
4697 return rc;
4698}
4699
4700HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4701 LONG aControllerPort,
4702 LONG aDevice,
4703 const ComPtr<IMedium> &aMedium,
4704 BOOL aForce)
4705{
4706 int rc = S_OK;
4707 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4708 aName.c_str(), aControllerPort, aDevice, aForce));
4709
4710 // request the host lock first, since might be calling Host methods for getting host drives;
4711 // next, protect the media tree all the while we're in here, as well as our member variables
4712 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4713 this->lockHandle(),
4714 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4715
4716 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4717 Bstr(aName).raw(),
4718 aControllerPort,
4719 aDevice);
4720 if (pAttach.isNull())
4721 return setError(VBOX_E_OBJECT_NOT_FOUND,
4722 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4723 aDevice, aControllerPort, aName.c_str());
4724
4725 /* Remember previously mounted medium. The medium before taking the
4726 * backup is not necessarily the same thing. */
4727 ComObjPtr<Medium> oldmedium;
4728 oldmedium = pAttach->i_getMedium();
4729
4730 IMedium *iM = aMedium;
4731 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4732 if (aMedium && pMedium.isNull())
4733 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4734
4735 AutoCaller mediumCaller(pMedium);
4736 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4737
4738 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4739 if (pMedium)
4740 {
4741 DeviceType_T mediumType = pAttach->i_getType();
4742 switch (mediumType)
4743 {
4744 case DeviceType_DVD:
4745 case DeviceType_Floppy:
4746 break;
4747
4748 default:
4749 return setError(VBOX_E_INVALID_OBJECT_STATE,
4750 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4751 aControllerPort,
4752 aDevice,
4753 aName.c_str());
4754 }
4755 }
4756
4757 i_setModified(IsModified_Storage);
4758 mMediaData.backup();
4759
4760 {
4761 // The backup operation makes the pAttach reference point to the
4762 // old settings. Re-get the correct reference.
4763 pAttach = i_findAttachment(mMediaData->mAttachments,
4764 Bstr(aName).raw(),
4765 aControllerPort,
4766 aDevice);
4767 if (!oldmedium.isNull())
4768 oldmedium->i_removeBackReference(mData->mUuid);
4769 if (!pMedium.isNull())
4770 {
4771 pMedium->i_addBackReference(mData->mUuid);
4772
4773 mediumLock.release();
4774 multiLock.release();
4775 i_addMediumToRegistry(pMedium);
4776 multiLock.acquire();
4777 mediumLock.acquire();
4778 }
4779
4780 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4781 pAttach->i_updateMedium(pMedium);
4782 }
4783
4784 i_setModified(IsModified_Storage);
4785
4786 mediumLock.release();
4787 multiLock.release();
4788 rc = i_onMediumChange(pAttach, aForce);
4789 multiLock.acquire();
4790 mediumLock.acquire();
4791
4792 /* On error roll back this change only. */
4793 if (FAILED(rc))
4794 {
4795 if (!pMedium.isNull())
4796 pMedium->i_removeBackReference(mData->mUuid);
4797 pAttach = i_findAttachment(mMediaData->mAttachments,
4798 Bstr(aName).raw(),
4799 aControllerPort,
4800 aDevice);
4801 /* If the attachment is gone in the meantime, bail out. */
4802 if (pAttach.isNull())
4803 return rc;
4804 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4805 if (!oldmedium.isNull())
4806 oldmedium->i_addBackReference(mData->mUuid);
4807 pAttach->i_updateMedium(oldmedium);
4808 }
4809
4810 mediumLock.release();
4811 multiLock.release();
4812
4813 /* Save modified registries, but skip this machine as it's the caller's
4814 * job to save its settings like all other settings changes. */
4815 mParent->i_unmarkRegistryModified(i_getId());
4816 mParent->i_saveModifiedRegistries();
4817
4818 return rc;
4819}
4820HRESULT Machine::getMedium(const com::Utf8Str &aName,
4821 LONG aControllerPort,
4822 LONG aDevice,
4823 ComPtr<IMedium> &aMedium)
4824{
4825 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4826 aName.c_str(), aControllerPort, aDevice));
4827
4828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 aMedium = NULL;
4831
4832 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4833 Bstr(aName).raw(),
4834 aControllerPort,
4835 aDevice);
4836 if (pAttach.isNull())
4837 return setError(VBOX_E_OBJECT_NOT_FOUND,
4838 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4839 aDevice, aControllerPort, aName.c_str());
4840
4841 aMedium = pAttach->i_getMedium();
4842
4843 return S_OK;
4844}
4845
4846HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4847{
4848
4849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4850
4851 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4852
4853 return S_OK;
4854}
4855
4856HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4857{
4858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4859
4860 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4861
4862 return S_OK;
4863}
4864
4865HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4866{
4867 /* Do not assert if slot is out of range, just return the advertised
4868 status. testdriver/vbox.py triggers this in logVmInfo. */
4869 if (aSlot >= mNetworkAdapters.size())
4870 return setError(E_INVALIDARG,
4871 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4872 aSlot, mNetworkAdapters.size());
4873
4874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4875
4876 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4877
4878 return S_OK;
4879}
4880
4881HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4882{
4883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4884
4885 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4886 size_t i = 0;
4887 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4888 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4889 ++it, ++i)
4890 aKeys[i] = it->first;
4891
4892 return S_OK;
4893}
4894
4895 /**
4896 * @note Locks this object for reading.
4897 */
4898HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4899 com::Utf8Str &aValue)
4900{
4901 /* start with nothing found */
4902 aValue = "";
4903
4904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4905
4906 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4907 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4908 // found:
4909 aValue = it->second; // source is a Utf8Str
4910
4911 /* return the result to caller (may be empty) */
4912 return S_OK;
4913}
4914
4915 /**
4916 * @note Locks mParent for writing + this object for writing.
4917 */
4918HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4919{
4920 Utf8Str strOldValue; // empty
4921
4922 // locking note: we only hold the read lock briefly to look up the old value,
4923 // then release it and call the onExtraCanChange callbacks. There is a small
4924 // chance of a race insofar as the callback might be called twice if two callers
4925 // change the same key at the same time, but that's a much better solution
4926 // than the deadlock we had here before. The actual changing of the extradata
4927 // is then performed under the write lock and race-free.
4928
4929 // look up the old value first; if nothing has changed then we need not do anything
4930 {
4931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4932
4933 // For snapshots don't even think about allowing changes, extradata
4934 // is global for a machine, so there is nothing snapshot specific.
4935 if (i_isSnapshotMachine())
4936 return setError(VBOX_E_INVALID_VM_STATE,
4937 tr("Cannot set extradata for a snapshot"));
4938
4939 // check if the right IMachine instance is used
4940 if (mData->mRegistered && !i_isSessionMachine())
4941 return setError(VBOX_E_INVALID_VM_STATE,
4942 tr("Cannot set extradata for an immutable machine"));
4943
4944 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4945 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4946 strOldValue = it->second;
4947 }
4948
4949 bool fChanged;
4950 if ((fChanged = (strOldValue != aValue)))
4951 {
4952 // ask for permission from all listeners outside the locks;
4953 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4954 // lock to copy the list of callbacks to invoke
4955 Bstr error;
4956 Bstr bstrValue(aValue);
4957
4958 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4959 {
4960 const char *sep = error.isEmpty() ? "" : ": ";
4961 CBSTR err = error.raw();
4962 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4963 sep, err));
4964 return setError(E_ACCESSDENIED,
4965 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4966 aKey.c_str(),
4967 aValue.c_str(),
4968 sep,
4969 err);
4970 }
4971
4972 // data is changing and change not vetoed: then write it out under the lock
4973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4974
4975 if (aValue.isEmpty())
4976 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4977 else
4978 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4979 // creates a new key if needed
4980
4981 bool fNeedsGlobalSaveSettings = false;
4982 // This saving of settings is tricky: there is no "old state" for the
4983 // extradata items at all (unlike all other settings), so the old/new
4984 // settings comparison would give a wrong result!
4985 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4986
4987 if (fNeedsGlobalSaveSettings)
4988 {
4989 // save the global settings; for that we should hold only the VirtualBox lock
4990 alock.release();
4991 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4992 mParent->i_saveSettings();
4993 }
4994 }
4995
4996 // fire notification outside the lock
4997 if (fChanged)
4998 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4999
5000 return S_OK;
5001}
5002
5003HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5004{
5005 aProgress = NULL;
5006 NOREF(aSettingsFilePath);
5007 ReturnComNotImplemented();
5008}
5009
5010HRESULT Machine::saveSettings()
5011{
5012 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5013
5014 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5015 if (FAILED(rc)) return rc;
5016
5017 /* the settings file path may never be null */
5018 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5019
5020 /* save all VM data excluding snapshots */
5021 bool fNeedsGlobalSaveSettings = false;
5022 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5023 mlock.release();
5024
5025 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5026 {
5027 // save the global settings; for that we should hold only the VirtualBox lock
5028 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5029 rc = mParent->i_saveSettings();
5030 }
5031
5032 return rc;
5033}
5034
5035
5036HRESULT Machine::discardSettings()
5037{
5038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5039
5040 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5041 if (FAILED(rc)) return rc;
5042
5043 /*
5044 * during this rollback, the session will be notified if data has
5045 * been actually changed
5046 */
5047 i_rollback(true /* aNotify */);
5048
5049 return S_OK;
5050}
5051
5052/** @note Locks objects! */
5053HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5054 std::vector<ComPtr<IMedium> > &aMedia)
5055{
5056 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5057 AutoLimitedCaller autoCaller(this);
5058 AssertComRCReturnRC(autoCaller.rc());
5059
5060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5061
5062 Guid id(i_getId());
5063
5064 if (mData->mSession.mState != SessionState_Unlocked)
5065 return setError(VBOX_E_INVALID_OBJECT_STATE,
5066 tr("Cannot unregister the machine '%s' while it is locked"),
5067 mUserData->s.strName.c_str());
5068
5069 // wait for state dependents to drop to zero
5070 i_ensureNoStateDependencies();
5071
5072 if (!mData->mAccessible)
5073 {
5074 // inaccessible maschines can only be unregistered; uninitialize ourselves
5075 // here because currently there may be no unregistered that are inaccessible
5076 // (this state combination is not supported). Note releasing the caller and
5077 // leaving the lock before calling uninit()
5078 alock.release();
5079 autoCaller.release();
5080
5081 uninit();
5082
5083 mParent->i_unregisterMachine(this, id);
5084 // calls VirtualBox::i_saveSettings()
5085
5086 return S_OK;
5087 }
5088
5089 HRESULT rc = S_OK;
5090
5091 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5092 // discard saved state
5093 if (mData->mMachineState == MachineState_Saved)
5094 {
5095 // add the saved state file to the list of files the caller should delete
5096 Assert(!mSSData->strStateFilePath.isEmpty());
5097 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5098
5099 mSSData->strStateFilePath.setNull();
5100
5101 // unconditionally set the machine state to powered off, we now
5102 // know no session has locked the machine
5103 mData->mMachineState = MachineState_PoweredOff;
5104 }
5105
5106 size_t cSnapshots = 0;
5107 if (mData->mFirstSnapshot)
5108 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5109 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5110 // fail now before we start detaching media
5111 return setError(VBOX_E_INVALID_OBJECT_STATE,
5112 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5113 mUserData->s.strName.c_str(), cSnapshots);
5114
5115 // This list collects the medium objects from all medium attachments
5116 // which we will detach from the machine and its snapshots, in a specific
5117 // order which allows for closing all media without getting "media in use"
5118 // errors, simply by going through the list from the front to the back:
5119 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5120 // and must be closed before the parent media from the snapshots, or closing the parents
5121 // will fail because they still have children);
5122 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5123 // the root ("first") snapshot of the machine.
5124 MediaList llMedia;
5125
5126 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5127 && mMediaData->mAttachments.size()
5128 )
5129 {
5130 // we have media attachments: detach them all and add the Medium objects to our list
5131 if (aCleanupMode != CleanupMode_UnregisterOnly)
5132 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5133 else
5134 return setError(VBOX_E_INVALID_OBJECT_STATE,
5135 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5136 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5137 }
5138
5139 if (cSnapshots)
5140 {
5141 // add the media from the medium attachments of the snapshots to llMedia
5142 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5143 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5144 // into the children first
5145
5146 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5147 MachineState_T oldState = mData->mMachineState;
5148 mData->mMachineState = MachineState_DeletingSnapshot;
5149
5150 // make a copy of the first snapshot so the refcount does not drop to 0
5151 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5152 // because of the AutoCaller voodoo)
5153 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5154
5155 // GO!
5156 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5157
5158 mData->mMachineState = oldState;
5159 }
5160
5161 if (FAILED(rc))
5162 {
5163 i_rollbackMedia();
5164 return rc;
5165 }
5166
5167 // commit all the media changes made above
5168 i_commitMedia();
5169
5170 mData->mRegistered = false;
5171
5172 // machine lock no longer needed
5173 alock.release();
5174
5175 // return media to caller
5176 size_t i = 0;
5177 aMedia.resize(llMedia.size());
5178 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5179 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5180
5181 mParent->i_unregisterMachine(this, id);
5182 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5183
5184 return S_OK;
5185}
5186
5187/**
5188 * Task record for deleting a machine config.
5189 */
5190struct Machine::DeleteConfigTask
5191 : public Machine::Task
5192{
5193 DeleteConfigTask(Machine *m,
5194 Progress *p,
5195 const Utf8Str &t,
5196 const RTCList<ComPtr<IMedium> > &llMediums,
5197 const StringsList &llFilesToDelete)
5198 : Task(m, p, t),
5199 m_llMediums(llMediums),
5200 m_llFilesToDelete(llFilesToDelete)
5201 {}
5202
5203 void handler()
5204 {
5205 m_pMachine->i_deleteConfigHandler(*this);
5206 }
5207
5208 RTCList<ComPtr<IMedium> > m_llMediums;
5209 StringsList m_llFilesToDelete;
5210};
5211
5212/**
5213 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5214 * SessionMachine::taskHandler().
5215 *
5216 * @note Locks this object for writing.
5217 *
5218 * @param task
5219 * @return
5220 */
5221void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5222{
5223 LogFlowThisFuncEnter();
5224
5225 AutoCaller autoCaller(this);
5226 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5227 if (FAILED(autoCaller.rc()))
5228 {
5229 /* we might have been uninitialized because the session was accidentally
5230 * closed by the client, so don't assert */
5231 HRESULT rc = setError(E_FAIL,
5232 tr("The session has been accidentally closed"));
5233 task.m_pProgress->i_notifyComplete(rc);
5234 LogFlowThisFuncLeave();
5235 return;
5236 }
5237
5238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5239
5240 HRESULT rc = S_OK;
5241
5242 try
5243 {
5244 ULONG uLogHistoryCount = 3;
5245 ComPtr<ISystemProperties> systemProperties;
5246 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5247 if (FAILED(rc)) throw rc;
5248
5249 if (!systemProperties.isNull())
5250 {
5251 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5252 if (FAILED(rc)) throw rc;
5253 }
5254
5255 MachineState_T oldState = mData->mMachineState;
5256 i_setMachineState(MachineState_SettingUp);
5257 alock.release();
5258 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5259 {
5260 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5261 {
5262 AutoCaller mac(pMedium);
5263 if (FAILED(mac.rc())) throw mac.rc();
5264 Utf8Str strLocation = pMedium->i_getLocationFull();
5265 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5266 if (FAILED(rc)) throw rc;
5267 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5268 }
5269 if (pMedium->i_isMediumFormatFile())
5270 {
5271 ComPtr<IProgress> pProgress2;
5272 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5273 if (FAILED(rc)) throw rc;
5274 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5275 if (FAILED(rc)) throw rc;
5276 /* Check the result of the asynchronous process. */
5277 LONG iRc;
5278 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5279 if (FAILED(rc)) throw rc;
5280 /* If the thread of the progress object has an error, then
5281 * retrieve the error info from there, or it'll be lost. */
5282 if (FAILED(iRc))
5283 throw setError(ProgressErrorInfo(pProgress2));
5284 }
5285
5286 /* Close the medium, deliberately without checking the return
5287 * code, and without leaving any trace in the error info, as
5288 * a failure here is a very minor issue, which shouldn't happen
5289 * as above we even managed to delete the medium. */
5290 {
5291 ErrorInfoKeeper eik;
5292 pMedium->Close();
5293 }
5294 }
5295 i_setMachineState(oldState);
5296 alock.acquire();
5297
5298 // delete the files pushed on the task list by Machine::Delete()
5299 // (this includes saved states of the machine and snapshots and
5300 // medium storage files from the IMedium list passed in, and the
5301 // machine XML file)
5302 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5303 while (it != task.m_llFilesToDelete.end())
5304 {
5305 const Utf8Str &strFile = *it;
5306 LogFunc(("Deleting file %s\n", strFile.c_str()));
5307 int vrc = RTFileDelete(strFile.c_str());
5308 if (RT_FAILURE(vrc))
5309 throw setError(VBOX_E_IPRT_ERROR,
5310 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5311
5312 ++it;
5313 if (it == task.m_llFilesToDelete.end())
5314 {
5315 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5316 if (FAILED(rc)) throw rc;
5317 break;
5318 }
5319
5320 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5321 if (FAILED(rc)) throw rc;
5322 }
5323
5324 /* delete the settings only when the file actually exists */
5325 if (mData->pMachineConfigFile->fileExists())
5326 {
5327 /* Delete any backup or uncommitted XML files. Ignore failures.
5328 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5329 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5330 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5331 RTFileDelete(otherXml.c_str());
5332 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5333 RTFileDelete(otherXml.c_str());
5334
5335 /* delete the Logs folder, nothing important should be left
5336 * there (we don't check for errors because the user might have
5337 * some private files there that we don't want to delete) */
5338 Utf8Str logFolder;
5339 getLogFolder(logFolder);
5340 Assert(logFolder.length());
5341 if (RTDirExists(logFolder.c_str()))
5342 {
5343 /* Delete all VBox.log[.N] files from the Logs folder
5344 * (this must be in sync with the rotation logic in
5345 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5346 * files that may have been created by the GUI. */
5347 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5348 logFolder.c_str(), RTPATH_DELIMITER);
5349 RTFileDelete(log.c_str());
5350 log = Utf8StrFmt("%s%cVBox.png",
5351 logFolder.c_str(), RTPATH_DELIMITER);
5352 RTFileDelete(log.c_str());
5353 for (int i = uLogHistoryCount; i > 0; i--)
5354 {
5355 log = Utf8StrFmt("%s%cVBox.log.%d",
5356 logFolder.c_str(), RTPATH_DELIMITER, i);
5357 RTFileDelete(log.c_str());
5358 log = Utf8StrFmt("%s%cVBox.png.%d",
5359 logFolder.c_str(), RTPATH_DELIMITER, i);
5360 RTFileDelete(log.c_str());
5361 }
5362#if defined(RT_OS_WINDOWS)
5363 log = Utf8StrFmt("%s%cVBoxStartup.log",
5364 logFolder.c_str(), RTPATH_DELIMITER);
5365 RTFileDelete(log.c_str());
5366#endif
5367
5368 RTDirRemove(logFolder.c_str());
5369 }
5370
5371 /* delete the Snapshots folder, nothing important should be left
5372 * there (we don't check for errors because the user might have
5373 * some private files there that we don't want to delete) */
5374 Utf8Str strFullSnapshotFolder;
5375 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5376 Assert(!strFullSnapshotFolder.isEmpty());
5377 if (RTDirExists(strFullSnapshotFolder.c_str()))
5378 RTDirRemove(strFullSnapshotFolder.c_str());
5379
5380 // delete the directory that contains the settings file, but only
5381 // if it matches the VM name
5382 Utf8Str settingsDir;
5383 if (i_isInOwnDir(&settingsDir))
5384 RTDirRemove(settingsDir.c_str());
5385 }
5386
5387 alock.release();
5388
5389 mParent->i_saveModifiedRegistries();
5390 }
5391 catch (HRESULT aRC) { rc = aRC; }
5392
5393 task.m_pProgress->i_notifyComplete(rc);
5394
5395 LogFlowThisFuncLeave();
5396}
5397
5398HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5399{
5400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 HRESULT rc = i_checkStateDependency(MutableStateDep);
5403 if (FAILED(rc)) return rc;
5404
5405 if (mData->mRegistered)
5406 return setError(VBOX_E_INVALID_VM_STATE,
5407 tr("Cannot delete settings of a registered machine"));
5408
5409 // collect files to delete
5410 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5411 if (mData->pMachineConfigFile->fileExists())
5412 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5413
5414 RTCList<ComPtr<IMedium> > llMediums;
5415 for (size_t i = 0; i < aMedia.size(); ++i)
5416 {
5417 IMedium *pIMedium(aMedia[i]);
5418 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5419 if (pMedium.isNull())
5420 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5421 SafeArray<BSTR> ids;
5422 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5423 if (FAILED(rc)) return rc;
5424 /* At this point the medium should not have any back references
5425 * anymore. If it has it is attached to another VM and *must* not
5426 * deleted. */
5427 if (ids.size() < 1)
5428 llMediums.append(pMedium);
5429 }
5430
5431 ComObjPtr<Progress> pProgress;
5432 pProgress.createObject();
5433 rc = pProgress->init(i_getVirtualBox(),
5434 static_cast<IMachine*>(this) /* aInitiator */,
5435 Bstr(tr("Deleting files")).raw(),
5436 true /* fCancellable */,
5437 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5438 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5439 if (FAILED(rc))
5440 return rc;
5441
5442 /* create and start the task on a separate thread (note that it will not
5443 * start working until we release alock) */
5444 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5445 rc = pTask->createThread();
5446 if (FAILED(rc))
5447 return rc;
5448
5449 pProgress.queryInterfaceTo(aProgress.asOutParam());
5450
5451 LogFlowFuncLeave();
5452
5453 return S_OK;
5454}
5455
5456HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5457{
5458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5459
5460 ComObjPtr<Snapshot> pSnapshot;
5461 HRESULT rc;
5462
5463 if (aNameOrId.isEmpty())
5464 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5465 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5466 else
5467 {
5468 Guid uuid(aNameOrId);
5469 if (uuid.isValid())
5470 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5471 else
5472 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5473 }
5474 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5475
5476 return rc;
5477}
5478
5479HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5480{
5481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5482
5483 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5484 if (FAILED(rc)) return rc;
5485
5486 ComObjPtr<SharedFolder> sharedFolder;
5487 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5488 if (SUCCEEDED(rc))
5489 return setError(VBOX_E_OBJECT_IN_USE,
5490 tr("Shared folder named '%s' already exists"),
5491 aName.c_str());
5492
5493 sharedFolder.createObject();
5494 rc = sharedFolder->init(i_getMachine(),
5495 aName,
5496 aHostPath,
5497 !!aWritable,
5498 !!aAutomount,
5499 true /* fFailOnError */);
5500 if (FAILED(rc)) return rc;
5501
5502 i_setModified(IsModified_SharedFolders);
5503 mHWData.backup();
5504 mHWData->mSharedFolders.push_back(sharedFolder);
5505
5506 /* inform the direct session if any */
5507 alock.release();
5508 i_onSharedFolderChange();
5509
5510 return S_OK;
5511}
5512
5513HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5514{
5515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5516
5517 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5518 if (FAILED(rc)) return rc;
5519
5520 ComObjPtr<SharedFolder> sharedFolder;
5521 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5522 if (FAILED(rc)) return rc;
5523
5524 i_setModified(IsModified_SharedFolders);
5525 mHWData.backup();
5526 mHWData->mSharedFolders.remove(sharedFolder);
5527
5528 /* inform the direct session if any */
5529 alock.release();
5530 i_onSharedFolderChange();
5531
5532 return S_OK;
5533}
5534
5535HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5536{
5537 /* start with No */
5538 *aCanShow = FALSE;
5539
5540 ComPtr<IInternalSessionControl> directControl;
5541 {
5542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5543
5544 if (mData->mSession.mState != SessionState_Locked)
5545 return setError(VBOX_E_INVALID_VM_STATE,
5546 tr("Machine is not locked for session (session state: %s)"),
5547 Global::stringifySessionState(mData->mSession.mState));
5548
5549 if (mData->mSession.mLockType == LockType_VM)
5550 directControl = mData->mSession.mDirectControl;
5551 }
5552
5553 /* ignore calls made after #OnSessionEnd() is called */
5554 if (!directControl)
5555 return S_OK;
5556
5557 LONG64 dummy;
5558 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5559}
5560
5561HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5562{
5563 ComPtr<IInternalSessionControl> directControl;
5564 {
5565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5566
5567 if (mData->mSession.mState != SessionState_Locked)
5568 return setError(E_FAIL,
5569 tr("Machine is not locked for session (session state: %s)"),
5570 Global::stringifySessionState(mData->mSession.mState));
5571
5572 if (mData->mSession.mLockType == LockType_VM)
5573 directControl = mData->mSession.mDirectControl;
5574 }
5575
5576 /* ignore calls made after #OnSessionEnd() is called */
5577 if (!directControl)
5578 return S_OK;
5579
5580 BOOL dummy;
5581 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5582}
5583
5584#ifdef VBOX_WITH_GUEST_PROPS
5585/**
5586 * Look up a guest property in VBoxSVC's internal structures.
5587 */
5588HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5589 com::Utf8Str &aValue,
5590 LONG64 *aTimestamp,
5591 com::Utf8Str &aFlags) const
5592{
5593 using namespace guestProp;
5594
5595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5596 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5597
5598 if (it != mHWData->mGuestProperties.end())
5599 {
5600 char szFlags[MAX_FLAGS_LEN + 1];
5601 aValue = it->second.strValue;
5602 *aTimestamp = it->second.mTimestamp;
5603 writeFlags(it->second.mFlags, szFlags);
5604 aFlags = Utf8Str(szFlags);
5605 }
5606
5607 return S_OK;
5608}
5609
5610/**
5611 * Query the VM that a guest property belongs to for the property.
5612 * @returns E_ACCESSDENIED if the VM process is not available or not
5613 * currently handling queries and the lookup should then be done in
5614 * VBoxSVC.
5615 */
5616HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5617 com::Utf8Str &aValue,
5618 LONG64 *aTimestamp,
5619 com::Utf8Str &aFlags) const
5620{
5621 HRESULT rc = S_OK;
5622 BSTR bValue = NULL;
5623 BSTR bFlags = NULL;
5624
5625 ComPtr<IInternalSessionControl> directControl;
5626 {
5627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5628 if (mData->mSession.mLockType == LockType_VM)
5629 directControl = mData->mSession.mDirectControl;
5630 }
5631
5632 /* ignore calls made after #OnSessionEnd() is called */
5633 if (!directControl)
5634 rc = E_ACCESSDENIED;
5635 else
5636 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5637 0 /* accessMode */,
5638 &bValue, aTimestamp, &bFlags);
5639
5640 aValue = bValue;
5641 aFlags = bFlags;
5642
5643 return rc;
5644}
5645#endif // VBOX_WITH_GUEST_PROPS
5646
5647HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5648 com::Utf8Str &aValue,
5649 LONG64 *aTimestamp,
5650 com::Utf8Str &aFlags)
5651{
5652#ifndef VBOX_WITH_GUEST_PROPS
5653 ReturnComNotImplemented();
5654#else // VBOX_WITH_GUEST_PROPS
5655
5656 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5657
5658 if (rc == E_ACCESSDENIED)
5659 /* The VM is not running or the service is not (yet) accessible */
5660 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5661 return rc;
5662#endif // VBOX_WITH_GUEST_PROPS
5663}
5664
5665HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5666{
5667 LONG64 dummyTimestamp;
5668 com::Utf8Str dummyFlags;
5669 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5670 return rc;
5671
5672}
5673HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5674{
5675 com::Utf8Str dummyFlags;
5676 com::Utf8Str dummyValue;
5677 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5678 return rc;
5679}
5680
5681#ifdef VBOX_WITH_GUEST_PROPS
5682/**
5683 * Set a guest property in VBoxSVC's internal structures.
5684 */
5685HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5686 const com::Utf8Str &aFlags, bool fDelete)
5687{
5688 using namespace guestProp;
5689
5690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5691 HRESULT rc = S_OK;
5692
5693 rc = i_checkStateDependency(MutableOrSavedStateDep);
5694 if (FAILED(rc)) return rc;
5695
5696 try
5697 {
5698 uint32_t fFlags = NILFLAG;
5699 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5700 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5701
5702 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5703 if (it == mHWData->mGuestProperties.end())
5704 {
5705 if (!fDelete)
5706 {
5707 i_setModified(IsModified_MachineData);
5708 mHWData.backupEx();
5709
5710 RTTIMESPEC time;
5711 HWData::GuestProperty prop;
5712 prop.strValue = Bstr(aValue).raw();
5713 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5714 prop.mFlags = fFlags;
5715 mHWData->mGuestProperties[aName] = prop;
5716 }
5717 }
5718 else
5719 {
5720 if (it->second.mFlags & (RDONLYHOST))
5721 {
5722 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5723 }
5724 else
5725 {
5726 i_setModified(IsModified_MachineData);
5727 mHWData.backupEx();
5728
5729 /* The backupEx() operation invalidates our iterator,
5730 * so get a new one. */
5731 it = mHWData->mGuestProperties.find(aName);
5732 Assert(it != mHWData->mGuestProperties.end());
5733
5734 if (!fDelete)
5735 {
5736 RTTIMESPEC time;
5737 it->second.strValue = aValue;
5738 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5739 it->second.mFlags = fFlags;
5740 }
5741 else
5742 mHWData->mGuestProperties.erase(it);
5743 }
5744 }
5745
5746 if ( SUCCEEDED(rc)
5747 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5748 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5749 RTSTR_MAX,
5750 aName.c_str(),
5751 RTSTR_MAX,
5752 NULL)
5753 )
5754 )
5755 {
5756 alock.release();
5757
5758 mParent->i_onGuestPropertyChange(mData->mUuid,
5759 Bstr(aName).raw(),
5760 Bstr(aValue).raw(),
5761 Bstr(aFlags).raw());
5762 }
5763 }
5764 catch (std::bad_alloc &)
5765 {
5766 rc = E_OUTOFMEMORY;
5767 }
5768
5769 return rc;
5770}
5771
5772/**
5773 * Set a property on the VM that that property belongs to.
5774 * @returns E_ACCESSDENIED if the VM process is not available or not
5775 * currently handling queries and the setting should then be done in
5776 * VBoxSVC.
5777 */
5778HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5779 const com::Utf8Str &aFlags, bool fDelete)
5780{
5781 HRESULT rc;
5782
5783 try
5784 {
5785 ComPtr<IInternalSessionControl> directControl;
5786 {
5787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5788 if (mData->mSession.mLockType == LockType_VM)
5789 directControl = mData->mSession.mDirectControl;
5790 }
5791
5792 BSTR dummy = NULL; /* will not be changed (setter) */
5793 LONG64 dummy64;
5794 if (!directControl)
5795 rc = E_ACCESSDENIED;
5796 else
5797 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5798 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5799 fDelete? 2: 1 /* accessMode */,
5800 &dummy, &dummy64, &dummy);
5801 }
5802 catch (std::bad_alloc &)
5803 {
5804 rc = E_OUTOFMEMORY;
5805 }
5806
5807 return rc;
5808}
5809#endif // VBOX_WITH_GUEST_PROPS
5810
5811HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5812 const com::Utf8Str &aFlags)
5813{
5814#ifndef VBOX_WITH_GUEST_PROPS
5815 ReturnComNotImplemented();
5816#else // VBOX_WITH_GUEST_PROPS
5817 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5818 if (rc == E_ACCESSDENIED)
5819 /* The VM is not running or the service is not (yet) accessible */
5820 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5821 return rc;
5822#endif // VBOX_WITH_GUEST_PROPS
5823}
5824
5825HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5826{
5827 return setGuestProperty(aProperty, aValue, "");
5828}
5829
5830HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5831{
5832#ifndef VBOX_WITH_GUEST_PROPS
5833 ReturnComNotImplemented();
5834#else // VBOX_WITH_GUEST_PROPS
5835 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5836 if (rc == E_ACCESSDENIED)
5837 /* The VM is not running or the service is not (yet) accessible */
5838 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5839 return rc;
5840#endif // VBOX_WITH_GUEST_PROPS
5841}
5842
5843#ifdef VBOX_WITH_GUEST_PROPS
5844/**
5845 * Enumerate the guest properties in VBoxSVC's internal structures.
5846 */
5847HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5848 std::vector<com::Utf8Str> &aNames,
5849 std::vector<com::Utf8Str> &aValues,
5850 std::vector<LONG64> &aTimestamps,
5851 std::vector<com::Utf8Str> &aFlags)
5852{
5853 using namespace guestProp;
5854
5855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5856 Utf8Str strPatterns(aPatterns);
5857
5858 HWData::GuestPropertyMap propMap;
5859
5860 /*
5861 * Look for matching patterns and build up a list.
5862 */
5863 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5864 while (it != mHWData->mGuestProperties.end())
5865 {
5866 if ( strPatterns.isEmpty()
5867 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5868 RTSTR_MAX,
5869 it->first.c_str(),
5870 RTSTR_MAX,
5871 NULL)
5872 )
5873 propMap.insert(*it);
5874 it++;
5875 }
5876
5877 alock.release();
5878
5879 /*
5880 * And build up the arrays for returning the property information.
5881 */
5882 size_t cEntries = propMap.size();
5883
5884 aNames.resize(cEntries);
5885 aValues.resize(cEntries);
5886 aTimestamps.resize(cEntries);
5887 aFlags.resize(cEntries);
5888
5889 char szFlags[MAX_FLAGS_LEN + 1];
5890 size_t i= 0;
5891 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5892 {
5893 aNames[i] = it->first;
5894 aValues[i] = it->second.strValue;
5895 aTimestamps[i] = it->second.mTimestamp;
5896 writeFlags(it->second.mFlags, szFlags);
5897 aFlags[i] = Utf8Str(szFlags);
5898 }
5899
5900 return S_OK;
5901}
5902
5903/**
5904 * Enumerate the properties managed by a VM.
5905 * @returns E_ACCESSDENIED if the VM process is not available or not
5906 * currently handling queries and the setting should then be done in
5907 * VBoxSVC.
5908 */
5909HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5910 std::vector<com::Utf8Str> &aNames,
5911 std::vector<com::Utf8Str> &aValues,
5912 std::vector<LONG64> &aTimestamps,
5913 std::vector<com::Utf8Str> &aFlags)
5914{
5915 HRESULT rc;
5916 ComPtr<IInternalSessionControl> directControl;
5917 {
5918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5919 if (mData->mSession.mLockType == LockType_VM)
5920 directControl = mData->mSession.mDirectControl;
5921 }
5922
5923 com::SafeArray<BSTR> bNames;
5924 com::SafeArray<BSTR> bValues;
5925 com::SafeArray<LONG64> bTimestamps;
5926 com::SafeArray<BSTR> bFlags;
5927
5928 if (!directControl)
5929 rc = E_ACCESSDENIED;
5930 else
5931 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5932 ComSafeArrayAsOutParam(bNames),
5933 ComSafeArrayAsOutParam(bValues),
5934 ComSafeArrayAsOutParam(bTimestamps),
5935 ComSafeArrayAsOutParam(bFlags));
5936 size_t i;
5937 aNames.resize(bNames.size());
5938 for (i = 0; i < bNames.size(); ++i)
5939 aNames[i] = Utf8Str(bNames[i]);
5940 aValues.resize(bValues.size());
5941 for (i = 0; i < bValues.size(); ++i)
5942 aValues[i] = Utf8Str(bValues[i]);
5943 aTimestamps.resize(bTimestamps.size());
5944 for (i = 0; i < bTimestamps.size(); ++i)
5945 aTimestamps[i] = bTimestamps[i];
5946 aFlags.resize(bFlags.size());
5947 for (i = 0; i < bFlags.size(); ++i)
5948 aFlags[i] = Utf8Str(bFlags[i]);
5949
5950 return rc;
5951}
5952#endif // VBOX_WITH_GUEST_PROPS
5953HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5954 std::vector<com::Utf8Str> &aNames,
5955 std::vector<com::Utf8Str> &aValues,
5956 std::vector<LONG64> &aTimestamps,
5957 std::vector<com::Utf8Str> &aFlags)
5958{
5959#ifndef VBOX_WITH_GUEST_PROPS
5960 ReturnComNotImplemented();
5961#else // VBOX_WITH_GUEST_PROPS
5962
5963 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5964
5965 if (rc == E_ACCESSDENIED)
5966 /* The VM is not running or the service is not (yet) accessible */
5967 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5968 return rc;
5969#endif // VBOX_WITH_GUEST_PROPS
5970}
5971
5972HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5973 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5974{
5975 MediaData::AttachmentList atts;
5976
5977 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5978 if (FAILED(rc)) return rc;
5979
5980 size_t i = 0;
5981 aMediumAttachments.resize(atts.size());
5982 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5983 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5984
5985 return S_OK;
5986}
5987
5988HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5989 LONG aControllerPort,
5990 LONG aDevice,
5991 ComPtr<IMediumAttachment> &aAttachment)
5992{
5993 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5994 aName.c_str(), aControllerPort, aDevice));
5995
5996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5997
5998 aAttachment = NULL;
5999
6000 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6001 Bstr(aName).raw(),
6002 aControllerPort,
6003 aDevice);
6004 if (pAttach.isNull())
6005 return setError(VBOX_E_OBJECT_NOT_FOUND,
6006 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6007 aDevice, aControllerPort, aName.c_str());
6008
6009 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6010
6011 return S_OK;
6012}
6013
6014
6015HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6016 StorageBus_T aConnectionType,
6017 ComPtr<IStorageController> &aController)
6018{
6019 if ( (aConnectionType <= StorageBus_Null)
6020 || (aConnectionType > StorageBus_USB))
6021 return setError(E_INVALIDARG,
6022 tr("Invalid connection type: %d"),
6023 aConnectionType);
6024
6025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6026
6027 HRESULT rc = i_checkStateDependency(MutableStateDep);
6028 if (FAILED(rc)) return rc;
6029
6030 /* try to find one with the name first. */
6031 ComObjPtr<StorageController> ctrl;
6032
6033 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6034 if (SUCCEEDED(rc))
6035 return setError(VBOX_E_OBJECT_IN_USE,
6036 tr("Storage controller named '%s' already exists"),
6037 aName.c_str());
6038
6039 ctrl.createObject();
6040
6041 /* get a new instance number for the storage controller */
6042 ULONG ulInstance = 0;
6043 bool fBootable = true;
6044 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6045 it != mStorageControllers->end();
6046 ++it)
6047 {
6048 if ((*it)->i_getStorageBus() == aConnectionType)
6049 {
6050 ULONG ulCurInst = (*it)->i_getInstance();
6051
6052 if (ulCurInst >= ulInstance)
6053 ulInstance = ulCurInst + 1;
6054
6055 /* Only one controller of each type can be marked as bootable. */
6056 if ((*it)->i_getBootable())
6057 fBootable = false;
6058 }
6059 }
6060
6061 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6062 if (FAILED(rc)) return rc;
6063
6064 i_setModified(IsModified_Storage);
6065 mStorageControllers.backup();
6066 mStorageControllers->push_back(ctrl);
6067
6068 ctrl.queryInterfaceTo(aController.asOutParam());
6069
6070 /* inform the direct session if any */
6071 alock.release();
6072 i_onStorageControllerChange();
6073
6074 return S_OK;
6075}
6076
6077HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6078 ComPtr<IStorageController> &aStorageController)
6079{
6080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6081
6082 ComObjPtr<StorageController> ctrl;
6083
6084 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6085 if (SUCCEEDED(rc))
6086 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6087
6088 return rc;
6089}
6090
6091HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6092 ComPtr<IStorageController> &aStorageController)
6093{
6094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6095
6096 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6097 it != mStorageControllers->end();
6098 ++it)
6099 {
6100 if ((*it)->i_getInstance() == aInstance)
6101 {
6102 (*it).queryInterfaceTo(aStorageController.asOutParam());
6103 return S_OK;
6104 }
6105 }
6106
6107 return setError(VBOX_E_OBJECT_NOT_FOUND,
6108 tr("Could not find a storage controller with instance number '%lu'"),
6109 aInstance);
6110}
6111
6112HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6113{
6114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6115
6116 HRESULT rc = i_checkStateDependency(MutableStateDep);
6117 if (FAILED(rc)) return rc;
6118
6119 ComObjPtr<StorageController> ctrl;
6120
6121 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6122 if (SUCCEEDED(rc))
6123 {
6124 /* Ensure that only one controller of each type is marked as bootable. */
6125 if (aBootable == TRUE)
6126 {
6127 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6128 it != mStorageControllers->end();
6129 ++it)
6130 {
6131 ComObjPtr<StorageController> aCtrl = (*it);
6132
6133 if ( (aCtrl->i_getName() != aName)
6134 && aCtrl->i_getBootable() == TRUE
6135 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6136 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6137 {
6138 aCtrl->i_setBootable(FALSE);
6139 break;
6140 }
6141 }
6142 }
6143
6144 if (SUCCEEDED(rc))
6145 {
6146 ctrl->i_setBootable(aBootable);
6147 i_setModified(IsModified_Storage);
6148 }
6149 }
6150
6151 if (SUCCEEDED(rc))
6152 {
6153 /* inform the direct session if any */
6154 alock.release();
6155 i_onStorageControllerChange();
6156 }
6157
6158 return rc;
6159}
6160
6161HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6162{
6163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6164
6165 HRESULT rc = i_checkStateDependency(MutableStateDep);
6166 if (FAILED(rc)) return rc;
6167
6168 ComObjPtr<StorageController> ctrl;
6169 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6170 if (FAILED(rc)) return rc;
6171
6172 {
6173 /* find all attached devices to the appropriate storage controller and detach them all */
6174 // make a temporary list because detachDevice invalidates iterators into
6175 // mMediaData->mAttachments
6176 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6177
6178 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6179 it != llAttachments2.end();
6180 ++it)
6181 {
6182 MediumAttachment *pAttachTemp = *it;
6183
6184 AutoCaller localAutoCaller(pAttachTemp);
6185 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6186
6187 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6188
6189 if (pAttachTemp->i_getControllerName() == aName)
6190 {
6191 rc = i_detachDevice(pAttachTemp, alock, NULL);
6192 if (FAILED(rc)) return rc;
6193 }
6194 }
6195 }
6196
6197 /* We can remove it now. */
6198 i_setModified(IsModified_Storage);
6199 mStorageControllers.backup();
6200
6201 ctrl->i_unshare();
6202
6203 mStorageControllers->remove(ctrl);
6204
6205 /* inform the direct session if any */
6206 alock.release();
6207 i_onStorageControllerChange();
6208
6209 return S_OK;
6210}
6211
6212HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6213 ComPtr<IUSBController> &aController)
6214{
6215 if ( (aType <= USBControllerType_Null)
6216 || (aType >= USBControllerType_Last))
6217 return setError(E_INVALIDARG,
6218 tr("Invalid USB controller type: %d"),
6219 aType);
6220
6221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6222
6223 HRESULT rc = i_checkStateDependency(MutableStateDep);
6224 if (FAILED(rc)) return rc;
6225
6226 /* try to find one with the same type first. */
6227 ComObjPtr<USBController> ctrl;
6228
6229 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6230 if (SUCCEEDED(rc))
6231 return setError(VBOX_E_OBJECT_IN_USE,
6232 tr("USB controller named '%s' already exists"),
6233 aName.c_str());
6234
6235 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6236 ULONG maxInstances;
6237 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6238 if (FAILED(rc))
6239 return rc;
6240
6241 ULONG cInstances = i_getUSBControllerCountByType(aType);
6242 if (cInstances >= maxInstances)
6243 return setError(E_INVALIDARG,
6244 tr("Too many USB controllers of this type"));
6245
6246 ctrl.createObject();
6247
6248 rc = ctrl->init(this, aName, aType);
6249 if (FAILED(rc)) return rc;
6250
6251 i_setModified(IsModified_USB);
6252 mUSBControllers.backup();
6253 mUSBControllers->push_back(ctrl);
6254
6255 ctrl.queryInterfaceTo(aController.asOutParam());
6256
6257 /* inform the direct session if any */
6258 alock.release();
6259 i_onUSBControllerChange();
6260
6261 return S_OK;
6262}
6263
6264HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6265{
6266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6267
6268 ComObjPtr<USBController> ctrl;
6269
6270 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6271 if (SUCCEEDED(rc))
6272 ctrl.queryInterfaceTo(aController.asOutParam());
6273
6274 return rc;
6275}
6276
6277HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6278 ULONG *aControllers)
6279{
6280 if ( (aType <= USBControllerType_Null)
6281 || (aType >= USBControllerType_Last))
6282 return setError(E_INVALIDARG,
6283 tr("Invalid USB controller type: %d"),
6284 aType);
6285
6286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6287
6288 ComObjPtr<USBController> ctrl;
6289
6290 *aControllers = i_getUSBControllerCountByType(aType);
6291
6292 return S_OK;
6293}
6294
6295HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6296{
6297
6298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6299
6300 HRESULT rc = i_checkStateDependency(MutableStateDep);
6301 if (FAILED(rc)) return rc;
6302
6303 ComObjPtr<USBController> ctrl;
6304 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6305 if (FAILED(rc)) return rc;
6306
6307 i_setModified(IsModified_USB);
6308 mUSBControllers.backup();
6309
6310 ctrl->i_unshare();
6311
6312 mUSBControllers->remove(ctrl);
6313
6314 /* inform the direct session if any */
6315 alock.release();
6316 i_onUSBControllerChange();
6317
6318 return S_OK;
6319}
6320
6321HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6322 ULONG *aOriginX,
6323 ULONG *aOriginY,
6324 ULONG *aWidth,
6325 ULONG *aHeight,
6326 BOOL *aEnabled)
6327{
6328 uint32_t u32OriginX= 0;
6329 uint32_t u32OriginY= 0;
6330 uint32_t u32Width = 0;
6331 uint32_t u32Height = 0;
6332 uint16_t u16Flags = 0;
6333
6334 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6335 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6336 if (RT_FAILURE(vrc))
6337 {
6338#ifdef RT_OS_WINDOWS
6339 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6340 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6341 * So just assign fEnable to TRUE again.
6342 * The right fix would be to change GUI API wrappers to make sure that parameters
6343 * are changed only if API succeeds.
6344 */
6345 *aEnabled = TRUE;
6346#endif
6347 return setError(VBOX_E_IPRT_ERROR,
6348 tr("Saved guest size is not available (%Rrc)"),
6349 vrc);
6350 }
6351
6352 *aOriginX = u32OriginX;
6353 *aOriginY = u32OriginY;
6354 *aWidth = u32Width;
6355 *aHeight = u32Height;
6356 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6357
6358 return S_OK;
6359}
6360
6361HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6362{
6363 if (aScreenId != 0)
6364 return E_NOTIMPL;
6365
6366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6367
6368 uint8_t *pu8Data = NULL;
6369 uint32_t cbData = 0;
6370 uint32_t u32Width = 0;
6371 uint32_t u32Height = 0;
6372
6373 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6374
6375 if (RT_FAILURE(vrc))
6376 return setError(VBOX_E_IPRT_ERROR,
6377 tr("Saved screenshot data is not available (%Rrc)"),
6378 vrc);
6379
6380 *aSize = cbData;
6381 *aWidth = u32Width;
6382 *aHeight = u32Height;
6383
6384 freeSavedDisplayScreenshot(pu8Data);
6385
6386 return S_OK;
6387}
6388
6389HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6390 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6391{
6392 if (aScreenId != 0)
6393 return E_NOTIMPL;
6394
6395 if ( aBitmapFormat != BitmapFormat_BGR0
6396 && aBitmapFormat != BitmapFormat_BGRA
6397 && aBitmapFormat != BitmapFormat_RGBA
6398 && aBitmapFormat != BitmapFormat_PNG)
6399 return setError(E_NOTIMPL,
6400 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6401
6402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6403
6404 uint8_t *pu8Data = NULL;
6405 uint32_t cbData = 0;
6406 uint32_t u32Width = 0;
6407 uint32_t u32Height = 0;
6408
6409 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6410
6411 if (RT_FAILURE(vrc))
6412 return setError(VBOX_E_IPRT_ERROR,
6413 tr("Saved thumbnail data is not available (%Rrc)"),
6414 vrc);
6415
6416 HRESULT hr = S_OK;
6417
6418 *aWidth = u32Width;
6419 *aHeight = u32Height;
6420
6421 if (cbData > 0)
6422 {
6423 /* Convert pixels to the format expected by the API caller. */
6424 if (aBitmapFormat == BitmapFormat_BGR0)
6425 {
6426 /* [0] B, [1] G, [2] R, [3] 0. */
6427 aData.resize(cbData);
6428 memcpy(&aData.front(), pu8Data, cbData);
6429 }
6430 else if (aBitmapFormat == BitmapFormat_BGRA)
6431 {
6432 /* [0] B, [1] G, [2] R, [3] A. */
6433 aData.resize(cbData);
6434 for (uint32_t i = 0; i < cbData; i += 4)
6435 {
6436 aData[i] = pu8Data[i];
6437 aData[i + 1] = pu8Data[i + 1];
6438 aData[i + 2] = pu8Data[i + 2];
6439 aData[i + 3] = 0xff;
6440 }
6441 }
6442 else if (aBitmapFormat == BitmapFormat_RGBA)
6443 {
6444 /* [0] R, [1] G, [2] B, [3] A. */
6445 aData.resize(cbData);
6446 for (uint32_t i = 0; i < cbData; i += 4)
6447 {
6448 aData[i] = pu8Data[i + 2];
6449 aData[i + 1] = pu8Data[i + 1];
6450 aData[i + 2] = pu8Data[i];
6451 aData[i + 3] = 0xff;
6452 }
6453 }
6454 else if (aBitmapFormat == BitmapFormat_PNG)
6455 {
6456 uint8_t *pu8PNG = NULL;
6457 uint32_t cbPNG = 0;
6458 uint32_t cxPNG = 0;
6459 uint32_t cyPNG = 0;
6460
6461 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6462
6463 if (RT_SUCCESS(vrc))
6464 {
6465 aData.resize(cbPNG);
6466 if (cbPNG)
6467 memcpy(&aData.front(), pu8PNG, cbPNG);
6468 }
6469 else
6470 hr = setError(VBOX_E_IPRT_ERROR,
6471 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6472 vrc);
6473
6474 RTMemFree(pu8PNG);
6475 }
6476 }
6477
6478 freeSavedDisplayScreenshot(pu8Data);
6479
6480 return hr;
6481}
6482
6483HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6484{
6485 if (aScreenId != 0)
6486 return E_NOTIMPL;
6487
6488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 uint8_t *pu8Data = NULL;
6491 uint32_t cbData = 0;
6492 uint32_t u32Width = 0;
6493 uint32_t u32Height = 0;
6494
6495 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6496
6497 if (RT_FAILURE(vrc))
6498 return setError(VBOX_E_IPRT_ERROR,
6499 tr("Saved screenshot data is not available (%Rrc)"),
6500 vrc);
6501
6502 *aSize = cbData;
6503 *aWidth = u32Width;
6504 *aHeight = u32Height;
6505
6506 freeSavedDisplayScreenshot(pu8Data);
6507
6508 return S_OK;
6509}
6510
6511HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6512{
6513 if (aScreenId != 0)
6514 return E_NOTIMPL;
6515
6516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6517
6518 uint8_t *pu8Data = NULL;
6519 uint32_t cbData = 0;
6520 uint32_t u32Width = 0;
6521 uint32_t u32Height = 0;
6522
6523 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6524
6525 if (RT_FAILURE(vrc))
6526 return setError(VBOX_E_IPRT_ERROR,
6527 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6528 vrc);
6529
6530 *aWidth = u32Width;
6531 *aHeight = u32Height;
6532
6533 aData.resize(cbData);
6534 if (cbData)
6535 memcpy(&aData.front(), pu8Data, cbData);
6536
6537 freeSavedDisplayScreenshot(pu8Data);
6538
6539 return S_OK;
6540}
6541
6542HRESULT Machine::hotPlugCPU(ULONG aCpu)
6543{
6544 HRESULT rc = S_OK;
6545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6546
6547 if (!mHWData->mCPUHotPlugEnabled)
6548 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6549
6550 if (aCpu >= mHWData->mCPUCount)
6551 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6552
6553 if (mHWData->mCPUAttached[aCpu])
6554 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6555
6556 alock.release();
6557 rc = i_onCPUChange(aCpu, false);
6558 alock.acquire();
6559 if (FAILED(rc)) return rc;
6560
6561 i_setModified(IsModified_MachineData);
6562 mHWData.backup();
6563 mHWData->mCPUAttached[aCpu] = true;
6564
6565 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6566 if (Global::IsOnline(mData->mMachineState))
6567 i_saveSettings(NULL);
6568
6569 return S_OK;
6570}
6571
6572HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6573{
6574 HRESULT rc = S_OK;
6575
6576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6577
6578 if (!mHWData->mCPUHotPlugEnabled)
6579 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6580
6581 if (aCpu >= SchemaDefs::MaxCPUCount)
6582 return setError(E_INVALIDARG,
6583 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6584 SchemaDefs::MaxCPUCount);
6585
6586 if (!mHWData->mCPUAttached[aCpu])
6587 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6588
6589 /* CPU 0 can't be detached */
6590 if (aCpu == 0)
6591 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6592
6593 alock.release();
6594 rc = i_onCPUChange(aCpu, true);
6595 alock.acquire();
6596 if (FAILED(rc)) return rc;
6597
6598 i_setModified(IsModified_MachineData);
6599 mHWData.backup();
6600 mHWData->mCPUAttached[aCpu] = false;
6601
6602 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6603 if (Global::IsOnline(mData->mMachineState))
6604 i_saveSettings(NULL);
6605
6606 return S_OK;
6607}
6608
6609HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6610{
6611 *aAttached = false;
6612
6613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6614
6615 /* If hotplug is enabled the CPU is always enabled. */
6616 if (!mHWData->mCPUHotPlugEnabled)
6617 {
6618 if (aCpu < mHWData->mCPUCount)
6619 *aAttached = true;
6620 }
6621 else
6622 {
6623 if (aCpu < SchemaDefs::MaxCPUCount)
6624 *aAttached = mHWData->mCPUAttached[aCpu];
6625 }
6626
6627 return S_OK;
6628}
6629
6630HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6631{
6632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6633
6634 Utf8Str log = i_queryLogFilename(aIdx);
6635 if (!RTFileExists(log.c_str()))
6636 log.setNull();
6637 aFilename = log;
6638
6639 return S_OK;
6640}
6641
6642HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6643{
6644 if (aSize < 0)
6645 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6646
6647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6648
6649 HRESULT rc = S_OK;
6650 Utf8Str log = i_queryLogFilename(aIdx);
6651
6652 /* do not unnecessarily hold the lock while doing something which does
6653 * not need the lock and potentially takes a long time. */
6654 alock.release();
6655
6656 /* Limit the chunk size to 32K for now, as that gives better performance
6657 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6658 * One byte expands to approx. 25 bytes of breathtaking XML. */
6659 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6660 aData.resize(cbData);
6661
6662 RTFILE LogFile;
6663 int vrc = RTFileOpen(&LogFile, log.c_str(),
6664 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6665 if (RT_SUCCESS(vrc))
6666 {
6667 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6668 if (RT_SUCCESS(vrc))
6669 aData.resize(cbData);
6670 else
6671 rc = setError(VBOX_E_IPRT_ERROR,
6672 tr("Could not read log file '%s' (%Rrc)"),
6673 log.c_str(), vrc);
6674 RTFileClose(LogFile);
6675 }
6676 else
6677 rc = setError(VBOX_E_IPRT_ERROR,
6678 tr("Could not open log file '%s' (%Rrc)"),
6679 log.c_str(), vrc);
6680
6681 if (FAILED(rc))
6682 aData.resize(0);
6683
6684 return rc;
6685}
6686
6687
6688/**
6689 * Currently this method doesn't attach device to the running VM,
6690 * just makes sure it's plugged on next VM start.
6691 */
6692HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6693{
6694 // lock scope
6695 {
6696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 HRESULT rc = i_checkStateDependency(MutableStateDep);
6699 if (FAILED(rc)) return rc;
6700
6701 ChipsetType_T aChipset = ChipsetType_PIIX3;
6702 COMGETTER(ChipsetType)(&aChipset);
6703
6704 if (aChipset != ChipsetType_ICH9)
6705 {
6706 return setError(E_INVALIDARG,
6707 tr("Host PCI attachment only supported with ICH9 chipset"));
6708 }
6709
6710 // check if device with this host PCI address already attached
6711 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6712 it != mHWData->mPCIDeviceAssignments.end();
6713 ++it)
6714 {
6715 LONG iHostAddress = -1;
6716 ComPtr<PCIDeviceAttachment> pAttach;
6717 pAttach = *it;
6718 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6719 if (iHostAddress == aHostAddress)
6720 return setError(E_INVALIDARG,
6721 tr("Device with host PCI address already attached to this VM"));
6722 }
6723
6724 ComObjPtr<PCIDeviceAttachment> pda;
6725 char name[32];
6726
6727 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6728 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6729 Bstr bname(name);
6730 pda.createObject();
6731 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6732 i_setModified(IsModified_MachineData);
6733 mHWData.backup();
6734 mHWData->mPCIDeviceAssignments.push_back(pda);
6735 }
6736
6737 return S_OK;
6738}
6739
6740/**
6741 * Currently this method doesn't detach device from the running VM,
6742 * just makes sure it's not plugged on next VM start.
6743 */
6744HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6745{
6746 ComObjPtr<PCIDeviceAttachment> pAttach;
6747 bool fRemoved = false;
6748 HRESULT rc;
6749
6750 // lock scope
6751 {
6752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6753
6754 rc = i_checkStateDependency(MutableStateDep);
6755 if (FAILED(rc)) return rc;
6756
6757 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6758 it != mHWData->mPCIDeviceAssignments.end();
6759 ++it)
6760 {
6761 LONG iHostAddress = -1;
6762 pAttach = *it;
6763 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6764 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6765 {
6766 i_setModified(IsModified_MachineData);
6767 mHWData.backup();
6768 mHWData->mPCIDeviceAssignments.remove(pAttach);
6769 fRemoved = true;
6770 break;
6771 }
6772 }
6773 }
6774
6775
6776 /* Fire event outside of the lock */
6777 if (fRemoved)
6778 {
6779 Assert(!pAttach.isNull());
6780 ComPtr<IEventSource> es;
6781 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6782 Assert(SUCCEEDED(rc));
6783 Bstr mid;
6784 rc = this->COMGETTER(Id)(mid.asOutParam());
6785 Assert(SUCCEEDED(rc));
6786 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6787 }
6788
6789 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6790 tr("No host PCI device %08x attached"),
6791 aHostAddress
6792 );
6793}
6794
6795HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6796{
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6800
6801 size_t i = 0;
6802 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6803 it != mHWData->mPCIDeviceAssignments.end();
6804 ++i, ++it)
6805 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6806
6807 return S_OK;
6808}
6809
6810HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6811{
6812 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6813
6814 return S_OK;
6815}
6816
6817HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6818{
6819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6820
6821 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6827{
6828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6829 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6830 if (SUCCEEDED(hrc))
6831 {
6832 hrc = mHWData.backupEx();
6833 if (SUCCEEDED(hrc))
6834 {
6835 i_setModified(IsModified_MachineData);
6836 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6837 }
6838 }
6839 return hrc;
6840}
6841
6842HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6843{
6844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6845 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6846 return S_OK;
6847}
6848
6849HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6850{
6851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6852 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6853 if (SUCCEEDED(hrc))
6854 {
6855 hrc = mHWData.backupEx();
6856 if (SUCCEEDED(hrc))
6857 {
6858 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6859 if (SUCCEEDED(hrc))
6860 i_setModified(IsModified_MachineData);
6861 }
6862 }
6863 return hrc;
6864}
6865
6866HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6867{
6868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6869
6870 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6871
6872 return S_OK;
6873}
6874
6875HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6876{
6877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6878 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6879 if (SUCCEEDED(hrc))
6880 {
6881 hrc = mHWData.backupEx();
6882 if (SUCCEEDED(hrc))
6883 {
6884 i_setModified(IsModified_MachineData);
6885 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6886 }
6887 }
6888 return hrc;
6889}
6890
6891HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6892{
6893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6894
6895 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6896
6897 return S_OK;
6898}
6899
6900HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6901{
6902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6905 if ( SUCCEEDED(hrc)
6906 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6907 {
6908 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6909 int vrc;
6910
6911 if (aAutostartEnabled)
6912 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6913 else
6914 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6915
6916 if (RT_SUCCESS(vrc))
6917 {
6918 hrc = mHWData.backupEx();
6919 if (SUCCEEDED(hrc))
6920 {
6921 i_setModified(IsModified_MachineData);
6922 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6923 }
6924 }
6925 else if (vrc == VERR_NOT_SUPPORTED)
6926 hrc = setError(VBOX_E_NOT_SUPPORTED,
6927 tr("The VM autostart feature is not supported on this platform"));
6928 else if (vrc == VERR_PATH_NOT_FOUND)
6929 hrc = setError(E_FAIL,
6930 tr("The path to the autostart database is not set"));
6931 else
6932 hrc = setError(E_UNEXPECTED,
6933 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6934 aAutostartEnabled ? "Adding" : "Removing",
6935 mUserData->s.strName.c_str(), vrc);
6936 }
6937 return hrc;
6938}
6939
6940HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6941{
6942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6943
6944 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6945
6946 return S_OK;
6947}
6948
6949HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6950{
6951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6952 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6953 if (SUCCEEDED(hrc))
6954 {
6955 hrc = mHWData.backupEx();
6956 if (SUCCEEDED(hrc))
6957 {
6958 i_setModified(IsModified_MachineData);
6959 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6960 }
6961 }
6962 return hrc;
6963}
6964
6965HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6966{
6967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6968
6969 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6970
6971 return S_OK;
6972}
6973
6974HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6975{
6976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6977 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6978 if ( SUCCEEDED(hrc)
6979 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6980 {
6981 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6982 int vrc;
6983
6984 if (aAutostopType != AutostopType_Disabled)
6985 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6986 else
6987 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6988
6989 if (RT_SUCCESS(vrc))
6990 {
6991 hrc = mHWData.backupEx();
6992 if (SUCCEEDED(hrc))
6993 {
6994 i_setModified(IsModified_MachineData);
6995 mHWData->mAutostart.enmAutostopType = aAutostopType;
6996 }
6997 }
6998 else if (vrc == VERR_NOT_SUPPORTED)
6999 hrc = setError(VBOX_E_NOT_SUPPORTED,
7000 tr("The VM autostop feature is not supported on this platform"));
7001 else if (vrc == VERR_PATH_NOT_FOUND)
7002 hrc = setError(E_FAIL,
7003 tr("The path to the autostart database is not set"));
7004 else
7005 hrc = setError(E_UNEXPECTED,
7006 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7007 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7008 mUserData->s.strName.c_str(), vrc);
7009 }
7010 return hrc;
7011}
7012
7013HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7014{
7015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7016
7017 aDefaultFrontend = mHWData->mDefaultFrontend;
7018
7019 return S_OK;
7020}
7021
7022HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7023{
7024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7025 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7026 if (SUCCEEDED(hrc))
7027 {
7028 hrc = mHWData.backupEx();
7029 if (SUCCEEDED(hrc))
7030 {
7031 i_setModified(IsModified_MachineData);
7032 mHWData->mDefaultFrontend = aDefaultFrontend;
7033 }
7034 }
7035 return hrc;
7036}
7037
7038HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7039{
7040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7041 size_t cbIcon = mUserData->mIcon.size();
7042 aIcon.resize(cbIcon);
7043 if (cbIcon)
7044 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7045 return S_OK;
7046}
7047
7048HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7049{
7050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7051 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7052 if (SUCCEEDED(hrc))
7053 {
7054 i_setModified(IsModified_MachineData);
7055 mUserData.backup();
7056 size_t cbIcon = aIcon.size();
7057 mUserData->mIcon.resize(cbIcon);
7058 if (cbIcon)
7059 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7060 }
7061 return hrc;
7062}
7063
7064HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7065{
7066#ifdef VBOX_WITH_USB
7067 *aUSBProxyAvailable = true;
7068#else
7069 *aUSBProxyAvailable = false;
7070#endif
7071 return S_OK;
7072}
7073
7074HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7075 ComPtr<IProgress> &aProgress)
7076{
7077 ComObjPtr<Progress> pP;
7078 Progress *ppP = pP;
7079 IProgress *iP = static_cast<IProgress *>(ppP);
7080 IProgress **pProgress = &iP;
7081
7082 IMachine *pTarget = aTarget;
7083
7084 /* Convert the options. */
7085 RTCList<CloneOptions_T> optList;
7086 if (aOptions.size())
7087 for (size_t i = 0; i < aOptions.size(); ++i)
7088 optList.append(aOptions[i]);
7089
7090 if (optList.contains(CloneOptions_Link))
7091 {
7092 if (!i_isSnapshotMachine())
7093 return setError(E_INVALIDARG,
7094 tr("Linked clone can only be created from a snapshot"));
7095 if (aMode != CloneMode_MachineState)
7096 return setError(E_INVALIDARG,
7097 tr("Linked clone can only be created for a single machine state"));
7098 }
7099 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7100
7101 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7102
7103 HRESULT rc = pWorker->start(pProgress);
7104
7105 pP = static_cast<Progress *>(*pProgress);
7106 pP.queryInterfaceTo(aProgress.asOutParam());
7107
7108 return rc;
7109
7110}
7111
7112HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7113{
7114 NOREF(aProgress);
7115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7116
7117 // This check should always fail.
7118 HRESULT rc = i_checkStateDependency(MutableStateDep);
7119 if (FAILED(rc)) return rc;
7120
7121 AssertFailedReturn(E_NOTIMPL);
7122}
7123
7124HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7125{
7126 NOREF(aSavedStateFile);
7127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7128
7129 // This check should always fail.
7130 HRESULT rc = i_checkStateDependency(MutableStateDep);
7131 if (FAILED(rc)) return rc;
7132
7133 AssertFailedReturn(E_NOTIMPL);
7134}
7135
7136HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7137{
7138 NOREF(aFRemoveFile);
7139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7140
7141 // This check should always fail.
7142 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7143 if (FAILED(rc)) return rc;
7144
7145 AssertFailedReturn(E_NOTIMPL);
7146}
7147
7148// public methods for internal purposes
7149/////////////////////////////////////////////////////////////////////////////
7150
7151/**
7152 * Adds the given IsModified_* flag to the dirty flags of the machine.
7153 * This must be called either during i_loadSettings or under the machine write lock.
7154 * @param fl
7155 */
7156void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7157{
7158 mData->flModifications |= fl;
7159 if (fAllowStateModification && i_isStateModificationAllowed())
7160 mData->mCurrentStateModified = true;
7161}
7162
7163/**
7164 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7165 * care of the write locking.
7166 *
7167 * @param fModifications The flag to add.
7168 */
7169void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7170{
7171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7172 i_setModified(fModification, fAllowStateModification);
7173}
7174
7175/**
7176 * Saves the registry entry of this machine to the given configuration node.
7177 *
7178 * @param aEntryNode Node to save the registry entry to.
7179 *
7180 * @note locks this object for reading.
7181 */
7182HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7183{
7184 AutoLimitedCaller autoCaller(this);
7185 AssertComRCReturnRC(autoCaller.rc());
7186
7187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7188
7189 data.uuid = mData->mUuid;
7190 data.strSettingsFile = mData->m_strConfigFile;
7191
7192 return S_OK;
7193}
7194
7195/**
7196 * Calculates the absolute path of the given path taking the directory of the
7197 * machine settings file as the current directory.
7198 *
7199 * @param aPath Path to calculate the absolute path for.
7200 * @param aResult Where to put the result (used only on success, can be the
7201 * same Utf8Str instance as passed in @a aPath).
7202 * @return IPRT result.
7203 *
7204 * @note Locks this object for reading.
7205 */
7206int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7207{
7208 AutoCaller autoCaller(this);
7209 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7210
7211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7212
7213 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7214
7215 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7216
7217 strSettingsDir.stripFilename();
7218 char folder[RTPATH_MAX];
7219 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7220 if (RT_SUCCESS(vrc))
7221 aResult = folder;
7222
7223 return vrc;
7224}
7225
7226/**
7227 * Copies strSource to strTarget, making it relative to the machine folder
7228 * if it is a subdirectory thereof, or simply copying it otherwise.
7229 *
7230 * @param strSource Path to evaluate and copy.
7231 * @param strTarget Buffer to receive target path.
7232 *
7233 * @note Locks this object for reading.
7234 */
7235void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7236 Utf8Str &strTarget)
7237{
7238 AutoCaller autoCaller(this);
7239 AssertComRCReturn(autoCaller.rc(), (void)0);
7240
7241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7242
7243 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7244 // use strTarget as a temporary buffer to hold the machine settings dir
7245 strTarget = mData->m_strConfigFileFull;
7246 strTarget.stripFilename();
7247 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7248 {
7249 // is relative: then append what's left
7250 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7251 // for empty paths (only possible for subdirs) use "." to avoid
7252 // triggering default settings for not present config attributes.
7253 if (strTarget.isEmpty())
7254 strTarget = ".";
7255 }
7256 else
7257 // is not relative: then overwrite
7258 strTarget = strSource;
7259}
7260
7261/**
7262 * Returns the full path to the machine's log folder in the
7263 * \a aLogFolder argument.
7264 */
7265void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7266{
7267 AutoCaller autoCaller(this);
7268 AssertComRCReturnVoid(autoCaller.rc());
7269
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271
7272 char szTmp[RTPATH_MAX];
7273 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7274 if (RT_SUCCESS(vrc))
7275 {
7276 if (szTmp[0] && !mUserData.isNull())
7277 {
7278 char szTmp2[RTPATH_MAX];
7279 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7280 if (RT_SUCCESS(vrc))
7281 aLogFolder = BstrFmt("%s%c%s",
7282 szTmp2,
7283 RTPATH_DELIMITER,
7284 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7285 }
7286 else
7287 vrc = VERR_PATH_IS_RELATIVE;
7288 }
7289
7290 if (RT_FAILURE(vrc))
7291 {
7292 // fallback if VBOX_USER_LOGHOME is not set or invalid
7293 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7294 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7295 aLogFolder.append(RTPATH_DELIMITER);
7296 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7297 }
7298}
7299
7300/**
7301 * Returns the full path to the machine's log file for an given index.
7302 */
7303Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7304 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7305{
7306 Utf8Str logFolder;
7307 getLogFolder(logFolder);
7308 Assert(logFolder.length());
7309 Utf8Str log;
7310 if (idx == 0)
7311 log = Utf8StrFmt("%s%cVBox.log",
7312 logFolder.c_str(), RTPATH_DELIMITER);
7313 else
7314 log = Utf8StrFmt("%s%cVBox.log.%d",
7315 logFolder.c_str(), RTPATH_DELIMITER, idx);
7316 return log;
7317}
7318
7319/**
7320 * Returns the full path to the machine's (hardened) startup log file.
7321 */
7322Utf8Str Machine::i_getStartupLogFilename(void)
7323{
7324 Utf8Str strFilename;
7325 getLogFolder(strFilename);
7326 Assert(strFilename.length());
7327 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7328 return strFilename;
7329}
7330
7331
7332/**
7333 * Composes a unique saved state filename based on the current system time. The filename is
7334 * granular to the second so this will work so long as no more than one snapshot is taken on
7335 * a machine per second.
7336 *
7337 * Before version 4.1, we used this formula for saved state files:
7338 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7339 * which no longer works because saved state files can now be shared between the saved state of the
7340 * "saved" machine and an online snapshot, and the following would cause problems:
7341 * 1) save machine
7342 * 2) create online snapshot from that machine state --> reusing saved state file
7343 * 3) save machine again --> filename would be reused, breaking the online snapshot
7344 *
7345 * So instead we now use a timestamp.
7346 *
7347 * @param str
7348 */
7349
7350void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7351{
7352 AutoCaller autoCaller(this);
7353 AssertComRCReturnVoid(autoCaller.rc());
7354
7355 {
7356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7357 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7358 }
7359
7360 RTTIMESPEC ts;
7361 RTTimeNow(&ts);
7362 RTTIME time;
7363 RTTimeExplode(&time, &ts);
7364
7365 strStateFilePath += RTPATH_DELIMITER;
7366 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7367 time.i32Year, time.u8Month, time.u8MonthDay,
7368 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7369}
7370
7371/**
7372 * Returns the full path to the default video capture file.
7373 */
7374void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7375{
7376 AutoCaller autoCaller(this);
7377 AssertComRCReturnVoid(autoCaller.rc());
7378
7379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7380
7381 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7382 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7383 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7384}
7385
7386/**
7387 * Returns whether at least one USB controller is present for the VM.
7388 */
7389bool Machine::i_isUSBControllerPresent()
7390{
7391 AutoCaller autoCaller(this);
7392 AssertComRCReturn(autoCaller.rc(), false);
7393
7394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7395
7396 return (mUSBControllers->size() > 0);
7397}
7398
7399/**
7400 * @note Locks this object for writing, calls the client process
7401 * (inside the lock).
7402 */
7403HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7404 const Utf8Str &strFrontend,
7405 const Utf8Str &strEnvironment,
7406 ProgressProxy *aProgress)
7407{
7408 LogFlowThisFuncEnter();
7409
7410 AssertReturn(aControl, E_FAIL);
7411 AssertReturn(aProgress, E_FAIL);
7412 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7413
7414 AutoCaller autoCaller(this);
7415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7416
7417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7418
7419 if (!mData->mRegistered)
7420 return setError(E_UNEXPECTED,
7421 tr("The machine '%s' is not registered"),
7422 mUserData->s.strName.c_str());
7423
7424 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7425
7426 /* The process started when launching a VM with separate UI/VM processes is always
7427 * the UI process, i.e. needs special handling as it won't claim the session. */
7428 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7429
7430 if (fSeparate)
7431 {
7432 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mType.compare("headless", Utf8Str::CaseInsensitive))
7433 return setError(VBOX_E_INVALID_OBJECT_STATE,
7434 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7435 mUserData->s.strName.c_str());
7436 }
7437 else
7438 {
7439 if ( mData->mSession.mState == SessionState_Locked
7440 || mData->mSession.mState == SessionState_Spawning
7441 || mData->mSession.mState == SessionState_Unlocking)
7442 return setError(VBOX_E_INVALID_OBJECT_STATE,
7443 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7444 mUserData->s.strName.c_str());
7445
7446 /* may not be busy */
7447 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7448 }
7449
7450 /* get the path to the executable */
7451 char szPath[RTPATH_MAX];
7452 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7453 size_t cchBufLeft = strlen(szPath);
7454 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7455 szPath[cchBufLeft] = 0;
7456 char *pszNamePart = szPath + cchBufLeft;
7457 cchBufLeft = sizeof(szPath) - cchBufLeft;
7458
7459 int vrc = VINF_SUCCESS;
7460 RTPROCESS pid = NIL_RTPROCESS;
7461
7462 RTENV env = RTENV_DEFAULT;
7463
7464 if (!strEnvironment.isEmpty())
7465 {
7466 char *newEnvStr = NULL;
7467
7468 do
7469 {
7470 /* clone the current environment */
7471 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7472 AssertRCBreakStmt(vrc2, vrc = vrc2);
7473
7474 newEnvStr = RTStrDup(strEnvironment.c_str());
7475 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7476
7477 /* put new variables to the environment
7478 * (ignore empty variable names here since RTEnv API
7479 * intentionally doesn't do that) */
7480 char *var = newEnvStr;
7481 for (char *p = newEnvStr; *p; ++p)
7482 {
7483 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7484 {
7485 *p = '\0';
7486 if (*var)
7487 {
7488 char *val = strchr(var, '=');
7489 if (val)
7490 {
7491 *val++ = '\0';
7492 vrc2 = RTEnvSetEx(env, var, val);
7493 }
7494 else
7495 vrc2 = RTEnvUnsetEx(env, var);
7496 if (RT_FAILURE(vrc2))
7497 break;
7498 }
7499 var = p + 1;
7500 }
7501 }
7502 if (RT_SUCCESS(vrc2) && *var)
7503 vrc2 = RTEnvPutEx(env, var);
7504
7505 AssertRCBreakStmt(vrc2, vrc = vrc2);
7506 }
7507 while (0);
7508
7509 if (newEnvStr != NULL)
7510 RTStrFree(newEnvStr);
7511 }
7512
7513 /* Hardened startup logging */
7514#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7515 Utf8Str strSupStartLogArg("--sup-startup-log=");
7516 {
7517 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7518 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7519 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7520 {
7521 Utf8Str strStartupLogDir = strStartupLogFile;
7522 strStartupLogDir.stripFilename();
7523 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7524 file without stripping the file. */
7525 }
7526 strSupStartLogArg.append(strStartupLogFile);
7527 }
7528 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7529#else
7530 const char *pszSupStartupLogArg = NULL;
7531#endif
7532
7533
7534#ifdef VBOX_WITH_QTGUI
7535 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7536 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7537 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7538 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7539 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7540 {
7541# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7542 /* Modify the base path so that we don't need to use ".." below. */
7543 RTPathStripTrailingSlash(szPath);
7544 RTPathStripFilename(szPath);
7545 cchBufLeft = strlen(szPath);
7546 pszNamePart = szPath + cchBufLeft;
7547 cchBufLeft = sizeof(szPath) - cchBufLeft;
7548
7549# define OSX_APP_NAME "VirtualBoxVM"
7550# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7551
7552 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7553 if ( strAppOverride.contains(".")
7554 || strAppOverride.contains("/")
7555 || strAppOverride.contains("\\")
7556 || strAppOverride.contains(":"))
7557 strAppOverride.setNull();
7558 Utf8Str strAppPath;
7559 if (!strAppOverride.isEmpty())
7560 {
7561 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7562 Utf8Str strFullPath(szPath);
7563 strFullPath.append(strAppPath);
7564 /* there is a race, but people using this deserve the failure */
7565 if (!RTFileExists(strFullPath.c_str()))
7566 strAppOverride.setNull();
7567 }
7568 if (strAppOverride.isEmpty())
7569 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7570 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7571 strcpy(pszNamePart, strAppPath.c_str());
7572# else
7573 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7574 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7575 strcpy(pszNamePart, s_szVirtualBox_exe);
7576# endif
7577
7578 Utf8Str idStr = mData->mUuid.toString();
7579 const char *apszArgs[] =
7580 {
7581 szPath,
7582 "--comment", mUserData->s.strName.c_str(),
7583 "--startvm", idStr.c_str(),
7584 "--no-startvm-errormsgbox",
7585 NULL, /* For "--separate". */
7586 NULL, /* For "--sup-startup-log". */
7587 NULL
7588 };
7589 unsigned iArg = 6;
7590 if (fSeparate)
7591 apszArgs[iArg++] = "--separate";
7592 apszArgs[iArg++] = pszSupStartupLogArg;
7593
7594 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7595 }
7596#else /* !VBOX_WITH_QTGUI */
7597 if (0)
7598 ;
7599#endif /* VBOX_WITH_QTGUI */
7600
7601 else
7602
7603#ifdef VBOX_WITH_VBOXSDL
7604 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7605 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7606 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7607 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7608 {
7609 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7610 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7611 strcpy(pszNamePart, s_szVBoxSDL_exe);
7612
7613 Utf8Str idStr = mData->mUuid.toString();
7614 const char *apszArgs[] =
7615 {
7616 szPath,
7617 "--comment", mUserData->s.strName.c_str(),
7618 "--startvm", idStr.c_str(),
7619 NULL, /* For "--separate". */
7620 NULL, /* For "--sup-startup-log". */
7621 NULL
7622 };
7623 unsigned iArg = 5;
7624 if (fSeparate)
7625 apszArgs[iArg++] = "--separate";
7626 apszArgs[iArg++] = pszSupStartupLogArg;
7627
7628 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7629 }
7630#else /* !VBOX_WITH_VBOXSDL */
7631 if (0)
7632 ;
7633#endif /* !VBOX_WITH_VBOXSDL */
7634
7635 else
7636
7637#ifdef VBOX_WITH_HEADLESS
7638 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7639 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7640 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7641 )
7642 {
7643 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7644 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7645 * and a VM works even if the server has not been installed.
7646 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7647 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7648 * differently in 4.0 and 3.x.
7649 */
7650 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7651 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7652 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7653
7654 Utf8Str idStr = mData->mUuid.toString();
7655 const char *apszArgs[] =
7656 {
7657 szPath,
7658 "--comment", mUserData->s.strName.c_str(),
7659 "--startvm", idStr.c_str(),
7660 "--vrde", "config",
7661 NULL, /* For "--capture". */
7662 NULL, /* For "--sup-startup-log". */
7663 NULL
7664 };
7665 unsigned iArg = 7;
7666 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7667 apszArgs[iArg++] = "--capture";
7668 apszArgs[iArg++] = pszSupStartupLogArg;
7669
7670# ifdef RT_OS_WINDOWS
7671 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7672# else
7673 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7674# endif
7675 }
7676#else /* !VBOX_WITH_HEADLESS */
7677 if (0)
7678 ;
7679#endif /* !VBOX_WITH_HEADLESS */
7680 else
7681 {
7682 RTEnvDestroy(env);
7683 return setError(E_INVALIDARG,
7684 tr("Invalid frontend name: '%s'"),
7685 strFrontend.c_str());
7686 }
7687
7688 RTEnvDestroy(env);
7689
7690 if (RT_FAILURE(vrc))
7691 return setError(VBOX_E_IPRT_ERROR,
7692 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7693 mUserData->s.strName.c_str(), vrc);
7694
7695 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7696
7697 if (!fSeparate)
7698 {
7699 /*
7700 * Note that we don't release the lock here before calling the client,
7701 * because it doesn't need to call us back if called with a NULL argument.
7702 * Releasing the lock here is dangerous because we didn't prepare the
7703 * launch data yet, but the client we've just started may happen to be
7704 * too fast and call LockMachine() that will fail (because of PID, etc.),
7705 * so that the Machine will never get out of the Spawning session state.
7706 */
7707
7708 /* inform the session that it will be a remote one */
7709 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7710#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7711 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7712#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7713 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7714#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7715 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7716
7717 if (FAILED(rc))
7718 {
7719 /* restore the session state */
7720 mData->mSession.mState = SessionState_Unlocked;
7721 alock.release();
7722 mParent->i_addProcessToReap(pid);
7723 /* The failure may occur w/o any error info (from RPC), so provide one */
7724 return setError(VBOX_E_VM_ERROR,
7725 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7726 }
7727
7728 /* attach launch data to the machine */
7729 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7730 mData->mSession.mRemoteControls.push_back(aControl);
7731 mData->mSession.mProgress = aProgress;
7732 mData->mSession.mPID = pid;
7733 mData->mSession.mState = SessionState_Spawning;
7734 mData->mSession.mType = strFrontend;
7735 }
7736 else
7737 {
7738 /* For separate UI process we declare the launch as completed instantly, as the
7739 * actual headless VM start may or may not come. No point in remembering anything
7740 * yet, as what matters for us is when the headless VM gets started. */
7741 aProgress->i_notifyComplete(S_OK);
7742 }
7743
7744 alock.release();
7745 mParent->i_addProcessToReap(pid);
7746
7747 LogFlowThisFuncLeave();
7748 return S_OK;
7749}
7750
7751/**
7752 * Returns @c true if the given session machine instance has an open direct
7753 * session (and optionally also for direct sessions which are closing) and
7754 * returns the session control machine instance if so.
7755 *
7756 * Note that when the method returns @c false, the arguments remain unchanged.
7757 *
7758 * @param aMachine Session machine object.
7759 * @param aControl Direct session control object (optional).
7760 * @param aAllowClosing If true then additionally a session which is currently
7761 * being closed will also be allowed.
7762 *
7763 * @note locks this object for reading.
7764 */
7765bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7766 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7767 bool aAllowClosing /*= false*/)
7768{
7769 AutoLimitedCaller autoCaller(this);
7770 AssertComRCReturn(autoCaller.rc(), false);
7771
7772 /* just return false for inaccessible machines */
7773 if (getObjectState().getState() != ObjectState::Ready)
7774 return false;
7775
7776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7777
7778 if ( ( mData->mSession.mState == SessionState_Locked
7779 && mData->mSession.mLockType == LockType_VM)
7780 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7781 )
7782 {
7783 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7784
7785 aMachine = mData->mSession.mMachine;
7786
7787 if (aControl != NULL)
7788 *aControl = mData->mSession.mDirectControl;
7789
7790 return true;
7791 }
7792
7793 return false;
7794}
7795
7796/**
7797 * Returns @c true if the given machine has an spawning direct session.
7798 *
7799 * @note locks this object for reading.
7800 */
7801bool Machine::i_isSessionSpawning()
7802{
7803 AutoLimitedCaller autoCaller(this);
7804 AssertComRCReturn(autoCaller.rc(), false);
7805
7806 /* just return false for inaccessible machines */
7807 if (getObjectState().getState() != ObjectState::Ready)
7808 return false;
7809
7810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7811
7812 if (mData->mSession.mState == SessionState_Spawning)
7813 return true;
7814
7815 return false;
7816}
7817
7818/**
7819 * Called from the client watcher thread to check for unexpected client process
7820 * death during Session_Spawning state (e.g. before it successfully opened a
7821 * direct session).
7822 *
7823 * On Win32 and on OS/2, this method is called only when we've got the
7824 * direct client's process termination notification, so it always returns @c
7825 * true.
7826 *
7827 * On other platforms, this method returns @c true if the client process is
7828 * terminated and @c false if it's still alive.
7829 *
7830 * @note Locks this object for writing.
7831 */
7832bool Machine::i_checkForSpawnFailure()
7833{
7834 AutoCaller autoCaller(this);
7835 if (!autoCaller.isOk())
7836 {
7837 /* nothing to do */
7838 LogFlowThisFunc(("Already uninitialized!\n"));
7839 return true;
7840 }
7841
7842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7843
7844 if (mData->mSession.mState != SessionState_Spawning)
7845 {
7846 /* nothing to do */
7847 LogFlowThisFunc(("Not spawning any more!\n"));
7848 return true;
7849 }
7850
7851 HRESULT rc = S_OK;
7852
7853 /* PID not yet initialized, skip check. */
7854 if (mData->mSession.mPID == NIL_RTPROCESS)
7855 return false;
7856
7857 RTPROCSTATUS status;
7858 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7859
7860 if (vrc != VERR_PROCESS_RUNNING)
7861 {
7862 Utf8Str strExtraInfo;
7863
7864#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7865 /* If the startup logfile exists and is of non-zero length, tell the
7866 user to look there for more details to encourage them to attach it
7867 when reporting startup issues. */
7868 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7869 uint64_t cbStartupLogFile = 0;
7870 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7871 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7872 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7873#endif
7874
7875 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7876 rc = setError(E_FAIL,
7877 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7878 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7879 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7880 rc = setError(E_FAIL,
7881 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7882 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7883 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7884 rc = setError(E_FAIL,
7885 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7886 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7887 else
7888 rc = setError(E_FAIL,
7889 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7890 i_getName().c_str(), vrc, strExtraInfo.c_str());
7891 }
7892
7893 if (FAILED(rc))
7894 {
7895 /* Close the remote session, remove the remote control from the list
7896 * and reset session state to Closed (@note keep the code in sync with
7897 * the relevant part in LockMachine()). */
7898
7899 Assert(mData->mSession.mRemoteControls.size() == 1);
7900 if (mData->mSession.mRemoteControls.size() == 1)
7901 {
7902 ErrorInfoKeeper eik;
7903 mData->mSession.mRemoteControls.front()->Uninitialize();
7904 }
7905
7906 mData->mSession.mRemoteControls.clear();
7907 mData->mSession.mState = SessionState_Unlocked;
7908
7909 /* finalize the progress after setting the state */
7910 if (!mData->mSession.mProgress.isNull())
7911 {
7912 mData->mSession.mProgress->notifyComplete(rc);
7913 mData->mSession.mProgress.setNull();
7914 }
7915
7916 mData->mSession.mPID = NIL_RTPROCESS;
7917
7918 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7919 return true;
7920 }
7921
7922 return false;
7923}
7924
7925/**
7926 * Checks whether the machine can be registered. If so, commits and saves
7927 * all settings.
7928 *
7929 * @note Must be called from mParent's write lock. Locks this object and
7930 * children for writing.
7931 */
7932HRESULT Machine::i_prepareRegister()
7933{
7934 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7935
7936 AutoLimitedCaller autoCaller(this);
7937 AssertComRCReturnRC(autoCaller.rc());
7938
7939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7940
7941 /* wait for state dependents to drop to zero */
7942 i_ensureNoStateDependencies();
7943
7944 if (!mData->mAccessible)
7945 return setError(VBOX_E_INVALID_OBJECT_STATE,
7946 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7947 mUserData->s.strName.c_str(),
7948 mData->mUuid.toString().c_str());
7949
7950 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7951
7952 if (mData->mRegistered)
7953 return setError(VBOX_E_INVALID_OBJECT_STATE,
7954 tr("The machine '%s' with UUID {%s} is already registered"),
7955 mUserData->s.strName.c_str(),
7956 mData->mUuid.toString().c_str());
7957
7958 HRESULT rc = S_OK;
7959
7960 // Ensure the settings are saved. If we are going to be registered and
7961 // no config file exists yet, create it by calling i_saveSettings() too.
7962 if ( (mData->flModifications)
7963 || (!mData->pMachineConfigFile->fileExists())
7964 )
7965 {
7966 rc = i_saveSettings(NULL);
7967 // no need to check whether VirtualBox.xml needs saving too since
7968 // we can't have a machine XML file rename pending
7969 if (FAILED(rc)) return rc;
7970 }
7971
7972 /* more config checking goes here */
7973
7974 if (SUCCEEDED(rc))
7975 {
7976 /* we may have had implicit modifications we want to fix on success */
7977 i_commit();
7978
7979 mData->mRegistered = true;
7980 }
7981 else
7982 {
7983 /* we may have had implicit modifications we want to cancel on failure*/
7984 i_rollback(false /* aNotify */);
7985 }
7986
7987 return rc;
7988}
7989
7990/**
7991 * Increases the number of objects dependent on the machine state or on the
7992 * registered state. Guarantees that these two states will not change at least
7993 * until #releaseStateDependency() is called.
7994 *
7995 * Depending on the @a aDepType value, additional state checks may be made.
7996 * These checks will set extended error info on failure. See
7997 * #checkStateDependency() for more info.
7998 *
7999 * If this method returns a failure, the dependency is not added and the caller
8000 * is not allowed to rely on any particular machine state or registration state
8001 * value and may return the failed result code to the upper level.
8002 *
8003 * @param aDepType Dependency type to add.
8004 * @param aState Current machine state (NULL if not interested).
8005 * @param aRegistered Current registered state (NULL if not interested).
8006 *
8007 * @note Locks this object for writing.
8008 */
8009HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8010 MachineState_T *aState /* = NULL */,
8011 BOOL *aRegistered /* = NULL */)
8012{
8013 AutoCaller autoCaller(this);
8014 AssertComRCReturnRC(autoCaller.rc());
8015
8016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8017
8018 HRESULT rc = i_checkStateDependency(aDepType);
8019 if (FAILED(rc)) return rc;
8020
8021 {
8022 if (mData->mMachineStateChangePending != 0)
8023 {
8024 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8025 * drop to zero so don't add more. It may make sense to wait a bit
8026 * and retry before reporting an error (since the pending state
8027 * transition should be really quick) but let's just assert for
8028 * now to see if it ever happens on practice. */
8029
8030 AssertFailed();
8031
8032 return setError(E_ACCESSDENIED,
8033 tr("Machine state change is in progress. Please retry the operation later."));
8034 }
8035
8036 ++mData->mMachineStateDeps;
8037 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8038 }
8039
8040 if (aState)
8041 *aState = mData->mMachineState;
8042 if (aRegistered)
8043 *aRegistered = mData->mRegistered;
8044
8045 return S_OK;
8046}
8047
8048/**
8049 * Decreases the number of objects dependent on the machine state.
8050 * Must always complete the #addStateDependency() call after the state
8051 * dependency is no more necessary.
8052 */
8053void Machine::i_releaseStateDependency()
8054{
8055 AutoCaller autoCaller(this);
8056 AssertComRCReturnVoid(autoCaller.rc());
8057
8058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8059
8060 /* releaseStateDependency() w/o addStateDependency()? */
8061 AssertReturnVoid(mData->mMachineStateDeps != 0);
8062 -- mData->mMachineStateDeps;
8063
8064 if (mData->mMachineStateDeps == 0)
8065 {
8066 /* inform i_ensureNoStateDependencies() that there are no more deps */
8067 if (mData->mMachineStateChangePending != 0)
8068 {
8069 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8070 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8071 }
8072 }
8073}
8074
8075Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8076{
8077 /* start with nothing found */
8078 Utf8Str strResult("");
8079
8080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8081
8082 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8083 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8084 // found:
8085 strResult = it->second; // source is a Utf8Str
8086
8087 return strResult;
8088}
8089
8090// protected methods
8091/////////////////////////////////////////////////////////////////////////////
8092
8093/**
8094 * Performs machine state checks based on the @a aDepType value. If a check
8095 * fails, this method will set extended error info, otherwise it will return
8096 * S_OK. It is supposed, that on failure, the caller will immediately return
8097 * the return value of this method to the upper level.
8098 *
8099 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8100 *
8101 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8102 * current state of this machine object allows to change settings of the
8103 * machine (i.e. the machine is not registered, or registered but not running
8104 * and not saved). It is useful to call this method from Machine setters
8105 * before performing any change.
8106 *
8107 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8108 * as for MutableStateDep except that if the machine is saved, S_OK is also
8109 * returned. This is useful in setters which allow changing machine
8110 * properties when it is in the saved state.
8111 *
8112 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8113 * if the current state of this machine object allows to change runtime
8114 * changeable settings of the machine (i.e. the machine is not registered, or
8115 * registered but either running or not running and not saved). It is useful
8116 * to call this method from Machine setters before performing any changes to
8117 * runtime changeable settings.
8118 *
8119 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8120 * the same as for MutableOrRunningStateDep except that if the machine is
8121 * saved, S_OK is also returned. This is useful in setters which allow
8122 * changing runtime and saved state changeable machine properties.
8123 *
8124 * @param aDepType Dependency type to check.
8125 *
8126 * @note Non Machine based classes should use #addStateDependency() and
8127 * #releaseStateDependency() methods or the smart AutoStateDependency
8128 * template.
8129 *
8130 * @note This method must be called from under this object's read or write
8131 * lock.
8132 */
8133HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8134{
8135 switch (aDepType)
8136 {
8137 case AnyStateDep:
8138 {
8139 break;
8140 }
8141 case MutableStateDep:
8142 {
8143 if ( mData->mRegistered
8144 && ( !i_isSessionMachine()
8145 || ( mData->mMachineState != MachineState_Aborted
8146 && mData->mMachineState != MachineState_Teleported
8147 && mData->mMachineState != MachineState_PoweredOff
8148 )
8149 )
8150 )
8151 return setError(VBOX_E_INVALID_VM_STATE,
8152 tr("The machine is not mutable (state is %s)"),
8153 Global::stringifyMachineState(mData->mMachineState));
8154 break;
8155 }
8156 case MutableOrSavedStateDep:
8157 {
8158 if ( mData->mRegistered
8159 && ( !i_isSessionMachine()
8160 || ( mData->mMachineState != MachineState_Aborted
8161 && mData->mMachineState != MachineState_Teleported
8162 && mData->mMachineState != MachineState_Saved
8163 && mData->mMachineState != MachineState_PoweredOff
8164 )
8165 )
8166 )
8167 return setError(VBOX_E_INVALID_VM_STATE,
8168 tr("The machine is not mutable (state is %s)"),
8169 Global::stringifyMachineState(mData->mMachineState));
8170 break;
8171 }
8172 case MutableOrRunningStateDep:
8173 {
8174 if ( mData->mRegistered
8175 && ( !i_isSessionMachine()
8176 || ( mData->mMachineState != MachineState_Aborted
8177 && mData->mMachineState != MachineState_Teleported
8178 && mData->mMachineState != MachineState_PoweredOff
8179 && !Global::IsOnline(mData->mMachineState)
8180 )
8181 )
8182 )
8183 return setError(VBOX_E_INVALID_VM_STATE,
8184 tr("The machine is not mutable (state is %s)"),
8185 Global::stringifyMachineState(mData->mMachineState));
8186 break;
8187 }
8188 case MutableOrSavedOrRunningStateDep:
8189 {
8190 if ( mData->mRegistered
8191 && ( !i_isSessionMachine()
8192 || ( mData->mMachineState != MachineState_Aborted
8193 && mData->mMachineState != MachineState_Teleported
8194 && mData->mMachineState != MachineState_Saved
8195 && mData->mMachineState != MachineState_PoweredOff
8196 && !Global::IsOnline(mData->mMachineState)
8197 )
8198 )
8199 )
8200 return setError(VBOX_E_INVALID_VM_STATE,
8201 tr("The machine is not mutable (state is %s)"),
8202 Global::stringifyMachineState(mData->mMachineState));
8203 break;
8204 }
8205 }
8206
8207 return S_OK;
8208}
8209
8210/**
8211 * Helper to initialize all associated child objects and allocate data
8212 * structures.
8213 *
8214 * This method must be called as a part of the object's initialization procedure
8215 * (usually done in the #init() method).
8216 *
8217 * @note Must be called only from #init() or from #registeredInit().
8218 */
8219HRESULT Machine::initDataAndChildObjects()
8220{
8221 AutoCaller autoCaller(this);
8222 AssertComRCReturnRC(autoCaller.rc());
8223 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8224 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8225
8226 AssertReturn(!mData->mAccessible, E_FAIL);
8227
8228 /* allocate data structures */
8229 mSSData.allocate();
8230 mUserData.allocate();
8231 mHWData.allocate();
8232 mMediaData.allocate();
8233 mStorageControllers.allocate();
8234 mUSBControllers.allocate();
8235
8236 /* initialize mOSTypeId */
8237 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8238
8239 /* create associated BIOS settings object */
8240 unconst(mBIOSSettings).createObject();
8241 mBIOSSettings->init(this);
8242
8243 /* create an associated VRDE object (default is disabled) */
8244 unconst(mVRDEServer).createObject();
8245 mVRDEServer->init(this);
8246
8247 /* create associated serial port objects */
8248 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8249 {
8250 unconst(mSerialPorts[slot]).createObject();
8251 mSerialPorts[slot]->init(this, slot);
8252 }
8253
8254 /* create associated parallel port objects */
8255 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8256 {
8257 unconst(mParallelPorts[slot]).createObject();
8258 mParallelPorts[slot]->init(this, slot);
8259 }
8260
8261 /* create the audio adapter object (always present, default is disabled) */
8262 unconst(mAudioAdapter).createObject();
8263 mAudioAdapter->init(this);
8264
8265 /* create the USB device filters object (always present) */
8266 unconst(mUSBDeviceFilters).createObject();
8267 mUSBDeviceFilters->init(this);
8268
8269 /* create associated network adapter objects */
8270 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8271 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8272 {
8273 unconst(mNetworkAdapters[slot]).createObject();
8274 mNetworkAdapters[slot]->init(this, slot);
8275 }
8276
8277 /* create the bandwidth control */
8278 unconst(mBandwidthControl).createObject();
8279 mBandwidthControl->init(this);
8280
8281 return S_OK;
8282}
8283
8284/**
8285 * Helper to uninitialize all associated child objects and to free all data
8286 * structures.
8287 *
8288 * This method must be called as a part of the object's uninitialization
8289 * procedure (usually done in the #uninit() method).
8290 *
8291 * @note Must be called only from #uninit() or from #registeredInit().
8292 */
8293void Machine::uninitDataAndChildObjects()
8294{
8295 AutoCaller autoCaller(this);
8296 AssertComRCReturnVoid(autoCaller.rc());
8297 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8298 || getObjectState().getState() == ObjectState::Limited);
8299
8300 /* tell all our other child objects we've been uninitialized */
8301 if (mBandwidthControl)
8302 {
8303 mBandwidthControl->uninit();
8304 unconst(mBandwidthControl).setNull();
8305 }
8306
8307 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8308 {
8309 if (mNetworkAdapters[slot])
8310 {
8311 mNetworkAdapters[slot]->uninit();
8312 unconst(mNetworkAdapters[slot]).setNull();
8313 }
8314 }
8315
8316 if (mUSBDeviceFilters)
8317 {
8318 mUSBDeviceFilters->uninit();
8319 unconst(mUSBDeviceFilters).setNull();
8320 }
8321
8322 if (mAudioAdapter)
8323 {
8324 mAudioAdapter->uninit();
8325 unconst(mAudioAdapter).setNull();
8326 }
8327
8328 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8329 {
8330 if (mParallelPorts[slot])
8331 {
8332 mParallelPorts[slot]->uninit();
8333 unconst(mParallelPorts[slot]).setNull();
8334 }
8335 }
8336
8337 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8338 {
8339 if (mSerialPorts[slot])
8340 {
8341 mSerialPorts[slot]->uninit();
8342 unconst(mSerialPorts[slot]).setNull();
8343 }
8344 }
8345
8346 if (mVRDEServer)
8347 {
8348 mVRDEServer->uninit();
8349 unconst(mVRDEServer).setNull();
8350 }
8351
8352 if (mBIOSSettings)
8353 {
8354 mBIOSSettings->uninit();
8355 unconst(mBIOSSettings).setNull();
8356 }
8357
8358 /* Deassociate media (only when a real Machine or a SnapshotMachine
8359 * instance is uninitialized; SessionMachine instances refer to real
8360 * Machine media). This is necessary for a clean re-initialization of
8361 * the VM after successfully re-checking the accessibility state. Note
8362 * that in case of normal Machine or SnapshotMachine uninitialization (as
8363 * a result of unregistering or deleting the snapshot), outdated media
8364 * attachments will already be uninitialized and deleted, so this
8365 * code will not affect them. */
8366 if ( !!mMediaData
8367 && (!i_isSessionMachine())
8368 )
8369 {
8370 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8371 it != mMediaData->mAttachments.end();
8372 ++it)
8373 {
8374 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8375 if (pMedium.isNull())
8376 continue;
8377 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8378 AssertComRC(rc);
8379 }
8380 }
8381
8382 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8383 {
8384 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8385 if (mData->mFirstSnapshot)
8386 {
8387 // snapshots tree is protected by machine write lock; strictly
8388 // this isn't necessary here since we're deleting the entire
8389 // machine, but otherwise we assert in Snapshot::uninit()
8390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8391 mData->mFirstSnapshot->uninit();
8392 mData->mFirstSnapshot.setNull();
8393 }
8394
8395 mData->mCurrentSnapshot.setNull();
8396 }
8397
8398 /* free data structures (the essential mData structure is not freed here
8399 * since it may be still in use) */
8400 mMediaData.free();
8401 mStorageControllers.free();
8402 mUSBControllers.free();
8403 mHWData.free();
8404 mUserData.free();
8405 mSSData.free();
8406}
8407
8408/**
8409 * Returns a pointer to the Machine object for this machine that acts like a
8410 * parent for complex machine data objects such as shared folders, etc.
8411 *
8412 * For primary Machine objects and for SnapshotMachine objects, returns this
8413 * object's pointer itself. For SessionMachine objects, returns the peer
8414 * (primary) machine pointer.
8415 */
8416Machine* Machine::i_getMachine()
8417{
8418 if (i_isSessionMachine())
8419 return (Machine*)mPeer;
8420 return this;
8421}
8422
8423/**
8424 * Makes sure that there are no machine state dependents. If necessary, waits
8425 * for the number of dependents to drop to zero.
8426 *
8427 * Make sure this method is called from under this object's write lock to
8428 * guarantee that no new dependents may be added when this method returns
8429 * control to the caller.
8430 *
8431 * @note Locks this object for writing. The lock will be released while waiting
8432 * (if necessary).
8433 *
8434 * @warning To be used only in methods that change the machine state!
8435 */
8436void Machine::i_ensureNoStateDependencies()
8437{
8438 AssertReturnVoid(isWriteLockOnCurrentThread());
8439
8440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8441
8442 /* Wait for all state dependents if necessary */
8443 if (mData->mMachineStateDeps != 0)
8444 {
8445 /* lazy semaphore creation */
8446 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8447 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8448
8449 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8450 mData->mMachineStateDeps));
8451
8452 ++mData->mMachineStateChangePending;
8453
8454 /* reset the semaphore before waiting, the last dependent will signal
8455 * it */
8456 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8457
8458 alock.release();
8459
8460 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8461
8462 alock.acquire();
8463
8464 -- mData->mMachineStateChangePending;
8465 }
8466}
8467
8468/**
8469 * Changes the machine state and informs callbacks.
8470 *
8471 * This method is not intended to fail so it either returns S_OK or asserts (and
8472 * returns a failure).
8473 *
8474 * @note Locks this object for writing.
8475 */
8476HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8477{
8478 LogFlowThisFuncEnter();
8479 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8480 Assert(aMachineState != MachineState_Null);
8481
8482 AutoCaller autoCaller(this);
8483 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8484
8485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8486
8487 /* wait for state dependents to drop to zero */
8488 i_ensureNoStateDependencies();
8489
8490 MachineState_T const enmOldState = mData->mMachineState;
8491 if (enmOldState != aMachineState)
8492 {
8493 mData->mMachineState = aMachineState;
8494 RTTimeNow(&mData->mLastStateChange);
8495
8496#ifdef VBOX_WITH_DTRACE_R3_MAIN
8497 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8498#endif
8499 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8500 }
8501
8502 LogFlowThisFuncLeave();
8503 return S_OK;
8504}
8505
8506/**
8507 * Searches for a shared folder with the given logical name
8508 * in the collection of shared folders.
8509 *
8510 * @param aName logical name of the shared folder
8511 * @param aSharedFolder where to return the found object
8512 * @param aSetError whether to set the error info if the folder is
8513 * not found
8514 * @return
8515 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8516 *
8517 * @note
8518 * must be called from under the object's lock!
8519 */
8520HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8521 ComObjPtr<SharedFolder> &aSharedFolder,
8522 bool aSetError /* = false */)
8523{
8524 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8525 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8526 it != mHWData->mSharedFolders.end();
8527 ++it)
8528 {
8529 SharedFolder *pSF = *it;
8530 AutoCaller autoCaller(pSF);
8531 if (pSF->i_getName() == aName)
8532 {
8533 aSharedFolder = pSF;
8534 rc = S_OK;
8535 break;
8536 }
8537 }
8538
8539 if (aSetError && FAILED(rc))
8540 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8541
8542 return rc;
8543}
8544
8545/**
8546 * Initializes all machine instance data from the given settings structures
8547 * from XML. The exception is the machine UUID which needs special handling
8548 * depending on the caller's use case, so the caller needs to set that herself.
8549 *
8550 * This gets called in several contexts during machine initialization:
8551 *
8552 * -- When machine XML exists on disk already and needs to be loaded into memory,
8553 * for example, from registeredInit() to load all registered machines on
8554 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8555 * attached to the machine should be part of some media registry already.
8556 *
8557 * -- During OVF import, when a machine config has been constructed from an
8558 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8559 * ensure that the media listed as attachments in the config (which have
8560 * been imported from the OVF) receive the correct registry ID.
8561 *
8562 * -- During VM cloning.
8563 *
8564 * @param config Machine settings from XML.
8565 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8566 * for each attached medium in the config.
8567 * @return
8568 */
8569HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8570 const Guid *puuidRegistry)
8571{
8572 // copy name, description, OS type, teleporter, UTC etc.
8573 mUserData->s = config.machineUserData;
8574
8575 // Decode the Icon overide data from config userdata and set onto Machine.
8576 #define DECODE_STR_MAX _1M
8577 const char* pszStr = config.machineUserData.ovIcon.c_str();
8578 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8579 if (cbOut > DECODE_STR_MAX)
8580 return setError(E_FAIL,
8581 tr("Icon Data too long.'%d' > '%d'"),
8582 cbOut,
8583 DECODE_STR_MAX);
8584 mUserData->mIcon.resize(cbOut);
8585 int vrc = VINF_SUCCESS;
8586 if (cbOut)
8587 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8588 if (RT_FAILURE(vrc))
8589 {
8590 mUserData->mIcon.resize(0);
8591 return setError(E_FAIL,
8592 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8593 pszStr,
8594 vrc);
8595 }
8596
8597 // look up the object by Id to check it is valid
8598 ComPtr<IGuestOSType> guestOSType;
8599 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8600 guestOSType.asOutParam());
8601 if (FAILED(rc)) return rc;
8602
8603 // stateFile (optional)
8604 if (config.strStateFile.isEmpty())
8605 mSSData->strStateFilePath.setNull();
8606 else
8607 {
8608 Utf8Str stateFilePathFull(config.strStateFile);
8609 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8610 if (RT_FAILURE(vrc))
8611 return setError(E_FAIL,
8612 tr("Invalid saved state file path '%s' (%Rrc)"),
8613 config.strStateFile.c_str(),
8614 vrc);
8615 mSSData->strStateFilePath = stateFilePathFull;
8616 }
8617
8618 // snapshot folder needs special processing so set it again
8619 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8620 if (FAILED(rc)) return rc;
8621
8622 /* Copy the extra data items (Not in any case config is already the same as
8623 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8624 * make sure the extra data map is copied). */
8625 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8626
8627 /* currentStateModified (optional, default is true) */
8628 mData->mCurrentStateModified = config.fCurrentStateModified;
8629
8630 mData->mLastStateChange = config.timeLastStateChange;
8631
8632 /*
8633 * note: all mUserData members must be assigned prior this point because
8634 * we need to commit changes in order to let mUserData be shared by all
8635 * snapshot machine instances.
8636 */
8637 mUserData.commitCopy();
8638
8639 // machine registry, if present (must be loaded before snapshots)
8640 if (config.canHaveOwnMediaRegistry())
8641 {
8642 // determine machine folder
8643 Utf8Str strMachineFolder = i_getSettingsFileFull();
8644 strMachineFolder.stripFilename();
8645 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8646 config.mediaRegistry,
8647 strMachineFolder);
8648 if (FAILED(rc)) return rc;
8649 }
8650
8651 /* Snapshot node (optional) */
8652 size_t cRootSnapshots;
8653 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8654 {
8655 // there must be only one root snapshot
8656 Assert(cRootSnapshots == 1);
8657
8658 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8659
8660 rc = i_loadSnapshot(snap,
8661 config.uuidCurrentSnapshot,
8662 NULL); // no parent == first snapshot
8663 if (FAILED(rc)) return rc;
8664 }
8665
8666 // hardware data
8667 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8668 if (FAILED(rc)) return rc;
8669
8670 // load storage controllers
8671 rc = i_loadStorageControllers(config.storageMachine,
8672 puuidRegistry,
8673 NULL /* puuidSnapshot */);
8674 if (FAILED(rc)) return rc;
8675
8676 /*
8677 * NOTE: the assignment below must be the last thing to do,
8678 * otherwise it will be not possible to change the settings
8679 * somewhere in the code above because all setters will be
8680 * blocked by i_checkStateDependency(MutableStateDep).
8681 */
8682
8683 /* set the machine state to Aborted or Saved when appropriate */
8684 if (config.fAborted)
8685 {
8686 mSSData->strStateFilePath.setNull();
8687
8688 /* no need to use i_setMachineState() during init() */
8689 mData->mMachineState = MachineState_Aborted;
8690 }
8691 else if (!mSSData->strStateFilePath.isEmpty())
8692 {
8693 /* no need to use i_setMachineState() during init() */
8694 mData->mMachineState = MachineState_Saved;
8695 }
8696
8697 // after loading settings, we are no longer different from the XML on disk
8698 mData->flModifications = 0;
8699
8700 return S_OK;
8701}
8702
8703/**
8704 * Recursively loads all snapshots starting from the given.
8705 *
8706 * @param aNode <Snapshot> node.
8707 * @param aCurSnapshotId Current snapshot ID from the settings file.
8708 * @param aParentSnapshot Parent snapshot.
8709 */
8710HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8711 const Guid &aCurSnapshotId,
8712 Snapshot *aParentSnapshot)
8713{
8714 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8715 AssertReturn(!i_isSessionMachine(), E_FAIL);
8716
8717 HRESULT rc = S_OK;
8718
8719 Utf8Str strStateFile;
8720 if (!data.strStateFile.isEmpty())
8721 {
8722 /* optional */
8723 strStateFile = data.strStateFile;
8724 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8725 if (RT_FAILURE(vrc))
8726 return setError(E_FAIL,
8727 tr("Invalid saved state file path '%s' (%Rrc)"),
8728 strStateFile.c_str(),
8729 vrc);
8730 }
8731
8732 /* create a snapshot machine object */
8733 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8734 pSnapshotMachine.createObject();
8735 rc = pSnapshotMachine->initFromSettings(this,
8736 data.hardware,
8737 &data.debugging,
8738 &data.autostart,
8739 data.storage,
8740 data.uuid.ref(),
8741 strStateFile);
8742 if (FAILED(rc)) return rc;
8743
8744 /* create a snapshot object */
8745 ComObjPtr<Snapshot> pSnapshot;
8746 pSnapshot.createObject();
8747 /* initialize the snapshot */
8748 rc = pSnapshot->init(mParent, // VirtualBox object
8749 data.uuid,
8750 data.strName,
8751 data.strDescription,
8752 data.timestamp,
8753 pSnapshotMachine,
8754 aParentSnapshot);
8755 if (FAILED(rc)) return rc;
8756
8757 /* memorize the first snapshot if necessary */
8758 if (!mData->mFirstSnapshot)
8759 mData->mFirstSnapshot = pSnapshot;
8760
8761 /* memorize the current snapshot when appropriate */
8762 if ( !mData->mCurrentSnapshot
8763 && pSnapshot->i_getId() == aCurSnapshotId
8764 )
8765 mData->mCurrentSnapshot = pSnapshot;
8766
8767 // now create the children
8768 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8769 it != data.llChildSnapshots.end();
8770 ++it)
8771 {
8772 const settings::Snapshot &childData = *it;
8773 // recurse
8774 rc = i_loadSnapshot(childData,
8775 aCurSnapshotId,
8776 pSnapshot); // parent = the one we created above
8777 if (FAILED(rc)) return rc;
8778 }
8779
8780 return rc;
8781}
8782
8783/**
8784 * Loads settings into mHWData.
8785 *
8786 * @param data Reference to the hardware settings.
8787 * @param pDbg Pointer to the debugging settings.
8788 * @param pAutostart Pointer to the autostart settings.
8789 */
8790HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8791 const settings::Autostart *pAutostart)
8792{
8793 AssertReturn(!i_isSessionMachine(), E_FAIL);
8794
8795 HRESULT rc = S_OK;
8796
8797 try
8798 {
8799 /* The hardware version attribute (optional). */
8800 mHWData->mHWVersion = data.strVersion;
8801 mHWData->mHardwareUUID = data.uuid;
8802
8803 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8804 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8805 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8806 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8807 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8808 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8809 mHWData->mPAEEnabled = data.fPAE;
8810 mHWData->mLongMode = data.enmLongMode;
8811 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8812 mHWData->mCPUCount = data.cCPUs;
8813 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8814 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8815 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8816
8817 // cpu
8818 if (mHWData->mCPUHotPlugEnabled)
8819 {
8820 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8821 it != data.llCpus.end();
8822 ++it)
8823 {
8824 const settings::Cpu &cpu = *it;
8825
8826 mHWData->mCPUAttached[cpu.ulId] = true;
8827 }
8828 }
8829
8830 // cpuid leafs
8831 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8832 it != data.llCpuIdLeafs.end();
8833 ++it)
8834 {
8835 const settings::CpuIdLeaf &leaf = *it;
8836
8837 switch (leaf.ulId)
8838 {
8839 case 0x0:
8840 case 0x1:
8841 case 0x2:
8842 case 0x3:
8843 case 0x4:
8844 case 0x5:
8845 case 0x6:
8846 case 0x7:
8847 case 0x8:
8848 case 0x9:
8849 case 0xA:
8850 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8851 break;
8852
8853 case 0x80000000:
8854 case 0x80000001:
8855 case 0x80000002:
8856 case 0x80000003:
8857 case 0x80000004:
8858 case 0x80000005:
8859 case 0x80000006:
8860 case 0x80000007:
8861 case 0x80000008:
8862 case 0x80000009:
8863 case 0x8000000A:
8864 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8865 break;
8866
8867 default:
8868 /* just ignore */
8869 break;
8870 }
8871 }
8872
8873 mHWData->mMemorySize = data.ulMemorySizeMB;
8874 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8875
8876 // boot order
8877 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8878 {
8879 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8880 if (it == data.mapBootOrder.end())
8881 mHWData->mBootOrder[i] = DeviceType_Null;
8882 else
8883 mHWData->mBootOrder[i] = it->second;
8884 }
8885
8886 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8887 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8888 mHWData->mMonitorCount = data.cMonitors;
8889 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8890 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8891 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8892 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8893 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8894 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8895 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8896 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8897 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8898 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8899 if (!data.strVideoCaptureFile.isEmpty())
8900 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8901 else
8902 mHWData->mVideoCaptureFile.setNull();
8903 mHWData->mFirmwareType = data.firmwareType;
8904 mHWData->mPointingHIDType = data.pointingHIDType;
8905 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8906 mHWData->mChipsetType = data.chipsetType;
8907 mHWData->mParavirtProvider = data.paravirtProvider;
8908 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8909 mHWData->mHPETEnabled = data.fHPETEnabled;
8910
8911 /* VRDEServer */
8912 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8913 if (FAILED(rc)) return rc;
8914
8915 /* BIOS */
8916 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8917 if (FAILED(rc)) return rc;
8918
8919 // Bandwidth control (must come before network adapters)
8920 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8921 if (FAILED(rc)) return rc;
8922
8923 /* Shared folders */
8924 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8925 it != data.usbSettings.llUSBControllers.end();
8926 ++it)
8927 {
8928 const settings::USBController &settingsCtrl = *it;
8929 ComObjPtr<USBController> newCtrl;
8930
8931 newCtrl.createObject();
8932 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8933 mUSBControllers->push_back(newCtrl);
8934 }
8935
8936 /* USB device filters */
8937 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8938 if (FAILED(rc)) return rc;
8939
8940 // network adapters
8941 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8942 size_t oldCount = mNetworkAdapters.size();
8943 if (newCount > oldCount)
8944 {
8945 mNetworkAdapters.resize(newCount);
8946 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8947 {
8948 unconst(mNetworkAdapters[slot]).createObject();
8949 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8950 }
8951 }
8952 else if (newCount < oldCount)
8953 mNetworkAdapters.resize(newCount);
8954 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8955 it != data.llNetworkAdapters.end();
8956 ++it)
8957 {
8958 const settings::NetworkAdapter &nic = *it;
8959
8960 /* slot unicity is guaranteed by XML Schema */
8961 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8962 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8963 if (FAILED(rc)) return rc;
8964 }
8965
8966 // serial ports
8967 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8968 it != data.llSerialPorts.end();
8969 ++it)
8970 {
8971 const settings::SerialPort &s = *it;
8972
8973 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8974 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8975 if (FAILED(rc)) return rc;
8976 }
8977
8978 // parallel ports (optional)
8979 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8980 it != data.llParallelPorts.end();
8981 ++it)
8982 {
8983 const settings::ParallelPort &p = *it;
8984
8985 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8986 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8987 if (FAILED(rc)) return rc;
8988 }
8989
8990 /* AudioAdapter */
8991 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8992 if (FAILED(rc)) return rc;
8993
8994 /* Shared folders */
8995 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8996 it != data.llSharedFolders.end();
8997 ++it)
8998 {
8999 const settings::SharedFolder &sf = *it;
9000
9001 ComObjPtr<SharedFolder> sharedFolder;
9002 /* Check for double entries. Not allowed! */
9003 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9004 if (SUCCEEDED(rc))
9005 return setError(VBOX_E_OBJECT_IN_USE,
9006 tr("Shared folder named '%s' already exists"),
9007 sf.strName.c_str());
9008
9009 /* Create the new shared folder. Don't break on error. This will be
9010 * reported when the machine starts. */
9011 sharedFolder.createObject();
9012 rc = sharedFolder->init(i_getMachine(),
9013 sf.strName,
9014 sf.strHostPath,
9015 RT_BOOL(sf.fWritable),
9016 RT_BOOL(sf.fAutoMount),
9017 false /* fFailOnError */);
9018 if (FAILED(rc)) return rc;
9019 mHWData->mSharedFolders.push_back(sharedFolder);
9020 }
9021
9022 // Clipboard
9023 mHWData->mClipboardMode = data.clipboardMode;
9024
9025 // drag'n'drop
9026 mHWData->mDnDMode = data.dndMode;
9027
9028 // guest settings
9029 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9030
9031 // IO settings
9032 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9033 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9034
9035 // Host PCI devices
9036 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9037 it != data.pciAttachments.end();
9038 ++it)
9039 {
9040 const settings::HostPCIDeviceAttachment &hpda = *it;
9041 ComObjPtr<PCIDeviceAttachment> pda;
9042
9043 pda.createObject();
9044 pda->i_loadSettings(this, hpda);
9045 mHWData->mPCIDeviceAssignments.push_back(pda);
9046 }
9047
9048 /*
9049 * (The following isn't really real hardware, but it lives in HWData
9050 * for reasons of convenience.)
9051 */
9052
9053#ifdef VBOX_WITH_GUEST_PROPS
9054 /* Guest properties (optional) */
9055 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9056 it != data.llGuestProperties.end();
9057 ++it)
9058 {
9059 const settings::GuestProperty &prop = *it;
9060 uint32_t fFlags = guestProp::NILFLAG;
9061 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9062 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9063 mHWData->mGuestProperties[prop.strName] = property;
9064 }
9065
9066 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9067#endif /* VBOX_WITH_GUEST_PROPS defined */
9068
9069 rc = i_loadDebugging(pDbg);
9070 if (FAILED(rc))
9071 return rc;
9072
9073 mHWData->mAutostart = *pAutostart;
9074
9075 /* default frontend */
9076 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9077 }
9078 catch(std::bad_alloc &)
9079 {
9080 return E_OUTOFMEMORY;
9081 }
9082
9083 AssertComRC(rc);
9084 return rc;
9085}
9086
9087/**
9088 * Called from Machine::loadHardware() to load the debugging settings of the
9089 * machine.
9090 *
9091 * @param pDbg Pointer to the settings.
9092 */
9093HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9094{
9095 mHWData->mDebugging = *pDbg;
9096 /* no more processing currently required, this will probably change. */
9097 return S_OK;
9098}
9099
9100/**
9101 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9102 *
9103 * @param data
9104 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9105 * @param puuidSnapshot
9106 * @return
9107 */
9108HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9109 const Guid *puuidRegistry,
9110 const Guid *puuidSnapshot)
9111{
9112 AssertReturn(!i_isSessionMachine(), E_FAIL);
9113
9114 HRESULT rc = S_OK;
9115
9116 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9117 it != data.llStorageControllers.end();
9118 ++it)
9119 {
9120 const settings::StorageController &ctlData = *it;
9121
9122 ComObjPtr<StorageController> pCtl;
9123 /* Try to find one with the name first. */
9124 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9125 if (SUCCEEDED(rc))
9126 return setError(VBOX_E_OBJECT_IN_USE,
9127 tr("Storage controller named '%s' already exists"),
9128 ctlData.strName.c_str());
9129
9130 pCtl.createObject();
9131 rc = pCtl->init(this,
9132 ctlData.strName,
9133 ctlData.storageBus,
9134 ctlData.ulInstance,
9135 ctlData.fBootable);
9136 if (FAILED(rc)) return rc;
9137
9138 mStorageControllers->push_back(pCtl);
9139
9140 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9141 if (FAILED(rc)) return rc;
9142
9143 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9144 if (FAILED(rc)) return rc;
9145
9146 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9147 if (FAILED(rc)) return rc;
9148
9149 /* Set IDE emulation settings (only for AHCI controller). */
9150 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9151 {
9152 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9153 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9154 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9155 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9156 )
9157 return rc;
9158 }
9159
9160 /* Load the attached devices now. */
9161 rc = i_loadStorageDevices(pCtl,
9162 ctlData,
9163 puuidRegistry,
9164 puuidSnapshot);
9165 if (FAILED(rc)) return rc;
9166 }
9167
9168 return S_OK;
9169}
9170
9171/**
9172 * Called from i_loadStorageControllers for a controller's devices.
9173 *
9174 * @param aStorageController
9175 * @param data
9176 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9177 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9178 * @return
9179 */
9180HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9181 const settings::StorageController &data,
9182 const Guid *puuidRegistry,
9183 const Guid *puuidSnapshot)
9184{
9185 HRESULT rc = S_OK;
9186
9187 /* paranoia: detect duplicate attachments */
9188 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9189 it != data.llAttachedDevices.end();
9190 ++it)
9191 {
9192 const settings::AttachedDevice &ad = *it;
9193
9194 for (settings::AttachedDevicesList::const_iterator it2 = it;
9195 it2 != data.llAttachedDevices.end();
9196 ++it2)
9197 {
9198 if (it == it2)
9199 continue;
9200
9201 const settings::AttachedDevice &ad2 = *it2;
9202
9203 if ( ad.lPort == ad2.lPort
9204 && ad.lDevice == ad2.lDevice)
9205 {
9206 return setError(E_FAIL,
9207 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9208 aStorageController->i_getName().c_str(),
9209 ad.lPort,
9210 ad.lDevice,
9211 mUserData->s.strName.c_str());
9212 }
9213 }
9214 }
9215
9216 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9217 it != data.llAttachedDevices.end();
9218 ++it)
9219 {
9220 const settings::AttachedDevice &dev = *it;
9221 ComObjPtr<Medium> medium;
9222
9223 switch (dev.deviceType)
9224 {
9225 case DeviceType_Floppy:
9226 case DeviceType_DVD:
9227 if (dev.strHostDriveSrc.isNotEmpty())
9228 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9229 false /* fRefresh */, medium);
9230 else
9231 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9232 dev.uuid,
9233 false /* fRefresh */,
9234 false /* aSetError */,
9235 medium);
9236 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9237 // This is not an error. The host drive or UUID might have vanished, so just go
9238 // ahead without this removeable medium attachment
9239 rc = S_OK;
9240 break;
9241
9242 case DeviceType_HardDisk:
9243 {
9244 /* find a hard disk by UUID */
9245 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9246 if (FAILED(rc))
9247 {
9248 if (i_isSnapshotMachine())
9249 {
9250 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9251 // so the user knows that the bad disk is in a snapshot somewhere
9252 com::ErrorInfo info;
9253 return setError(E_FAIL,
9254 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9255 puuidSnapshot->raw(),
9256 info.getText().raw());
9257 }
9258 else
9259 return rc;
9260 }
9261
9262 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9263
9264 if (medium->i_getType() == MediumType_Immutable)
9265 {
9266 if (i_isSnapshotMachine())
9267 return setError(E_FAIL,
9268 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9269 "of the virtual machine '%s' ('%s')"),
9270 medium->i_getLocationFull().c_str(),
9271 dev.uuid.raw(),
9272 puuidSnapshot->raw(),
9273 mUserData->s.strName.c_str(),
9274 mData->m_strConfigFileFull.c_str());
9275
9276 return setError(E_FAIL,
9277 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9278 medium->i_getLocationFull().c_str(),
9279 dev.uuid.raw(),
9280 mUserData->s.strName.c_str(),
9281 mData->m_strConfigFileFull.c_str());
9282 }
9283
9284 if (medium->i_getType() == MediumType_MultiAttach)
9285 {
9286 if (i_isSnapshotMachine())
9287 return setError(E_FAIL,
9288 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9289 "of the virtual machine '%s' ('%s')"),
9290 medium->i_getLocationFull().c_str(),
9291 dev.uuid.raw(),
9292 puuidSnapshot->raw(),
9293 mUserData->s.strName.c_str(),
9294 mData->m_strConfigFileFull.c_str());
9295
9296 return setError(E_FAIL,
9297 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9298 medium->i_getLocationFull().c_str(),
9299 dev.uuid.raw(),
9300 mUserData->s.strName.c_str(),
9301 mData->m_strConfigFileFull.c_str());
9302 }
9303
9304 if ( !i_isSnapshotMachine()
9305 && medium->i_getChildren().size() != 0
9306 )
9307 return setError(E_FAIL,
9308 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9309 "because it has %d differencing child hard disks"),
9310 medium->i_getLocationFull().c_str(),
9311 dev.uuid.raw(),
9312 mUserData->s.strName.c_str(),
9313 mData->m_strConfigFileFull.c_str(),
9314 medium->i_getChildren().size());
9315
9316 if (i_findAttachment(mMediaData->mAttachments,
9317 medium))
9318 return setError(E_FAIL,
9319 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9320 medium->i_getLocationFull().c_str(),
9321 dev.uuid.raw(),
9322 mUserData->s.strName.c_str(),
9323 mData->m_strConfigFileFull.c_str());
9324
9325 break;
9326 }
9327
9328 default:
9329 return setError(E_FAIL,
9330 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9331 medium->i_getLocationFull().c_str(),
9332 mUserData->s.strName.c_str(),
9333 mData->m_strConfigFileFull.c_str());
9334 }
9335
9336 if (FAILED(rc))
9337 break;
9338
9339 /* Bandwidth groups are loaded at this point. */
9340 ComObjPtr<BandwidthGroup> pBwGroup;
9341
9342 if (!dev.strBwGroup.isEmpty())
9343 {
9344 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9345 if (FAILED(rc))
9346 return setError(E_FAIL,
9347 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9348 medium->i_getLocationFull().c_str(),
9349 dev.strBwGroup.c_str(),
9350 mUserData->s.strName.c_str(),
9351 mData->m_strConfigFileFull.c_str());
9352 pBwGroup->i_reference();
9353 }
9354
9355 const Bstr controllerName = aStorageController->i_getName();
9356 ComObjPtr<MediumAttachment> pAttachment;
9357 pAttachment.createObject();
9358 rc = pAttachment->init(this,
9359 medium,
9360 controllerName,
9361 dev.lPort,
9362 dev.lDevice,
9363 dev.deviceType,
9364 false,
9365 dev.fPassThrough,
9366 dev.fTempEject,
9367 dev.fNonRotational,
9368 dev.fDiscard,
9369 dev.fHotPluggable,
9370 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9371 if (FAILED(rc)) break;
9372
9373 /* associate the medium with this machine and snapshot */
9374 if (!medium.isNull())
9375 {
9376 AutoCaller medCaller(medium);
9377 if (FAILED(medCaller.rc())) return medCaller.rc();
9378 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9379
9380 if (i_isSnapshotMachine())
9381 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9382 else
9383 rc = medium->i_addBackReference(mData->mUuid);
9384 /* If the medium->addBackReference fails it sets an appropriate
9385 * error message, so no need to do any guesswork here. */
9386
9387 if (puuidRegistry)
9388 // caller wants registry ID to be set on all attached media (OVF import case)
9389 medium->i_addRegistry(*puuidRegistry);
9390 }
9391
9392 if (FAILED(rc))
9393 break;
9394
9395 /* back up mMediaData to let registeredInit() properly rollback on failure
9396 * (= limited accessibility) */
9397 i_setModified(IsModified_Storage);
9398 mMediaData.backup();
9399 mMediaData->mAttachments.push_back(pAttachment);
9400 }
9401
9402 return rc;
9403}
9404
9405/**
9406 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9407 *
9408 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9409 * @param aSnapshot where to return the found snapshot
9410 * @param aSetError true to set extended error info on failure
9411 */
9412HRESULT Machine::i_findSnapshotById(const Guid &aId,
9413 ComObjPtr<Snapshot> &aSnapshot,
9414 bool aSetError /* = false */)
9415{
9416 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9417
9418 if (!mData->mFirstSnapshot)
9419 {
9420 if (aSetError)
9421 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9422 return E_FAIL;
9423 }
9424
9425 if (aId.isZero())
9426 aSnapshot = mData->mFirstSnapshot;
9427 else
9428 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9429
9430 if (!aSnapshot)
9431 {
9432 if (aSetError)
9433 return setError(E_FAIL,
9434 tr("Could not find a snapshot with UUID {%s}"),
9435 aId.toString().c_str());
9436 return E_FAIL;
9437 }
9438
9439 return S_OK;
9440}
9441
9442/**
9443 * Returns the snapshot with the given name or fails of no such snapshot.
9444 *
9445 * @param aName snapshot name to find
9446 * @param aSnapshot where to return the found snapshot
9447 * @param aSetError true to set extended error info on failure
9448 */
9449HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9450 ComObjPtr<Snapshot> &aSnapshot,
9451 bool aSetError /* = false */)
9452{
9453 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9454
9455 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9456
9457 if (!mData->mFirstSnapshot)
9458 {
9459 if (aSetError)
9460 return setError(VBOX_E_OBJECT_NOT_FOUND,
9461 tr("This machine does not have any snapshots"));
9462 return VBOX_E_OBJECT_NOT_FOUND;
9463 }
9464
9465 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9466
9467 if (!aSnapshot)
9468 {
9469 if (aSetError)
9470 return setError(VBOX_E_OBJECT_NOT_FOUND,
9471 tr("Could not find a snapshot named '%s'"), strName.c_str());
9472 return VBOX_E_OBJECT_NOT_FOUND;
9473 }
9474
9475 return S_OK;
9476}
9477
9478/**
9479 * Returns a storage controller object with the given name.
9480 *
9481 * @param aName storage controller name to find
9482 * @param aStorageController where to return the found storage controller
9483 * @param aSetError true to set extended error info on failure
9484 */
9485HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9486 ComObjPtr<StorageController> &aStorageController,
9487 bool aSetError /* = false */)
9488{
9489 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9490
9491 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9492 it != mStorageControllers->end();
9493 ++it)
9494 {
9495 if ((*it)->i_getName() == aName)
9496 {
9497 aStorageController = (*it);
9498 return S_OK;
9499 }
9500 }
9501
9502 if (aSetError)
9503 return setError(VBOX_E_OBJECT_NOT_FOUND,
9504 tr("Could not find a storage controller named '%s'"),
9505 aName.c_str());
9506 return VBOX_E_OBJECT_NOT_FOUND;
9507}
9508
9509/**
9510 * Returns a USB controller object with the given name.
9511 *
9512 * @param aName USB controller name to find
9513 * @param aUSBController where to return the found USB controller
9514 * @param aSetError true to set extended error info on failure
9515 */
9516HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9517 ComObjPtr<USBController> &aUSBController,
9518 bool aSetError /* = false */)
9519{
9520 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9521
9522 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9523 it != mUSBControllers->end();
9524 ++it)
9525 {
9526 if ((*it)->i_getName() == aName)
9527 {
9528 aUSBController = (*it);
9529 return S_OK;
9530 }
9531 }
9532
9533 if (aSetError)
9534 return setError(VBOX_E_OBJECT_NOT_FOUND,
9535 tr("Could not find a storage controller named '%s'"),
9536 aName.c_str());
9537 return VBOX_E_OBJECT_NOT_FOUND;
9538}
9539
9540/**
9541 * Returns the number of USB controller instance of the given type.
9542 *
9543 * @param enmType USB controller type.
9544 */
9545ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9546{
9547 ULONG cCtrls = 0;
9548
9549 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9550 it != mUSBControllers->end();
9551 ++it)
9552 {
9553 if ((*it)->i_getControllerType() == enmType)
9554 cCtrls++;
9555 }
9556
9557 return cCtrls;
9558}
9559
9560HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9561 MediaData::AttachmentList &atts)
9562{
9563 AutoCaller autoCaller(this);
9564 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9565
9566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9567
9568 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9569 it != mMediaData->mAttachments.end();
9570 ++it)
9571 {
9572 const ComObjPtr<MediumAttachment> &pAtt = *it;
9573 // should never happen, but deal with NULL pointers in the list.
9574 AssertStmt(!pAtt.isNull(), continue);
9575
9576 // getControllerName() needs caller+read lock
9577 AutoCaller autoAttCaller(pAtt);
9578 if (FAILED(autoAttCaller.rc()))
9579 {
9580 atts.clear();
9581 return autoAttCaller.rc();
9582 }
9583 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9584
9585 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9586 atts.push_back(pAtt);
9587 }
9588
9589 return S_OK;
9590}
9591
9592
9593/**
9594 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9595 * file if the machine name was changed and about creating a new settings file
9596 * if this is a new machine.
9597 *
9598 * @note Must be never called directly but only from #saveSettings().
9599 */
9600HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9601{
9602 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9603
9604 HRESULT rc = S_OK;
9605
9606 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9607
9608 /// @todo need to handle primary group change, too
9609
9610 /* attempt to rename the settings file if machine name is changed */
9611 if ( mUserData->s.fNameSync
9612 && mUserData.isBackedUp()
9613 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9614 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9615 )
9616 {
9617 bool dirRenamed = false;
9618 bool fileRenamed = false;
9619
9620 Utf8Str configFile, newConfigFile;
9621 Utf8Str configFilePrev, newConfigFilePrev;
9622 Utf8Str configDir, newConfigDir;
9623
9624 do
9625 {
9626 int vrc = VINF_SUCCESS;
9627
9628 Utf8Str name = mUserData.backedUpData()->s.strName;
9629 Utf8Str newName = mUserData->s.strName;
9630 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9631 if (group == "/")
9632 group.setNull();
9633 Utf8Str newGroup = mUserData->s.llGroups.front();
9634 if (newGroup == "/")
9635 newGroup.setNull();
9636
9637 configFile = mData->m_strConfigFileFull;
9638
9639 /* first, rename the directory if it matches the group and machine name */
9640 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9641 group.c_str(), RTPATH_DELIMITER, name.c_str());
9642 /** @todo hack, make somehow use of ComposeMachineFilename */
9643 if (mUserData->s.fDirectoryIncludesUUID)
9644 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9645 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9646 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9647 /** @todo hack, make somehow use of ComposeMachineFilename */
9648 if (mUserData->s.fDirectoryIncludesUUID)
9649 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9650 configDir = configFile;
9651 configDir.stripFilename();
9652 newConfigDir = configDir;
9653 if ( configDir.length() >= groupPlusName.length()
9654 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9655 groupPlusName.c_str()))
9656 {
9657 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9658 Utf8Str newConfigBaseDir(newConfigDir);
9659 newConfigDir.append(newGroupPlusName);
9660 /* consistency: use \ if appropriate on the platform */
9661 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9662 /* new dir and old dir cannot be equal here because of 'if'
9663 * above and because name != newName */
9664 Assert(configDir != newConfigDir);
9665 if (!fSettingsFileIsNew)
9666 {
9667 /* perform real rename only if the machine is not new */
9668 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9669 if ( vrc == VERR_FILE_NOT_FOUND
9670 || vrc == VERR_PATH_NOT_FOUND)
9671 {
9672 /* create the parent directory, then retry renaming */
9673 Utf8Str parent(newConfigDir);
9674 parent.stripFilename();
9675 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9676 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9677 }
9678 if (RT_FAILURE(vrc))
9679 {
9680 rc = setError(E_FAIL,
9681 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9682 configDir.c_str(),
9683 newConfigDir.c_str(),
9684 vrc);
9685 break;
9686 }
9687 /* delete subdirectories which are no longer needed */
9688 Utf8Str dir(configDir);
9689 dir.stripFilename();
9690 while (dir != newConfigBaseDir && dir != ".")
9691 {
9692 vrc = RTDirRemove(dir.c_str());
9693 if (RT_FAILURE(vrc))
9694 break;
9695 dir.stripFilename();
9696 }
9697 dirRenamed = true;
9698 }
9699 }
9700
9701 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9702 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9703
9704 /* then try to rename the settings file itself */
9705 if (newConfigFile != configFile)
9706 {
9707 /* get the path to old settings file in renamed directory */
9708 configFile = Utf8StrFmt("%s%c%s",
9709 newConfigDir.c_str(),
9710 RTPATH_DELIMITER,
9711 RTPathFilename(configFile.c_str()));
9712 if (!fSettingsFileIsNew)
9713 {
9714 /* perform real rename only if the machine is not new */
9715 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9716 if (RT_FAILURE(vrc))
9717 {
9718 rc = setError(E_FAIL,
9719 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9720 configFile.c_str(),
9721 newConfigFile.c_str(),
9722 vrc);
9723 break;
9724 }
9725 fileRenamed = true;
9726 configFilePrev = configFile;
9727 configFilePrev += "-prev";
9728 newConfigFilePrev = newConfigFile;
9729 newConfigFilePrev += "-prev";
9730 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9731 }
9732 }
9733
9734 // update m_strConfigFileFull amd mConfigFile
9735 mData->m_strConfigFileFull = newConfigFile;
9736 // compute the relative path too
9737 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9738
9739 // store the old and new so that VirtualBox::i_saveSettings() can update
9740 // the media registry
9741 if ( mData->mRegistered
9742 && (configDir != newConfigDir || configFile != newConfigFile))
9743 {
9744 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9745
9746 if (pfNeedsGlobalSaveSettings)
9747 *pfNeedsGlobalSaveSettings = true;
9748 }
9749
9750 // in the saved state file path, replace the old directory with the new directory
9751 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9752 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9753
9754 // and do the same thing for the saved state file paths of all the online snapshots
9755 if (mData->mFirstSnapshot)
9756 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9757 newConfigDir.c_str());
9758 }
9759 while (0);
9760
9761 if (FAILED(rc))
9762 {
9763 /* silently try to rename everything back */
9764 if (fileRenamed)
9765 {
9766 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9767 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9768 }
9769 if (dirRenamed)
9770 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9771 }
9772
9773 if (FAILED(rc)) return rc;
9774 }
9775
9776 if (fSettingsFileIsNew)
9777 {
9778 /* create a virgin config file */
9779 int vrc = VINF_SUCCESS;
9780
9781 /* ensure the settings directory exists */
9782 Utf8Str path(mData->m_strConfigFileFull);
9783 path.stripFilename();
9784 if (!RTDirExists(path.c_str()))
9785 {
9786 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9787 if (RT_FAILURE(vrc))
9788 {
9789 return setError(E_FAIL,
9790 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9791 path.c_str(),
9792 vrc);
9793 }
9794 }
9795
9796 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9797 path = Utf8Str(mData->m_strConfigFileFull);
9798 RTFILE f = NIL_RTFILE;
9799 vrc = RTFileOpen(&f, path.c_str(),
9800 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9801 if (RT_FAILURE(vrc))
9802 return setError(E_FAIL,
9803 tr("Could not create the settings file '%s' (%Rrc)"),
9804 path.c_str(),
9805 vrc);
9806 RTFileClose(f);
9807 }
9808
9809 return rc;
9810}
9811
9812/**
9813 * Saves and commits machine data, user data and hardware data.
9814 *
9815 * Note that on failure, the data remains uncommitted.
9816 *
9817 * @a aFlags may combine the following flags:
9818 *
9819 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9820 * Used when saving settings after an operation that makes them 100%
9821 * correspond to the settings from the current snapshot.
9822 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9823 * #isReallyModified() returns false. This is necessary for cases when we
9824 * change machine data directly, not through the backup()/commit() mechanism.
9825 * - SaveS_Force: settings will be saved without doing a deep compare of the
9826 * settings structures. This is used when this is called because snapshots
9827 * have changed to avoid the overhead of the deep compare.
9828 *
9829 * @note Must be called from under this object's write lock. Locks children for
9830 * writing.
9831 *
9832 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9833 * initialized to false and that will be set to true by this function if
9834 * the caller must invoke VirtualBox::i_saveSettings() because the global
9835 * settings have changed. This will happen if a machine rename has been
9836 * saved and the global machine and media registries will therefore need
9837 * updating.
9838 */
9839HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9840 int aFlags /*= 0*/)
9841{
9842 LogFlowThisFuncEnter();
9843
9844 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9845
9846 /* make sure child objects are unable to modify the settings while we are
9847 * saving them */
9848 i_ensureNoStateDependencies();
9849
9850 AssertReturn(!i_isSnapshotMachine(),
9851 E_FAIL);
9852
9853 HRESULT rc = S_OK;
9854 bool fNeedsWrite = false;
9855
9856 /* First, prepare to save settings. It will care about renaming the
9857 * settings directory and file if the machine name was changed and about
9858 * creating a new settings file if this is a new machine. */
9859 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9860 if (FAILED(rc)) return rc;
9861
9862 // keep a pointer to the current settings structures
9863 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9864 settings::MachineConfigFile *pNewConfig = NULL;
9865
9866 try
9867 {
9868 // make a fresh one to have everyone write stuff into
9869 pNewConfig = new settings::MachineConfigFile(NULL);
9870 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9871
9872 // now go and copy all the settings data from COM to the settings structures
9873 // (this calles i_saveSettings() on all the COM objects in the machine)
9874 i_copyMachineDataToSettings(*pNewConfig);
9875
9876 if (aFlags & SaveS_ResetCurStateModified)
9877 {
9878 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9879 mData->mCurrentStateModified = FALSE;
9880 fNeedsWrite = true; // always, no need to compare
9881 }
9882 else if (aFlags & SaveS_Force)
9883 {
9884 fNeedsWrite = true; // always, no need to compare
9885 }
9886 else
9887 {
9888 if (!mData->mCurrentStateModified)
9889 {
9890 // do a deep compare of the settings that we just saved with the settings
9891 // previously stored in the config file; this invokes MachineConfigFile::operator==
9892 // which does a deep compare of all the settings, which is expensive but less expensive
9893 // than writing out XML in vain
9894 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9895
9896 // could still be modified if any settings changed
9897 mData->mCurrentStateModified = fAnySettingsChanged;
9898
9899 fNeedsWrite = fAnySettingsChanged;
9900 }
9901 else
9902 fNeedsWrite = true;
9903 }
9904
9905 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9906
9907 if (fNeedsWrite)
9908 // now spit it all out!
9909 pNewConfig->write(mData->m_strConfigFileFull);
9910
9911 mData->pMachineConfigFile = pNewConfig;
9912 delete pOldConfig;
9913 i_commit();
9914
9915 // after saving settings, we are no longer different from the XML on disk
9916 mData->flModifications = 0;
9917 }
9918 catch (HRESULT err)
9919 {
9920 // we assume that error info is set by the thrower
9921 rc = err;
9922
9923 // restore old config
9924 delete pNewConfig;
9925 mData->pMachineConfigFile = pOldConfig;
9926 }
9927 catch (...)
9928 {
9929 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9930 }
9931
9932 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9933 {
9934 /* Fire the data change event, even on failure (since we've already
9935 * committed all data). This is done only for SessionMachines because
9936 * mutable Machine instances are always not registered (i.e. private
9937 * to the client process that creates them) and thus don't need to
9938 * inform callbacks. */
9939 if (i_isSessionMachine())
9940 mParent->i_onMachineDataChange(mData->mUuid);
9941 }
9942
9943 LogFlowThisFunc(("rc=%08X\n", rc));
9944 LogFlowThisFuncLeave();
9945 return rc;
9946}
9947
9948/**
9949 * Implementation for saving the machine settings into the given
9950 * settings::MachineConfigFile instance. This copies machine extradata
9951 * from the previous machine config file in the instance data, if any.
9952 *
9953 * This gets called from two locations:
9954 *
9955 * -- Machine::i_saveSettings(), during the regular XML writing;
9956 *
9957 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9958 * exported to OVF and we write the VirtualBox proprietary XML
9959 * into a <vbox:Machine> tag.
9960 *
9961 * This routine fills all the fields in there, including snapshots, *except*
9962 * for the following:
9963 *
9964 * -- fCurrentStateModified. There is some special logic associated with that.
9965 *
9966 * The caller can then call MachineConfigFile::write() or do something else
9967 * with it.
9968 *
9969 * Caller must hold the machine lock!
9970 *
9971 * This throws XML errors and HRESULT, so the caller must have a catch block!
9972 */
9973void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9974{
9975 // deep copy extradata
9976 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9977
9978 config.uuid = mData->mUuid;
9979
9980 // copy name, description, OS type, teleport, UTC etc.
9981 config.machineUserData = mUserData->s;
9982
9983 // Encode the Icon Override data from Machine and store on config userdata.
9984 std::vector<BYTE> iconByte;
9985 getIcon(iconByte);
9986 ssize_t cbData = iconByte.size();
9987 if (cbData > 0)
9988 {
9989 ssize_t cchOut = RTBase64EncodedLength(cbData);
9990 Utf8Str strIconData;
9991 strIconData.reserve(cchOut+1);
9992 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9993 strIconData.mutableRaw(), strIconData.capacity(),
9994 NULL);
9995 if (RT_FAILURE(vrc))
9996 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9997 strIconData.jolt();
9998 config.machineUserData.ovIcon = strIconData;
9999 }
10000 else
10001 config.machineUserData.ovIcon.setNull();
10002
10003 if ( mData->mMachineState == MachineState_Saved
10004 || mData->mMachineState == MachineState_Restoring
10005 // when doing certain snapshot operations we may or may not have
10006 // a saved state in the current state, so keep everything as is
10007 || ( ( mData->mMachineState == MachineState_Snapshotting
10008 || mData->mMachineState == MachineState_DeletingSnapshot
10009 || mData->mMachineState == MachineState_RestoringSnapshot)
10010 && (!mSSData->strStateFilePath.isEmpty())
10011 )
10012 )
10013 {
10014 Assert(!mSSData->strStateFilePath.isEmpty());
10015 /* try to make the file name relative to the settings file dir */
10016 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10017 }
10018 else
10019 {
10020 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10021 config.strStateFile.setNull();
10022 }
10023
10024 if (mData->mCurrentSnapshot)
10025 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10026 else
10027 config.uuidCurrentSnapshot.clear();
10028
10029 config.timeLastStateChange = mData->mLastStateChange;
10030 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10031 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10032
10033 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10034 if (FAILED(rc)) throw rc;
10035
10036 rc = i_saveStorageControllers(config.storageMachine);
10037 if (FAILED(rc)) throw rc;
10038
10039 // save machine's media registry if this is VirtualBox 4.0 or later
10040 if (config.canHaveOwnMediaRegistry())
10041 {
10042 // determine machine folder
10043 Utf8Str strMachineFolder = i_getSettingsFileFull();
10044 strMachineFolder.stripFilename();
10045 mParent->i_saveMediaRegistry(config.mediaRegistry,
10046 i_getId(), // only media with registry ID == machine UUID
10047 strMachineFolder);
10048 // this throws HRESULT
10049 }
10050
10051 // save snapshots
10052 rc = i_saveAllSnapshots(config);
10053 if (FAILED(rc)) throw rc;
10054}
10055
10056/**
10057 * Saves all snapshots of the machine into the given machine config file. Called
10058 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10059 * @param config
10060 * @return
10061 */
10062HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10063{
10064 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10065
10066 HRESULT rc = S_OK;
10067
10068 try
10069 {
10070 config.llFirstSnapshot.clear();
10071
10072 if (mData->mFirstSnapshot)
10073 {
10074 // the settings use a list for "the first snapshot"
10075 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10076
10077 // get reference to the snapshot on the list and work on that
10078 // element straight in the list to avoid excessive copying later
10079 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10080 if (FAILED(rc)) throw rc;
10081 }
10082
10083// if (mType == IsSessionMachine)
10084// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10085
10086 }
10087 catch (HRESULT err)
10088 {
10089 /* we assume that error info is set by the thrower */
10090 rc = err;
10091 }
10092 catch (...)
10093 {
10094 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10095 }
10096
10097 return rc;
10098}
10099
10100/**
10101 * Saves the VM hardware configuration. It is assumed that the
10102 * given node is empty.
10103 *
10104 * @param data Reference to the settings object for the hardware config.
10105 * @param pDbg Pointer to the settings object for the debugging config
10106 * which happens to live in mHWData.
10107 * @param pAutostart Pointer to the settings object for the autostart config
10108 * which happens to live in mHWData.
10109 */
10110HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10111 settings::Autostart *pAutostart)
10112{
10113 HRESULT rc = S_OK;
10114
10115 try
10116 {
10117 /* The hardware version attribute (optional).
10118 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10119 if ( mHWData->mHWVersion == "1"
10120 && mSSData->strStateFilePath.isEmpty()
10121 )
10122 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10123 other point needs to be found where this can be done. */
10124
10125 data.strVersion = mHWData->mHWVersion;
10126 data.uuid = mHWData->mHardwareUUID;
10127
10128 // CPU
10129 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10130 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10131 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10132 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10133 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10134 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10135 data.fPAE = !!mHWData->mPAEEnabled;
10136 data.enmLongMode = mHWData->mLongMode;
10137 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10138 data.cCPUs = mHWData->mCPUCount;
10139 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10140 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10141 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10142
10143 data.llCpus.clear();
10144 if (data.fCpuHotPlug)
10145 {
10146 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10147 {
10148 if (mHWData->mCPUAttached[idx])
10149 {
10150 settings::Cpu cpu;
10151 cpu.ulId = idx;
10152 data.llCpus.push_back(cpu);
10153 }
10154 }
10155 }
10156
10157 /* Standard and Extended CPUID leafs. */
10158 data.llCpuIdLeafs.clear();
10159 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10160 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10161 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10162 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10163 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10164 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10165
10166 // memory
10167 data.ulMemorySizeMB = mHWData->mMemorySize;
10168 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10169
10170 // firmware
10171 data.firmwareType = mHWData->mFirmwareType;
10172
10173 // HID
10174 data.pointingHIDType = mHWData->mPointingHIDType;
10175 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10176
10177 // chipset
10178 data.chipsetType = mHWData->mChipsetType;
10179
10180 // paravirt
10181 data.paravirtProvider = mHWData->mParavirtProvider;
10182
10183
10184 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10185
10186 // HPET
10187 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10188
10189 // boot order
10190 data.mapBootOrder.clear();
10191 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10192 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10193
10194 // display
10195 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10196 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10197 data.cMonitors = mHWData->mMonitorCount;
10198 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10199 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10200 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10201 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10202 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10203 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10204 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10205 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10206 {
10207 if (mHWData->maVideoCaptureScreens[i])
10208 ASMBitSet(&data.u64VideoCaptureScreens, i);
10209 else
10210 ASMBitClear(&data.u64VideoCaptureScreens, i);
10211 }
10212 /* store relative video capture file if possible */
10213 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10214
10215 /* VRDEServer settings (optional) */
10216 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10217 if (FAILED(rc)) throw rc;
10218
10219 /* BIOS (required) */
10220 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10221 if (FAILED(rc)) throw rc;
10222
10223 /* USB Controller (required) */
10224 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10225 {
10226 ComObjPtr<USBController> ctrl = *it;
10227 settings::USBController settingsCtrl;
10228
10229 settingsCtrl.strName = ctrl->i_getName();
10230 settingsCtrl.enmType = ctrl->i_getControllerType();
10231
10232 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10233 }
10234
10235 /* USB device filters (required) */
10236 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10237 if (FAILED(rc)) throw rc;
10238
10239 /* Network adapters (required) */
10240 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10241 data.llNetworkAdapters.clear();
10242 /* Write out only the nominal number of network adapters for this
10243 * chipset type. Since Machine::commit() hasn't been called there
10244 * may be extra NIC settings in the vector. */
10245 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10246 {
10247 settings::NetworkAdapter nic;
10248 nic.ulSlot = (uint32_t)slot;
10249 /* paranoia check... must not be NULL, but must not crash either. */
10250 if (mNetworkAdapters[slot])
10251 {
10252 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10253 if (FAILED(rc)) throw rc;
10254
10255 data.llNetworkAdapters.push_back(nic);
10256 }
10257 }
10258
10259 /* Serial ports */
10260 data.llSerialPorts.clear();
10261 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10262 {
10263 settings::SerialPort s;
10264 s.ulSlot = slot;
10265 rc = mSerialPorts[slot]->i_saveSettings(s);
10266 if (FAILED(rc)) return rc;
10267
10268 data.llSerialPorts.push_back(s);
10269 }
10270
10271 /* Parallel ports */
10272 data.llParallelPorts.clear();
10273 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10274 {
10275 settings::ParallelPort p;
10276 p.ulSlot = slot;
10277 rc = mParallelPorts[slot]->i_saveSettings(p);
10278 if (FAILED(rc)) return rc;
10279
10280 data.llParallelPorts.push_back(p);
10281 }
10282
10283 /* Audio adapter */
10284 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10285 if (FAILED(rc)) return rc;
10286
10287 /* Shared folders */
10288 data.llSharedFolders.clear();
10289 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10290 it != mHWData->mSharedFolders.end();
10291 ++it)
10292 {
10293 SharedFolder *pSF = *it;
10294 AutoCaller sfCaller(pSF);
10295 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10296 settings::SharedFolder sf;
10297 sf.strName = pSF->i_getName();
10298 sf.strHostPath = pSF->i_getHostPath();
10299 sf.fWritable = !!pSF->i_isWritable();
10300 sf.fAutoMount = !!pSF->i_isAutoMounted();
10301
10302 data.llSharedFolders.push_back(sf);
10303 }
10304
10305 // clipboard
10306 data.clipboardMode = mHWData->mClipboardMode;
10307
10308 // drag'n'drop
10309 data.dndMode = mHWData->mDnDMode;
10310
10311 /* Guest */
10312 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10313
10314 // IO settings
10315 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10316 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10317
10318 /* BandwidthControl (required) */
10319 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10320 if (FAILED(rc)) throw rc;
10321
10322 /* Host PCI devices */
10323 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10324 it != mHWData->mPCIDeviceAssignments.end();
10325 ++it)
10326 {
10327 ComObjPtr<PCIDeviceAttachment> pda = *it;
10328 settings::HostPCIDeviceAttachment hpda;
10329
10330 rc = pda->i_saveSettings(hpda);
10331 if (FAILED(rc)) throw rc;
10332
10333 data.pciAttachments.push_back(hpda);
10334 }
10335
10336
10337 // guest properties
10338 data.llGuestProperties.clear();
10339#ifdef VBOX_WITH_GUEST_PROPS
10340 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10341 it != mHWData->mGuestProperties.end();
10342 ++it)
10343 {
10344 HWData::GuestProperty property = it->second;
10345
10346 /* Remove transient guest properties at shutdown unless we
10347 * are saving state */
10348 if ( ( mData->mMachineState == MachineState_PoweredOff
10349 || mData->mMachineState == MachineState_Aborted
10350 || mData->mMachineState == MachineState_Teleported)
10351 && ( property.mFlags & guestProp::TRANSIENT
10352 || property.mFlags & guestProp::TRANSRESET))
10353 continue;
10354 settings::GuestProperty prop;
10355 prop.strName = it->first;
10356 prop.strValue = property.strValue;
10357 prop.timestamp = property.mTimestamp;
10358 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10359 guestProp::writeFlags(property.mFlags, szFlags);
10360 prop.strFlags = szFlags;
10361
10362 data.llGuestProperties.push_back(prop);
10363 }
10364
10365 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10366 /* I presume this doesn't require a backup(). */
10367 mData->mGuestPropertiesModified = FALSE;
10368#endif /* VBOX_WITH_GUEST_PROPS defined */
10369
10370 *pDbg = mHWData->mDebugging;
10371 *pAutostart = mHWData->mAutostart;
10372
10373 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10374 }
10375 catch(std::bad_alloc &)
10376 {
10377 return E_OUTOFMEMORY;
10378 }
10379
10380 AssertComRC(rc);
10381 return rc;
10382}
10383
10384/**
10385 * Saves the storage controller configuration.
10386 *
10387 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10388 */
10389HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10390{
10391 data.llStorageControllers.clear();
10392
10393 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10394 it != mStorageControllers->end();
10395 ++it)
10396 {
10397 HRESULT rc;
10398 ComObjPtr<StorageController> pCtl = *it;
10399
10400 settings::StorageController ctl;
10401 ctl.strName = pCtl->i_getName();
10402 ctl.controllerType = pCtl->i_getControllerType();
10403 ctl.storageBus = pCtl->i_getStorageBus();
10404 ctl.ulInstance = pCtl->i_getInstance();
10405 ctl.fBootable = pCtl->i_getBootable();
10406
10407 /* Save the port count. */
10408 ULONG portCount;
10409 rc = pCtl->COMGETTER(PortCount)(&portCount);
10410 ComAssertComRCRet(rc, rc);
10411 ctl.ulPortCount = portCount;
10412
10413 /* Save fUseHostIOCache */
10414 BOOL fUseHostIOCache;
10415 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10416 ComAssertComRCRet(rc, rc);
10417 ctl.fUseHostIOCache = !!fUseHostIOCache;
10418
10419 /* Save IDE emulation settings. */
10420 if (ctl.controllerType == StorageControllerType_IntelAhci)
10421 {
10422 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10423 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10424 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10425 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10426 )
10427 ComAssertComRCRet(rc, rc);
10428 }
10429
10430 /* save the devices now. */
10431 rc = i_saveStorageDevices(pCtl, ctl);
10432 ComAssertComRCRet(rc, rc);
10433
10434 data.llStorageControllers.push_back(ctl);
10435 }
10436
10437 return S_OK;
10438}
10439
10440/**
10441 * Saves the hard disk configuration.
10442 */
10443HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10444 settings::StorageController &data)
10445{
10446 MediaData::AttachmentList atts;
10447
10448 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10449 if (FAILED(rc)) return rc;
10450
10451 data.llAttachedDevices.clear();
10452 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10453 it != atts.end();
10454 ++it)
10455 {
10456 settings::AttachedDevice dev;
10457 IMediumAttachment *iA = *it;
10458 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10459 Medium *pMedium = pAttach->i_getMedium();
10460
10461 dev.deviceType = pAttach->i_getType();
10462 dev.lPort = pAttach->i_getPort();
10463 dev.lDevice = pAttach->i_getDevice();
10464 dev.fPassThrough = pAttach->i_getPassthrough();
10465 dev.fHotPluggable = pAttach->i_getHotPluggable();
10466 if (pMedium)
10467 {
10468 if (pMedium->i_isHostDrive())
10469 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10470 else
10471 dev.uuid = pMedium->i_getId();
10472 dev.fTempEject = pAttach->i_getTempEject();
10473 dev.fNonRotational = pAttach->i_getNonRotational();
10474 dev.fDiscard = pAttach->i_getDiscard();
10475 }
10476
10477 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10478
10479 data.llAttachedDevices.push_back(dev);
10480 }
10481
10482 return S_OK;
10483}
10484
10485/**
10486 * Saves machine state settings as defined by aFlags
10487 * (SaveSTS_* values).
10488 *
10489 * @param aFlags Combination of SaveSTS_* flags.
10490 *
10491 * @note Locks objects for writing.
10492 */
10493HRESULT Machine::i_saveStateSettings(int aFlags)
10494{
10495 if (aFlags == 0)
10496 return S_OK;
10497
10498 AutoCaller autoCaller(this);
10499 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10500
10501 /* This object's write lock is also necessary to serialize file access
10502 * (prevent concurrent reads and writes) */
10503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10504
10505 HRESULT rc = S_OK;
10506
10507 Assert(mData->pMachineConfigFile);
10508
10509 try
10510 {
10511 if (aFlags & SaveSTS_CurStateModified)
10512 mData->pMachineConfigFile->fCurrentStateModified = true;
10513
10514 if (aFlags & SaveSTS_StateFilePath)
10515 {
10516 if (!mSSData->strStateFilePath.isEmpty())
10517 /* try to make the file name relative to the settings file dir */
10518 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10519 else
10520 mData->pMachineConfigFile->strStateFile.setNull();
10521 }
10522
10523 if (aFlags & SaveSTS_StateTimeStamp)
10524 {
10525 Assert( mData->mMachineState != MachineState_Aborted
10526 || mSSData->strStateFilePath.isEmpty());
10527
10528 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10529
10530 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10531//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10532 }
10533
10534 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10535 }
10536 catch (...)
10537 {
10538 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10539 }
10540
10541 return rc;
10542}
10543
10544/**
10545 * Ensures that the given medium is added to a media registry. If this machine
10546 * was created with 4.0 or later, then the machine registry is used. Otherwise
10547 * the global VirtualBox media registry is used.
10548 *
10549 * Caller must NOT hold machine lock, media tree or any medium locks!
10550 *
10551 * @param pMedium
10552 */
10553void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10554{
10555 /* Paranoia checks: do not hold machine or media tree locks. */
10556 AssertReturnVoid(!isWriteLockOnCurrentThread());
10557 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10558
10559 ComObjPtr<Medium> pBase;
10560 {
10561 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10562 pBase = pMedium->i_getBase();
10563 }
10564
10565 /* Paranoia checks: do not hold medium locks. */
10566 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10567 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10568
10569 // decide which medium registry to use now that the medium is attached:
10570 Guid uuid;
10571 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10572 // machine XML is VirtualBox 4.0 or higher:
10573 uuid = i_getId(); // machine UUID
10574 else
10575 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10576
10577 if (pMedium->i_addRegistry(uuid))
10578 mParent->i_markRegistryModified(uuid);
10579
10580 /* For more complex hard disk structures it can happen that the base
10581 * medium isn't yet associated with any medium registry. Do that now. */
10582 if (pMedium != pBase)
10583 {
10584 /* Tree lock needed by Medium::addRegistry when recursing. */
10585 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10586 if (pBase->i_addRegistryRecursive(uuid))
10587 {
10588 treeLock.release();
10589 mParent->i_markRegistryModified(uuid);
10590 }
10591 }
10592}
10593
10594/**
10595 * Creates differencing hard disks for all normal hard disks attached to this
10596 * machine and a new set of attachments to refer to created disks.
10597 *
10598 * Used when taking a snapshot or when deleting the current state. Gets called
10599 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10600 *
10601 * This method assumes that mMediaData contains the original hard disk attachments
10602 * it needs to create diffs for. On success, these attachments will be replaced
10603 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10604 * called to delete created diffs which will also rollback mMediaData and restore
10605 * whatever was backed up before calling this method.
10606 *
10607 * Attachments with non-normal hard disks are left as is.
10608 *
10609 * If @a aOnline is @c false then the original hard disks that require implicit
10610 * diffs will be locked for reading. Otherwise it is assumed that they are
10611 * already locked for writing (when the VM was started). Note that in the latter
10612 * case it is responsibility of the caller to lock the newly created diffs for
10613 * writing if this method succeeds.
10614 *
10615 * @param aProgress Progress object to run (must contain at least as
10616 * many operations left as the number of hard disks
10617 * attached).
10618 * @param aOnline Whether the VM was online prior to this operation.
10619 *
10620 * @note The progress object is not marked as completed, neither on success nor
10621 * on failure. This is a responsibility of the caller.
10622 *
10623 * @note Locks this object and the media tree for writing.
10624 */
10625HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10626 ULONG aWeight,
10627 bool aOnline)
10628{
10629 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10630
10631 AutoCaller autoCaller(this);
10632 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10633
10634 AutoMultiWriteLock2 alock(this->lockHandle(),
10635 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10636
10637 /* must be in a protective state because we release the lock below */
10638 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10639 || mData->mMachineState == MachineState_OnlineSnapshotting
10640 || mData->mMachineState == MachineState_LiveSnapshotting
10641 || mData->mMachineState == MachineState_RestoringSnapshot
10642 || mData->mMachineState == MachineState_DeletingSnapshot
10643 , E_FAIL);
10644
10645 HRESULT rc = S_OK;
10646
10647 // use appropriate locked media map (online or offline)
10648 MediumLockListMap lockedMediaOffline;
10649 MediumLockListMap *lockedMediaMap;
10650 if (aOnline)
10651 lockedMediaMap = &mData->mSession.mLockedMedia;
10652 else
10653 lockedMediaMap = &lockedMediaOffline;
10654
10655 try
10656 {
10657 if (!aOnline)
10658 {
10659 /* lock all attached hard disks early to detect "in use"
10660 * situations before creating actual diffs */
10661 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10662 it != mMediaData->mAttachments.end();
10663 ++it)
10664 {
10665 MediumAttachment* pAtt = *it;
10666 if (pAtt->i_getType() == DeviceType_HardDisk)
10667 {
10668 Medium* pMedium = pAtt->i_getMedium();
10669 Assert(pMedium);
10670
10671 MediumLockList *pMediumLockList(new MediumLockList());
10672 alock.release();
10673 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10674 false /* fMediumLockWrite */,
10675 false /* fMediumLockWriteAll */,
10676 NULL,
10677 *pMediumLockList);
10678 alock.acquire();
10679 if (FAILED(rc))
10680 {
10681 delete pMediumLockList;
10682 throw rc;
10683 }
10684 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10685 if (FAILED(rc))
10686 {
10687 throw setError(rc,
10688 tr("Collecting locking information for all attached media failed"));
10689 }
10690 }
10691 }
10692
10693 /* Now lock all media. If this fails, nothing is locked. */
10694 alock.release();
10695 rc = lockedMediaMap->Lock();
10696 alock.acquire();
10697 if (FAILED(rc))
10698 {
10699 throw setError(rc,
10700 tr("Locking of attached media failed"));
10701 }
10702 }
10703
10704 /* remember the current list (note that we don't use backup() since
10705 * mMediaData may be already backed up) */
10706 MediaData::AttachmentList atts = mMediaData->mAttachments;
10707
10708 /* start from scratch */
10709 mMediaData->mAttachments.clear();
10710
10711 /* go through remembered attachments and create diffs for normal hard
10712 * disks and attach them */
10713 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10714 it != atts.end();
10715 ++it)
10716 {
10717 MediumAttachment* pAtt = *it;
10718
10719 DeviceType_T devType = pAtt->i_getType();
10720 Medium* pMedium = pAtt->i_getMedium();
10721
10722 if ( devType != DeviceType_HardDisk
10723 || pMedium == NULL
10724 || pMedium->i_getType() != MediumType_Normal)
10725 {
10726 /* copy the attachment as is */
10727
10728 /** @todo the progress object created in SessionMachine::TakeSnaphot
10729 * only expects operations for hard disks. Later other
10730 * device types need to show up in the progress as well. */
10731 if (devType == DeviceType_HardDisk)
10732 {
10733 if (pMedium == NULL)
10734 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10735 aWeight); // weight
10736 else
10737 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10738 pMedium->i_getBase()->i_getName().c_str()).raw(),
10739 aWeight); // weight
10740 }
10741
10742 mMediaData->mAttachments.push_back(pAtt);
10743 continue;
10744 }
10745
10746 /* need a diff */
10747 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10748 pMedium->i_getBase()->i_getName().c_str()).raw(),
10749 aWeight); // weight
10750
10751 Utf8Str strFullSnapshotFolder;
10752 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10753
10754 ComObjPtr<Medium> diff;
10755 diff.createObject();
10756 // store the diff in the same registry as the parent
10757 // (this cannot fail here because we can't create implicit diffs for
10758 // unregistered images)
10759 Guid uuidRegistryParent;
10760 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10761 Assert(fInRegistry); NOREF(fInRegistry);
10762 rc = diff->init(mParent,
10763 pMedium->i_getPreferredDiffFormat(),
10764 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10765 uuidRegistryParent,
10766 DeviceType_HardDisk);
10767 if (FAILED(rc)) throw rc;
10768
10769 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10770 * the push_back? Looks like we're going to release medium with the
10771 * wrong kind of lock (general issue with if we fail anywhere at all)
10772 * and an orphaned VDI in the snapshots folder. */
10773
10774 /* update the appropriate lock list */
10775 MediumLockList *pMediumLockList;
10776 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10777 AssertComRCThrowRC(rc);
10778 if (aOnline)
10779 {
10780 alock.release();
10781 /* The currently attached medium will be read-only, change
10782 * the lock type to read. */
10783 rc = pMediumLockList->Update(pMedium, false);
10784 alock.acquire();
10785 AssertComRCThrowRC(rc);
10786 }
10787
10788 /* release the locks before the potentially lengthy operation */
10789 alock.release();
10790 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10791 pMediumLockList,
10792 NULL /* aProgress */,
10793 true /* aWait */);
10794 alock.acquire();
10795 if (FAILED(rc)) throw rc;
10796
10797 /* actual lock list update is done in Medium::commitMedia */
10798
10799 rc = diff->i_addBackReference(mData->mUuid);
10800 AssertComRCThrowRC(rc);
10801
10802 /* add a new attachment */
10803 ComObjPtr<MediumAttachment> attachment;
10804 attachment.createObject();
10805 rc = attachment->init(this,
10806 diff,
10807 pAtt->i_getControllerName(),
10808 pAtt->i_getPort(),
10809 pAtt->i_getDevice(),
10810 DeviceType_HardDisk,
10811 true /* aImplicit */,
10812 false /* aPassthrough */,
10813 false /* aTempEject */,
10814 pAtt->i_getNonRotational(),
10815 pAtt->i_getDiscard(),
10816 pAtt->i_getHotPluggable(),
10817 pAtt->i_getBandwidthGroup());
10818 if (FAILED(rc)) throw rc;
10819
10820 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10821 AssertComRCThrowRC(rc);
10822 mMediaData->mAttachments.push_back(attachment);
10823 }
10824 }
10825 catch (HRESULT aRC) { rc = aRC; }
10826
10827 /* unlock all hard disks we locked when there is no VM */
10828 if (!aOnline)
10829 {
10830 ErrorInfoKeeper eik;
10831
10832 HRESULT rc1 = lockedMediaMap->Clear();
10833 AssertComRC(rc1);
10834 }
10835
10836 return rc;
10837}
10838
10839/**
10840 * Deletes implicit differencing hard disks created either by
10841 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10842 *
10843 * Note that to delete hard disks created by #AttachDevice() this method is
10844 * called from #fixupMedia() when the changes are rolled back.
10845 *
10846 * @note Locks this object and the media tree for writing.
10847 */
10848HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10849{
10850 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10851
10852 AutoCaller autoCaller(this);
10853 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10854
10855 AutoMultiWriteLock2 alock(this->lockHandle(),
10856 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10857
10858 /* We absolutely must have backed up state. */
10859 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10860
10861 /* Check if there are any implicitly created diff images. */
10862 bool fImplicitDiffs = false;
10863 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10864 it != mMediaData->mAttachments.end();
10865 ++it)
10866 {
10867 const ComObjPtr<MediumAttachment> &pAtt = *it;
10868 if (pAtt->i_isImplicit())
10869 {
10870 fImplicitDiffs = true;
10871 break;
10872 }
10873 }
10874 /* If there is nothing to do, leave early. This saves lots of image locking
10875 * effort. It also avoids a MachineStateChanged event without real reason.
10876 * This is important e.g. when loading a VM config, because there should be
10877 * no events. Otherwise API clients can become thoroughly confused for
10878 * inaccessible VMs (the code for loading VM configs uses this method for
10879 * cleanup if the config makes no sense), as they take such events as an
10880 * indication that the VM is alive, and they would force the VM config to
10881 * be reread, leading to an endless loop. */
10882 if (!fImplicitDiffs)
10883 return S_OK;
10884
10885 HRESULT rc = S_OK;
10886 MachineState_T oldState = mData->mMachineState;
10887
10888 /* will release the lock before the potentially lengthy operation,
10889 * so protect with the special state (unless already protected) */
10890 if ( oldState != MachineState_Snapshotting
10891 && oldState != MachineState_OnlineSnapshotting
10892 && oldState != MachineState_LiveSnapshotting
10893 && oldState != MachineState_RestoringSnapshot
10894 && oldState != MachineState_DeletingSnapshot
10895 && oldState != MachineState_DeletingSnapshotOnline
10896 && oldState != MachineState_DeletingSnapshotPaused
10897 )
10898 i_setMachineState(MachineState_SettingUp);
10899
10900 // use appropriate locked media map (online or offline)
10901 MediumLockListMap lockedMediaOffline;
10902 MediumLockListMap *lockedMediaMap;
10903 if (aOnline)
10904 lockedMediaMap = &mData->mSession.mLockedMedia;
10905 else
10906 lockedMediaMap = &lockedMediaOffline;
10907
10908 try
10909 {
10910 if (!aOnline)
10911 {
10912 /* lock all attached hard disks early to detect "in use"
10913 * situations before deleting actual diffs */
10914 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10915 it != mMediaData->mAttachments.end();
10916 ++it)
10917 {
10918 MediumAttachment* pAtt = *it;
10919 if (pAtt->i_getType() == DeviceType_HardDisk)
10920 {
10921 Medium* pMedium = pAtt->i_getMedium();
10922 Assert(pMedium);
10923
10924 MediumLockList *pMediumLockList(new MediumLockList());
10925 alock.release();
10926 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10927 false /* fMediumLockWrite */,
10928 false /* fMediumLockWriteAll */,
10929 NULL,
10930 *pMediumLockList);
10931 alock.acquire();
10932
10933 if (FAILED(rc))
10934 {
10935 delete pMediumLockList;
10936 throw rc;
10937 }
10938
10939 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10940 if (FAILED(rc))
10941 throw rc;
10942 }
10943 }
10944
10945 if (FAILED(rc))
10946 throw rc;
10947 } // end of offline
10948
10949 /* Lock lists are now up to date and include implicitly created media */
10950
10951 /* Go through remembered attachments and delete all implicitly created
10952 * diffs and fix up the attachment information */
10953 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10954 MediaData::AttachmentList implicitAtts;
10955 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10956 it != mMediaData->mAttachments.end();
10957 ++it)
10958 {
10959 ComObjPtr<MediumAttachment> pAtt = *it;
10960 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10961 if (pMedium.isNull())
10962 continue;
10963
10964 // Implicit attachments go on the list for deletion and back references are removed.
10965 if (pAtt->i_isImplicit())
10966 {
10967 /* Deassociate and mark for deletion */
10968 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10969 rc = pMedium->i_removeBackReference(mData->mUuid);
10970 if (FAILED(rc))
10971 throw rc;
10972 implicitAtts.push_back(pAtt);
10973 continue;
10974 }
10975
10976 /* Was this medium attached before? */
10977 if (!i_findAttachment(oldAtts, pMedium))
10978 {
10979 /* no: de-associate */
10980 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10981 rc = pMedium->i_removeBackReference(mData->mUuid);
10982 if (FAILED(rc))
10983 throw rc;
10984 continue;
10985 }
10986 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10987 }
10988
10989 /* If there are implicit attachments to delete, throw away the lock
10990 * map contents (which will unlock all media) since the medium
10991 * attachments will be rolled back. Below we need to completely
10992 * recreate the lock map anyway since it is infinitely complex to
10993 * do this incrementally (would need reconstructing each attachment
10994 * change, which would be extremely hairy). */
10995 if (implicitAtts.size() != 0)
10996 {
10997 ErrorInfoKeeper eik;
10998
10999 HRESULT rc1 = lockedMediaMap->Clear();
11000 AssertComRC(rc1);
11001 }
11002
11003 /* rollback hard disk changes */
11004 mMediaData.rollback();
11005
11006 MultiResult mrc(S_OK);
11007
11008 // Delete unused implicit diffs.
11009 if (implicitAtts.size() != 0)
11010 {
11011 alock.release();
11012
11013 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11014 {
11015 // Remove medium associated with this attachment.
11016 ComObjPtr<MediumAttachment> pAtt = *it;
11017 Assert(pAtt);
11018 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11019 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11020 Assert(pMedium);
11021
11022 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11023 // continue on delete failure, just collect error messages
11024 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11025 pMedium->i_getLocationFull().c_str() ));
11026 mrc = rc;
11027 }
11028 // Clear the list of deleted implicit attachments now, while not
11029 // holding the lock, as it will ultimately trigger Medium::uninit()
11030 // calls which assume that the media tree lock isn't held.
11031 implicitAtts.clear();
11032
11033 alock.acquire();
11034
11035 /* if there is a VM recreate media lock map as mentioned above,
11036 * otherwise it is a waste of time and we leave things unlocked */
11037 if (aOnline)
11038 {
11039 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11040 /* must never be NULL, but better safe than sorry */
11041 if (!pMachine.isNull())
11042 {
11043 alock.release();
11044 rc = mData->mSession.mMachine->i_lockMedia();
11045 alock.acquire();
11046 if (FAILED(rc))
11047 throw rc;
11048 }
11049 }
11050 }
11051 }
11052 catch (HRESULT aRC) {rc = aRC;}
11053
11054 if (mData->mMachineState == MachineState_SettingUp)
11055 i_setMachineState(oldState);
11056
11057 /* unlock all hard disks we locked when there is no VM */
11058 if (!aOnline)
11059 {
11060 ErrorInfoKeeper eik;
11061
11062 HRESULT rc1 = lockedMediaMap->Clear();
11063 AssertComRC(rc1);
11064 }
11065
11066 return rc;
11067}
11068
11069
11070/**
11071 * Looks through the given list of media attachments for one with the given parameters
11072 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11073 * can be searched as well if needed.
11074 *
11075 * @param list
11076 * @param aControllerName
11077 * @param aControllerPort
11078 * @param aDevice
11079 * @return
11080 */
11081MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11082 IN_BSTR aControllerName,
11083 LONG aControllerPort,
11084 LONG aDevice)
11085{
11086 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11087 {
11088 MediumAttachment *pAttach = *it;
11089 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11090 return pAttach;
11091 }
11092
11093 return NULL;
11094}
11095
11096/**
11097 * Looks through the given list of media attachments for one with the given parameters
11098 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11099 * can be searched as well if needed.
11100 *
11101 * @param list
11102 * @param aControllerName
11103 * @param aControllerPort
11104 * @param aDevice
11105 * @return
11106 */
11107MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11108 ComObjPtr<Medium> pMedium)
11109{
11110 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11111 {
11112 MediumAttachment *pAttach = *it;
11113 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11114 if (pMediumThis == pMedium)
11115 return pAttach;
11116 }
11117
11118 return NULL;
11119}
11120
11121/**
11122 * Looks through the given list of media attachments for one with the given parameters
11123 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11124 * can be searched as well if needed.
11125 *
11126 * @param list
11127 * @param aControllerName
11128 * @param aControllerPort
11129 * @param aDevice
11130 * @return
11131 */
11132MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11133 Guid &id)
11134{
11135 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11136 {
11137 MediumAttachment *pAttach = *it;
11138 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11139 if (pMediumThis->i_getId() == id)
11140 return pAttach;
11141 }
11142
11143 return NULL;
11144}
11145
11146/**
11147 * Main implementation for Machine::DetachDevice. This also gets called
11148 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11149 *
11150 * @param pAttach Medium attachment to detach.
11151 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11152 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11153 * SnapshotMachine, and this must be its snapshot.
11154 * @return
11155 */
11156HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11157 AutoWriteLock &writeLock,
11158 Snapshot *pSnapshot)
11159{
11160 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11161 DeviceType_T mediumType = pAttach->i_getType();
11162
11163 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11164
11165 if (pAttach->i_isImplicit())
11166 {
11167 /* attempt to implicitly delete the implicitly created diff */
11168
11169 /// @todo move the implicit flag from MediumAttachment to Medium
11170 /// and forbid any hard disk operation when it is implicit. Or maybe
11171 /// a special media state for it to make it even more simple.
11172
11173 Assert(mMediaData.isBackedUp());
11174
11175 /* will release the lock before the potentially lengthy operation, so
11176 * protect with the special state */
11177 MachineState_T oldState = mData->mMachineState;
11178 i_setMachineState(MachineState_SettingUp);
11179
11180 writeLock.release();
11181
11182 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11183 true /*aWait*/);
11184
11185 writeLock.acquire();
11186
11187 i_setMachineState(oldState);
11188
11189 if (FAILED(rc)) return rc;
11190 }
11191
11192 i_setModified(IsModified_Storage);
11193 mMediaData.backup();
11194 mMediaData->mAttachments.remove(pAttach);
11195
11196 if (!oldmedium.isNull())
11197 {
11198 // if this is from a snapshot, do not defer detachment to commitMedia()
11199 if (pSnapshot)
11200 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11201 // else if non-hard disk media, do not defer detachment to commitMedia() either
11202 else if (mediumType != DeviceType_HardDisk)
11203 oldmedium->i_removeBackReference(mData->mUuid);
11204 }
11205
11206 return S_OK;
11207}
11208
11209/**
11210 * Goes thru all media of the given list and
11211 *
11212 * 1) calls i_detachDevice() on each of them for this machine and
11213 * 2) adds all Medium objects found in the process to the given list,
11214 * depending on cleanupMode.
11215 *
11216 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11217 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11218 * media to the list.
11219 *
11220 * This gets called from Machine::Unregister, both for the actual Machine and
11221 * the SnapshotMachine objects that might be found in the snapshots.
11222 *
11223 * Requires caller and locking. The machine lock must be passed in because it
11224 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11225 *
11226 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11227 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11228 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11229 * Full, then all media get added;
11230 * otherwise no media get added.
11231 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11232 * @return
11233 */
11234HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11235 Snapshot *pSnapshot,
11236 CleanupMode_T cleanupMode,
11237 MediaList &llMedia)
11238{
11239 Assert(isWriteLockOnCurrentThread());
11240
11241 HRESULT rc;
11242
11243 // make a temporary list because i_detachDevice invalidates iterators into
11244 // mMediaData->mAttachments
11245 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11246
11247 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11248 {
11249 ComObjPtr<MediumAttachment> &pAttach = *it;
11250 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11251
11252 if (!pMedium.isNull())
11253 {
11254 AutoCaller mac(pMedium);
11255 if (FAILED(mac.rc())) return mac.rc();
11256 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11257 DeviceType_T devType = pMedium->i_getDeviceType();
11258 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11259 && devType == DeviceType_HardDisk)
11260 || (cleanupMode == CleanupMode_Full)
11261 )
11262 {
11263 llMedia.push_back(pMedium);
11264 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11265 /* Not allowed to keep this lock as below we need the parent
11266 * medium lock, and the lock order is parent to child. */
11267 lock.release();
11268 /*
11269 * Search for medias which are not attached to any machine, but
11270 * in the chain to an attached disk. Mediums are only consided
11271 * if they are:
11272 * - have only one child
11273 * - no references to any machines
11274 * - are of normal medium type
11275 */
11276 while (!pParent.isNull())
11277 {
11278 AutoCaller mac1(pParent);
11279 if (FAILED(mac1.rc())) return mac1.rc();
11280 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11281 if (pParent->i_getChildren().size() == 1)
11282 {
11283 if ( pParent->i_getMachineBackRefCount() == 0
11284 && pParent->i_getType() == MediumType_Normal
11285 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11286 llMedia.push_back(pParent);
11287 }
11288 else
11289 break;
11290 pParent = pParent->i_getParent();
11291 }
11292 }
11293 }
11294
11295 // real machine: then we need to use the proper method
11296 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11297
11298 if (FAILED(rc))
11299 return rc;
11300 }
11301
11302 return S_OK;
11303}
11304
11305/**
11306 * Perform deferred hard disk detachments.
11307 *
11308 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11309 * backed up).
11310 *
11311 * If @a aOnline is @c true then this method will also unlock the old hard disks
11312 * for which the new implicit diffs were created and will lock these new diffs for
11313 * writing.
11314 *
11315 * @param aOnline Whether the VM was online prior to this operation.
11316 *
11317 * @note Locks this object for writing!
11318 */
11319void Machine::i_commitMedia(bool aOnline /*= false*/)
11320{
11321 AutoCaller autoCaller(this);
11322 AssertComRCReturnVoid(autoCaller.rc());
11323
11324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11325
11326 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11327
11328 HRESULT rc = S_OK;
11329
11330 /* no attach/detach operations -- nothing to do */
11331 if (!mMediaData.isBackedUp())
11332 return;
11333
11334 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11335 bool fMediaNeedsLocking = false;
11336
11337 /* enumerate new attachments */
11338 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11339 it != mMediaData->mAttachments.end();
11340 ++it)
11341 {
11342 MediumAttachment *pAttach = *it;
11343
11344 pAttach->i_commit();
11345
11346 Medium* pMedium = pAttach->i_getMedium();
11347 bool fImplicit = pAttach->i_isImplicit();
11348
11349 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11350 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11351 fImplicit));
11352
11353 /** @todo convert all this Machine-based voodoo to MediumAttachment
11354 * based commit logic. */
11355 if (fImplicit)
11356 {
11357 /* convert implicit attachment to normal */
11358 pAttach->i_setImplicit(false);
11359
11360 if ( aOnline
11361 && pMedium
11362 && pAttach->i_getType() == DeviceType_HardDisk
11363 )
11364 {
11365 /* update the appropriate lock list */
11366 MediumLockList *pMediumLockList;
11367 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11368 AssertComRC(rc);
11369 if (pMediumLockList)
11370 {
11371 /* unlock if there's a need to change the locking */
11372 if (!fMediaNeedsLocking)
11373 {
11374 rc = mData->mSession.mLockedMedia.Unlock();
11375 AssertComRC(rc);
11376 fMediaNeedsLocking = true;
11377 }
11378 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11379 AssertComRC(rc);
11380 rc = pMediumLockList->Append(pMedium, true);
11381 AssertComRC(rc);
11382 }
11383 }
11384
11385 continue;
11386 }
11387
11388 if (pMedium)
11389 {
11390 /* was this medium attached before? */
11391 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11392 {
11393 MediumAttachment *pOldAttach = *oldIt;
11394 if (pOldAttach->i_getMedium() == pMedium)
11395 {
11396 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11397
11398 /* yes: remove from old to avoid de-association */
11399 oldAtts.erase(oldIt);
11400 break;
11401 }
11402 }
11403 }
11404 }
11405
11406 /* enumerate remaining old attachments and de-associate from the
11407 * current machine state */
11408 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11409 {
11410 MediumAttachment *pAttach = *it;
11411 Medium* pMedium = pAttach->i_getMedium();
11412
11413 /* Detach only hard disks, since DVD/floppy media is detached
11414 * instantly in MountMedium. */
11415 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11416 {
11417 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11418
11419 /* now de-associate from the current machine state */
11420 rc = pMedium->i_removeBackReference(mData->mUuid);
11421 AssertComRC(rc);
11422
11423 if (aOnline)
11424 {
11425 /* unlock since medium is not used anymore */
11426 MediumLockList *pMediumLockList;
11427 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11428 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11429 {
11430 /* this happens for online snapshots, there the attachment
11431 * is changing, but only to a diff image created under
11432 * the old one, so there is no separate lock list */
11433 Assert(!pMediumLockList);
11434 }
11435 else
11436 {
11437 AssertComRC(rc);
11438 if (pMediumLockList)
11439 {
11440 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11441 AssertComRC(rc);
11442 }
11443 }
11444 }
11445 }
11446 }
11447
11448 /* take media locks again so that the locking state is consistent */
11449 if (fMediaNeedsLocking)
11450 {
11451 Assert(aOnline);
11452 rc = mData->mSession.mLockedMedia.Lock();
11453 AssertComRC(rc);
11454 }
11455
11456 /* commit the hard disk changes */
11457 mMediaData.commit();
11458
11459 if (i_isSessionMachine())
11460 {
11461 /*
11462 * Update the parent machine to point to the new owner.
11463 * This is necessary because the stored parent will point to the
11464 * session machine otherwise and cause crashes or errors later
11465 * when the session machine gets invalid.
11466 */
11467 /** @todo Change the MediumAttachment class to behave like any other
11468 * class in this regard by creating peer MediumAttachment
11469 * objects for session machines and share the data with the peer
11470 * machine.
11471 */
11472 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11473 it != mMediaData->mAttachments.end();
11474 ++it)
11475 (*it)->i_updateParentMachine(mPeer);
11476
11477 /* attach new data to the primary machine and reshare it */
11478 mPeer->mMediaData.attach(mMediaData);
11479 }
11480
11481 return;
11482}
11483
11484/**
11485 * Perform deferred deletion of implicitly created diffs.
11486 *
11487 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11488 * backed up).
11489 *
11490 * @note Locks this object for writing!
11491 */
11492void Machine::i_rollbackMedia()
11493{
11494 AutoCaller autoCaller(this);
11495 AssertComRCReturnVoid(autoCaller.rc());
11496
11497 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11498 LogFlowThisFunc(("Entering rollbackMedia\n"));
11499
11500 HRESULT rc = S_OK;
11501
11502 /* no attach/detach operations -- nothing to do */
11503 if (!mMediaData.isBackedUp())
11504 return;
11505
11506 /* enumerate new attachments */
11507 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11508 it != mMediaData->mAttachments.end();
11509 ++it)
11510 {
11511 MediumAttachment *pAttach = *it;
11512 /* Fix up the backrefs for DVD/floppy media. */
11513 if (pAttach->i_getType() != DeviceType_HardDisk)
11514 {
11515 Medium* pMedium = pAttach->i_getMedium();
11516 if (pMedium)
11517 {
11518 rc = pMedium->i_removeBackReference(mData->mUuid);
11519 AssertComRC(rc);
11520 }
11521 }
11522
11523 (*it)->i_rollback();
11524
11525 pAttach = *it;
11526 /* Fix up the backrefs for DVD/floppy media. */
11527 if (pAttach->i_getType() != DeviceType_HardDisk)
11528 {
11529 Medium* pMedium = pAttach->i_getMedium();
11530 if (pMedium)
11531 {
11532 rc = pMedium->i_addBackReference(mData->mUuid);
11533 AssertComRC(rc);
11534 }
11535 }
11536 }
11537
11538 /** @todo convert all this Machine-based voodoo to MediumAttachment
11539 * based rollback logic. */
11540 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11541
11542 return;
11543}
11544
11545/**
11546 * Returns true if the settings file is located in the directory named exactly
11547 * as the machine; this means, among other things, that the machine directory
11548 * should be auto-renamed.
11549 *
11550 * @param aSettingsDir if not NULL, the full machine settings file directory
11551 * name will be assigned there.
11552 *
11553 * @note Doesn't lock anything.
11554 * @note Not thread safe (must be called from this object's lock).
11555 */
11556bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11557{
11558 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11559 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11560 if (aSettingsDir)
11561 *aSettingsDir = strMachineDirName;
11562 strMachineDirName.stripPath(); // vmname
11563 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11564 strConfigFileOnly.stripPath() // vmname.vbox
11565 .stripSuffix(); // vmname
11566 /** @todo hack, make somehow use of ComposeMachineFilename */
11567 if (mUserData->s.fDirectoryIncludesUUID)
11568 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11569
11570 AssertReturn(!strMachineDirName.isEmpty(), false);
11571 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11572
11573 return strMachineDirName == strConfigFileOnly;
11574}
11575
11576/**
11577 * Discards all changes to machine settings.
11578 *
11579 * @param aNotify Whether to notify the direct session about changes or not.
11580 *
11581 * @note Locks objects for writing!
11582 */
11583void Machine::i_rollback(bool aNotify)
11584{
11585 AutoCaller autoCaller(this);
11586 AssertComRCReturn(autoCaller.rc(), (void)0);
11587
11588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11589
11590 if (!mStorageControllers.isNull())
11591 {
11592 if (mStorageControllers.isBackedUp())
11593 {
11594 /* unitialize all new devices (absent in the backed up list). */
11595 StorageControllerList::const_iterator it = mStorageControllers->begin();
11596 StorageControllerList *backedList = mStorageControllers.backedUpData();
11597 while (it != mStorageControllers->end())
11598 {
11599 if ( std::find(backedList->begin(), backedList->end(), *it)
11600 == backedList->end()
11601 )
11602 {
11603 (*it)->uninit();
11604 }
11605 ++it;
11606 }
11607
11608 /* restore the list */
11609 mStorageControllers.rollback();
11610 }
11611
11612 /* rollback any changes to devices after restoring the list */
11613 if (mData->flModifications & IsModified_Storage)
11614 {
11615 StorageControllerList::const_iterator it = mStorageControllers->begin();
11616 while (it != mStorageControllers->end())
11617 {
11618 (*it)->i_rollback();
11619 ++it;
11620 }
11621 }
11622 }
11623
11624 if (!mUSBControllers.isNull())
11625 {
11626 if (mUSBControllers.isBackedUp())
11627 {
11628 /* unitialize all new devices (absent in the backed up list). */
11629 USBControllerList::const_iterator it = mUSBControllers->begin();
11630 USBControllerList *backedList = mUSBControllers.backedUpData();
11631 while (it != mUSBControllers->end())
11632 {
11633 if ( std::find(backedList->begin(), backedList->end(), *it)
11634 == backedList->end()
11635 )
11636 {
11637 (*it)->uninit();
11638 }
11639 ++it;
11640 }
11641
11642 /* restore the list */
11643 mUSBControllers.rollback();
11644 }
11645
11646 /* rollback any changes to devices after restoring the list */
11647 if (mData->flModifications & IsModified_USB)
11648 {
11649 USBControllerList::const_iterator it = mUSBControllers->begin();
11650 while (it != mUSBControllers->end())
11651 {
11652 (*it)->i_rollback();
11653 ++it;
11654 }
11655 }
11656 }
11657
11658 mUserData.rollback();
11659
11660 mHWData.rollback();
11661
11662 if (mData->flModifications & IsModified_Storage)
11663 i_rollbackMedia();
11664
11665 if (mBIOSSettings)
11666 mBIOSSettings->i_rollback();
11667
11668 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11669 mVRDEServer->i_rollback();
11670
11671 if (mAudioAdapter)
11672 mAudioAdapter->i_rollback();
11673
11674 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11675 mUSBDeviceFilters->i_rollback();
11676
11677 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11678 mBandwidthControl->i_rollback();
11679
11680 if (!mHWData.isNull())
11681 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11682 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11683 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11684 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11685
11686 if (mData->flModifications & IsModified_NetworkAdapters)
11687 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11688 if ( mNetworkAdapters[slot]
11689 && mNetworkAdapters[slot]->i_isModified())
11690 {
11691 mNetworkAdapters[slot]->i_rollback();
11692 networkAdapters[slot] = mNetworkAdapters[slot];
11693 }
11694
11695 if (mData->flModifications & IsModified_SerialPorts)
11696 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11697 if ( mSerialPorts[slot]
11698 && mSerialPorts[slot]->i_isModified())
11699 {
11700 mSerialPorts[slot]->i_rollback();
11701 serialPorts[slot] = mSerialPorts[slot];
11702 }
11703
11704 if (mData->flModifications & IsModified_ParallelPorts)
11705 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11706 if ( mParallelPorts[slot]
11707 && mParallelPorts[slot]->i_isModified())
11708 {
11709 mParallelPorts[slot]->i_rollback();
11710 parallelPorts[slot] = mParallelPorts[slot];
11711 }
11712
11713 if (aNotify)
11714 {
11715 /* inform the direct session about changes */
11716
11717 ComObjPtr<Machine> that = this;
11718 uint32_t flModifications = mData->flModifications;
11719 alock.release();
11720
11721 if (flModifications & IsModified_SharedFolders)
11722 that->i_onSharedFolderChange();
11723
11724 if (flModifications & IsModified_VRDEServer)
11725 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11726 if (flModifications & IsModified_USB)
11727 that->i_onUSBControllerChange();
11728
11729 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11730 if (networkAdapters[slot])
11731 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11732 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11733 if (serialPorts[slot])
11734 that->i_onSerialPortChange(serialPorts[slot]);
11735 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11736 if (parallelPorts[slot])
11737 that->i_onParallelPortChange(parallelPorts[slot]);
11738
11739 if (flModifications & IsModified_Storage)
11740 that->i_onStorageControllerChange();
11741
11742#if 0
11743 if (flModifications & IsModified_BandwidthControl)
11744 that->onBandwidthControlChange();
11745#endif
11746 }
11747}
11748
11749/**
11750 * Commits all the changes to machine settings.
11751 *
11752 * Note that this operation is supposed to never fail.
11753 *
11754 * @note Locks this object and children for writing.
11755 */
11756void Machine::i_commit()
11757{
11758 AutoCaller autoCaller(this);
11759 AssertComRCReturnVoid(autoCaller.rc());
11760
11761 AutoCaller peerCaller(mPeer);
11762 AssertComRCReturnVoid(peerCaller.rc());
11763
11764 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11765
11766 /*
11767 * use safe commit to ensure Snapshot machines (that share mUserData)
11768 * will still refer to a valid memory location
11769 */
11770 mUserData.commitCopy();
11771
11772 mHWData.commit();
11773
11774 if (mMediaData.isBackedUp())
11775 i_commitMedia(Global::IsOnline(mData->mMachineState));
11776
11777 mBIOSSettings->i_commit();
11778 mVRDEServer->i_commit();
11779 mAudioAdapter->i_commit();
11780 mUSBDeviceFilters->i_commit();
11781 mBandwidthControl->i_commit();
11782
11783 /* Since mNetworkAdapters is a list which might have been changed (resized)
11784 * without using the Backupable<> template we need to handle the copying
11785 * of the list entries manually, including the creation of peers for the
11786 * new objects. */
11787 bool commitNetworkAdapters = false;
11788 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11789 if (mPeer)
11790 {
11791 /* commit everything, even the ones which will go away */
11792 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11793 mNetworkAdapters[slot]->i_commit();
11794 /* copy over the new entries, creating a peer and uninit the original */
11795 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11796 for (size_t slot = 0; slot < newSize; slot++)
11797 {
11798 /* look if this adapter has a peer device */
11799 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11800 if (!peer)
11801 {
11802 /* no peer means the adapter is a newly created one;
11803 * create a peer owning data this data share it with */
11804 peer.createObject();
11805 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11806 }
11807 mPeer->mNetworkAdapters[slot] = peer;
11808 }
11809 /* uninit any no longer needed network adapters */
11810 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11811 mNetworkAdapters[slot]->uninit();
11812 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11813 {
11814 if (mPeer->mNetworkAdapters[slot])
11815 mPeer->mNetworkAdapters[slot]->uninit();
11816 }
11817 /* Keep the original network adapter count until this point, so that
11818 * discarding a chipset type change will not lose settings. */
11819 mNetworkAdapters.resize(newSize);
11820 mPeer->mNetworkAdapters.resize(newSize);
11821 }
11822 else
11823 {
11824 /* we have no peer (our parent is the newly created machine);
11825 * just commit changes to the network adapters */
11826 commitNetworkAdapters = true;
11827 }
11828 if (commitNetworkAdapters)
11829 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11830 mNetworkAdapters[slot]->i_commit();
11831
11832 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11833 mSerialPorts[slot]->i_commit();
11834 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11835 mParallelPorts[slot]->i_commit();
11836
11837 bool commitStorageControllers = false;
11838
11839 if (mStorageControllers.isBackedUp())
11840 {
11841 mStorageControllers.commit();
11842
11843 if (mPeer)
11844 {
11845 /* Commit all changes to new controllers (this will reshare data with
11846 * peers for those who have peers) */
11847 StorageControllerList *newList = new StorageControllerList();
11848 StorageControllerList::const_iterator it = mStorageControllers->begin();
11849 while (it != mStorageControllers->end())
11850 {
11851 (*it)->i_commit();
11852
11853 /* look if this controller has a peer device */
11854 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11855 if (!peer)
11856 {
11857 /* no peer means the device is a newly created one;
11858 * create a peer owning data this device share it with */
11859 peer.createObject();
11860 peer->init(mPeer, *it, true /* aReshare */);
11861 }
11862 else
11863 {
11864 /* remove peer from the old list */
11865 mPeer->mStorageControllers->remove(peer);
11866 }
11867 /* and add it to the new list */
11868 newList->push_back(peer);
11869
11870 ++it;
11871 }
11872
11873 /* uninit old peer's controllers that are left */
11874 it = mPeer->mStorageControllers->begin();
11875 while (it != mPeer->mStorageControllers->end())
11876 {
11877 (*it)->uninit();
11878 ++it;
11879 }
11880
11881 /* attach new list of controllers to our peer */
11882 mPeer->mStorageControllers.attach(newList);
11883 }
11884 else
11885 {
11886 /* we have no peer (our parent is the newly created machine);
11887 * just commit changes to devices */
11888 commitStorageControllers = true;
11889 }
11890 }
11891 else
11892 {
11893 /* the list of controllers itself is not changed,
11894 * just commit changes to controllers themselves */
11895 commitStorageControllers = true;
11896 }
11897
11898 if (commitStorageControllers)
11899 {
11900 StorageControllerList::const_iterator it = mStorageControllers->begin();
11901 while (it != mStorageControllers->end())
11902 {
11903 (*it)->i_commit();
11904 ++it;
11905 }
11906 }
11907
11908 bool commitUSBControllers = false;
11909
11910 if (mUSBControllers.isBackedUp())
11911 {
11912 mUSBControllers.commit();
11913
11914 if (mPeer)
11915 {
11916 /* Commit all changes to new controllers (this will reshare data with
11917 * peers for those who have peers) */
11918 USBControllerList *newList = new USBControllerList();
11919 USBControllerList::const_iterator it = mUSBControllers->begin();
11920 while (it != mUSBControllers->end())
11921 {
11922 (*it)->i_commit();
11923
11924 /* look if this controller has a peer device */
11925 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11926 if (!peer)
11927 {
11928 /* no peer means the device is a newly created one;
11929 * create a peer owning data this device share it with */
11930 peer.createObject();
11931 peer->init(mPeer, *it, true /* aReshare */);
11932 }
11933 else
11934 {
11935 /* remove peer from the old list */
11936 mPeer->mUSBControllers->remove(peer);
11937 }
11938 /* and add it to the new list */
11939 newList->push_back(peer);
11940
11941 ++it;
11942 }
11943
11944 /* uninit old peer's controllers that are left */
11945 it = mPeer->mUSBControllers->begin();
11946 while (it != mPeer->mUSBControllers->end())
11947 {
11948 (*it)->uninit();
11949 ++it;
11950 }
11951
11952 /* attach new list of controllers to our peer */
11953 mPeer->mUSBControllers.attach(newList);
11954 }
11955 else
11956 {
11957 /* we have no peer (our parent is the newly created machine);
11958 * just commit changes to devices */
11959 commitUSBControllers = true;
11960 }
11961 }
11962 else
11963 {
11964 /* the list of controllers itself is not changed,
11965 * just commit changes to controllers themselves */
11966 commitUSBControllers = true;
11967 }
11968
11969 if (commitUSBControllers)
11970 {
11971 USBControllerList::const_iterator it = mUSBControllers->begin();
11972 while (it != mUSBControllers->end())
11973 {
11974 (*it)->i_commit();
11975 ++it;
11976 }
11977 }
11978
11979 if (i_isSessionMachine())
11980 {
11981 /* attach new data to the primary machine and reshare it */
11982 mPeer->mUserData.attach(mUserData);
11983 mPeer->mHWData.attach(mHWData);
11984 /* mMediaData is reshared by fixupMedia */
11985 // mPeer->mMediaData.attach(mMediaData);
11986 Assert(mPeer->mMediaData.data() == mMediaData.data());
11987 }
11988}
11989
11990/**
11991 * Copies all the hardware data from the given machine.
11992 *
11993 * Currently, only called when the VM is being restored from a snapshot. In
11994 * particular, this implies that the VM is not running during this method's
11995 * call.
11996 *
11997 * @note This method must be called from under this object's lock.
11998 *
11999 * @note This method doesn't call #commit(), so all data remains backed up and
12000 * unsaved.
12001 */
12002void Machine::i_copyFrom(Machine *aThat)
12003{
12004 AssertReturnVoid(!i_isSnapshotMachine());
12005 AssertReturnVoid(aThat->i_isSnapshotMachine());
12006
12007 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12008
12009 mHWData.assignCopy(aThat->mHWData);
12010
12011 // create copies of all shared folders (mHWData after attaching a copy
12012 // contains just references to original objects)
12013 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12014 it != mHWData->mSharedFolders.end();
12015 ++it)
12016 {
12017 ComObjPtr<SharedFolder> folder;
12018 folder.createObject();
12019 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12020 AssertComRC(rc);
12021 *it = folder;
12022 }
12023
12024 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12025 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12026 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12027 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12028 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12029
12030 /* create private copies of all controllers */
12031 mStorageControllers.backup();
12032 mStorageControllers->clear();
12033 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12034 it != aThat->mStorageControllers->end();
12035 ++it)
12036 {
12037 ComObjPtr<StorageController> ctrl;
12038 ctrl.createObject();
12039 ctrl->initCopy(this, *it);
12040 mStorageControllers->push_back(ctrl);
12041 }
12042
12043 /* create private copies of all USB controllers */
12044 mUSBControllers.backup();
12045 mUSBControllers->clear();
12046 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12047 it != aThat->mUSBControllers->end();
12048 ++it)
12049 {
12050 ComObjPtr<USBController> ctrl;
12051 ctrl.createObject();
12052 ctrl->initCopy(this, *it);
12053 mUSBControllers->push_back(ctrl);
12054 }
12055
12056 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12057 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12058 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12059 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12060 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12061 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12062 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12063}
12064
12065/**
12066 * Returns whether the given storage controller is hotplug capable.
12067 *
12068 * @returns true if the controller supports hotplugging
12069 * false otherwise.
12070 * @param enmCtrlType The controller type to check for.
12071 */
12072bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12073{
12074 ComPtr<ISystemProperties> systemProperties;
12075 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12076 if (FAILED(rc))
12077 return false;
12078
12079 BOOL aHotplugCapable = FALSE;
12080 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12081
12082 return RT_BOOL(aHotplugCapable);
12083}
12084
12085#ifdef VBOX_WITH_RESOURCE_USAGE_API
12086
12087void Machine::i_getDiskList(MediaList &list)
12088{
12089 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12090 it != mMediaData->mAttachments.end();
12091 ++it)
12092 {
12093 MediumAttachment* pAttach = *it;
12094 /* just in case */
12095 AssertStmt(pAttach, continue);
12096
12097 AutoCaller localAutoCallerA(pAttach);
12098 if (FAILED(localAutoCallerA.rc())) continue;
12099
12100 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12101
12102 if (pAttach->i_getType() == DeviceType_HardDisk)
12103 list.push_back(pAttach->i_getMedium());
12104 }
12105}
12106
12107void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12108{
12109 AssertReturnVoid(isWriteLockOnCurrentThread());
12110 AssertPtrReturnVoid(aCollector);
12111
12112 pm::CollectorHAL *hal = aCollector->getHAL();
12113 /* Create sub metrics */
12114 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12115 "Percentage of processor time spent in user mode by the VM process.");
12116 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12117 "Percentage of processor time spent in kernel mode by the VM process.");
12118 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12119 "Size of resident portion of VM process in memory.");
12120 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12121 "Actual size of all VM disks combined.");
12122 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12123 "Network receive rate.");
12124 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12125 "Network transmit rate.");
12126 /* Create and register base metrics */
12127 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12128 cpuLoadUser, cpuLoadKernel);
12129 aCollector->registerBaseMetric(cpuLoad);
12130 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12131 ramUsageUsed);
12132 aCollector->registerBaseMetric(ramUsage);
12133 MediaList disks;
12134 i_getDiskList(disks);
12135 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12136 diskUsageUsed);
12137 aCollector->registerBaseMetric(diskUsage);
12138
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12140 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12141 new pm::AggregateAvg()));
12142 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12143 new pm::AggregateMin()));
12144 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12145 new pm::AggregateMax()));
12146 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12147 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12148 new pm::AggregateAvg()));
12149 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12150 new pm::AggregateMin()));
12151 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12152 new pm::AggregateMax()));
12153
12154 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12155 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12156 new pm::AggregateAvg()));
12157 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12158 new pm::AggregateMin()));
12159 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12160 new pm::AggregateMax()));
12161
12162 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12163 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12164 new pm::AggregateAvg()));
12165 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12166 new pm::AggregateMin()));
12167 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12168 new pm::AggregateMax()));
12169
12170
12171 /* Guest metrics collector */
12172 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12173 aCollector->registerGuest(mCollectorGuest);
12174 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12175 this, __PRETTY_FUNCTION__, mCollectorGuest));
12176
12177 /* Create sub metrics */
12178 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12179 "Percentage of processor time spent in user mode as seen by the guest.");
12180 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12181 "Percentage of processor time spent in kernel mode as seen by the guest.");
12182 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12183 "Percentage of processor time spent idling as seen by the guest.");
12184
12185 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12186 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12187 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12188 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12189 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12190 pm::SubMetric *guestMemCache = new pm::SubMetric(
12191 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12192
12193 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12194 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12195
12196 /* Create and register base metrics */
12197 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12198 machineNetRx, machineNetTx);
12199 aCollector->registerBaseMetric(machineNetRate);
12200
12201 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12202 guestLoadUser, guestLoadKernel, guestLoadIdle);
12203 aCollector->registerBaseMetric(guestCpuLoad);
12204
12205 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12206 guestMemTotal, guestMemFree,
12207 guestMemBalloon, guestMemShared,
12208 guestMemCache, guestPagedTotal);
12209 aCollector->registerBaseMetric(guestCpuMem);
12210
12211 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12213 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12215
12216 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12217 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12218 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12219 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12220
12221 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12225
12226 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12230
12231 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12232 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12233 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12235
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12240
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12245
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12250
12251 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12255
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12260
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12265}
12266
12267void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12268{
12269 AssertReturnVoid(isWriteLockOnCurrentThread());
12270
12271 if (aCollector)
12272 {
12273 aCollector->unregisterMetricsFor(aMachine);
12274 aCollector->unregisterBaseMetricsFor(aMachine);
12275 }
12276}
12277
12278#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12279
12280
12281////////////////////////////////////////////////////////////////////////////////
12282
12283DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12284
12285HRESULT SessionMachine::FinalConstruct()
12286{
12287 LogFlowThisFunc(("\n"));
12288
12289 mClientToken = NULL;
12290
12291 return BaseFinalConstruct();
12292}
12293
12294void SessionMachine::FinalRelease()
12295{
12296 LogFlowThisFunc(("\n"));
12297
12298 Assert(!mClientToken);
12299 /* paranoia, should not hang around any more */
12300 if (mClientToken)
12301 {
12302 delete mClientToken;
12303 mClientToken = NULL;
12304 }
12305
12306 uninit(Uninit::Unexpected);
12307
12308 BaseFinalRelease();
12309}
12310
12311/**
12312 * @note Must be called only by Machine::LockMachine() from its own write lock.
12313 */
12314HRESULT SessionMachine::init(Machine *aMachine)
12315{
12316 LogFlowThisFuncEnter();
12317 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12318
12319 AssertReturn(aMachine, E_INVALIDARG);
12320
12321 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12322
12323 /* Enclose the state transition NotReady->InInit->Ready */
12324 AutoInitSpan autoInitSpan(this);
12325 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12326
12327 HRESULT rc = S_OK;
12328
12329 /* create the machine client token */
12330 try
12331 {
12332 mClientToken = new ClientToken(aMachine, this);
12333 if (!mClientToken->isReady())
12334 {
12335 delete mClientToken;
12336 mClientToken = NULL;
12337 rc = E_FAIL;
12338 }
12339 }
12340 catch (std::bad_alloc &)
12341 {
12342 rc = E_OUTOFMEMORY;
12343 }
12344 if (FAILED(rc))
12345 return rc;
12346
12347 /* memorize the peer Machine */
12348 unconst(mPeer) = aMachine;
12349 /* share the parent pointer */
12350 unconst(mParent) = aMachine->mParent;
12351
12352 /* take the pointers to data to share */
12353 mData.share(aMachine->mData);
12354 mSSData.share(aMachine->mSSData);
12355
12356 mUserData.share(aMachine->mUserData);
12357 mHWData.share(aMachine->mHWData);
12358 mMediaData.share(aMachine->mMediaData);
12359
12360 mStorageControllers.allocate();
12361 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12362 it != aMachine->mStorageControllers->end();
12363 ++it)
12364 {
12365 ComObjPtr<StorageController> ctl;
12366 ctl.createObject();
12367 ctl->init(this, *it);
12368 mStorageControllers->push_back(ctl);
12369 }
12370
12371 mUSBControllers.allocate();
12372 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12373 it != aMachine->mUSBControllers->end();
12374 ++it)
12375 {
12376 ComObjPtr<USBController> ctl;
12377 ctl.createObject();
12378 ctl->init(this, *it);
12379 mUSBControllers->push_back(ctl);
12380 }
12381
12382 unconst(mBIOSSettings).createObject();
12383 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12384 /* create another VRDEServer object that will be mutable */
12385 unconst(mVRDEServer).createObject();
12386 mVRDEServer->init(this, aMachine->mVRDEServer);
12387 /* create another audio adapter object that will be mutable */
12388 unconst(mAudioAdapter).createObject();
12389 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12390 /* create a list of serial ports that will be mutable */
12391 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12392 {
12393 unconst(mSerialPorts[slot]).createObject();
12394 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12395 }
12396 /* create a list of parallel ports that will be mutable */
12397 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12398 {
12399 unconst(mParallelPorts[slot]).createObject();
12400 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12401 }
12402
12403 /* create another USB device filters object that will be mutable */
12404 unconst(mUSBDeviceFilters).createObject();
12405 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12406
12407 /* create a list of network adapters that will be mutable */
12408 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12409 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12410 {
12411 unconst(mNetworkAdapters[slot]).createObject();
12412 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12413 }
12414
12415 /* create another bandwidth control object that will be mutable */
12416 unconst(mBandwidthControl).createObject();
12417 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12418
12419 /* default is to delete saved state on Saved -> PoweredOff transition */
12420 mRemoveSavedState = true;
12421
12422 /* Confirm a successful initialization when it's the case */
12423 autoInitSpan.setSucceeded();
12424
12425 miNATNetworksStarted = 0;
12426
12427 LogFlowThisFuncLeave();
12428 return rc;
12429}
12430
12431/**
12432 * Uninitializes this session object. If the reason is other than
12433 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12434 * or the client watcher code.
12435 *
12436 * @param aReason uninitialization reason
12437 *
12438 * @note Locks mParent + this object for writing.
12439 */
12440void SessionMachine::uninit(Uninit::Reason aReason)
12441{
12442 LogFlowThisFuncEnter();
12443 LogFlowThisFunc(("reason=%d\n", aReason));
12444
12445 /*
12446 * Strongly reference ourselves to prevent this object deletion after
12447 * mData->mSession.mMachine.setNull() below (which can release the last
12448 * reference and call the destructor). Important: this must be done before
12449 * accessing any members (and before AutoUninitSpan that does it as well).
12450 * This self reference will be released as the very last step on return.
12451 */
12452 ComObjPtr<SessionMachine> selfRef = this;
12453
12454 /* Enclose the state transition Ready->InUninit->NotReady */
12455 AutoUninitSpan autoUninitSpan(this);
12456 if (autoUninitSpan.uninitDone())
12457 {
12458 LogFlowThisFunc(("Already uninitialized\n"));
12459 LogFlowThisFuncLeave();
12460 return;
12461 }
12462
12463 if (autoUninitSpan.initFailed())
12464 {
12465 /* We've been called by init() because it's failed. It's not really
12466 * necessary (nor it's safe) to perform the regular uninit sequence
12467 * below, the following is enough.
12468 */
12469 LogFlowThisFunc(("Initialization failed.\n"));
12470 /* destroy the machine client token */
12471 if (mClientToken)
12472 {
12473 delete mClientToken;
12474 mClientToken = NULL;
12475 }
12476 uninitDataAndChildObjects();
12477 mData.free();
12478 unconst(mParent) = NULL;
12479 unconst(mPeer) = NULL;
12480 LogFlowThisFuncLeave();
12481 return;
12482 }
12483
12484 MachineState_T lastState;
12485 {
12486 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12487 lastState = mData->mMachineState;
12488 }
12489 NOREF(lastState);
12490
12491#ifdef VBOX_WITH_USB
12492 // release all captured USB devices, but do this before requesting the locks below
12493 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12494 {
12495 /* Console::captureUSBDevices() is called in the VM process only after
12496 * setting the machine state to Starting or Restoring.
12497 * Console::detachAllUSBDevices() will be called upon successful
12498 * termination. So, we need to release USB devices only if there was
12499 * an abnormal termination of a running VM.
12500 *
12501 * This is identical to SessionMachine::DetachAllUSBDevices except
12502 * for the aAbnormal argument. */
12503 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12504 AssertComRC(rc);
12505 NOREF(rc);
12506
12507 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12508 if (service)
12509 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12510 }
12511#endif /* VBOX_WITH_USB */
12512
12513 // we need to lock this object in uninit() because the lock is shared
12514 // with mPeer (as well as data we modify below). mParent lock is needed
12515 // by several calls to it, and USB needs host lock.
12516 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12517
12518#ifdef VBOX_WITH_RESOURCE_USAGE_API
12519 /*
12520 * It is safe to call Machine::i_unregisterMetrics() here because
12521 * PerformanceCollector::samplerCallback no longer accesses guest methods
12522 * holding the lock.
12523 */
12524 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12525 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12526 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12527 this, __PRETTY_FUNCTION__, mCollectorGuest));
12528 if (mCollectorGuest)
12529 {
12530 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12531 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12532 mCollectorGuest = NULL;
12533 }
12534#endif
12535
12536 if (aReason == Uninit::Abnormal)
12537 {
12538 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12539 Global::IsOnlineOrTransient(lastState)));
12540
12541 /* reset the state to Aborted */
12542 if (mData->mMachineState != MachineState_Aborted)
12543 i_setMachineState(MachineState_Aborted);
12544 }
12545
12546 // any machine settings modified?
12547 if (mData->flModifications)
12548 {
12549 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12550 i_rollback(false /* aNotify */);
12551 }
12552
12553 mData->mSession.mPID = NIL_RTPROCESS;
12554
12555 if (aReason == Uninit::Unexpected)
12556 {
12557 /* Uninitialization didn't come from #checkForDeath(), so tell the
12558 * client watcher thread to update the set of machines that have open
12559 * sessions. */
12560 mParent->i_updateClientWatcher();
12561 }
12562
12563 /* uninitialize all remote controls */
12564 if (mData->mSession.mRemoteControls.size())
12565 {
12566 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12567 mData->mSession.mRemoteControls.size()));
12568
12569 Data::Session::RemoteControlList::iterator it =
12570 mData->mSession.mRemoteControls.begin();
12571 while (it != mData->mSession.mRemoteControls.end())
12572 {
12573 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12574 HRESULT rc = (*it)->Uninitialize();
12575 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12576 if (FAILED(rc))
12577 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12578 ++it;
12579 }
12580 mData->mSession.mRemoteControls.clear();
12581 }
12582
12583 /* Remove all references to the NAT network service. The service will stop
12584 * if all references (also from other VMs) are removed. */
12585 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12586 {
12587 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12588 {
12589 NetworkAttachmentType_T type;
12590 HRESULT hrc;
12591
12592 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12593 if ( SUCCEEDED(hrc)
12594 && type == NetworkAttachmentType_NATNetwork)
12595 {
12596 Bstr name;
12597 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12598 if (SUCCEEDED(hrc))
12599 {
12600 multilock.release();
12601 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12602 mUserData->s.strName.c_str(), name.raw()));
12603 mParent->i_natNetworkRefDec(name.raw());
12604 multilock.acquire();
12605 }
12606 }
12607 }
12608 }
12609
12610 /*
12611 * An expected uninitialization can come only from #checkForDeath().
12612 * Otherwise it means that something's gone really wrong (for example,
12613 * the Session implementation has released the VirtualBox reference
12614 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12615 * etc). However, it's also possible, that the client releases the IPC
12616 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12617 * but the VirtualBox release event comes first to the server process.
12618 * This case is practically possible, so we should not assert on an
12619 * unexpected uninit, just log a warning.
12620 */
12621
12622 if ((aReason == Uninit::Unexpected))
12623 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12624
12625 if (aReason != Uninit::Normal)
12626 {
12627 mData->mSession.mDirectControl.setNull();
12628 }
12629 else
12630 {
12631 /* this must be null here (see #OnSessionEnd()) */
12632 Assert(mData->mSession.mDirectControl.isNull());
12633 Assert(mData->mSession.mState == SessionState_Unlocking);
12634 Assert(!mData->mSession.mProgress.isNull());
12635 }
12636 if (mData->mSession.mProgress)
12637 {
12638 if (aReason == Uninit::Normal)
12639 mData->mSession.mProgress->i_notifyComplete(S_OK);
12640 else
12641 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12642 COM_IIDOF(ISession),
12643 getComponentName(),
12644 tr("The VM session was aborted"));
12645 mData->mSession.mProgress.setNull();
12646 }
12647
12648 /* remove the association between the peer machine and this session machine */
12649 Assert( (SessionMachine*)mData->mSession.mMachine == this
12650 || aReason == Uninit::Unexpected);
12651
12652 /* reset the rest of session data */
12653 mData->mSession.mLockType = LockType_Null;
12654 mData->mSession.mMachine.setNull();
12655 mData->mSession.mState = SessionState_Unlocked;
12656 mData->mSession.mType.setNull();
12657
12658 /* destroy the machine client token before leaving the exclusive lock */
12659 if (mClientToken)
12660 {
12661 delete mClientToken;
12662 mClientToken = NULL;
12663 }
12664
12665 /* fire an event */
12666 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12667
12668 uninitDataAndChildObjects();
12669
12670 /* free the essential data structure last */
12671 mData.free();
12672
12673 /* release the exclusive lock before setting the below two to NULL */
12674 multilock.release();
12675
12676 unconst(mParent) = NULL;
12677 unconst(mPeer) = NULL;
12678
12679 LogFlowThisFuncLeave();
12680}
12681
12682// util::Lockable interface
12683////////////////////////////////////////////////////////////////////////////////
12684
12685/**
12686 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12687 * with the primary Machine instance (mPeer).
12688 */
12689RWLockHandle *SessionMachine::lockHandle() const
12690{
12691 AssertReturn(mPeer != NULL, NULL);
12692 return mPeer->lockHandle();
12693}
12694
12695// IInternalMachineControl methods
12696////////////////////////////////////////////////////////////////////////////////
12697
12698/**
12699 * Passes collected guest statistics to performance collector object
12700 */
12701HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12702 ULONG aCpuKernel, ULONG aCpuIdle,
12703 ULONG aMemTotal, ULONG aMemFree,
12704 ULONG aMemBalloon, ULONG aMemShared,
12705 ULONG aMemCache, ULONG aPageTotal,
12706 ULONG aAllocVMM, ULONG aFreeVMM,
12707 ULONG aBalloonedVMM, ULONG aSharedVMM,
12708 ULONG aVmNetRx, ULONG aVmNetTx)
12709{
12710#ifdef VBOX_WITH_RESOURCE_USAGE_API
12711 if (mCollectorGuest)
12712 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12713 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12714 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12715 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12716
12717 return S_OK;
12718#else
12719 NOREF(aValidStats);
12720 NOREF(aCpuUser);
12721 NOREF(aCpuKernel);
12722 NOREF(aCpuIdle);
12723 NOREF(aMemTotal);
12724 NOREF(aMemFree);
12725 NOREF(aMemBalloon);
12726 NOREF(aMemShared);
12727 NOREF(aMemCache);
12728 NOREF(aPageTotal);
12729 NOREF(aAllocVMM);
12730 NOREF(aFreeVMM);
12731 NOREF(aBalloonedVMM);
12732 NOREF(aSharedVMM);
12733 NOREF(aVmNetRx);
12734 NOREF(aVmNetTx);
12735 return E_NOTIMPL;
12736#endif
12737}
12738
12739////////////////////////////////////////////////////////////////////////////////
12740//
12741// SessionMachine task records
12742//
12743////////////////////////////////////////////////////////////////////////////////
12744
12745/**
12746 * Task record for saving the machine state.
12747 */
12748struct SessionMachine::SaveStateTask
12749 : public Machine::Task
12750{
12751 SaveStateTask(SessionMachine *m,
12752 Progress *p,
12753 const Utf8Str &t,
12754 Reason_T enmReason,
12755 const Utf8Str &strStateFilePath)
12756 : Task(m, p, t),
12757 m_enmReason(enmReason),
12758 m_strStateFilePath(strStateFilePath)
12759 {}
12760
12761 void handler()
12762 {
12763 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12764 }
12765
12766 Reason_T m_enmReason;
12767 Utf8Str m_strStateFilePath;
12768};
12769
12770/**
12771 * Task thread implementation for SessionMachine::SaveState(), called from
12772 * SessionMachine::taskHandler().
12773 *
12774 * @note Locks this object for writing.
12775 *
12776 * @param task
12777 * @return
12778 */
12779void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12780{
12781 LogFlowThisFuncEnter();
12782
12783 AutoCaller autoCaller(this);
12784 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12785 if (FAILED(autoCaller.rc()))
12786 {
12787 /* we might have been uninitialized because the session was accidentally
12788 * closed by the client, so don't assert */
12789 HRESULT rc = setError(E_FAIL,
12790 tr("The session has been accidentally closed"));
12791 task.m_pProgress->i_notifyComplete(rc);
12792 LogFlowThisFuncLeave();
12793 return;
12794 }
12795
12796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12797
12798 HRESULT rc = S_OK;
12799
12800 try
12801 {
12802 ComPtr<IInternalSessionControl> directControl;
12803 if (mData->mSession.mLockType == LockType_VM)
12804 directControl = mData->mSession.mDirectControl;
12805 if (directControl.isNull())
12806 throw setError(VBOX_E_INVALID_VM_STATE,
12807 tr("Trying to save state without a running VM"));
12808 alock.release();
12809 BOOL fSuspendedBySave;
12810 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12811 Assert(!fSuspendedBySave);
12812 alock.acquire();
12813
12814 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12815 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12816 throw E_FAIL);
12817
12818 if (SUCCEEDED(rc))
12819 {
12820 mSSData->strStateFilePath = task.m_strStateFilePath;
12821
12822 /* save all VM settings */
12823 rc = i_saveSettings(NULL);
12824 // no need to check whether VirtualBox.xml needs saving also since
12825 // we can't have a name change pending at this point
12826 }
12827 else
12828 {
12829 // On failure, set the state to the state we had at the beginning.
12830 i_setMachineState(task.m_machineStateBackup);
12831 i_updateMachineStateOnClient();
12832
12833 // Delete the saved state file (might have been already created).
12834 // No need to check whether this is shared with a snapshot here
12835 // because we certainly created a fresh saved state file here.
12836 RTFileDelete(task.m_strStateFilePath.c_str());
12837 }
12838 }
12839 catch (HRESULT aRC) { rc = aRC; }
12840
12841 task.m_pProgress->i_notifyComplete(rc);
12842
12843 LogFlowThisFuncLeave();
12844}
12845
12846/**
12847 * @note Locks this object for writing.
12848 */
12849HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12850{
12851 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12852}
12853
12854HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12855{
12856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12857
12858 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12859 if (FAILED(rc)) return rc;
12860
12861 if ( mData->mMachineState != MachineState_Running
12862 && mData->mMachineState != MachineState_Paused
12863 )
12864 return setError(VBOX_E_INVALID_VM_STATE,
12865 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12866 Global::stringifyMachineState(mData->mMachineState));
12867
12868 ComObjPtr<Progress> pProgress;
12869 pProgress.createObject();
12870 rc = pProgress->init(i_getVirtualBox(),
12871 static_cast<IMachine *>(this) /* aInitiator */,
12872 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12873 FALSE /* aCancelable */);
12874 if (FAILED(rc))
12875 return rc;
12876
12877 Utf8Str strStateFilePath;
12878 i_composeSavedStateFilename(strStateFilePath);
12879
12880 /* create and start the task on a separate thread (note that it will not
12881 * start working until we release alock) */
12882 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12883 rc = pTask->createThread();
12884 if (FAILED(rc))
12885 return rc;
12886
12887 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12888 i_setMachineState(MachineState_Saving);
12889 i_updateMachineStateOnClient();
12890
12891 pProgress.queryInterfaceTo(aProgress.asOutParam());
12892
12893 return S_OK;
12894}
12895
12896/**
12897 * @note Locks this object for writing.
12898 */
12899HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12900{
12901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12902
12903 HRESULT rc = i_checkStateDependency(MutableStateDep);
12904 if (FAILED(rc)) return rc;
12905
12906 if ( mData->mMachineState != MachineState_PoweredOff
12907 && mData->mMachineState != MachineState_Teleported
12908 && mData->mMachineState != MachineState_Aborted
12909 )
12910 return setError(VBOX_E_INVALID_VM_STATE,
12911 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12912 Global::stringifyMachineState(mData->mMachineState));
12913
12914 com::Utf8Str stateFilePathFull;
12915 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12916 if (RT_FAILURE(vrc))
12917 return setError(VBOX_E_FILE_ERROR,
12918 tr("Invalid saved state file path '%s' (%Rrc)"),
12919 aSavedStateFile.c_str(),
12920 vrc);
12921
12922 mSSData->strStateFilePath = stateFilePathFull;
12923
12924 /* The below i_setMachineState() will detect the state transition and will
12925 * update the settings file */
12926
12927 return i_setMachineState(MachineState_Saved);
12928}
12929
12930/**
12931 * @note Locks this object for writing.
12932 */
12933HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12934{
12935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12936
12937 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12938 if (FAILED(rc)) return rc;
12939
12940 if (mData->mMachineState != MachineState_Saved)
12941 return setError(VBOX_E_INVALID_VM_STATE,
12942 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12943 Global::stringifyMachineState(mData->mMachineState));
12944
12945 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12946
12947 /*
12948 * Saved -> PoweredOff transition will be detected in the SessionMachine
12949 * and properly handled.
12950 */
12951 rc = i_setMachineState(MachineState_PoweredOff);
12952 return rc;
12953}
12954
12955
12956/**
12957 * @note Locks the same as #i_setMachineState() does.
12958 */
12959HRESULT SessionMachine::updateState(MachineState_T aState)
12960{
12961 return i_setMachineState(aState);
12962}
12963
12964/**
12965 * @note Locks this object for writing.
12966 */
12967HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12968{
12969 IProgress* pProgress(aProgress);
12970
12971 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12972
12973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12974
12975 if (mData->mSession.mState != SessionState_Locked)
12976 return VBOX_E_INVALID_OBJECT_STATE;
12977
12978 if (!mData->mSession.mProgress.isNull())
12979 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12980
12981 /* If we didn't reference the NAT network service yet, add a reference to
12982 * force a start */
12983 if (miNATNetworksStarted < 1)
12984 {
12985 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12986 {
12987 NetworkAttachmentType_T type;
12988 HRESULT hrc;
12989 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12990 if ( SUCCEEDED(hrc)
12991 && type == NetworkAttachmentType_NATNetwork)
12992 {
12993 Bstr name;
12994 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12995 if (SUCCEEDED(hrc))
12996 {
12997 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12998 mUserData->s.strName.c_str(), name.raw()));
12999 mPeer->lockHandle()->unlockWrite();
13000 mParent->i_natNetworkRefInc(name.raw());
13001#ifdef RT_LOCK_STRICT
13002 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13003#else
13004 mPeer->lockHandle()->lockWrite();
13005#endif
13006 }
13007 }
13008 }
13009 miNATNetworksStarted++;
13010 }
13011
13012 LogFlowThisFunc(("returns S_OK.\n"));
13013 return S_OK;
13014}
13015
13016/**
13017 * @note Locks this object for writing.
13018 */
13019HRESULT SessionMachine::endPowerUp(LONG aResult)
13020{
13021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13022
13023 if (mData->mSession.mState != SessionState_Locked)
13024 return VBOX_E_INVALID_OBJECT_STATE;
13025
13026 /* Finalize the LaunchVMProcess progress object. */
13027 if (mData->mSession.mProgress)
13028 {
13029 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13030 mData->mSession.mProgress.setNull();
13031 }
13032
13033 if (SUCCEEDED((HRESULT)aResult))
13034 {
13035#ifdef VBOX_WITH_RESOURCE_USAGE_API
13036 /* The VM has been powered up successfully, so it makes sense
13037 * now to offer the performance metrics for a running machine
13038 * object. Doing it earlier wouldn't be safe. */
13039 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13040 mData->mSession.mPID);
13041#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13042 }
13043
13044 return S_OK;
13045}
13046
13047/**
13048 * @note Locks this object for writing.
13049 */
13050HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13051{
13052 LogFlowThisFuncEnter();
13053
13054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13055
13056 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13057 E_FAIL);
13058
13059 /* create a progress object to track operation completion */
13060 ComObjPtr<Progress> pProgress;
13061 pProgress.createObject();
13062 pProgress->init(i_getVirtualBox(),
13063 static_cast<IMachine *>(this) /* aInitiator */,
13064 Bstr(tr("Stopping the virtual machine")).raw(),
13065 FALSE /* aCancelable */);
13066
13067 /* fill in the console task data */
13068 mConsoleTaskData.mLastState = mData->mMachineState;
13069 mConsoleTaskData.mProgress = pProgress;
13070
13071 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13072 i_setMachineState(MachineState_Stopping);
13073
13074 pProgress.queryInterfaceTo(aProgress.asOutParam());
13075
13076 return S_OK;
13077}
13078
13079/**
13080 * @note Locks this object for writing.
13081 */
13082HRESULT SessionMachine::endPoweringDown(LONG aResult,
13083 const com::Utf8Str &aErrMsg)
13084{
13085 LogFlowThisFuncEnter();
13086
13087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13088
13089 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13090 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13091 && mConsoleTaskData.mLastState != MachineState_Null,
13092 E_FAIL);
13093
13094 /*
13095 * On failure, set the state to the state we had when BeginPoweringDown()
13096 * was called (this is expected by Console::PowerDown() and the associated
13097 * task). On success the VM process already changed the state to
13098 * MachineState_PoweredOff, so no need to do anything.
13099 */
13100 if (FAILED(aResult))
13101 i_setMachineState(mConsoleTaskData.mLastState);
13102
13103 /* notify the progress object about operation completion */
13104 Assert(mConsoleTaskData.mProgress);
13105 if (SUCCEEDED(aResult))
13106 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13107 else
13108 {
13109 if (aErrMsg.length())
13110 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13111 COM_IIDOF(ISession),
13112 getComponentName(),
13113 aErrMsg.c_str());
13114 else
13115 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13116 }
13117
13118 /* clear out the temporary saved state data */
13119 mConsoleTaskData.mLastState = MachineState_Null;
13120 mConsoleTaskData.mProgress.setNull();
13121
13122 LogFlowThisFuncLeave();
13123 return S_OK;
13124}
13125
13126
13127/**
13128 * Goes through the USB filters of the given machine to see if the given
13129 * device matches any filter or not.
13130 *
13131 * @note Locks the same as USBController::hasMatchingFilter() does.
13132 */
13133HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13134 BOOL *aMatched,
13135 ULONG *aMaskedInterfaces)
13136{
13137 LogFlowThisFunc(("\n"));
13138
13139#ifdef VBOX_WITH_USB
13140 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13141#else
13142 NOREF(aDevice);
13143 NOREF(aMaskedInterfaces);
13144 *aMatched = FALSE;
13145#endif
13146
13147 return S_OK;
13148}
13149
13150/**
13151 * @note Locks the same as Host::captureUSBDevice() does.
13152 */
13153HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13154{
13155 LogFlowThisFunc(("\n"));
13156
13157#ifdef VBOX_WITH_USB
13158 /* if captureDeviceForVM() fails, it must have set extended error info */
13159 clearError();
13160 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13161 if (FAILED(rc)) return rc;
13162
13163 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13164 AssertReturn(service, E_FAIL);
13165 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13166#else
13167 NOREF(aId);
13168 return E_NOTIMPL;
13169#endif
13170}
13171
13172/**
13173 * @note Locks the same as Host::detachUSBDevice() does.
13174 */
13175HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13176 BOOL aDone)
13177{
13178 LogFlowThisFunc(("\n"));
13179
13180#ifdef VBOX_WITH_USB
13181 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13182 AssertReturn(service, E_FAIL);
13183 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13184#else
13185 NOREF(aId);
13186 NOREF(aDone);
13187 return E_NOTIMPL;
13188#endif
13189}
13190
13191/**
13192 * Inserts all machine filters to the USB proxy service and then calls
13193 * Host::autoCaptureUSBDevices().
13194 *
13195 * Called by Console from the VM process upon VM startup.
13196 *
13197 * @note Locks what called methods lock.
13198 */
13199HRESULT SessionMachine::autoCaptureUSBDevices()
13200{
13201 LogFlowThisFunc(("\n"));
13202
13203#ifdef VBOX_WITH_USB
13204 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13205 AssertComRC(rc);
13206 NOREF(rc);
13207
13208 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13209 AssertReturn(service, E_FAIL);
13210 return service->autoCaptureDevicesForVM(this);
13211#else
13212 return S_OK;
13213#endif
13214}
13215
13216/**
13217 * Removes all machine filters from the USB proxy service and then calls
13218 * Host::detachAllUSBDevices().
13219 *
13220 * Called by Console from the VM process upon normal VM termination or by
13221 * SessionMachine::uninit() upon abnormal VM termination (from under the
13222 * Machine/SessionMachine lock).
13223 *
13224 * @note Locks what called methods lock.
13225 */
13226HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13227{
13228 LogFlowThisFunc(("\n"));
13229
13230#ifdef VBOX_WITH_USB
13231 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13232 AssertComRC(rc);
13233 NOREF(rc);
13234
13235 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13236 AssertReturn(service, E_FAIL);
13237 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13238#else
13239 NOREF(aDone);
13240 return S_OK;
13241#endif
13242}
13243
13244/**
13245 * @note Locks this object for writing.
13246 */
13247HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13248 ComPtr<IProgress> &aProgress)
13249{
13250 LogFlowThisFuncEnter();
13251
13252 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13253 /*
13254 * We don't assert below because it might happen that a non-direct session
13255 * informs us it is closed right after we've been uninitialized -- it's ok.
13256 */
13257
13258 /* get IInternalSessionControl interface */
13259 ComPtr<IInternalSessionControl> control(aSession);
13260
13261 ComAssertRet(!control.isNull(), E_INVALIDARG);
13262
13263 /* Creating a Progress object requires the VirtualBox lock, and
13264 * thus locking it here is required by the lock order rules. */
13265 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13266
13267 if (control == mData->mSession.mDirectControl)
13268 {
13269 /* The direct session is being normally closed by the client process
13270 * ----------------------------------------------------------------- */
13271
13272 /* go to the closing state (essential for all open*Session() calls and
13273 * for #checkForDeath()) */
13274 Assert(mData->mSession.mState == SessionState_Locked);
13275 mData->mSession.mState = SessionState_Unlocking;
13276
13277 /* set direct control to NULL to release the remote instance */
13278 mData->mSession.mDirectControl.setNull();
13279 LogFlowThisFunc(("Direct control is set to NULL\n"));
13280
13281 if (mData->mSession.mProgress)
13282 {
13283 /* finalize the progress, someone might wait if a frontend
13284 * closes the session before powering on the VM. */
13285 mData->mSession.mProgress->notifyComplete(E_FAIL,
13286 COM_IIDOF(ISession),
13287 getComponentName(),
13288 tr("The VM session was closed before any attempt to power it on"));
13289 mData->mSession.mProgress.setNull();
13290 }
13291
13292 /* Create the progress object the client will use to wait until
13293 * #checkForDeath() is called to uninitialize this session object after
13294 * it releases the IPC semaphore.
13295 * Note! Because we're "reusing" mProgress here, this must be a proxy
13296 * object just like for LaunchVMProcess. */
13297 Assert(mData->mSession.mProgress.isNull());
13298 ComObjPtr<ProgressProxy> progress;
13299 progress.createObject();
13300 ComPtr<IUnknown> pPeer(mPeer);
13301 progress->init(mParent, pPeer,
13302 Bstr(tr("Closing session")).raw(),
13303 FALSE /* aCancelable */);
13304 progress.queryInterfaceTo(aProgress.asOutParam());
13305 mData->mSession.mProgress = progress;
13306 }
13307 else
13308 {
13309 /* the remote session is being normally closed */
13310 Data::Session::RemoteControlList::iterator it =
13311 mData->mSession.mRemoteControls.begin();
13312 while (it != mData->mSession.mRemoteControls.end())
13313 {
13314 if (control == *it)
13315 break;
13316 ++it;
13317 }
13318 BOOL found = it != mData->mSession.mRemoteControls.end();
13319 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13320 E_INVALIDARG);
13321 // This MUST be erase(it), not remove(*it) as the latter triggers a
13322 // very nasty use after free due to the place where the value "lives".
13323 mData->mSession.mRemoteControls.erase(it);
13324 }
13325
13326 /* signal the client watcher thread, because the client is going away */
13327 mParent->i_updateClientWatcher();
13328
13329 LogFlowThisFuncLeave();
13330 return S_OK;
13331}
13332
13333HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13334 std::vector<com::Utf8Str> &aValues,
13335 std::vector<LONG64> &aTimestamps,
13336 std::vector<com::Utf8Str> &aFlags)
13337{
13338 LogFlowThisFunc(("\n"));
13339
13340#ifdef VBOX_WITH_GUEST_PROPS
13341 using namespace guestProp;
13342
13343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13344
13345 size_t cEntries = mHWData->mGuestProperties.size();
13346 aNames.resize(cEntries);
13347 aValues.resize(cEntries);
13348 aTimestamps.resize(cEntries);
13349 aFlags.resize(cEntries);
13350
13351 size_t i = 0;
13352 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13353 it != mHWData->mGuestProperties.end();
13354 ++it, ++i)
13355 {
13356 char szFlags[MAX_FLAGS_LEN + 1];
13357 aNames[i] = it->first;
13358 aValues[i] = it->second.strValue;
13359 aTimestamps[i] = it->second.mTimestamp;
13360
13361 /* If it is NULL, keep it NULL. */
13362 if (it->second.mFlags)
13363 {
13364 writeFlags(it->second.mFlags, szFlags);
13365 aFlags[i] = szFlags;
13366 }
13367 else
13368 aFlags[i] = "";
13369 }
13370 return S_OK;
13371#else
13372 ReturnComNotImplemented();
13373#endif
13374}
13375
13376HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13377 const com::Utf8Str &aValue,
13378 LONG64 aTimestamp,
13379 const com::Utf8Str &aFlags,
13380 BOOL *aNotify)
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384#ifdef VBOX_WITH_GUEST_PROPS
13385 using namespace guestProp;
13386
13387 *aNotify = FALSE;
13388
13389 try
13390 {
13391 /*
13392 * Convert input up front.
13393 */
13394 uint32_t fFlags = NILFLAG;
13395 if (aFlags.length())
13396 {
13397 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13398 AssertRCReturn(vrc, E_INVALIDARG);
13399 }
13400
13401 /*
13402 * Now grab the object lock, validate the state and do the update.
13403 */
13404
13405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13406
13407 switch (mData->mMachineState)
13408 {
13409 case MachineState_Paused:
13410 case MachineState_Running:
13411 case MachineState_Teleporting:
13412 case MachineState_TeleportingPausedVM:
13413 case MachineState_OnlineSnapshotting:
13414 case MachineState_LiveSnapshotting:
13415 case MachineState_DeletingSnapshotOnline:
13416 case MachineState_DeletingSnapshotPaused:
13417 case MachineState_Saving:
13418 case MachineState_Stopping:
13419 break;
13420
13421 default:
13422 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13423 VBOX_E_INVALID_VM_STATE);
13424 }
13425
13426 i_setModified(IsModified_MachineData);
13427 mHWData.backup();
13428
13429 bool fDelete = !aValue.length();
13430 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13431 if (it != mHWData->mGuestProperties.end())
13432 {
13433 if (!fDelete)
13434 {
13435 it->second.strValue = aValue;
13436 it->second.mTimestamp = aTimestamp;
13437 it->second.mFlags = fFlags;
13438 }
13439 else
13440 mHWData->mGuestProperties.erase(it);
13441
13442 mData->mGuestPropertiesModified = TRUE;
13443 }
13444 else if (!fDelete)
13445 {
13446 HWData::GuestProperty prop;
13447 prop.strValue = aValue;
13448 prop.mTimestamp = aTimestamp;
13449 prop.mFlags = fFlags;
13450
13451 mHWData->mGuestProperties[aName] = prop;
13452 mData->mGuestPropertiesModified = TRUE;
13453 }
13454
13455 /*
13456 * Send a callback notification if appropriate
13457 */
13458 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13459 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13460 RTSTR_MAX,
13461 aName.c_str(),
13462 RTSTR_MAX, NULL)
13463 )
13464 {
13465 alock.release();
13466
13467 mParent->i_onGuestPropertyChange(mData->mUuid,
13468 Bstr(aName).raw(),
13469 Bstr(aValue).raw(),
13470 Bstr(aFlags).raw());
13471 *aNotify = TRUE;
13472 }
13473 }
13474 catch (...)
13475 {
13476 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13477 }
13478 return S_OK;
13479#else
13480 ReturnComNotImplemented();
13481#endif
13482}
13483
13484
13485HRESULT SessionMachine::lockMedia()
13486{
13487 AutoMultiWriteLock2 alock(this->lockHandle(),
13488 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13489
13490 AssertReturn( mData->mMachineState == MachineState_Starting
13491 || mData->mMachineState == MachineState_Restoring
13492 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13493
13494 clearError();
13495 alock.release();
13496 return i_lockMedia();
13497}
13498
13499HRESULT SessionMachine::unlockMedia()
13500{
13501 HRESULT hrc = i_unlockMedia();
13502 return hrc;
13503}
13504
13505HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13506 ComPtr<IMediumAttachment> &aNewAttachment)
13507{
13508 // request the host lock first, since might be calling Host methods for getting host drives;
13509 // next, protect the media tree all the while we're in here, as well as our member variables
13510 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13511 this->lockHandle(),
13512 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13513
13514 IMediumAttachment *iAttach = aAttachment;
13515 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13516
13517 Bstr ctrlName;
13518 LONG lPort;
13519 LONG lDevice;
13520 bool fTempEject;
13521 {
13522 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13523
13524 /* Need to query the details first, as the IMediumAttachment reference
13525 * might be to the original settings, which we are going to change. */
13526 ctrlName = pAttach->i_getControllerName();
13527 lPort = pAttach->i_getPort();
13528 lDevice = pAttach->i_getDevice();
13529 fTempEject = pAttach->i_getTempEject();
13530 }
13531
13532 if (!fTempEject)
13533 {
13534 /* Remember previously mounted medium. The medium before taking the
13535 * backup is not necessarily the same thing. */
13536 ComObjPtr<Medium> oldmedium;
13537 oldmedium = pAttach->i_getMedium();
13538
13539 i_setModified(IsModified_Storage);
13540 mMediaData.backup();
13541
13542 // The backup operation makes the pAttach reference point to the
13543 // old settings. Re-get the correct reference.
13544 pAttach = i_findAttachment(mMediaData->mAttachments,
13545 ctrlName.raw(),
13546 lPort,
13547 lDevice);
13548
13549 {
13550 AutoCaller autoAttachCaller(this);
13551 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13552
13553 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13554 if (!oldmedium.isNull())
13555 oldmedium->i_removeBackReference(mData->mUuid);
13556
13557 pAttach->i_updateMedium(NULL);
13558 pAttach->i_updateEjected();
13559 }
13560
13561 i_setModified(IsModified_Storage);
13562 }
13563 else
13564 {
13565 {
13566 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13567 pAttach->i_updateEjected();
13568 }
13569 }
13570
13571 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13572
13573 return S_OK;
13574}
13575
13576// public methods only for internal purposes
13577/////////////////////////////////////////////////////////////////////////////
13578
13579#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13580/**
13581 * Called from the client watcher thread to check for expected or unexpected
13582 * death of the client process that has a direct session to this machine.
13583 *
13584 * On Win32 and on OS/2, this method is called only when we've got the
13585 * mutex (i.e. the client has either died or terminated normally) so it always
13586 * returns @c true (the client is terminated, the session machine is
13587 * uninitialized).
13588 *
13589 * On other platforms, the method returns @c true if the client process has
13590 * terminated normally or abnormally and the session machine was uninitialized,
13591 * and @c false if the client process is still alive.
13592 *
13593 * @note Locks this object for writing.
13594 */
13595bool SessionMachine::i_checkForDeath()
13596{
13597 Uninit::Reason reason;
13598 bool terminated = false;
13599
13600 /* Enclose autoCaller with a block because calling uninit() from under it
13601 * will deadlock. */
13602 {
13603 AutoCaller autoCaller(this);
13604 if (!autoCaller.isOk())
13605 {
13606 /* return true if not ready, to cause the client watcher to exclude
13607 * the corresponding session from watching */
13608 LogFlowThisFunc(("Already uninitialized!\n"));
13609 return true;
13610 }
13611
13612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13613
13614 /* Determine the reason of death: if the session state is Closing here,
13615 * everything is fine. Otherwise it means that the client did not call
13616 * OnSessionEnd() before it released the IPC semaphore. This may happen
13617 * either because the client process has abnormally terminated, or
13618 * because it simply forgot to call ISession::Close() before exiting. We
13619 * threat the latter also as an abnormal termination (see
13620 * Session::uninit() for details). */
13621 reason = mData->mSession.mState == SessionState_Unlocking ?
13622 Uninit::Normal :
13623 Uninit::Abnormal;
13624
13625 if (mClientToken)
13626 terminated = mClientToken->release();
13627 } /* AutoCaller block */
13628
13629 if (terminated)
13630 uninit(reason);
13631
13632 return terminated;
13633}
13634
13635void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13636{
13637 LogFlowThisFunc(("\n"));
13638
13639 strTokenId.setNull();
13640
13641 AutoCaller autoCaller(this);
13642 AssertComRCReturnVoid(autoCaller.rc());
13643
13644 Assert(mClientToken);
13645 if (mClientToken)
13646 mClientToken->getId(strTokenId);
13647}
13648#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13649IToken *SessionMachine::i_getToken()
13650{
13651 LogFlowThisFunc(("\n"));
13652
13653 AutoCaller autoCaller(this);
13654 AssertComRCReturn(autoCaller.rc(), NULL);
13655
13656 Assert(mClientToken);
13657 if (mClientToken)
13658 return mClientToken->getToken();
13659 else
13660 return NULL;
13661}
13662#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13663
13664Machine::ClientToken *SessionMachine::i_getClientToken()
13665{
13666 LogFlowThisFunc(("\n"));
13667
13668 AutoCaller autoCaller(this);
13669 AssertComRCReturn(autoCaller.rc(), NULL);
13670
13671 return mClientToken;
13672}
13673
13674
13675/**
13676 * @note Locks this object for reading.
13677 */
13678HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13679{
13680 LogFlowThisFunc(("\n"));
13681
13682 AutoCaller autoCaller(this);
13683 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13684
13685 ComPtr<IInternalSessionControl> directControl;
13686 {
13687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13688 if (mData->mSession.mLockType == LockType_VM)
13689 directControl = mData->mSession.mDirectControl;
13690 }
13691
13692 /* ignore notifications sent after #OnSessionEnd() is called */
13693 if (!directControl)
13694 return S_OK;
13695
13696 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13697}
13698
13699/**
13700 * @note Locks this object for reading.
13701 */
13702HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13703 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13704 IN_BSTR aGuestIp, LONG aGuestPort)
13705{
13706 LogFlowThisFunc(("\n"));
13707
13708 AutoCaller autoCaller(this);
13709 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13710
13711 ComPtr<IInternalSessionControl> directControl;
13712 {
13713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13714 if (mData->mSession.mLockType == LockType_VM)
13715 directControl = mData->mSession.mDirectControl;
13716 }
13717
13718 /* ignore notifications sent after #OnSessionEnd() is called */
13719 if (!directControl)
13720 return S_OK;
13721 /*
13722 * instead acting like callback we ask IVirtualBox deliver corresponding event
13723 */
13724
13725 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13726 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13727 return S_OK;
13728}
13729
13730/**
13731 * @note Locks this object for reading.
13732 */
13733HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13734{
13735 LogFlowThisFunc(("\n"));
13736
13737 AutoCaller autoCaller(this);
13738 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13739
13740 ComPtr<IInternalSessionControl> directControl;
13741 {
13742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13743 if (mData->mSession.mLockType == LockType_VM)
13744 directControl = mData->mSession.mDirectControl;
13745 }
13746
13747 /* ignore notifications sent after #OnSessionEnd() is called */
13748 if (!directControl)
13749 return S_OK;
13750
13751 return directControl->OnSerialPortChange(serialPort);
13752}
13753
13754/**
13755 * @note Locks this object for reading.
13756 */
13757HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13758{
13759 LogFlowThisFunc(("\n"));
13760
13761 AutoCaller autoCaller(this);
13762 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13763
13764 ComPtr<IInternalSessionControl> directControl;
13765 {
13766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13767 if (mData->mSession.mLockType == LockType_VM)
13768 directControl = mData->mSession.mDirectControl;
13769 }
13770
13771 /* ignore notifications sent after #OnSessionEnd() is called */
13772 if (!directControl)
13773 return S_OK;
13774
13775 return directControl->OnParallelPortChange(parallelPort);
13776}
13777
13778/**
13779 * @note Locks this object for reading.
13780 */
13781HRESULT SessionMachine::i_onStorageControllerChange()
13782{
13783 LogFlowThisFunc(("\n"));
13784
13785 AutoCaller autoCaller(this);
13786 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13787
13788 ComPtr<IInternalSessionControl> directControl;
13789 {
13790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13791 if (mData->mSession.mLockType == LockType_VM)
13792 directControl = mData->mSession.mDirectControl;
13793 }
13794
13795 /* ignore notifications sent after #OnSessionEnd() is called */
13796 if (!directControl)
13797 return S_OK;
13798
13799 return directControl->OnStorageControllerChange();
13800}
13801
13802/**
13803 * @note Locks this object for reading.
13804 */
13805HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13806{
13807 LogFlowThisFunc(("\n"));
13808
13809 AutoCaller autoCaller(this);
13810 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13811
13812 ComPtr<IInternalSessionControl> directControl;
13813 {
13814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13815 if (mData->mSession.mLockType == LockType_VM)
13816 directControl = mData->mSession.mDirectControl;
13817 }
13818
13819 /* ignore notifications sent after #OnSessionEnd() is called */
13820 if (!directControl)
13821 return S_OK;
13822
13823 return directControl->OnMediumChange(aAttachment, aForce);
13824}
13825
13826/**
13827 * @note Locks this object for reading.
13828 */
13829HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13830{
13831 LogFlowThisFunc(("\n"));
13832
13833 AutoCaller autoCaller(this);
13834 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13835
13836 ComPtr<IInternalSessionControl> directControl;
13837 {
13838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13839 if (mData->mSession.mLockType == LockType_VM)
13840 directControl = mData->mSession.mDirectControl;
13841 }
13842
13843 /* ignore notifications sent after #OnSessionEnd() is called */
13844 if (!directControl)
13845 return S_OK;
13846
13847 return directControl->OnCPUChange(aCPU, aRemove);
13848}
13849
13850HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13851{
13852 LogFlowThisFunc(("\n"));
13853
13854 AutoCaller autoCaller(this);
13855 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13856
13857 ComPtr<IInternalSessionControl> directControl;
13858 {
13859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13860 if (mData->mSession.mLockType == LockType_VM)
13861 directControl = mData->mSession.mDirectControl;
13862 }
13863
13864 /* ignore notifications sent after #OnSessionEnd() is called */
13865 if (!directControl)
13866 return S_OK;
13867
13868 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13869}
13870
13871/**
13872 * @note Locks this object for reading.
13873 */
13874HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13875{
13876 LogFlowThisFunc(("\n"));
13877
13878 AutoCaller autoCaller(this);
13879 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13880
13881 ComPtr<IInternalSessionControl> directControl;
13882 {
13883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13884 if (mData->mSession.mLockType == LockType_VM)
13885 directControl = mData->mSession.mDirectControl;
13886 }
13887
13888 /* ignore notifications sent after #OnSessionEnd() is called */
13889 if (!directControl)
13890 return S_OK;
13891
13892 return directControl->OnVRDEServerChange(aRestart);
13893}
13894
13895/**
13896 * @note Locks this object for reading.
13897 */
13898HRESULT SessionMachine::i_onVideoCaptureChange()
13899{
13900 LogFlowThisFunc(("\n"));
13901
13902 AutoCaller autoCaller(this);
13903 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13904
13905 ComPtr<IInternalSessionControl> directControl;
13906 {
13907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13908 if (mData->mSession.mLockType == LockType_VM)
13909 directControl = mData->mSession.mDirectControl;
13910 }
13911
13912 /* ignore notifications sent after #OnSessionEnd() is called */
13913 if (!directControl)
13914 return S_OK;
13915
13916 return directControl->OnVideoCaptureChange();
13917}
13918
13919/**
13920 * @note Locks this object for reading.
13921 */
13922HRESULT SessionMachine::i_onUSBControllerChange()
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926 AutoCaller autoCaller(this);
13927 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13928
13929 ComPtr<IInternalSessionControl> directControl;
13930 {
13931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13932 if (mData->mSession.mLockType == LockType_VM)
13933 directControl = mData->mSession.mDirectControl;
13934 }
13935
13936 /* ignore notifications sent after #OnSessionEnd() is called */
13937 if (!directControl)
13938 return S_OK;
13939
13940 return directControl->OnUSBControllerChange();
13941}
13942
13943/**
13944 * @note Locks this object for reading.
13945 */
13946HRESULT SessionMachine::i_onSharedFolderChange()
13947{
13948 LogFlowThisFunc(("\n"));
13949
13950 AutoCaller autoCaller(this);
13951 AssertComRCReturnRC(autoCaller.rc());
13952
13953 ComPtr<IInternalSessionControl> directControl;
13954 {
13955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13956 if (mData->mSession.mLockType == LockType_VM)
13957 directControl = mData->mSession.mDirectControl;
13958 }
13959
13960 /* ignore notifications sent after #OnSessionEnd() is called */
13961 if (!directControl)
13962 return S_OK;
13963
13964 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13965}
13966
13967/**
13968 * @note Locks this object for reading.
13969 */
13970HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13971{
13972 LogFlowThisFunc(("\n"));
13973
13974 AutoCaller autoCaller(this);
13975 AssertComRCReturnRC(autoCaller.rc());
13976
13977 ComPtr<IInternalSessionControl> directControl;
13978 {
13979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13980 if (mData->mSession.mLockType == LockType_VM)
13981 directControl = mData->mSession.mDirectControl;
13982 }
13983
13984 /* ignore notifications sent after #OnSessionEnd() is called */
13985 if (!directControl)
13986 return S_OK;
13987
13988 return directControl->OnClipboardModeChange(aClipboardMode);
13989}
13990
13991/**
13992 * @note Locks this object for reading.
13993 */
13994HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13995{
13996 LogFlowThisFunc(("\n"));
13997
13998 AutoCaller autoCaller(this);
13999 AssertComRCReturnRC(autoCaller.rc());
14000
14001 ComPtr<IInternalSessionControl> directControl;
14002 {
14003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14004 if (mData->mSession.mLockType == LockType_VM)
14005 directControl = mData->mSession.mDirectControl;
14006 }
14007
14008 /* ignore notifications sent after #OnSessionEnd() is called */
14009 if (!directControl)
14010 return S_OK;
14011
14012 return directControl->OnDnDModeChange(aDnDMode);
14013}
14014
14015/**
14016 * @note Locks this object for reading.
14017 */
14018HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14019{
14020 LogFlowThisFunc(("\n"));
14021
14022 AutoCaller autoCaller(this);
14023 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14024
14025 ComPtr<IInternalSessionControl> directControl;
14026 {
14027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14028 if (mData->mSession.mLockType == LockType_VM)
14029 directControl = mData->mSession.mDirectControl;
14030 }
14031
14032 /* ignore notifications sent after #OnSessionEnd() is called */
14033 if (!directControl)
14034 return S_OK;
14035
14036 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14037}
14038
14039/**
14040 * @note Locks this object for reading.
14041 */
14042HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14043{
14044 LogFlowThisFunc(("\n"));
14045
14046 AutoCaller autoCaller(this);
14047 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14048
14049 ComPtr<IInternalSessionControl> directControl;
14050 {
14051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14052 if (mData->mSession.mLockType == LockType_VM)
14053 directControl = mData->mSession.mDirectControl;
14054 }
14055
14056 /* ignore notifications sent after #OnSessionEnd() is called */
14057 if (!directControl)
14058 return S_OK;
14059
14060 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14061}
14062
14063/**
14064 * Returns @c true if this machine's USB controller reports it has a matching
14065 * filter for the given USB device and @c false otherwise.
14066 *
14067 * @note locks this object for reading.
14068 */
14069bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14070{
14071 AutoCaller autoCaller(this);
14072 /* silently return if not ready -- this method may be called after the
14073 * direct machine session has been called */
14074 if (!autoCaller.isOk())
14075 return false;
14076
14077#ifdef VBOX_WITH_USB
14078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14079
14080 switch (mData->mMachineState)
14081 {
14082 case MachineState_Starting:
14083 case MachineState_Restoring:
14084 case MachineState_TeleportingIn:
14085 case MachineState_Paused:
14086 case MachineState_Running:
14087 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14088 * elsewhere... */
14089 alock.release();
14090 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14091 default: break;
14092 }
14093#else
14094 NOREF(aDevice);
14095 NOREF(aMaskedIfs);
14096#endif
14097 return false;
14098}
14099
14100/**
14101 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14102 */
14103HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14104 IVirtualBoxErrorInfo *aError,
14105 ULONG aMaskedIfs,
14106 const com::Utf8Str &aCaptureFilename)
14107{
14108 LogFlowThisFunc(("\n"));
14109
14110 AutoCaller autoCaller(this);
14111
14112 /* This notification may happen after the machine object has been
14113 * uninitialized (the session was closed), so don't assert. */
14114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14115
14116 ComPtr<IInternalSessionControl> directControl;
14117 {
14118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14119 if (mData->mSession.mLockType == LockType_VM)
14120 directControl = mData->mSession.mDirectControl;
14121 }
14122
14123 /* fail on notifications sent after #OnSessionEnd() is called, it is
14124 * expected by the caller */
14125 if (!directControl)
14126 return E_FAIL;
14127
14128 /* No locks should be held at this point. */
14129 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14130 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14131
14132 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14133}
14134
14135/**
14136 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14137 */
14138HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14139 IVirtualBoxErrorInfo *aError)
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144
14145 /* This notification may happen after the machine object has been
14146 * uninitialized (the session was closed), so don't assert. */
14147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14148
14149 ComPtr<IInternalSessionControl> directControl;
14150 {
14151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14152 if (mData->mSession.mLockType == LockType_VM)
14153 directControl = mData->mSession.mDirectControl;
14154 }
14155
14156 /* fail on notifications sent after #OnSessionEnd() is called, it is
14157 * expected by the caller */
14158 if (!directControl)
14159 return E_FAIL;
14160
14161 /* No locks should be held at this point. */
14162 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14163 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14164
14165 return directControl->OnUSBDeviceDetach(aId, aError);
14166}
14167
14168// protected methods
14169/////////////////////////////////////////////////////////////////////////////
14170
14171/**
14172 * Deletes the given file if it is no longer in use by either the current machine state
14173 * (if the machine is "saved") or any of the machine's snapshots.
14174 *
14175 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14176 * but is different for each SnapshotMachine. When calling this, the order of calling this
14177 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14178 * is therefore critical. I know, it's all rather messy.
14179 *
14180 * @param strStateFile
14181 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14182 * the test for whether the saved state file is in use.
14183 */
14184void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14185 Snapshot *pSnapshotToIgnore)
14186{
14187 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14188 if ( (strStateFile.isNotEmpty())
14189 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14190 )
14191 // ... and it must also not be shared with other snapshots
14192 if ( !mData->mFirstSnapshot
14193 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14194 // this checks the SnapshotMachine's state file paths
14195 )
14196 RTFileDelete(strStateFile.c_str());
14197}
14198
14199/**
14200 * Locks the attached media.
14201 *
14202 * All attached hard disks are locked for writing and DVD/floppy are locked for
14203 * reading. Parents of attached hard disks (if any) are locked for reading.
14204 *
14205 * This method also performs accessibility check of all media it locks: if some
14206 * media is inaccessible, the method will return a failure and a bunch of
14207 * extended error info objects per each inaccessible medium.
14208 *
14209 * Note that this method is atomic: if it returns a success, all media are
14210 * locked as described above; on failure no media is locked at all (all
14211 * succeeded individual locks will be undone).
14212 *
14213 * The caller is responsible for doing the necessary state sanity checks.
14214 *
14215 * The locks made by this method must be undone by calling #unlockMedia() when
14216 * no more needed.
14217 */
14218HRESULT SessionMachine::i_lockMedia()
14219{
14220 AutoCaller autoCaller(this);
14221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14222
14223 AutoMultiWriteLock2 alock(this->lockHandle(),
14224 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14225
14226 /* bail out if trying to lock things with already set up locking */
14227 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14228
14229 MultiResult mrc(S_OK);
14230
14231 /* Collect locking information for all medium objects attached to the VM. */
14232 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14233 it != mMediaData->mAttachments.end();
14234 ++it)
14235 {
14236 MediumAttachment* pAtt = *it;
14237 DeviceType_T devType = pAtt->i_getType();
14238 Medium *pMedium = pAtt->i_getMedium();
14239
14240 MediumLockList *pMediumLockList(new MediumLockList());
14241 // There can be attachments without a medium (floppy/dvd), and thus
14242 // it's impossible to create a medium lock list. It still makes sense
14243 // to have the empty medium lock list in the map in case a medium is
14244 // attached later.
14245 if (pMedium != NULL)
14246 {
14247 MediumType_T mediumType = pMedium->i_getType();
14248 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14249 || mediumType == MediumType_Shareable;
14250 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14251
14252 alock.release();
14253 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14254 !fIsReadOnlyLock /* fMediumLockWrite */,
14255 false /* fMediumLockWriteAll */,
14256 NULL,
14257 *pMediumLockList);
14258 alock.acquire();
14259 if (FAILED(mrc))
14260 {
14261 delete pMediumLockList;
14262 mData->mSession.mLockedMedia.Clear();
14263 break;
14264 }
14265 }
14266
14267 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14268 if (FAILED(rc))
14269 {
14270 mData->mSession.mLockedMedia.Clear();
14271 mrc = setError(rc,
14272 tr("Collecting locking information for all attached media failed"));
14273 break;
14274 }
14275 }
14276
14277 if (SUCCEEDED(mrc))
14278 {
14279 /* Now lock all media. If this fails, nothing is locked. */
14280 alock.release();
14281 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14282 alock.acquire();
14283 if (FAILED(rc))
14284 {
14285 mrc = setError(rc,
14286 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14287 }
14288 }
14289
14290 return mrc;
14291}
14292
14293/**
14294 * Undoes the locks made by by #lockMedia().
14295 */
14296HRESULT SessionMachine::i_unlockMedia()
14297{
14298 AutoCaller autoCaller(this);
14299 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14300
14301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14302
14303 /* we may be holding important error info on the current thread;
14304 * preserve it */
14305 ErrorInfoKeeper eik;
14306
14307 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14308 AssertComRC(rc);
14309 return rc;
14310}
14311
14312/**
14313 * Helper to change the machine state (reimplementation).
14314 *
14315 * @note Locks this object for writing.
14316 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14317 * it can cause crashes in random places due to unexpectedly committing
14318 * the current settings. The caller is responsible for that. The call
14319 * to saveStateSettings is fine, because this method does not commit.
14320 */
14321HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14322{
14323 LogFlowThisFuncEnter();
14324 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14325
14326 AutoCaller autoCaller(this);
14327 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14328
14329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14330
14331 MachineState_T oldMachineState = mData->mMachineState;
14332
14333 AssertMsgReturn(oldMachineState != aMachineState,
14334 ("oldMachineState=%s, aMachineState=%s\n",
14335 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14336 E_FAIL);
14337
14338 HRESULT rc = S_OK;
14339
14340 int stsFlags = 0;
14341 bool deleteSavedState = false;
14342
14343 /* detect some state transitions */
14344
14345 if ( ( oldMachineState == MachineState_Saved
14346 && aMachineState == MachineState_Restoring)
14347 || ( ( oldMachineState == MachineState_PoweredOff
14348 || oldMachineState == MachineState_Teleported
14349 || oldMachineState == MachineState_Aborted
14350 )
14351 && ( aMachineState == MachineState_TeleportingIn
14352 || aMachineState == MachineState_Starting
14353 )
14354 )
14355 )
14356 {
14357 /* The EMT thread is about to start */
14358
14359 /* Nothing to do here for now... */
14360
14361 /// @todo NEWMEDIA don't let mDVDDrive and other children
14362 /// change anything when in the Starting/Restoring state
14363 }
14364 else if ( ( oldMachineState == MachineState_Running
14365 || oldMachineState == MachineState_Paused
14366 || oldMachineState == MachineState_Teleporting
14367 || oldMachineState == MachineState_OnlineSnapshotting
14368 || oldMachineState == MachineState_LiveSnapshotting
14369 || oldMachineState == MachineState_Stuck
14370 || oldMachineState == MachineState_Starting
14371 || oldMachineState == MachineState_Stopping
14372 || oldMachineState == MachineState_Saving
14373 || oldMachineState == MachineState_Restoring
14374 || oldMachineState == MachineState_TeleportingPausedVM
14375 || oldMachineState == MachineState_TeleportingIn
14376 )
14377 && ( aMachineState == MachineState_PoweredOff
14378 || aMachineState == MachineState_Saved
14379 || aMachineState == MachineState_Teleported
14380 || aMachineState == MachineState_Aborted
14381 )
14382 )
14383 {
14384 /* The EMT thread has just stopped, unlock attached media. Note that as
14385 * opposed to locking that is done from Console, we do unlocking here
14386 * because the VM process may have aborted before having a chance to
14387 * properly unlock all media it locked. */
14388
14389 unlockMedia();
14390 }
14391
14392 if (oldMachineState == MachineState_Restoring)
14393 {
14394 if (aMachineState != MachineState_Saved)
14395 {
14396 /*
14397 * delete the saved state file once the machine has finished
14398 * restoring from it (note that Console sets the state from
14399 * Restoring to Saved if the VM couldn't restore successfully,
14400 * to give the user an ability to fix an error and retry --
14401 * we keep the saved state file in this case)
14402 */
14403 deleteSavedState = true;
14404 }
14405 }
14406 else if ( oldMachineState == MachineState_Saved
14407 && ( aMachineState == MachineState_PoweredOff
14408 || aMachineState == MachineState_Aborted
14409 || aMachineState == MachineState_Teleported
14410 )
14411 )
14412 {
14413 /*
14414 * delete the saved state after SessionMachine::ForgetSavedState() is called
14415 * or if the VM process (owning a direct VM session) crashed while the
14416 * VM was Saved
14417 */
14418
14419 /// @todo (dmik)
14420 // Not sure that deleting the saved state file just because of the
14421 // client death before it attempted to restore the VM is a good
14422 // thing. But when it crashes we need to go to the Aborted state
14423 // which cannot have the saved state file associated... The only
14424 // way to fix this is to make the Aborted condition not a VM state
14425 // but a bool flag: i.e., when a crash occurs, set it to true and
14426 // change the state to PoweredOff or Saved depending on the
14427 // saved state presence.
14428
14429 deleteSavedState = true;
14430 mData->mCurrentStateModified = TRUE;
14431 stsFlags |= SaveSTS_CurStateModified;
14432 }
14433
14434 if ( aMachineState == MachineState_Starting
14435 || aMachineState == MachineState_Restoring
14436 || aMachineState == MachineState_TeleportingIn
14437 )
14438 {
14439 /* set the current state modified flag to indicate that the current
14440 * state is no more identical to the state in the
14441 * current snapshot */
14442 if (!mData->mCurrentSnapshot.isNull())
14443 {
14444 mData->mCurrentStateModified = TRUE;
14445 stsFlags |= SaveSTS_CurStateModified;
14446 }
14447 }
14448
14449 if (deleteSavedState)
14450 {
14451 if (mRemoveSavedState)
14452 {
14453 Assert(!mSSData->strStateFilePath.isEmpty());
14454
14455 // it is safe to delete the saved state file if ...
14456 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14457 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14458 // ... none of the snapshots share the saved state file
14459 )
14460 RTFileDelete(mSSData->strStateFilePath.c_str());
14461 }
14462
14463 mSSData->strStateFilePath.setNull();
14464 stsFlags |= SaveSTS_StateFilePath;
14465 }
14466
14467 /* redirect to the underlying peer machine */
14468 mPeer->i_setMachineState(aMachineState);
14469
14470 if ( aMachineState == MachineState_PoweredOff
14471 || aMachineState == MachineState_Teleported
14472 || aMachineState == MachineState_Aborted
14473 || aMachineState == MachineState_Saved)
14474 {
14475 /* the machine has stopped execution
14476 * (or the saved state file was adopted) */
14477 stsFlags |= SaveSTS_StateTimeStamp;
14478 }
14479
14480 if ( ( oldMachineState == MachineState_PoweredOff
14481 || oldMachineState == MachineState_Aborted
14482 || oldMachineState == MachineState_Teleported
14483 )
14484 && aMachineState == MachineState_Saved)
14485 {
14486 /* the saved state file was adopted */
14487 Assert(!mSSData->strStateFilePath.isEmpty());
14488 stsFlags |= SaveSTS_StateFilePath;
14489 }
14490
14491#ifdef VBOX_WITH_GUEST_PROPS
14492 if ( aMachineState == MachineState_PoweredOff
14493 || aMachineState == MachineState_Aborted
14494 || aMachineState == MachineState_Teleported)
14495 {
14496 /* Make sure any transient guest properties get removed from the
14497 * property store on shutdown. */
14498 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14499
14500 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14501 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14502 while (it != llGuestProperties.end())
14503 {
14504 const settings::GuestProperty &prop = *it;
14505 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14506 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14507 {
14508 it = llGuestProperties.erase(it);
14509 fNeedsSaving = true;
14510 }
14511 else
14512 {
14513 ++it;
14514 }
14515 }
14516
14517 if (fNeedsSaving)
14518 {
14519 mData->mCurrentStateModified = TRUE;
14520 stsFlags |= SaveSTS_CurStateModified;
14521 }
14522 }
14523#endif /* VBOX_WITH_GUEST_PROPS */
14524
14525 rc = i_saveStateSettings(stsFlags);
14526
14527 if ( ( oldMachineState != MachineState_PoweredOff
14528 && oldMachineState != MachineState_Aborted
14529 && oldMachineState != MachineState_Teleported
14530 )
14531 && ( aMachineState == MachineState_PoweredOff
14532 || aMachineState == MachineState_Aborted
14533 || aMachineState == MachineState_Teleported
14534 )
14535 )
14536 {
14537 /* we've been shut down for any reason */
14538 /* no special action so far */
14539 }
14540
14541 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14542 LogFlowThisFuncLeave();
14543 return rc;
14544}
14545
14546/**
14547 * Sends the current machine state value to the VM process.
14548 *
14549 * @note Locks this object for reading, then calls a client process.
14550 */
14551HRESULT SessionMachine::i_updateMachineStateOnClient()
14552{
14553 AutoCaller autoCaller(this);
14554 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14555
14556 ComPtr<IInternalSessionControl> directControl;
14557 {
14558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14559 AssertReturn(!!mData, E_FAIL);
14560 if (mData->mSession.mLockType == LockType_VM)
14561 directControl = mData->mSession.mDirectControl;
14562
14563 /* directControl may be already set to NULL here in #OnSessionEnd()
14564 * called too early by the direct session process while there is still
14565 * some operation (like deleting the snapshot) in progress. The client
14566 * process in this case is waiting inside Session::close() for the
14567 * "end session" process object to complete, while #uninit() called by
14568 * #checkForDeath() on the Watcher thread is waiting for the pending
14569 * operation to complete. For now, we accept this inconsistent behavior
14570 * and simply do nothing here. */
14571
14572 if (mData->mSession.mState == SessionState_Unlocking)
14573 return S_OK;
14574 }
14575
14576 /* ignore notifications sent after #OnSessionEnd() is called */
14577 if (!directControl)
14578 return S_OK;
14579
14580 return directControl->UpdateMachineState(mData->mMachineState);
14581}
14582
14583
14584/**
14585 * Static Machine method that can get passed to RTThreadCreate to
14586 * have a thread started for a Task. See Machine::Task.
14587 */
14588/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14589{
14590 AssertReturn(pvUser, VERR_INVALID_POINTER);
14591
14592 Task *pTask = static_cast<Task *>(pvUser);
14593 pTask->handler();
14594 /** @todo r=klaus it would be safer to update the progress object here,
14595 * as it avoids possible races due to scoping issues/tricks in the handler */
14596 // it's our responsibility to delete the task
14597 delete pTask;
14598
14599 return 0;
14600}
14601
14602/*static*/
14603HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14604{
14605 va_list args;
14606 va_start(args, pcszMsg);
14607 HRESULT rc = setErrorInternal(aResultCode,
14608 getStaticClassIID(),
14609 getStaticComponentName(),
14610 Utf8Str(pcszMsg, args),
14611 false /* aWarning */,
14612 true /* aLogIt */);
14613 va_end(args);
14614 return rc;
14615}
14616
14617
14618HRESULT Machine::updateState(MachineState_T aState)
14619{
14620 NOREF(aState);
14621 ReturnComNotImplemented();
14622}
14623
14624HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14625{
14626 NOREF(aProgress);
14627 ReturnComNotImplemented();
14628}
14629
14630HRESULT Machine::endPowerUp(LONG aResult)
14631{
14632 NOREF(aResult);
14633 ReturnComNotImplemented();
14634}
14635
14636HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14637{
14638 NOREF(aProgress);
14639 ReturnComNotImplemented();
14640}
14641
14642HRESULT Machine::endPoweringDown(LONG aResult,
14643 const com::Utf8Str &aErrMsg)
14644{
14645 NOREF(aResult);
14646 NOREF(aErrMsg);
14647 ReturnComNotImplemented();
14648}
14649
14650HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14651 BOOL *aMatched,
14652 ULONG *aMaskedInterfaces)
14653{
14654 NOREF(aDevice);
14655 NOREF(aMatched);
14656 NOREF(aMaskedInterfaces);
14657 ReturnComNotImplemented();
14658
14659}
14660
14661HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14662{
14663 NOREF(aId); NOREF(aCaptureFilename);
14664 ReturnComNotImplemented();
14665}
14666
14667HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14668 BOOL aDone)
14669{
14670 NOREF(aId);
14671 NOREF(aDone);
14672 ReturnComNotImplemented();
14673}
14674
14675HRESULT Machine::autoCaptureUSBDevices()
14676{
14677 ReturnComNotImplemented();
14678}
14679
14680HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14681{
14682 NOREF(aDone);
14683 ReturnComNotImplemented();
14684}
14685
14686HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14687 ComPtr<IProgress> &aProgress)
14688{
14689 NOREF(aSession);
14690 NOREF(aProgress);
14691 ReturnComNotImplemented();
14692}
14693
14694HRESULT Machine::finishOnlineMergeMedium()
14695{
14696 ReturnComNotImplemented();
14697}
14698
14699HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14700 std::vector<com::Utf8Str> &aValues,
14701 std::vector<LONG64> &aTimestamps,
14702 std::vector<com::Utf8Str> &aFlags)
14703{
14704 NOREF(aNames);
14705 NOREF(aValues);
14706 NOREF(aTimestamps);
14707 NOREF(aFlags);
14708 ReturnComNotImplemented();
14709}
14710
14711HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14712 const com::Utf8Str &aValue,
14713 LONG64 aTimestamp,
14714 const com::Utf8Str &aFlags,
14715 BOOL *aNotify)
14716{
14717 NOREF(aName);
14718 NOREF(aValue);
14719 NOREF(aTimestamp);
14720 NOREF(aFlags);
14721 NOREF(aNotify);
14722 ReturnComNotImplemented();
14723}
14724
14725HRESULT Machine::lockMedia()
14726{
14727 ReturnComNotImplemented();
14728}
14729
14730HRESULT Machine::unlockMedia()
14731{
14732 ReturnComNotImplemented();
14733}
14734
14735HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14736 ComPtr<IMediumAttachment> &aNewAttachment)
14737{
14738 NOREF(aAttachment);
14739 NOREF(aNewAttachment);
14740 ReturnComNotImplemented();
14741}
14742
14743HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14744 ULONG aCpuUser,
14745 ULONG aCpuKernel,
14746 ULONG aCpuIdle,
14747 ULONG aMemTotal,
14748 ULONG aMemFree,
14749 ULONG aMemBalloon,
14750 ULONG aMemShared,
14751 ULONG aMemCache,
14752 ULONG aPagedTotal,
14753 ULONG aMemAllocTotal,
14754 ULONG aMemFreeTotal,
14755 ULONG aMemBalloonTotal,
14756 ULONG aMemSharedTotal,
14757 ULONG aVmNetRx,
14758 ULONG aVmNetTx)
14759{
14760 NOREF(aValidStats);
14761 NOREF(aCpuUser);
14762 NOREF(aCpuKernel);
14763 NOREF(aCpuIdle);
14764 NOREF(aMemTotal);
14765 NOREF(aMemFree);
14766 NOREF(aMemBalloon);
14767 NOREF(aMemShared);
14768 NOREF(aMemCache);
14769 NOREF(aPagedTotal);
14770 NOREF(aMemAllocTotal);
14771 NOREF(aMemFreeTotal);
14772 NOREF(aMemBalloonTotal);
14773 NOREF(aMemSharedTotal);
14774 NOREF(aVmNetRx);
14775 NOREF(aVmNetTx);
14776 ReturnComNotImplemented();
14777}
Note: See TracBrowser for help on using the repository browser.

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