VirtualBox

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

Last change on this file since 52958 was 52958, checked in by vboxsync, 11 years ago

Main: merged IMachine::readSavedThumbnailToArray and readSavedThumbnailPNGToArray

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 497.9 KB
Line 
1/* $Id: MachineImpl.cpp 52958 2014-10-06 17:57:01Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "Windows81"
1284 || mUserData->s.strOsType == "Windows81_64"
1285 || mUserData->s.strOsType == "Windows8"
1286 || mUserData->s.strOsType == "Windows8_64"
1287 || mUserData->s.strOsType == "Windows7"
1288 || mUserData->s.strOsType == "Windows7_64"
1289 || mUserData->s.strOsType == "WindowsVista"
1290 || mUserData->s.strOsType == "WindowsVista_64"
1291 || mUserData->s.strOsType == "Windows2012"
1292 || mUserData->s.strOsType == "Windows2012_64"
1293 || mUserData->s.strOsType == "Windows2008"
1294 || mUserData->s.strOsType == "Windows2008_64")
1295 {
1296 *aParavirtProvider = ParavirtProvider_HyperV;
1297 }
1298 else
1299 *aParavirtProvider = ParavirtProvider_None;
1300 break;
1301 }
1302 }
1303 break;
1304 }
1305 }
1306
1307 Assert( *aParavirtProvider == ParavirtProvider_None
1308 || *aParavirtProvider == ParavirtProvider_Minimal
1309 || *aParavirtProvider == ParavirtProvider_HyperV);
1310 return S_OK;
1311}
1312
1313HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1314{
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 aHardwareVersion = mHWData->mHWVersion;
1318
1319 return S_OK;
1320}
1321
1322HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHardwareVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 HRESULT rc = i_checkStateDependency(MutableStateDep);
1334 if (FAILED(rc)) return rc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 mHWData->mHWVersion = aHardwareVersion;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 if (!mHWData->mHardwareUUID.isZero())
1348 aHardwareUUID = mHWData->mHardwareUUID;
1349 else
1350 aHardwareUUID = mData->mUuid;
1351
1352 return S_OK;
1353}
1354
1355HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1356{
1357 if (!aHardwareUUID.isValid())
1358 return E_INVALIDARG;
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = i_checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 i_setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (aHardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = aHardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aMemorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setMemorySize(ULONG aMemorySize)
1385{
1386 /* check RAM limits */
1387 if ( aMemorySize < MM_RAM_MIN_IN_MB
1388 || aMemorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
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 mHWData->mMemorySize = aMemorySize;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 *aCPUCount = mHWData->mCPUCount;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setCPUCount(ULONG aCPUCount)
1416{
1417 /* check CPU limits */
1418 if ( aCPUCount < SchemaDefs::MinCPUCount
1419 || aCPUCount > SchemaDefs::MaxCPUCount
1420 )
1421 return setError(E_INVALIDARG,
1422 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1423 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1424
1425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1426
1427 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1428 if (mHWData->mCPUHotPlugEnabled)
1429 {
1430 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1431 {
1432 if (mHWData->mCPUAttached[idx])
1433 return setError(E_INVALIDARG,
1434 tr("There is still a CPU attached to socket %lu."
1435 "Detach the CPU before removing the socket"),
1436 aCPUCount, idx+1);
1437 }
1438 }
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCPUCount = aCPUCount;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1460{
1461 HRESULT rc = S_OK;
1462
1463 /* check throttle limits */
1464 if ( aCPUExecutionCap < 1
1465 || aCPUExecutionCap > 100
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1469 aCPUExecutionCap, 1, 100);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 alock.release();
1474 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1475 alock.acquire();
1476 if (FAILED(rc)) return rc;
1477
1478 i_setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1481
1482 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1483 if (Global::IsOnline(mData->mMachineState))
1484 i_saveSettings(NULL);
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1499{
1500 HRESULT rc = S_OK;
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 rc = i_checkStateDependency(MutableStateDep);
1505 if (FAILED(rc)) return rc;
1506
1507 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1508 {
1509 if (aCPUHotPlugEnabled)
1510 {
1511 i_setModified(IsModified_MachineData);
1512 mHWData.backup();
1513
1514 /* Add the amount of CPUs currently attached */
1515 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1516 mHWData->mCPUAttached[i] = true;
1517 }
1518 else
1519 {
1520 /*
1521 * We can disable hotplug only if the amount of maximum CPUs is equal
1522 * to the amount of attached CPUs
1523 */
1524 unsigned cCpusAttached = 0;
1525 unsigned iHighestId = 0;
1526
1527 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1528 {
1529 if (mHWData->mCPUAttached[i])
1530 {
1531 cCpusAttached++;
1532 iHighestId = i;
1533 }
1534 }
1535
1536 if ( (cCpusAttached != mHWData->mCPUCount)
1537 || (iHighestId >= mHWData->mCPUCount))
1538 return setError(E_INVALIDARG,
1539 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 }
1544 }
1545
1546 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1547
1548 return rc;
1549}
1550
1551HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1552{
1553#ifdef VBOX_WITH_USB_CARDREADER
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1557
1558 return S_OK;
1559#else
1560 NOREF(aEmulatedUSBCardReaderEnabled);
1561 return E_NOTIMPL;
1562#endif
1563}
1564
1565HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1566{
1567#ifdef VBOX_WITH_USB_CARDREADER
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 HRESULT rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1576
1577 return S_OK;
1578#else
1579 NOREF(aEmulatedUSBCardReaderEnabled);
1580 return E_NOTIMPL;
1581#endif
1582}
1583
1584HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1585{
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *aHPETEnabled = mHWData->mHPETEnabled;
1589
1590 return S_OK;
1591}
1592
1593HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 rc = i_checkStateDependency(MutableStateDep);
1600 if (FAILED(rc)) return rc;
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604
1605 mHWData->mHPETEnabled = aHPETEnabled;
1606
1607 return rc;
1608}
1609
1610HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1611{
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1615 return S_OK;
1616}
1617
1618HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1619{
1620 HRESULT rc = S_OK;
1621
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1627
1628 alock.release();
1629 rc = i_onVideoCaptureChange();
1630 alock.acquire();
1631 if (FAILED(rc))
1632 {
1633 /*
1634 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1635 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1636 * determine if it should start or stop capturing. Therefore we need to manually
1637 * undo change.
1638 */
1639 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1640 return rc;
1641 }
1642
1643 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1644 if (Global::IsOnline(mData->mMachineState))
1645 i_saveSettings(NULL);
1646
1647 return rc;
1648}
1649
1650HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1654 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1655 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1660{
1661 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1662 bool fChanged = false;
1663
1664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1667 {
1668 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1669 {
1670 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1671 fChanged = true;
1672 }
1673 }
1674 if (fChanged)
1675 {
1676 alock.release();
1677 HRESULT rc = i_onVideoCaptureChange();
1678 alock.acquire();
1679 if (FAILED(rc)) return rc;
1680 i_setModified(IsModified_MachineData);
1681
1682 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1683 if (Global::IsOnline(mData->mMachineState))
1684 i_saveSettings(NULL);
1685 }
1686
1687 return S_OK;
1688}
1689
1690HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1691{
1692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1693 if (mHWData->mVideoCaptureFile.isEmpty())
1694 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1695 else
1696 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1697 return S_OK;
1698}
1699
1700HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1701{
1702 Utf8Str strFile(aVideoCaptureFile);
1703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 if ( Global::IsOnline(mData->mMachineState)
1706 && mHWData->mVideoCaptureEnabled)
1707 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1708
1709 if (!RTPathStartsWithRoot(strFile.c_str()))
1710 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1711
1712 if (!strFile.isEmpty())
1713 {
1714 Utf8Str defaultFile;
1715 i_getDefaultVideoCaptureFile(defaultFile);
1716 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1717 strFile.setNull();
1718 }
1719
1720 i_setModified(IsModified_MachineData);
1721 mHWData.backup();
1722 mHWData->mVideoCaptureFile = strFile;
1723
1724 return S_OK;
1725}
1726
1727HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1731 return S_OK;
1732}
1733
1734HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1735{
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 if ( Global::IsOnline(mData->mMachineState)
1739 && mHWData->mVideoCaptureEnabled)
1740 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1741
1742 i_setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1745
1746 return S_OK;
1747}
1748
1749HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1750{
1751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1752 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1753 return S_OK;
1754}
1755
1756HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1757{
1758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1759
1760 if ( Global::IsOnline(mData->mMachineState)
1761 && mHWData->mVideoCaptureEnabled)
1762 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1763
1764 i_setModified(IsModified_MachineData);
1765 mHWData.backup();
1766 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1767
1768 return S_OK;
1769}
1770
1771HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1772{
1773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1774 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1775 return S_OK;
1776}
1777
1778HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1779{
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 if ( Global::IsOnline(mData->mMachineState)
1783 && mHWData->mVideoCaptureEnabled)
1784 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1785
1786 i_setModified(IsModified_MachineData);
1787 mHWData.backup();
1788 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1789
1790 return S_OK;
1791}
1792
1793HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1794{
1795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1796 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1797 return S_OK;
1798}
1799
1800HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1801{
1802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 if ( Global::IsOnline(mData->mMachineState)
1805 && mHWData->mVideoCaptureEnabled)
1806 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1807
1808 i_setModified(IsModified_MachineData);
1809 mHWData.backup();
1810 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1811
1812 return S_OK;
1813}
1814
1815HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1816{
1817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1818 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1819 return S_OK;
1820}
1821
1822HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1823{
1824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1825
1826 if ( Global::IsOnline(mData->mMachineState)
1827 && mHWData->mVideoCaptureEnabled)
1828 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862
1863 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1864 return S_OK;
1865}
1866
1867HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1868{
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 if ( Global::IsOnline(mData->mMachineState)
1872 && mHWData->mVideoCaptureEnabled)
1873 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1874
1875 i_setModified(IsModified_MachineData);
1876 mHWData.backup();
1877 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1892{
1893 switch (aGraphicsControllerType)
1894 {
1895 case GraphicsControllerType_Null:
1896 case GraphicsControllerType_VBoxVGA:
1897#ifdef VBOX_WITH_VMSVGA
1898 case GraphicsControllerType_VMSVGA:
1899#endif
1900 break;
1901 default:
1902 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1903 }
1904
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 HRESULT rc = i_checkStateDependency(MutableStateDep);
1908 if (FAILED(rc)) return rc;
1909
1910 i_setModified(IsModified_MachineData);
1911 mHWData.backup();
1912 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1913
1914 return S_OK;
1915}
1916
1917HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1918{
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 *aVRAMSize = mHWData->mVRAMSize;
1922
1923 return S_OK;
1924}
1925
1926HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1927{
1928 /* check VRAM limits */
1929 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1930 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1931 return setError(E_INVALIDARG,
1932 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1933 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1934
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 HRESULT rc = i_checkStateDependency(MutableStateDep);
1938 if (FAILED(rc)) return rc;
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVRAMSize = aVRAMSize;
1943
1944 return S_OK;
1945}
1946
1947/** @todo this method should not be public */
1948HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1949{
1950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1951
1952 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1953
1954 return S_OK;
1955}
1956
1957/**
1958 * Set the memory balloon size.
1959 *
1960 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1961 * we have to make sure that we never call IGuest from here.
1962 */
1963HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1964{
1965 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1966#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1967 /* check limits */
1968 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1969 return setError(E_INVALIDARG,
1970 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1971 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1972
1973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1978
1979 return S_OK;
1980#else
1981 NOREF(aMemoryBalloonSize);
1982 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1983#endif
1984}
1985
1986HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1987{
1988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1991 return S_OK;
1992}
1993
1994HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1995{
1996#ifdef VBOX_WITH_PAGE_SHARING
1997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2000 i_setModified(IsModified_MachineData);
2001 mHWData.backup();
2002 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2003 return S_OK;
2004#else
2005 NOREF(aPageFusionEnabled);
2006 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2007#endif
2008}
2009
2010HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2011{
2012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2013
2014 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2015
2016 return S_OK;
2017}
2018
2019HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2020{
2021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2022
2023 HRESULT rc = i_checkStateDependency(MutableStateDep);
2024 if (FAILED(rc)) return rc;
2025
2026 /** @todo check validity! */
2027
2028 i_setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2031
2032 return S_OK;
2033}
2034
2035
2036HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2037{
2038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2039
2040 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2041
2042 return S_OK;
2043}
2044
2045HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2046{
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 HRESULT rc = i_checkStateDependency(MutableStateDep);
2050 if (FAILED(rc)) return rc;
2051
2052 /** @todo check validity! */
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2056
2057 return S_OK;
2058}
2059
2060HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2061{
2062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 *aMonitorCount = mHWData->mMonitorCount;
2065
2066 return S_OK;
2067}
2068
2069HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2070{
2071 /* make sure monitor count is a sensible number */
2072 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2073 return setError(E_INVALIDARG,
2074 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2075 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2076
2077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2078
2079 HRESULT rc = i_checkStateDependency(MutableStateDep);
2080 if (FAILED(rc)) return rc;
2081
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mMonitorCount = aMonitorCount;
2085
2086 return S_OK;
2087}
2088
2089HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2090{
2091 /* mBIOSSettings is constant during life time, no need to lock */
2092 aBIOSSettings = mBIOSSettings;
2093
2094 return S_OK;
2095}
2096
2097HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2098{
2099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2100
2101 switch (aProperty)
2102 {
2103 case CPUPropertyType_PAE:
2104 *aValue = mHWData->mPAEEnabled;
2105 break;
2106
2107 case CPUPropertyType_Synthetic:
2108 *aValue = mHWData->mSyntheticCpu;
2109 break;
2110
2111 case CPUPropertyType_LongMode:
2112 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2113 *aValue = TRUE;
2114 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2115 *aValue = FALSE;
2116#if HC_ARCH_BITS == 64
2117 else
2118 *aValue = TRUE;
2119#else
2120 else
2121 {
2122 *aValue = FALSE;
2123
2124 ComPtr<IGuestOSType> ptrGuestOSType;
2125 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2126 if (SUCCEEDED(hrc2))
2127 {
2128 BOOL fIs64Bit = FALSE;
2129 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2130 if (SUCCEEDED(hrc2) && fIs64Bit)
2131 {
2132 ComObjPtr<Host> ptrHost = mParent->i_host();
2133 alock.release();
2134
2135 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2136 if (FAILED(hrc2))
2137 *aValue = FALSE;
2138 }
2139 }
2140 }
2141#endif
2142 break;
2143
2144 case CPUPropertyType_TripleFaultReset:
2145 *aValue = mHWData->mTripleFaultReset;
2146 break;
2147
2148 default:
2149 return E_INVALIDARG;
2150 }
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 switch (aProperty)
2162 {
2163 case CPUPropertyType_PAE:
2164 i_setModified(IsModified_MachineData);
2165 mHWData.backup();
2166 mHWData->mPAEEnabled = !!aValue;
2167 break;
2168
2169 case CPUPropertyType_Synthetic:
2170 i_setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mSyntheticCpu = !!aValue;
2173 break;
2174
2175 case CPUPropertyType_LongMode:
2176 i_setModified(IsModified_MachineData);
2177 mHWData.backup();
2178 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2179 break;
2180
2181 case CPUPropertyType_TripleFaultReset:
2182 i_setModified(IsModified_MachineData);
2183 mHWData.backup();
2184 mHWData->mTripleFaultReset = !!aValue;
2185 break;
2186
2187 default:
2188 return E_INVALIDARG;
2189 }
2190 return S_OK;
2191}
2192
2193HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2194{
2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 switch(aId)
2198 {
2199 case 0x0:
2200 case 0x1:
2201 case 0x2:
2202 case 0x3:
2203 case 0x4:
2204 case 0x5:
2205 case 0x6:
2206 case 0x7:
2207 case 0x8:
2208 case 0x9:
2209 case 0xA:
2210 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2211 return E_INVALIDARG;
2212
2213 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2214 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2215 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2216 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2217 break;
2218
2219 case 0x80000000:
2220 case 0x80000001:
2221 case 0x80000002:
2222 case 0x80000003:
2223 case 0x80000004:
2224 case 0x80000005:
2225 case 0x80000006:
2226 case 0x80000007:
2227 case 0x80000008:
2228 case 0x80000009:
2229 case 0x8000000A:
2230 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2231 return E_INVALIDARG;
2232
2233 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2234 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2235 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2236 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2237 break;
2238
2239 default:
2240 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2241 }
2242 return S_OK;
2243}
2244
2245
2246HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2247{
2248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2249
2250 HRESULT rc = i_checkStateDependency(MutableStateDep);
2251 if (FAILED(rc)) return rc;
2252
2253 switch(aId)
2254 {
2255 case 0x0:
2256 case 0x1:
2257 case 0x2:
2258 case 0x3:
2259 case 0x4:
2260 case 0x5:
2261 case 0x6:
2262 case 0x7:
2263 case 0x8:
2264 case 0x9:
2265 case 0xA:
2266 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2267 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2268 i_setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2271 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2272 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2273 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2274 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2275 break;
2276
2277 case 0x80000000:
2278 case 0x80000001:
2279 case 0x80000002:
2280 case 0x80000003:
2281 case 0x80000004:
2282 case 0x80000005:
2283 case 0x80000006:
2284 case 0x80000007:
2285 case 0x80000008:
2286 case 0x80000009:
2287 case 0x8000000A:
2288 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2289 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2293 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2294 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2295 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2296 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2297 break;
2298
2299 default:
2300 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2301 }
2302 return S_OK;
2303}
2304
2305HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2306{
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 switch(aId)
2313 {
2314 case 0x0:
2315 case 0x1:
2316 case 0x2:
2317 case 0x3:
2318 case 0x4:
2319 case 0x5:
2320 case 0x6:
2321 case 0x7:
2322 case 0x8:
2323 case 0x9:
2324 case 0xA:
2325 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2326 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 /* Invalidate leaf. */
2330 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2331 break;
2332
2333 case 0x80000000:
2334 case 0x80000001:
2335 case 0x80000002:
2336 case 0x80000003:
2337 case 0x80000004:
2338 case 0x80000005:
2339 case 0x80000006:
2340 case 0x80000007:
2341 case 0x80000008:
2342 case 0x80000009:
2343 case 0x8000000A:
2344 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2345 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 /* Invalidate leaf. */
2349 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2350 break;
2351
2352 default:
2353 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2354 }
2355 return S_OK;
2356}
2357
2358HRESULT Machine::removeAllCPUIDLeaves()
2359{
2360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2361
2362 HRESULT rc = i_checkStateDependency(MutableStateDep);
2363 if (FAILED(rc)) return rc;
2364
2365 i_setModified(IsModified_MachineData);
2366 mHWData.backup();
2367
2368 /* Invalidate all standard leafs. */
2369 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2370 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2371
2372 /* Invalidate all extended leafs. */
2373 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2374 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2375
2376 return S_OK;
2377}
2378HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2379{
2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2381
2382 switch(aProperty)
2383 {
2384 case HWVirtExPropertyType_Enabled:
2385 *aValue = mHWData->mHWVirtExEnabled;
2386 break;
2387
2388 case HWVirtExPropertyType_VPID:
2389 *aValue = mHWData->mHWVirtExVPIDEnabled;
2390 break;
2391
2392 case HWVirtExPropertyType_NestedPaging:
2393 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2394 break;
2395
2396 case HWVirtExPropertyType_UnrestrictedExecution:
2397 *aValue = mHWData->mHWVirtExUXEnabled;
2398 break;
2399
2400 case HWVirtExPropertyType_LargePages:
2401 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2402#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2403 *aValue = FALSE;
2404#endif
2405 break;
2406
2407 case HWVirtExPropertyType_Force:
2408 *aValue = mHWData->mHWVirtExForceEnabled;
2409 break;
2410
2411 default:
2412 return E_INVALIDARG;
2413 }
2414 return S_OK;
2415}
2416
2417HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2418{
2419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2420
2421 HRESULT rc = i_checkStateDependency(MutableStateDep);
2422 if (FAILED(rc)) return rc;
2423
2424 switch(aProperty)
2425 {
2426 case HWVirtExPropertyType_Enabled:
2427 i_setModified(IsModified_MachineData);
2428 mHWData.backup();
2429 mHWData->mHWVirtExEnabled = !!aValue;
2430 break;
2431
2432 case HWVirtExPropertyType_VPID:
2433 i_setModified(IsModified_MachineData);
2434 mHWData.backup();
2435 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2436 break;
2437
2438 case HWVirtExPropertyType_NestedPaging:
2439 i_setModified(IsModified_MachineData);
2440 mHWData.backup();
2441 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2442 break;
2443
2444 case HWVirtExPropertyType_UnrestrictedExecution:
2445 i_setModified(IsModified_MachineData);
2446 mHWData.backup();
2447 mHWData->mHWVirtExUXEnabled = !!aValue;
2448 break;
2449
2450 case HWVirtExPropertyType_LargePages:
2451 i_setModified(IsModified_MachineData);
2452 mHWData.backup();
2453 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2454 break;
2455
2456 case HWVirtExPropertyType_Force:
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459 mHWData->mHWVirtExForceEnabled = !!aValue;
2460 break;
2461
2462 default:
2463 return E_INVALIDARG;
2464 }
2465
2466 return S_OK;
2467}
2468
2469HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2470{
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
2473 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2474
2475 return S_OK;
2476}
2477
2478HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2479{
2480 /* @todo (r=dmik):
2481 * 1. Allow to change the name of the snapshot folder containing snapshots
2482 * 2. Rename the folder on disk instead of just changing the property
2483 * value (to be smart and not to leave garbage). Note that it cannot be
2484 * done here because the change may be rolled back. Thus, the right
2485 * place is #saveSettings().
2486 */
2487
2488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 HRESULT rc = i_checkStateDependency(MutableStateDep);
2491 if (FAILED(rc)) return rc;
2492
2493 if (!mData->mCurrentSnapshot.isNull())
2494 return setError(E_FAIL,
2495 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2496
2497 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2498
2499 if (strSnapshotFolder.isEmpty())
2500 strSnapshotFolder = "Snapshots";
2501 int vrc = i_calculateFullPath(strSnapshotFolder,
2502 strSnapshotFolder);
2503 if (RT_FAILURE(vrc))
2504 return setError(E_FAIL,
2505 tr("Invalid snapshot folder '%s' (%Rrc)"),
2506 strSnapshotFolder.c_str(), vrc);
2507
2508 i_setModified(IsModified_MachineData);
2509 mUserData.backup();
2510
2511 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2512
2513 return S_OK;
2514}
2515
2516HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 aMediumAttachments.resize(mMediaData->mAttachments.size());
2521 size_t i = 0;
2522 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2523 it != mMediaData->mAttachments.end(); ++it, ++i)
2524 aMediumAttachments[i] = *it;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 Assert(!!mVRDEServer);
2534
2535 aVRDEServer = mVRDEServer;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 aAudioAdapter = mAudioAdapter;
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2550{
2551#ifdef VBOX_WITH_VUSB
2552 clearError();
2553 MultiResult rc(S_OK);
2554
2555# ifdef VBOX_WITH_USB
2556 rc = mParent->i_host()->i_checkUSBProxyService();
2557 if (FAILED(rc)) return rc;
2558# endif
2559
2560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 USBControllerList data = *mUSBControllers.data();
2563 aUSBControllers.resize(data.size());
2564 size_t i = 0;
2565 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2566 aUSBControllers[i] = *it;
2567
2568 return S_OK;
2569#else
2570 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2571 * extended error info to indicate that USB is simply not available
2572 * (w/o treating it as a failure), for example, as in OSE */
2573 NOREF(aUSBControllers);
2574 ReturnComNotImplemented();
2575#endif /* VBOX_WITH_VUSB */
2576}
2577
2578HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2579{
2580#ifdef VBOX_WITH_VUSB
2581 clearError();
2582 MultiResult rc(S_OK);
2583
2584# ifdef VBOX_WITH_USB
2585 rc = mParent->i_host()->i_checkUSBProxyService();
2586 if (FAILED(rc)) return rc;
2587# endif
2588
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 aUSBDeviceFilters = mUSBDeviceFilters;
2592 return rc;
2593#else
2594 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2595 * extended error info to indicate that USB is simply not available
2596 * (w/o treating it as a failure), for example, as in OSE */
2597 NOREF(aUSBDeviceFilters);
2598 ReturnComNotImplemented();
2599#endif /* VBOX_WITH_VUSB */
2600}
2601
2602HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2603{
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 aSettingsFilePath = mData->m_strConfigFileFull;
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 HRESULT rc = i_checkStateDependency(MutableStateDep);
2616 if (FAILED(rc)) return rc;
2617
2618 if (!mData->pMachineConfigFile->fileExists())
2619 // this is a new machine, and no config file exists yet:
2620 *aSettingsModified = TRUE;
2621 else
2622 *aSettingsModified = (mData->flModifications != 0);
2623
2624 return S_OK;
2625}
2626
2627HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2628{
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aSessionState = mData->mSession.mState;
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 aSessionType = mData->mSession.mType;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 *aSessionPID = mData->mSession.mPID;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getState(MachineState_T *aState)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 *aState = mData->mMachineState;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aStateFilePath = mSSData->strStateFilePath;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 i_getLogFolder(aLogFolder);
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 aCurrentSnapshot = mData->mCurrentSnapshot;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2705 ? 0
2706 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 /* Note: for machines with no snapshots, we always return FALSE
2716 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2717 * reasons :) */
2718
2719 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2720 ? FALSE
2721 : mData->mCurrentStateModified;
2722
2723 return S_OK;
2724}
2725
2726HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2727{
2728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 aSharedFolders.resize(mHWData->mSharedFolders.size());
2731 size_t i = 0;
2732 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2733 it != mHWData->mSharedFolders.end(); ++i, ++it)
2734 aSharedFolders[i] = *it;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 *aClipboardMode = mHWData->mClipboardMode;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2749{
2750 HRESULT rc = S_OK;
2751
2752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 alock.release();
2755 rc = i_onClipboardModeChange(aClipboardMode);
2756 alock.acquire();
2757 if (FAILED(rc)) return rc;
2758
2759 i_setModified(IsModified_MachineData);
2760 mHWData.backup();
2761 mHWData->mClipboardMode = aClipboardMode;
2762
2763 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2764 if (Global::IsOnline(mData->mMachineState))
2765 i_saveSettings(NULL);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 *aDnDMode = mHWData->mDnDMode;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2780{
2781 HRESULT rc = S_OK;
2782
2783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 alock.release();
2786 rc = i_onDnDModeChange(aDnDMode);
2787
2788 alock.acquire();
2789 if (FAILED(rc)) return rc;
2790
2791 i_setModified(IsModified_MachineData);
2792 mHWData.backup();
2793 mHWData->mDnDMode = aDnDMode;
2794
2795 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2796 if (Global::IsOnline(mData->mMachineState))
2797 i_saveSettings(NULL);
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 try
2807 {
2808 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2809 }
2810 catch (...)
2811 {
2812 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2813 }
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2819{
2820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 HRESULT rc = i_checkStateDependency(MutableStateDep);
2823 if (FAILED(rc)) return rc;
2824
2825 i_setModified(IsModified_MachineData);
2826 mHWData.backup();
2827 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2828 return rc;
2829}
2830
2831HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 StorageControllerList data = *mStorageControllers.data();
2835 size_t i = 0;
2836 aStorageControllers.resize(data.size());
2837 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2838 aStorageControllers[i] = *it;
2839 return S_OK;
2840}
2841
2842HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2843{
2844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2845
2846 *aEnabled = mUserData->s.fTeleporterEnabled;
2847
2848 return S_OK;
2849}
2850
2851HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2852{
2853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2854
2855 /* Only allow it to be set to true when PoweredOff or Aborted.
2856 (Clearing it is always permitted.) */
2857 if ( aTeleporterEnabled
2858 && mData->mRegistered
2859 && ( !i_isSessionMachine()
2860 || ( mData->mMachineState != MachineState_PoweredOff
2861 && mData->mMachineState != MachineState_Teleported
2862 && mData->mMachineState != MachineState_Aborted
2863 )
2864 )
2865 )
2866 return setError(VBOX_E_INVALID_VM_STATE,
2867 tr("The machine is not powered off (state is %s)"),
2868 Global::stringifyMachineState(mData->mMachineState));
2869
2870 i_setModified(IsModified_MachineData);
2871 mUserData.backup();
2872 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2878{
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2882
2883 return S_OK;
2884}
2885
2886HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2887{
2888 if (aTeleporterPort >= _64K)
2889 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2890
2891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 HRESULT rc = i_checkStateDependency(MutableStateDep);
2894 if (FAILED(rc)) return rc;
2895
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2913{
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 HRESULT rc = i_checkStateDependency(MutableStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mUserData.backup();
2921 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2927{
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2935{
2936 /*
2937 * Hash the password first.
2938 */
2939 com::Utf8Str aT = aTeleporterPassword;
2940
2941 if (!aT.isEmpty())
2942 {
2943 if (VBoxIsPasswordHashed(&aT))
2944 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2945 VBoxHashPassword(&aT);
2946 }
2947
2948 /*
2949 * Do the update.
2950 */
2951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2952 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2953 if (SUCCEEDED(hrc))
2954 {
2955 i_setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterPassword = aT;
2958 }
2959
2960 return hrc;
2961}
2962
2963HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2964{
2965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2972{
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 /* @todo deal with running state change. */
2976 HRESULT rc = i_checkStateDependency(MutableStateDep);
2977 if (FAILED(rc)) return rc;
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2982 return S_OK;
2983}
2984
2985HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2986{
2987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2988
2989 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2990 return S_OK;
2991}
2992
2993HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2994{
2995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2996
2997 /* @todo deal with running state change. */
2998 HRESULT rc = i_checkStateDependency(MutableStateDep);
2999 if (FAILED(rc)) return rc;
3000
3001 i_setModified(IsModified_MachineData);
3002 mUserData.backup();
3003 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3008{
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3012 return S_OK;
3013}
3014
3015HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3016{
3017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3018
3019 /* @todo deal with running state change. */
3020 HRESULT rc = i_checkStateDependency(MutableStateDep);
3021 if (FAILED(rc)) return rc;
3022
3023 i_setModified(IsModified_MachineData);
3024 mUserData.backup();
3025 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3030{
3031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3034
3035 return S_OK;
3036}
3037
3038HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3039{
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 /* @todo deal with running state change. */
3043 HRESULT rc = i_checkStateDependency(MutableStateDep);
3044 if (FAILED(rc)) return rc;
3045
3046 i_setModified(IsModified_MachineData);
3047 mUserData.backup();
3048 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3054{
3055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3058 return S_OK;
3059}
3060
3061HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3062{
3063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 /* @todo deal with running state change. */
3066 HRESULT rc = i_checkStateDependency(MutableStateDep);
3067 if (FAILED(rc)) return rc;
3068
3069 i_setModified(IsModified_MachineData);
3070 mUserData.backup();
3071 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3072 return S_OK;
3073}
3074
3075HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3076{
3077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3080
3081 return S_OK;
3082}
3083
3084HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3085{
3086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3087
3088 /* Only allow it to be set to true when PoweredOff or Aborted.
3089 (Clearing it is always permitted.) */
3090 if ( aRTCUseUTC
3091 && mData->mRegistered
3092 && ( !i_isSessionMachine()
3093 || ( mData->mMachineState != MachineState_PoweredOff
3094 && mData->mMachineState != MachineState_Teleported
3095 && mData->mMachineState != MachineState_Aborted
3096 )
3097 )
3098 )
3099 return setError(VBOX_E_INVALID_VM_STATE,
3100 tr("The machine is not powered off (state is %s)"),
3101 Global::stringifyMachineState(mData->mMachineState));
3102
3103 i_setModified(IsModified_MachineData);
3104 mUserData.backup();
3105 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3106
3107 return S_OK;
3108}
3109
3110HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3111{
3112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3113
3114 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3115
3116 return S_OK;
3117}
3118
3119HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3120{
3121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 HRESULT rc = i_checkStateDependency(MutableStateDep);
3124 if (FAILED(rc)) return rc;
3125
3126 i_setModified(IsModified_MachineData);
3127 mHWData.backup();
3128 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3129
3130 return S_OK;
3131}
3132
3133HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3134{
3135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3136
3137 *aIOCacheSize = mHWData->mIOCacheSize;
3138
3139 return S_OK;
3140}
3141
3142HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3143{
3144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3145
3146 HRESULT rc = i_checkStateDependency(MutableStateDep);
3147 if (FAILED(rc)) return rc;
3148
3149 i_setModified(IsModified_MachineData);
3150 mHWData.backup();
3151 mHWData->mIOCacheSize = aIOCacheSize;
3152
3153 return S_OK;
3154}
3155
3156
3157/**
3158 * @note Locks objects!
3159 */
3160HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3161 LockType_T aLockType)
3162
3163{
3164 /* check the session state */
3165 SessionState_T state;
3166 HRESULT rc = aSession->COMGETTER(State)(&state);
3167 if (FAILED(rc)) return rc;
3168
3169 if (state != SessionState_Unlocked)
3170 return setError(VBOX_E_INVALID_OBJECT_STATE,
3171 tr("The given session is busy"));
3172
3173 // get the client's IInternalSessionControl interface
3174 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3175 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3176 E_INVALIDARG);
3177
3178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 if (!mData->mRegistered)
3181 return setError(E_UNEXPECTED,
3182 tr("The machine '%s' is not registered"),
3183 mUserData->s.strName.c_str());
3184
3185 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3186
3187 SessionState_T oldState = mData->mSession.mState;
3188 /* Hack: in case the session is closing and there is a progress object
3189 * which allows waiting for the session to be closed, take the opportunity
3190 * and do a limited wait (max. 1 second). This helps a lot when the system
3191 * is busy and thus session closing can take a little while. */
3192 if ( mData->mSession.mState == SessionState_Unlocking
3193 && mData->mSession.mProgress)
3194 {
3195 alock.release();
3196 mData->mSession.mProgress->WaitForCompletion(1000);
3197 alock.acquire();
3198 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3199 }
3200
3201 // try again now
3202 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3203 // (i.e. session machine exists)
3204 && (aLockType == LockType_Shared) // caller wants a shared link to the
3205 // existing session that holds the write lock:
3206 )
3207 {
3208 // OK, share the session... we are now dealing with three processes:
3209 // 1) VBoxSVC (where this code runs);
3210 // 2) process C: the caller's client process (who wants a shared session);
3211 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3212
3213 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3214 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3215 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3216 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3217 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3218
3219 /*
3220 * Release the lock before calling the client process. It's safe here
3221 * since the only thing to do after we get the lock again is to add
3222 * the remote control to the list (which doesn't directly influence
3223 * anything).
3224 */
3225 alock.release();
3226
3227 // get the console of the session holding the write lock (this is a remote call)
3228 ComPtr<IConsole> pConsoleW;
3229 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3230 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3231 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3232 if (FAILED(rc))
3233 // the failure may occur w/o any error info (from RPC), so provide one
3234 return setError(VBOX_E_VM_ERROR,
3235 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3236
3237 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3238
3239 // share the session machine and W's console with the caller's session
3240 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3241 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3242 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3243
3244 if (FAILED(rc))
3245 // the failure may occur w/o any error info (from RPC), so provide one
3246 return setError(VBOX_E_VM_ERROR,
3247 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3248 alock.acquire();
3249
3250 // need to revalidate the state after acquiring the lock again
3251 if (mData->mSession.mState != SessionState_Locked)
3252 {
3253 pSessionControl->Uninitialize();
3254 return setError(VBOX_E_INVALID_SESSION_STATE,
3255 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3256 mUserData->s.strName.c_str());
3257 }
3258
3259 // add the caller's session to the list
3260 mData->mSession.mRemoteControls.push_back(pSessionControl);
3261 }
3262 else if ( mData->mSession.mState == SessionState_Locked
3263 || mData->mSession.mState == SessionState_Unlocking
3264 )
3265 {
3266 // sharing not permitted, or machine still unlocking:
3267 return setError(VBOX_E_INVALID_OBJECT_STATE,
3268 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3269 mUserData->s.strName.c_str());
3270 }
3271 else
3272 {
3273 // machine is not locked: then write-lock the machine (create the session machine)
3274
3275 // must not be busy
3276 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3277
3278 // get the caller's session PID
3279 RTPROCESS pid = NIL_RTPROCESS;
3280 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3281 pSessionControl->GetPID((ULONG*)&pid);
3282 Assert(pid != NIL_RTPROCESS);
3283
3284 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3285
3286 if (fLaunchingVMProcess)
3287 {
3288 if (mData->mSession.mPID == NIL_RTPROCESS)
3289 {
3290 // two or more clients racing for a lock, the one which set the
3291 // session state to Spawning will win, the others will get an
3292 // error as we can't decide here if waiting a little would help
3293 // (only for shared locks this would avoid an error)
3294 return setError(VBOX_E_INVALID_OBJECT_STATE,
3295 tr("The machine '%s' already has a lock request pending"),
3296 mUserData->s.strName.c_str());
3297 }
3298
3299 // this machine is awaiting for a spawning session to be opened:
3300 // then the calling process must be the one that got started by
3301 // LaunchVMProcess()
3302
3303 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3304 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3305
3306#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3307 /* Hardened windows builds spawns three processes when a VM is
3308 launched, the 3rd one is the one that will end up here. */
3309 RTPROCESS ppid;
3310 int rc = RTProcQueryParent(pid, &ppid);
3311 if (RT_SUCCESS(rc))
3312 rc = RTProcQueryParent(ppid, &ppid);
3313 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3314 || rc == VERR_ACCESS_DENIED)
3315 {
3316 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3317 mData->mSession.mPID = pid;
3318 }
3319#endif
3320
3321 if (mData->mSession.mPID != pid)
3322 return setError(E_ACCESSDENIED,
3323 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3324 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3325 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3326 }
3327
3328 // create the mutable SessionMachine from the current machine
3329 ComObjPtr<SessionMachine> sessionMachine;
3330 sessionMachine.createObject();
3331 rc = sessionMachine->init(this);
3332 AssertComRC(rc);
3333
3334 /* NOTE: doing return from this function after this point but
3335 * before the end is forbidden since it may call SessionMachine::uninit()
3336 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3337 * lock while still holding the Machine lock in alock so that a deadlock
3338 * is possible due to the wrong lock order. */
3339
3340 if (SUCCEEDED(rc))
3341 {
3342 /*
3343 * Set the session state to Spawning to protect against subsequent
3344 * attempts to open a session and to unregister the machine after
3345 * we release the lock.
3346 */
3347 SessionState_T origState = mData->mSession.mState;
3348 mData->mSession.mState = SessionState_Spawning;
3349
3350#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3351 /* Get the client token ID to be passed to the client process */
3352 Utf8Str strTokenId;
3353 sessionMachine->i_getTokenId(strTokenId);
3354 Assert(!strTokenId.isEmpty());
3355#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3356 /* Get the client token to be passed to the client process */
3357 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3358 /* The token is now "owned" by pToken, fix refcount */
3359 if (!pToken.isNull())
3360 pToken->Release();
3361#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3362
3363 /*
3364 * Release the lock before calling the client process -- it will call
3365 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3366 * because the state is Spawning, so that LaunchVMProcess() and
3367 * LockMachine() calls will fail. This method, called before we
3368 * acquire the lock again, will fail because of the wrong PID.
3369 *
3370 * Note that mData->mSession.mRemoteControls accessed outside
3371 * the lock may not be modified when state is Spawning, so it's safe.
3372 */
3373 alock.release();
3374
3375 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3376#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3377 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3378#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3379 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3380 /* Now the token is owned by the client process. */
3381 pToken.setNull();
3382#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3383 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3384
3385 /* The failure may occur w/o any error info (from RPC), so provide one */
3386 if (FAILED(rc))
3387 setError(VBOX_E_VM_ERROR,
3388 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3389
3390 if ( SUCCEEDED(rc)
3391 && fLaunchingVMProcess
3392 )
3393 {
3394 /* complete the remote session initialization */
3395
3396 /* get the console from the direct session */
3397 ComPtr<IConsole> console;
3398 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3399 ComAssertComRC(rc);
3400
3401 if (SUCCEEDED(rc) && !console)
3402 {
3403 ComAssert(!!console);
3404 rc = E_FAIL;
3405 }
3406
3407 /* assign machine & console to the remote session */
3408 if (SUCCEEDED(rc))
3409 {
3410 /*
3411 * after LaunchVMProcess(), the first and the only
3412 * entry in remoteControls is that remote session
3413 */
3414 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3415 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3416 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3417
3418 /* The failure may occur w/o any error info (from RPC), so provide one */
3419 if (FAILED(rc))
3420 setError(VBOX_E_VM_ERROR,
3421 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3422 }
3423
3424 if (FAILED(rc))
3425 pSessionControl->Uninitialize();
3426 }
3427
3428 /* acquire the lock again */
3429 alock.acquire();
3430
3431 /* Restore the session state */
3432 mData->mSession.mState = origState;
3433 }
3434
3435 // finalize spawning anyway (this is why we don't return on errors above)
3436 if (fLaunchingVMProcess)
3437 {
3438 /* Note that the progress object is finalized later */
3439 /** @todo Consider checking mData->mSession.mProgress for cancellation
3440 * around here. */
3441
3442 /* We don't reset mSession.mPID here because it is necessary for
3443 * SessionMachine::uninit() to reap the child process later. */
3444
3445 if (FAILED(rc))
3446 {
3447 /* Close the remote session, remove the remote control from the list
3448 * and reset session state to Closed (@note keep the code in sync
3449 * with the relevant part in checkForSpawnFailure()). */
3450
3451 Assert(mData->mSession.mRemoteControls.size() == 1);
3452 if (mData->mSession.mRemoteControls.size() == 1)
3453 {
3454 ErrorInfoKeeper eik;
3455 mData->mSession.mRemoteControls.front()->Uninitialize();
3456 }
3457
3458 mData->mSession.mRemoteControls.clear();
3459 mData->mSession.mState = SessionState_Unlocked;
3460 }
3461 }
3462 else
3463 {
3464 /* memorize PID of the directly opened session */
3465 if (SUCCEEDED(rc))
3466 mData->mSession.mPID = pid;
3467 }
3468
3469 if (SUCCEEDED(rc))
3470 {
3471 /* memorize the direct session control and cache IUnknown for it */
3472 mData->mSession.mDirectControl = pSessionControl;
3473 mData->mSession.mState = SessionState_Locked;
3474 /* associate the SessionMachine with this Machine */
3475 mData->mSession.mMachine = sessionMachine;
3476
3477 /* request an IUnknown pointer early from the remote party for later
3478 * identity checks (it will be internally cached within mDirectControl
3479 * at least on XPCOM) */
3480 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3481 NOREF(unk);
3482 }
3483
3484 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3485 * would break the lock order */
3486 alock.release();
3487
3488 /* uninitialize the created session machine on failure */
3489 if (FAILED(rc))
3490 sessionMachine->uninit();
3491
3492 }
3493
3494 if (SUCCEEDED(rc))
3495 {
3496 /*
3497 * tell the client watcher thread to update the set of
3498 * machines that have open sessions
3499 */
3500 mParent->i_updateClientWatcher();
3501
3502 if (oldState != SessionState_Locked)
3503 /* fire an event */
3504 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3505 }
3506
3507 return rc;
3508}
3509
3510/**
3511 * @note Locks objects!
3512 */
3513HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3514 const com::Utf8Str &aType,
3515 const com::Utf8Str &aEnvironment,
3516 ComPtr<IProgress> &aProgress)
3517{
3518 Utf8Str strFrontend(aType);
3519 /* "emergencystop" doesn't need the session, so skip the checks/interface
3520 * retrieval. This code doesn't quite fit in here, but introducing a
3521 * special API method would be even more effort, and would require explicit
3522 * support by every API client. It's better to hide the feature a bit. */
3523 if (strFrontend != "emergencystop")
3524 CheckComArgNotNull(aSession);
3525
3526 HRESULT rc = S_OK;
3527 if (strFrontend.isEmpty())
3528 {
3529 Bstr bstrFrontend;
3530 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3531 if (FAILED(rc))
3532 return rc;
3533 strFrontend = bstrFrontend;
3534 if (strFrontend.isEmpty())
3535 {
3536 ComPtr<ISystemProperties> systemProperties;
3537 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3538 if (FAILED(rc))
3539 return rc;
3540 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3541 if (FAILED(rc))
3542 return rc;
3543 strFrontend = bstrFrontend;
3544 }
3545 /* paranoia - emergencystop is not a valid default */
3546 if (strFrontend == "emergencystop")
3547 strFrontend = Utf8Str::Empty;
3548 }
3549 /* default frontend: Qt GUI */
3550 if (strFrontend.isEmpty())
3551 strFrontend = "GUI/Qt";
3552
3553 if (strFrontend != "emergencystop")
3554 {
3555 /* check the session state */
3556 SessionState_T state;
3557 rc = aSession->COMGETTER(State)(&state);
3558 if (FAILED(rc))
3559 return rc;
3560
3561 if (state != SessionState_Unlocked)
3562 return setError(VBOX_E_INVALID_OBJECT_STATE,
3563 tr("The given session is busy"));
3564
3565 /* get the IInternalSessionControl interface */
3566 ComPtr<IInternalSessionControl> control(aSession);
3567 ComAssertMsgRet(!control.isNull(),
3568 ("No IInternalSessionControl interface"),
3569 E_INVALIDARG);
3570
3571 /* get the teleporter enable state for the progress object init. */
3572 BOOL fTeleporterEnabled;
3573 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3574 if (FAILED(rc))
3575 return rc;
3576
3577 /* create a progress object */
3578 ComObjPtr<ProgressProxy> progress;
3579 progress.createObject();
3580 rc = progress->init(mParent,
3581 static_cast<IMachine*>(this),
3582 Bstr(tr("Starting VM")).raw(),
3583 TRUE /* aCancelable */,
3584 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3585 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3586 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3587 2 /* uFirstOperationWeight */,
3588 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3589
3590 if (SUCCEEDED(rc))
3591 {
3592 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3593 if (SUCCEEDED(rc))
3594 {
3595 aProgress = progress;
3596
3597 /* signal the client watcher thread */
3598 mParent->i_updateClientWatcher();
3599
3600 /* fire an event */
3601 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3602 }
3603 }
3604 }
3605 else
3606 {
3607 /* no progress object - either instant success or failure */
3608 aProgress = NULL;
3609
3610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3611
3612 if (mData->mSession.mState != SessionState_Locked)
3613 return setError(VBOX_E_INVALID_OBJECT_STATE,
3614 tr("The machine '%s' is not locked by a session"),
3615 mUserData->s.strName.c_str());
3616
3617 /* must have a VM process associated - do not kill normal API clients
3618 * with an open session */
3619 if (!Global::IsOnline(mData->mMachineState))
3620 return setError(VBOX_E_INVALID_OBJECT_STATE,
3621 tr("The machine '%s' does not have a VM process"),
3622 mUserData->s.strName.c_str());
3623
3624 /* forcibly terminate the VM process */
3625 if (mData->mSession.mPID != NIL_RTPROCESS)
3626 RTProcTerminate(mData->mSession.mPID);
3627
3628 /* signal the client watcher thread, as most likely the client has
3629 * been terminated */
3630 mParent->i_updateClientWatcher();
3631 }
3632
3633 return rc;
3634}
3635
3636HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3637{
3638 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3639 return setError(E_INVALIDARG,
3640 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3641 aPosition, SchemaDefs::MaxBootPosition);
3642
3643 if (aDevice == DeviceType_USB)
3644 return setError(E_NOTIMPL,
3645 tr("Booting from USB device is currently not supported"));
3646
3647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3648
3649 HRESULT rc = i_checkStateDependency(MutableStateDep);
3650 if (FAILED(rc)) return rc;
3651
3652 i_setModified(IsModified_MachineData);
3653 mHWData.backup();
3654 mHWData->mBootOrder[aPosition - 1] = aDevice;
3655
3656 return S_OK;
3657}
3658
3659HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3660{
3661 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3662 return setError(E_INVALIDARG,
3663 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3664 aPosition, SchemaDefs::MaxBootPosition);
3665
3666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3667
3668 *aDevice = mHWData->mBootOrder[aPosition - 1];
3669
3670 return S_OK;
3671}
3672
3673HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3674 LONG aControllerPort,
3675 LONG aDevice,
3676 DeviceType_T aType,
3677 const ComPtr<IMedium> &aMedium)
3678{
3679 IMedium *aM = aMedium;
3680 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3681 aName.c_str(), aControllerPort, aDevice, aType, aM));
3682
3683 // request the host lock first, since might be calling Host methods for getting host drives;
3684 // next, protect the media tree all the while we're in here, as well as our member variables
3685 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3686 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3687
3688 HRESULT rc = i_checkStateDependency(MutableStateDep);
3689 if (FAILED(rc)) return rc;
3690
3691 /// @todo NEWMEDIA implicit machine registration
3692 if (!mData->mRegistered)
3693 return setError(VBOX_E_INVALID_OBJECT_STATE,
3694 tr("Cannot attach storage devices to an unregistered machine"));
3695
3696 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3697
3698 /* Check for an existing controller. */
3699 ComObjPtr<StorageController> ctl;
3700 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3701 if (FAILED(rc)) return rc;
3702
3703 StorageControllerType_T ctrlType;
3704 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3705 if (FAILED(rc))
3706 return setError(E_FAIL,
3707 tr("Could not get type of controller '%s'"),
3708 aName.c_str());
3709
3710 bool fSilent = false;
3711 Utf8Str strReconfig;
3712
3713 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3714 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3715 if ( mData->mMachineState == MachineState_Paused
3716 && strReconfig == "1")
3717 fSilent = true;
3718
3719 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3720 bool fHotplug = false;
3721 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3722 fHotplug = true;
3723
3724 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3725 return setError(VBOX_E_INVALID_VM_STATE,
3726 tr("Controller '%s' does not support hotplugging"),
3727 aName.c_str());
3728
3729 // check that the port and device are not out of range
3730 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3731 if (FAILED(rc)) return rc;
3732
3733 /* check if the device slot is already busy */
3734 MediumAttachment *pAttachTemp;
3735 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3736 Bstr(aName).raw(),
3737 aControllerPort,
3738 aDevice)))
3739 {
3740 Medium *pMedium = pAttachTemp->i_getMedium();
3741 if (pMedium)
3742 {
3743 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3744 return setError(VBOX_E_OBJECT_IN_USE,
3745 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3746 pMedium->i_getLocationFull().c_str(),
3747 aControllerPort,
3748 aDevice,
3749 aName.c_str());
3750 }
3751 else
3752 return setError(VBOX_E_OBJECT_IN_USE,
3753 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3754 aControllerPort, aDevice, aName.c_str());
3755 }
3756
3757 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3758 if (aMedium && medium.isNull())
3759 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3760
3761 AutoCaller mediumCaller(medium);
3762 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3763
3764 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3765
3766 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3767 && !medium.isNull()
3768 )
3769 return setError(VBOX_E_OBJECT_IN_USE,
3770 tr("Medium '%s' is already attached to this virtual machine"),
3771 medium->i_getLocationFull().c_str());
3772
3773 if (!medium.isNull())
3774 {
3775 MediumType_T mtype = medium->i_getType();
3776 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3777 // For DVDs it's not written to the config file, so needs no global config
3778 // version bump. For floppies it's a new attribute "type", which is ignored
3779 // by older VirtualBox version, so needs no global config version bump either.
3780 // For hard disks this type is not accepted.
3781 if (mtype == MediumType_MultiAttach)
3782 {
3783 // This type is new with VirtualBox 4.0 and therefore requires settings
3784 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3785 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3786 // two reasons: The medium type is a property of the media registry tree, which
3787 // can reside in the global config file (for pre-4.0 media); we would therefore
3788 // possibly need to bump the global config version. We don't want to do that though
3789 // because that might make downgrading to pre-4.0 impossible.
3790 // As a result, we can only use these two new types if the medium is NOT in the
3791 // global registry:
3792 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3793 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3794 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3795 )
3796 return setError(VBOX_E_INVALID_OBJECT_STATE,
3797 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3798 "to machines that were created with VirtualBox 4.0 or later"),
3799 medium->i_getLocationFull().c_str());
3800 }
3801 }
3802
3803 bool fIndirect = false;
3804 if (!medium.isNull())
3805 fIndirect = medium->i_isReadOnly();
3806 bool associate = true;
3807
3808 do
3809 {
3810 if ( aType == DeviceType_HardDisk
3811 && mMediaData.isBackedUp())
3812 {
3813 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3814
3815 /* check if the medium was attached to the VM before we started
3816 * changing attachments in which case the attachment just needs to
3817 * be restored */
3818 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3819 {
3820 AssertReturn(!fIndirect, E_FAIL);
3821
3822 /* see if it's the same bus/channel/device */
3823 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3824 {
3825 /* the simplest case: restore the whole attachment
3826 * and return, nothing else to do */
3827 mMediaData->mAttachments.push_back(pAttachTemp);
3828
3829 /* Reattach the medium to the VM. */
3830 if (fHotplug || fSilent)
3831 {
3832 mediumLock.release();
3833 treeLock.release();
3834 alock.release();
3835
3836 MediumLockList *pMediumLockList(new MediumLockList());
3837
3838 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3839 true /* fMediumLockWrite */,
3840 NULL,
3841 *pMediumLockList);
3842 alock.acquire();
3843 if (FAILED(rc))
3844 delete pMediumLockList;
3845 else
3846 {
3847 mData->mSession.mLockedMedia.Unlock();
3848 alock.release();
3849 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3850 mData->mSession.mLockedMedia.Lock();
3851 alock.acquire();
3852 }
3853 alock.release();
3854
3855 if (SUCCEEDED(rc))
3856 {
3857 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3858 /* Remove lock list in case of error. */
3859 if (FAILED(rc))
3860 {
3861 mData->mSession.mLockedMedia.Unlock();
3862 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3863 mData->mSession.mLockedMedia.Lock();
3864 }
3865 }
3866 }
3867
3868 return S_OK;
3869 }
3870
3871 /* bus/channel/device differ; we need a new attachment object,
3872 * but don't try to associate it again */
3873 associate = false;
3874 break;
3875 }
3876 }
3877
3878 /* go further only if the attachment is to be indirect */
3879 if (!fIndirect)
3880 break;
3881
3882 /* perform the so called smart attachment logic for indirect
3883 * attachments. Note that smart attachment is only applicable to base
3884 * hard disks. */
3885
3886 if (medium->i_getParent().isNull())
3887 {
3888 /* first, investigate the backup copy of the current hard disk
3889 * attachments to make it possible to re-attach existing diffs to
3890 * another device slot w/o losing their contents */
3891 if (mMediaData.isBackedUp())
3892 {
3893 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3894
3895 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3896 uint32_t foundLevel = 0;
3897
3898 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3899 {
3900 uint32_t level = 0;
3901 MediumAttachment *pAttach = *it;
3902 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3903 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3904 if (pMedium.isNull())
3905 continue;
3906
3907 if (pMedium->i_getBase(&level) == medium)
3908 {
3909 /* skip the hard disk if its currently attached (we
3910 * cannot attach the same hard disk twice) */
3911 if (i_findAttachment(mMediaData->mAttachments,
3912 pMedium))
3913 continue;
3914
3915 /* matched device, channel and bus (i.e. attached to the
3916 * same place) will win and immediately stop the search;
3917 * otherwise the attachment that has the youngest
3918 * descendant of medium will be used
3919 */
3920 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3921 {
3922 /* the simplest case: restore the whole attachment
3923 * and return, nothing else to do */
3924 mMediaData->mAttachments.push_back(*it);
3925
3926 /* Reattach the medium to the VM. */
3927 if (fHotplug || fSilent)
3928 {
3929 mediumLock.release();
3930 treeLock.release();
3931 alock.release();
3932
3933 MediumLockList *pMediumLockList(new MediumLockList());
3934
3935 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3936 true /* fMediumLockWrite */,
3937 NULL,
3938 *pMediumLockList);
3939 alock.acquire();
3940 if (FAILED(rc))
3941 delete pMediumLockList;
3942 else
3943 {
3944 mData->mSession.mLockedMedia.Unlock();
3945 alock.release();
3946 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3947 mData->mSession.mLockedMedia.Lock();
3948 alock.acquire();
3949 }
3950 alock.release();
3951
3952 if (SUCCEEDED(rc))
3953 {
3954 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3955 /* Remove lock list in case of error. */
3956 if (FAILED(rc))
3957 {
3958 mData->mSession.mLockedMedia.Unlock();
3959 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3960 mData->mSession.mLockedMedia.Lock();
3961 }
3962 }
3963 }
3964
3965 return S_OK;
3966 }
3967 else if ( foundIt == oldAtts.end()
3968 || level > foundLevel /* prefer younger */
3969 )
3970 {
3971 foundIt = it;
3972 foundLevel = level;
3973 }
3974 }
3975 }
3976
3977 if (foundIt != oldAtts.end())
3978 {
3979 /* use the previously attached hard disk */
3980 medium = (*foundIt)->i_getMedium();
3981 mediumCaller.attach(medium);
3982 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3983 mediumLock.attach(medium);
3984 /* not implicit, doesn't require association with this VM */
3985 fIndirect = false;
3986 associate = false;
3987 /* go right to the MediumAttachment creation */
3988 break;
3989 }
3990 }
3991
3992 /* must give up the medium lock and medium tree lock as below we
3993 * go over snapshots, which needs a lock with higher lock order. */
3994 mediumLock.release();
3995 treeLock.release();
3996
3997 /* then, search through snapshots for the best diff in the given
3998 * hard disk's chain to base the new diff on */
3999
4000 ComObjPtr<Medium> base;
4001 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4002 while (snap)
4003 {
4004 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4005
4006 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4007
4008 MediumAttachment *pAttachFound = NULL;
4009 uint32_t foundLevel = 0;
4010
4011 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4012 {
4013 MediumAttachment *pAttach = *it;
4014 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4015 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4016 if (pMedium.isNull())
4017 continue;
4018
4019 uint32_t level = 0;
4020 if (pMedium->i_getBase(&level) == medium)
4021 {
4022 /* matched device, channel and bus (i.e. attached to the
4023 * same place) will win and immediately stop the search;
4024 * otherwise the attachment that has the youngest
4025 * descendant of medium will be used
4026 */
4027 if ( pAttach->i_getDevice() == aDevice
4028 && pAttach->i_getPort() == aControllerPort
4029 && pAttach->i_getControllerName() == aName
4030 )
4031 {
4032 pAttachFound = pAttach;
4033 break;
4034 }
4035 else if ( !pAttachFound
4036 || level > foundLevel /* prefer younger */
4037 )
4038 {
4039 pAttachFound = pAttach;
4040 foundLevel = level;
4041 }
4042 }
4043 }
4044
4045 if (pAttachFound)
4046 {
4047 base = pAttachFound->i_getMedium();
4048 break;
4049 }
4050
4051 snap = snap->i_getParent();
4052 }
4053
4054 /* re-lock medium tree and the medium, as we need it below */
4055 treeLock.acquire();
4056 mediumLock.acquire();
4057
4058 /* found a suitable diff, use it as a base */
4059 if (!base.isNull())
4060 {
4061 medium = base;
4062 mediumCaller.attach(medium);
4063 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4064 mediumLock.attach(medium);
4065 }
4066 }
4067
4068 Utf8Str strFullSnapshotFolder;
4069 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4070
4071 ComObjPtr<Medium> diff;
4072 diff.createObject();
4073 // store this diff in the same registry as the parent
4074 Guid uuidRegistryParent;
4075 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4076 {
4077 // parent image has no registry: this can happen if we're attaching a new immutable
4078 // image that has not yet been attached (medium then points to the base and we're
4079 // creating the diff image for the immutable, and the parent is not yet registered);
4080 // put the parent in the machine registry then
4081 mediumLock.release();
4082 treeLock.release();
4083 alock.release();
4084 i_addMediumToRegistry(medium);
4085 alock.acquire();
4086 treeLock.acquire();
4087 mediumLock.acquire();
4088 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4089 }
4090 rc = diff->init(mParent,
4091 medium->i_getPreferredDiffFormat(),
4092 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4093 uuidRegistryParent);
4094 if (FAILED(rc)) return rc;
4095
4096 /* Apply the normal locking logic to the entire chain. */
4097 MediumLockList *pMediumLockList(new MediumLockList());
4098 mediumLock.release();
4099 treeLock.release();
4100 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4101 true /* fMediumLockWrite */,
4102 medium,
4103 *pMediumLockList);
4104 treeLock.acquire();
4105 mediumLock.acquire();
4106 if (SUCCEEDED(rc))
4107 {
4108 mediumLock.release();
4109 treeLock.release();
4110 rc = pMediumLockList->Lock();
4111 treeLock.acquire();
4112 mediumLock.acquire();
4113 if (FAILED(rc))
4114 setError(rc,
4115 tr("Could not lock medium when creating diff '%s'"),
4116 diff->i_getLocationFull().c_str());
4117 else
4118 {
4119 /* will release the lock before the potentially lengthy
4120 * operation, so protect with the special state */
4121 MachineState_T oldState = mData->mMachineState;
4122 i_setMachineState(MachineState_SettingUp);
4123
4124 mediumLock.release();
4125 treeLock.release();
4126 alock.release();
4127
4128 rc = medium->i_createDiffStorage(diff,
4129 MediumVariant_Standard,
4130 pMediumLockList,
4131 NULL /* aProgress */,
4132 true /* aWait */);
4133
4134 alock.acquire();
4135 treeLock.acquire();
4136 mediumLock.acquire();
4137
4138 i_setMachineState(oldState);
4139 }
4140 }
4141
4142 /* Unlock the media and free the associated memory. */
4143 delete pMediumLockList;
4144
4145 if (FAILED(rc)) return rc;
4146
4147 /* use the created diff for the actual attachment */
4148 medium = diff;
4149 mediumCaller.attach(medium);
4150 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4151 mediumLock.attach(medium);
4152 }
4153 while (0);
4154
4155 ComObjPtr<MediumAttachment> attachment;
4156 attachment.createObject();
4157 rc = attachment->init(this,
4158 medium,
4159 aName,
4160 aControllerPort,
4161 aDevice,
4162 aType,
4163 fIndirect,
4164 false /* fPassthrough */,
4165 false /* fTempEject */,
4166 false /* fNonRotational */,
4167 false /* fDiscard */,
4168 fHotplug /* fHotPluggable */,
4169 Utf8Str::Empty);
4170 if (FAILED(rc)) return rc;
4171
4172 if (associate && !medium.isNull())
4173 {
4174 // as the last step, associate the medium to the VM
4175 rc = medium->i_addBackReference(mData->mUuid);
4176 // here we can fail because of Deleting, or being in process of creating a Diff
4177 if (FAILED(rc)) return rc;
4178
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182 i_addMediumToRegistry(medium);
4183 alock.acquire();
4184 treeLock.acquire();
4185 mediumLock.acquire();
4186 }
4187
4188 /* success: finally remember the attachment */
4189 i_setModified(IsModified_Storage);
4190 mMediaData.backup();
4191 mMediaData->mAttachments.push_back(attachment);
4192
4193 mediumLock.release();
4194 treeLock.release();
4195 alock.release();
4196
4197 if (fHotplug || fSilent)
4198 {
4199 if (!medium.isNull())
4200 {
4201 MediumLockList *pMediumLockList(new MediumLockList());
4202
4203 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4204 true /* fMediumLockWrite */,
4205 NULL,
4206 *pMediumLockList);
4207 alock.acquire();
4208 if (FAILED(rc))
4209 delete pMediumLockList;
4210 else
4211 {
4212 mData->mSession.mLockedMedia.Unlock();
4213 alock.release();
4214 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4215 mData->mSession.mLockedMedia.Lock();
4216 alock.acquire();
4217 }
4218 alock.release();
4219 }
4220
4221 if (SUCCEEDED(rc))
4222 {
4223 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4224 /* Remove lock list in case of error. */
4225 if (FAILED(rc))
4226 {
4227 mData->mSession.mLockedMedia.Unlock();
4228 mData->mSession.mLockedMedia.Remove(attachment);
4229 mData->mSession.mLockedMedia.Lock();
4230 }
4231 }
4232 }
4233
4234 mParent->i_saveModifiedRegistries();
4235
4236 return rc;
4237}
4238
4239HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4240 LONG aDevice)
4241{
4242 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4243 aName.c_str(), aControllerPort, aDevice));
4244
4245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4246
4247 HRESULT rc = i_checkStateDependency(MutableStateDep);
4248 if (FAILED(rc)) return rc;
4249
4250 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4251
4252 /* Check for an existing controller. */
4253 ComObjPtr<StorageController> ctl;
4254 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4255 if (FAILED(rc)) return rc;
4256
4257 StorageControllerType_T ctrlType;
4258 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4259 if (FAILED(rc))
4260 return setError(E_FAIL,
4261 tr("Could not get type of controller '%s'"),
4262 aName.c_str());
4263
4264 bool fSilent = false;
4265 Utf8Str strReconfig;
4266
4267 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4268 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4269 if ( mData->mMachineState == MachineState_Paused
4270 && strReconfig == "1")
4271 fSilent = true;
4272
4273 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4274 bool fHotplug = false;
4275 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4276 fHotplug = true;
4277
4278 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4279 return setError(VBOX_E_INVALID_VM_STATE,
4280 tr("Controller '%s' does not support hotplugging"),
4281 aName.c_str());
4282
4283 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4284 Bstr(aName).raw(),
4285 aControllerPort,
4286 aDevice);
4287 if (!pAttach)
4288 return setError(VBOX_E_OBJECT_NOT_FOUND,
4289 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4290 aDevice, aControllerPort, aName.c_str());
4291
4292 if (fHotplug && !pAttach->i_getHotPluggable())
4293 return setError(VBOX_E_NOT_SUPPORTED,
4294 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4295 aDevice, aControllerPort, aName.c_str());
4296
4297 /*
4298 * The VM has to detach the device before we delete any implicit diffs.
4299 * If this fails we can roll back without loosing data.
4300 */
4301 if (fHotplug || fSilent)
4302 {
4303 alock.release();
4304 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4305 alock.acquire();
4306 }
4307 if (FAILED(rc)) return rc;
4308
4309 /* If we are here everything went well and we can delete the implicit now. */
4310 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4311
4312 alock.release();
4313
4314 mParent->i_saveModifiedRegistries();
4315
4316 return rc;
4317}
4318
4319HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4320 LONG aDevice, BOOL aPassthrough)
4321{
4322 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4323 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4324
4325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4326
4327 HRESULT rc = i_checkStateDependency(MutableStateDep);
4328 if (FAILED(rc)) return rc;
4329
4330 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4331
4332 if (Global::IsOnlineOrTransient(mData->mMachineState))
4333 return setError(VBOX_E_INVALID_VM_STATE,
4334 tr("Invalid machine state: %s"),
4335 Global::stringifyMachineState(mData->mMachineState));
4336
4337 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4338 Bstr(aName).raw(),
4339 aControllerPort,
4340 aDevice);
4341 if (!pAttach)
4342 return setError(VBOX_E_OBJECT_NOT_FOUND,
4343 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4344 aDevice, aControllerPort, aName.c_str());
4345
4346
4347 i_setModified(IsModified_Storage);
4348 mMediaData.backup();
4349
4350 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4351
4352 if (pAttach->i_getType() != DeviceType_DVD)
4353 return setError(E_INVALIDARG,
4354 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4355 aDevice, aControllerPort, aName.c_str());
4356 pAttach->i_updatePassthrough(!!aPassthrough);
4357
4358 return S_OK;
4359}
4360
4361HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4362 LONG aDevice, BOOL aTemporaryEject)
4363{
4364
4365 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4366 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4367
4368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4369
4370 HRESULT rc = i_checkStateDependency(MutableStateDep);
4371 if (FAILED(rc)) return rc;
4372
4373 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4374 Bstr(aName).raw(),
4375 aControllerPort,
4376 aDevice);
4377 if (!pAttach)
4378 return setError(VBOX_E_OBJECT_NOT_FOUND,
4379 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4380 aDevice, aControllerPort, aName.c_str());
4381
4382
4383 i_setModified(IsModified_Storage);
4384 mMediaData.backup();
4385
4386 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4387
4388 if (pAttach->i_getType() != DeviceType_DVD)
4389 return setError(E_INVALIDARG,
4390 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4391 aDevice, aControllerPort, aName.c_str());
4392 pAttach->i_updateTempEject(!!aTemporaryEject);
4393
4394 return S_OK;
4395}
4396
4397HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4398 LONG aDevice, BOOL aNonRotational)
4399{
4400
4401 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4402 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4403
4404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4405
4406 HRESULT rc = i_checkStateDependency(MutableStateDep);
4407 if (FAILED(rc)) return rc;
4408
4409 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4410
4411 if (Global::IsOnlineOrTransient(mData->mMachineState))
4412 return setError(VBOX_E_INVALID_VM_STATE,
4413 tr("Invalid machine state: %s"),
4414 Global::stringifyMachineState(mData->mMachineState));
4415
4416 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4417 Bstr(aName).raw(),
4418 aControllerPort,
4419 aDevice);
4420 if (!pAttach)
4421 return setError(VBOX_E_OBJECT_NOT_FOUND,
4422 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4423 aDevice, aControllerPort, aName.c_str());
4424
4425
4426 i_setModified(IsModified_Storage);
4427 mMediaData.backup();
4428
4429 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4430
4431 if (pAttach->i_getType() != DeviceType_HardDisk)
4432 return setError(E_INVALIDARG,
4433 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"),
4434 aDevice, aControllerPort, aName.c_str());
4435 pAttach->i_updateNonRotational(!!aNonRotational);
4436
4437 return S_OK;
4438}
4439
4440HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4441 LONG aDevice, BOOL aDiscard)
4442{
4443
4444 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4445 aName.c_str(), aControllerPort, aDevice, aDiscard));
4446
4447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4448
4449 HRESULT rc = i_checkStateDependency(MutableStateDep);
4450 if (FAILED(rc)) return rc;
4451
4452 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4453
4454 if (Global::IsOnlineOrTransient(mData->mMachineState))
4455 return setError(VBOX_E_INVALID_VM_STATE,
4456 tr("Invalid machine state: %s"),
4457 Global::stringifyMachineState(mData->mMachineState));
4458
4459 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4460 Bstr(aName).raw(),
4461 aControllerPort,
4462 aDevice);
4463 if (!pAttach)
4464 return setError(VBOX_E_OBJECT_NOT_FOUND,
4465 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4466 aDevice, aControllerPort, aName.c_str());
4467
4468
4469 i_setModified(IsModified_Storage);
4470 mMediaData.backup();
4471
4472 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4473
4474 if (pAttach->i_getType() != DeviceType_HardDisk)
4475 return setError(E_INVALIDARG,
4476 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"),
4477 aDevice, aControllerPort, aName.c_str());
4478 pAttach->i_updateDiscard(!!aDiscard);
4479
4480 return S_OK;
4481}
4482
4483HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4484 LONG aDevice, BOOL aHotPluggable)
4485{
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4488
4489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 HRESULT rc = i_checkStateDependency(MutableStateDep);
4492 if (FAILED(rc)) return rc;
4493
4494 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4495
4496 if (Global::IsOnlineOrTransient(mData->mMachineState))
4497 return setError(VBOX_E_INVALID_VM_STATE,
4498 tr("Invalid machine state: %s"),
4499 Global::stringifyMachineState(mData->mMachineState));
4500
4501 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4502 Bstr(aName).raw(),
4503 aControllerPort,
4504 aDevice);
4505 if (!pAttach)
4506 return setError(VBOX_E_OBJECT_NOT_FOUND,
4507 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4508 aDevice, aControllerPort, aName.c_str());
4509
4510 /* Check for an existing controller. */
4511 ComObjPtr<StorageController> ctl;
4512 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4513 if (FAILED(rc)) return rc;
4514
4515 StorageControllerType_T ctrlType;
4516 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4517 if (FAILED(rc))
4518 return setError(E_FAIL,
4519 tr("Could not get type of controller '%s'"),
4520 aName.c_str());
4521
4522 if (!i_isControllerHotplugCapable(ctrlType))
4523 return setError(VBOX_E_NOT_SUPPORTED,
4524 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4525 aName.c_str());
4526
4527 i_setModified(IsModified_Storage);
4528 mMediaData.backup();
4529
4530 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4531
4532 if (pAttach->i_getType() == DeviceType_Floppy)
4533 return setError(E_INVALIDARG,
4534 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"),
4535 aDevice, aControllerPort, aName.c_str());
4536 pAttach->i_updateHotPluggable(!!aHotPluggable);
4537
4538 return S_OK;
4539}
4540
4541HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4542 LONG aDevice)
4543{
4544 int rc = S_OK;
4545 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4546 aName.c_str(), aControllerPort, aDevice));
4547
4548 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4549
4550 return rc;
4551}
4552
4553HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4554 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4555{
4556 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4557 aName.c_str(), aControllerPort, aDevice));
4558
4559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4560
4561 HRESULT rc = i_checkStateDependency(MutableStateDep);
4562 if (FAILED(rc)) return rc;
4563
4564 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4565
4566 if (Global::IsOnlineOrTransient(mData->mMachineState))
4567 return setError(VBOX_E_INVALID_VM_STATE,
4568 tr("Invalid machine state: %s"),
4569 Global::stringifyMachineState(mData->mMachineState));
4570
4571 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4572 Bstr(aName).raw(),
4573 aControllerPort,
4574 aDevice);
4575 if (!pAttach)
4576 return setError(VBOX_E_OBJECT_NOT_FOUND,
4577 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4578 aDevice, aControllerPort, aName.c_str());
4579
4580
4581 i_setModified(IsModified_Storage);
4582 mMediaData.backup();
4583
4584 IBandwidthGroup *iB = aBandwidthGroup;
4585 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4586 if (aBandwidthGroup && group.isNull())
4587 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4588
4589 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4590
4591 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4592 if (strBandwidthGroupOld.isNotEmpty())
4593 {
4594 /* Get the bandwidth group object and release it - this must not fail. */
4595 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4596 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4597 Assert(SUCCEEDED(rc));
4598
4599 pBandwidthGroupOld->i_release();
4600 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4601 }
4602
4603 if (!group.isNull())
4604 {
4605 group->i_reference();
4606 pAttach->i_updateBandwidthGroup(group->i_getName());
4607 }
4608
4609 return S_OK;
4610}
4611
4612HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4613 LONG aControllerPort,
4614 LONG aDevice,
4615 DeviceType_T aType)
4616{
4617 HRESULT rc = S_OK;
4618
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4620 aName.c_str(), aControllerPort, aDevice, aType));
4621
4622 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4623
4624 return rc;
4625}
4626
4627
4628HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4629 LONG aControllerPort,
4630 LONG aDevice,
4631 BOOL aForce)
4632{
4633 int rc = S_OK;
4634 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4635 aName.c_str(), aControllerPort, aForce));
4636
4637 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4638
4639 return rc;
4640}
4641
4642HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4643 LONG aControllerPort,
4644 LONG aDevice,
4645 const ComPtr<IMedium> &aMedium,
4646 BOOL aForce)
4647{
4648 int rc = S_OK;
4649 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4650 aName.c_str(), aControllerPort, aDevice, aForce));
4651
4652 // request the host lock first, since might be calling Host methods for getting host drives;
4653 // next, protect the media tree all the while we're in here, as well as our member variables
4654 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4655 this->lockHandle(),
4656 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4657
4658 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4659 Bstr(aName).raw(),
4660 aControllerPort,
4661 aDevice);
4662 if (pAttach.isNull())
4663 return setError(VBOX_E_OBJECT_NOT_FOUND,
4664 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4665 aDevice, aControllerPort, aName.c_str());
4666
4667 /* Remember previously mounted medium. The medium before taking the
4668 * backup is not necessarily the same thing. */
4669 ComObjPtr<Medium> oldmedium;
4670 oldmedium = pAttach->i_getMedium();
4671
4672 IMedium *iM = aMedium;
4673 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4674 if (aMedium && pMedium.isNull())
4675 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4676
4677 AutoCaller mediumCaller(pMedium);
4678 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4679
4680 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4681 if (pMedium)
4682 {
4683 DeviceType_T mediumType = pAttach->i_getType();
4684 switch (mediumType)
4685 {
4686 case DeviceType_DVD:
4687 case DeviceType_Floppy:
4688 break;
4689
4690 default:
4691 return setError(VBOX_E_INVALID_OBJECT_STATE,
4692 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4693 aControllerPort,
4694 aDevice,
4695 aName.c_str());
4696 }
4697 }
4698
4699 i_setModified(IsModified_Storage);
4700 mMediaData.backup();
4701
4702 {
4703 // The backup operation makes the pAttach reference point to the
4704 // old settings. Re-get the correct reference.
4705 pAttach = i_findAttachment(mMediaData->mAttachments,
4706 Bstr(aName).raw(),
4707 aControllerPort,
4708 aDevice);
4709 if (!oldmedium.isNull())
4710 oldmedium->i_removeBackReference(mData->mUuid);
4711 if (!pMedium.isNull())
4712 {
4713 pMedium->i_addBackReference(mData->mUuid);
4714
4715 mediumLock.release();
4716 multiLock.release();
4717 i_addMediumToRegistry(pMedium);
4718 multiLock.acquire();
4719 mediumLock.acquire();
4720 }
4721
4722 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4723 pAttach->i_updateMedium(pMedium);
4724 }
4725
4726 i_setModified(IsModified_Storage);
4727
4728 mediumLock.release();
4729 multiLock.release();
4730 rc = i_onMediumChange(pAttach, aForce);
4731 multiLock.acquire();
4732 mediumLock.acquire();
4733
4734 /* On error roll back this change only. */
4735 if (FAILED(rc))
4736 {
4737 if (!pMedium.isNull())
4738 pMedium->i_removeBackReference(mData->mUuid);
4739 pAttach = i_findAttachment(mMediaData->mAttachments,
4740 Bstr(aName).raw(),
4741 aControllerPort,
4742 aDevice);
4743 /* If the attachment is gone in the meantime, bail out. */
4744 if (pAttach.isNull())
4745 return rc;
4746 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4747 if (!oldmedium.isNull())
4748 oldmedium->i_addBackReference(mData->mUuid);
4749 pAttach->i_updateMedium(oldmedium);
4750 }
4751
4752 mediumLock.release();
4753 multiLock.release();
4754
4755 mParent->i_saveModifiedRegistries();
4756
4757 return rc;
4758}
4759HRESULT Machine::getMedium(const com::Utf8Str &aName,
4760 LONG aControllerPort,
4761 LONG aDevice,
4762 ComPtr<IMedium> &aMedium)
4763{
4764 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4765 aName.c_str(), aControllerPort, aDevice));
4766
4767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
4769 aMedium = NULL;
4770
4771 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4772 Bstr(aName).raw(),
4773 aControllerPort,
4774 aDevice);
4775 if (pAttach.isNull())
4776 return setError(VBOX_E_OBJECT_NOT_FOUND,
4777 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4778 aDevice, aControllerPort, aName.c_str());
4779
4780 aMedium = pAttach->i_getMedium();
4781
4782 return S_OK;
4783}
4784
4785HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4786{
4787
4788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4789
4790 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4791
4792 return S_OK;
4793}
4794
4795HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4796{
4797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4798
4799 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4800
4801 return S_OK;
4802}
4803
4804HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4805{
4806 /* Do not assert if slot is out of range, just return the advertised
4807 status. testdriver/vbox.py triggers this in logVmInfo. */
4808 if (aSlot >= mNetworkAdapters.size())
4809 return setError(E_INVALIDARG,
4810 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4811 aSlot, mNetworkAdapters.size());
4812
4813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4814
4815 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4816
4817 return S_OK;
4818}
4819
4820HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4821{
4822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4823
4824 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4825 size_t i = 0;
4826 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4827 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4828 ++it, ++i)
4829 aKeys[i] = it->first;
4830
4831 return S_OK;
4832}
4833
4834 /**
4835 * @note Locks this object for reading.
4836 */
4837HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4838 com::Utf8Str &aValue)
4839{
4840 /* start with nothing found */
4841 aValue = "";
4842
4843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4844
4845 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4846 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4847 // found:
4848 aValue = it->second; // source is a Utf8Str
4849
4850 /* return the result to caller (may be empty) */
4851 return S_OK;
4852}
4853
4854 /**
4855 * @note Locks mParent for writing + this object for writing.
4856 */
4857HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4858{
4859 Utf8Str strOldValue; // empty
4860
4861 // locking note: we only hold the read lock briefly to look up the old value,
4862 // then release it and call the onExtraCanChange callbacks. There is a small
4863 // chance of a race insofar as the callback might be called twice if two callers
4864 // change the same key at the same time, but that's a much better solution
4865 // than the deadlock we had here before. The actual changing of the extradata
4866 // is then performed under the write lock and race-free.
4867
4868 // look up the old value first; if nothing has changed then we need not do anything
4869 {
4870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4871 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4872 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4873 strOldValue = it->second;
4874 }
4875
4876 bool fChanged;
4877 if ((fChanged = (strOldValue != aValue)))
4878 {
4879 // ask for permission from all listeners outside the locks;
4880 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4881 // lock to copy the list of callbacks to invoke
4882 Bstr error;
4883 Bstr bstrValue(aValue);
4884
4885 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4886 {
4887 const char *sep = error.isEmpty() ? "" : ": ";
4888 CBSTR err = error.raw();
4889 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4890 sep, err));
4891 return setError(E_ACCESSDENIED,
4892 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4893 aKey.c_str(),
4894 aValue.c_str(),
4895 sep,
4896 err);
4897 }
4898
4899 // data is changing and change not vetoed: then write it out under the lock
4900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4901
4902 if (i_isSnapshotMachine())
4903 {
4904 HRESULT rc = i_checkStateDependency(MutableStateDep);
4905 if (FAILED(rc)) return rc;
4906 }
4907
4908 if (aValue.isEmpty())
4909 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4910 else
4911 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4912 // creates a new key if needed
4913
4914 bool fNeedsGlobalSaveSettings = false;
4915 // This saving of settings is tricky: there is no "old state" for the
4916 // extradata items at all (unlike all other settings), so the old/new
4917 // settings comparison would give a wrong result!
4918 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4919
4920 if (fNeedsGlobalSaveSettings)
4921 {
4922 // save the global settings; for that we should hold only the VirtualBox lock
4923 alock.release();
4924 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4925 mParent->i_saveSettings();
4926 }
4927 }
4928
4929 // fire notification outside the lock
4930 if (fChanged)
4931 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4932
4933 return S_OK;
4934}
4935
4936HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4937{
4938 aProgress = NULL;
4939 NOREF(aSettingsFilePath);
4940 ReturnComNotImplemented();
4941}
4942
4943HRESULT Machine::saveSettings()
4944{
4945 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4946
4947 /* when there was auto-conversion, we want to save the file even if
4948 * the VM is saved */
4949 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4950 if (FAILED(rc)) return rc;
4951
4952 /* the settings file path may never be null */
4953 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4954
4955 /* save all VM data excluding snapshots */
4956 bool fNeedsGlobalSaveSettings = false;
4957 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4958 mlock.release();
4959
4960 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4961 {
4962 // save the global settings; for that we should hold only the VirtualBox lock
4963 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4964 rc = mParent->i_saveSettings();
4965 }
4966
4967 return rc;
4968}
4969
4970
4971HRESULT Machine::discardSettings()
4972{
4973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4974
4975 HRESULT rc = i_checkStateDependency(MutableStateDep);
4976 if (FAILED(rc)) return rc;
4977
4978 /*
4979 * during this rollback, the session will be notified if data has
4980 * been actually changed
4981 */
4982 i_rollback(true /* aNotify */);
4983
4984 return S_OK;
4985}
4986
4987/** @note Locks objects! */
4988HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4989 std::vector<ComPtr<IMedium> > &aMedia)
4990{
4991 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4992 AutoLimitedCaller autoCaller(this);
4993 AssertComRCReturnRC(autoCaller.rc());
4994
4995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4996
4997 Guid id(i_getId());
4998
4999 if (mData->mSession.mState != SessionState_Unlocked)
5000 return setError(VBOX_E_INVALID_OBJECT_STATE,
5001 tr("Cannot unregister the machine '%s' while it is locked"),
5002 mUserData->s.strName.c_str());
5003
5004 // wait for state dependents to drop to zero
5005 i_ensureNoStateDependencies();
5006
5007 if (!mData->mAccessible)
5008 {
5009 // inaccessible maschines can only be unregistered; uninitialize ourselves
5010 // here because currently there may be no unregistered that are inaccessible
5011 // (this state combination is not supported). Note releasing the caller and
5012 // leaving the lock before calling uninit()
5013 alock.release();
5014 autoCaller.release();
5015
5016 uninit();
5017
5018 mParent->i_unregisterMachine(this, id);
5019 // calls VirtualBox::i_saveSettings()
5020
5021 return S_OK;
5022 }
5023
5024 HRESULT rc = S_OK;
5025
5026 // discard saved state
5027 if (mData->mMachineState == MachineState_Saved)
5028 {
5029 // add the saved state file to the list of files the caller should delete
5030 Assert(!mSSData->strStateFilePath.isEmpty());
5031 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5032
5033 mSSData->strStateFilePath.setNull();
5034
5035 // unconditionally set the machine state to powered off, we now
5036 // know no session has locked the machine
5037 mData->mMachineState = MachineState_PoweredOff;
5038 }
5039
5040 size_t cSnapshots = 0;
5041 if (mData->mFirstSnapshot)
5042 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5043 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5044 // fail now before we start detaching media
5045 return setError(VBOX_E_INVALID_OBJECT_STATE,
5046 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5047 mUserData->s.strName.c_str(), cSnapshots);
5048
5049 // This list collects the medium objects from all medium attachments
5050 // which we will detach from the machine and its snapshots, in a specific
5051 // order which allows for closing all media without getting "media in use"
5052 // errors, simply by going through the list from the front to the back:
5053 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5054 // and must be closed before the parent media from the snapshots, or closing the parents
5055 // will fail because they still have children);
5056 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5057 // the root ("first") snapshot of the machine.
5058 MediaList llMedia;
5059
5060 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5061 && mMediaData->mAttachments.size()
5062 )
5063 {
5064 // we have media attachments: detach them all and add the Medium objects to our list
5065 if (aCleanupMode != CleanupMode_UnregisterOnly)
5066 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5067 else
5068 return setError(VBOX_E_INVALID_OBJECT_STATE,
5069 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5070 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5071 }
5072
5073 if (cSnapshots)
5074 {
5075 // autoCleanup must be true here, or we would have failed above
5076
5077 // add the media from the medium attachments of the snapshots to llMedia
5078 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5079 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5080 // into the children first
5081
5082 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5083 MachineState_T oldState = mData->mMachineState;
5084 mData->mMachineState = MachineState_DeletingSnapshot;
5085
5086 // make a copy of the first snapshot so the refcount does not drop to 0
5087 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5088 // because of the AutoCaller voodoo)
5089 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5090
5091 // GO!
5092 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5093
5094 mData->mMachineState = oldState;
5095 }
5096
5097 if (FAILED(rc))
5098 {
5099 i_rollbackMedia();
5100 return rc;
5101 }
5102
5103 // commit all the media changes made above
5104 i_commitMedia();
5105
5106 mData->mRegistered = false;
5107
5108 // machine lock no longer needed
5109 alock.release();
5110
5111 // return media to caller
5112 size_t i = 0;
5113 aMedia.resize(llMedia.size());
5114 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5115 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5116
5117 mParent->i_unregisterMachine(this, id);
5118 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5119
5120 return S_OK;
5121}
5122
5123struct Machine::DeleteTask
5124{
5125 ComObjPtr<Machine> pMachine;
5126 RTCList<ComPtr<IMedium> > llMediums;
5127 StringsList llFilesToDelete;
5128 ComObjPtr<Progress> pProgress;
5129};
5130
5131HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5132{
5133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5134
5135 HRESULT rc = i_checkStateDependency(MutableStateDep);
5136 if (FAILED(rc)) return rc;
5137
5138 if (mData->mRegistered)
5139 return setError(VBOX_E_INVALID_VM_STATE,
5140 tr("Cannot delete settings of a registered machine"));
5141
5142 DeleteTask *pTask = new DeleteTask;
5143 pTask->pMachine = this;
5144
5145 // collect files to delete
5146 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5147
5148 for (size_t i = 0; i < aMedia.size(); ++i)
5149 {
5150 IMedium *pIMedium(aMedia[i]);
5151 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5152 if (pMedium.isNull())
5153 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5154 SafeArray<BSTR> ids;
5155 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5156 if (FAILED(rc)) return rc;
5157 /* At this point the medium should not have any back references
5158 * anymore. If it has it is attached to another VM and *must* not
5159 * deleted. */
5160 if (ids.size() < 1)
5161 pTask->llMediums.append(pMedium);
5162 }
5163 if (mData->pMachineConfigFile->fileExists())
5164 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5165
5166 pTask->pProgress.createObject();
5167 pTask->pProgress->init(i_getVirtualBox(),
5168 static_cast<IMachine*>(this) /* aInitiator */,
5169 Bstr(tr("Deleting files")).raw(),
5170 true /* fCancellable */,
5171 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5172 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5173
5174 int vrc = RTThreadCreate(NULL,
5175 Machine::deleteThread,
5176 (void*)pTask,
5177 0,
5178 RTTHREADTYPE_MAIN_WORKER,
5179 0,
5180 "MachineDelete");
5181
5182 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5183
5184 if (RT_FAILURE(vrc))
5185 {
5186 delete pTask;
5187 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5188 }
5189
5190 LogFlowFuncLeave();
5191
5192 return S_OK;
5193}
5194
5195/**
5196 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5197 * calls Machine::deleteTaskWorker() on the actual machine object.
5198 * @param Thread
5199 * @param pvUser
5200 * @return
5201 */
5202/*static*/
5203DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5204{
5205 LogFlowFuncEnter();
5206
5207 DeleteTask *pTask = (DeleteTask*)pvUser;
5208 Assert(pTask);
5209 Assert(pTask->pMachine);
5210 Assert(pTask->pProgress);
5211
5212 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5213 pTask->pProgress->i_notifyComplete(rc);
5214
5215 delete pTask;
5216
5217 LogFlowFuncLeave();
5218
5219 NOREF(Thread);
5220
5221 return VINF_SUCCESS;
5222}
5223
5224/**
5225 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5226 * @param task
5227 * @return
5228 */
5229HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5230{
5231 AutoCaller autoCaller(this);
5232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5233
5234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5235
5236 HRESULT rc = S_OK;
5237
5238 try
5239 {
5240 ULONG uLogHistoryCount = 3;
5241 ComPtr<ISystemProperties> systemProperties;
5242 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5243 if (FAILED(rc)) throw rc;
5244
5245 if (!systemProperties.isNull())
5246 {
5247 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5248 if (FAILED(rc)) throw rc;
5249 }
5250
5251 MachineState_T oldState = mData->mMachineState;
5252 i_setMachineState(MachineState_SettingUp);
5253 alock.release();
5254 for (size_t i = 0; i < task.llMediums.size(); ++i)
5255 {
5256 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5257 {
5258 AutoCaller mac(pMedium);
5259 if (FAILED(mac.rc())) throw mac.rc();
5260 Utf8Str strLocation = pMedium->i_getLocationFull();
5261 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5262 if (FAILED(rc)) throw rc;
5263 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5264 }
5265 ComPtr<IProgress> pProgress2;
5266 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5267 if (FAILED(rc)) throw rc;
5268 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5269 if (FAILED(rc)) throw rc;
5270 /* Check the result of the asynchronous process. */
5271 LONG iRc;
5272 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5273 if (FAILED(rc)) throw rc;
5274 /* If the thread of the progress object has an error, then
5275 * retrieve the error info from there, or it'll be lost. */
5276 if (FAILED(iRc))
5277 throw setError(ProgressErrorInfo(pProgress2));
5278
5279 /* Close the medium, deliberately without checking the return
5280- * code, and without leaving any trace in the error info, as
5281- * a failure here is a very minor issue, which shouldn't happen
5282- * as above we even managed to delete the medium. */
5283 {
5284 ErrorInfoKeeper eik;
5285 pMedium->Close();
5286 }
5287 }
5288 i_setMachineState(oldState);
5289 alock.acquire();
5290
5291 // delete the files pushed on the task list by Machine::Delete()
5292 // (this includes saved states of the machine and snapshots and
5293 // medium storage files from the IMedium list passed in, and the
5294 // machine XML file)
5295 StringsList::const_iterator it = task.llFilesToDelete.begin();
5296 while (it != task.llFilesToDelete.end())
5297 {
5298 const Utf8Str &strFile = *it;
5299 LogFunc(("Deleting file %s\n", strFile.c_str()));
5300 int vrc = RTFileDelete(strFile.c_str());
5301 if (RT_FAILURE(vrc))
5302 throw setError(VBOX_E_IPRT_ERROR,
5303 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5304
5305 ++it;
5306 if (it == task.llFilesToDelete.end())
5307 {
5308 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5309 if (FAILED(rc)) throw rc;
5310 break;
5311 }
5312
5313 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5314 if (FAILED(rc)) throw rc;
5315 }
5316
5317 /* delete the settings only when the file actually exists */
5318 if (mData->pMachineConfigFile->fileExists())
5319 {
5320 /* Delete any backup or uncommitted XML files. Ignore failures.
5321 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5322 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5323 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5324 RTFileDelete(otherXml.c_str());
5325 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5326 RTFileDelete(otherXml.c_str());
5327
5328 /* delete the Logs folder, nothing important should be left
5329 * there (we don't check for errors because the user might have
5330 * some private files there that we don't want to delete) */
5331 Utf8Str logFolder;
5332 getLogFolder(logFolder);
5333 Assert(logFolder.length());
5334 if (RTDirExists(logFolder.c_str()))
5335 {
5336 /* Delete all VBox.log[.N] files from the Logs folder
5337 * (this must be in sync with the rotation logic in
5338 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5339 * files that may have been created by the GUI. */
5340 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5341 logFolder.c_str(), RTPATH_DELIMITER);
5342 RTFileDelete(log.c_str());
5343 log = Utf8StrFmt("%s%cVBox.png",
5344 logFolder.c_str(), RTPATH_DELIMITER);
5345 RTFileDelete(log.c_str());
5346 for (int i = uLogHistoryCount; i > 0; i--)
5347 {
5348 log = Utf8StrFmt("%s%cVBox.log.%d",
5349 logFolder.c_str(), RTPATH_DELIMITER, i);
5350 RTFileDelete(log.c_str());
5351 log = Utf8StrFmt("%s%cVBox.png.%d",
5352 logFolder.c_str(), RTPATH_DELIMITER, i);
5353 RTFileDelete(log.c_str());
5354 }
5355
5356 RTDirRemove(logFolder.c_str());
5357 }
5358
5359 /* delete the Snapshots folder, nothing important should be left
5360 * there (we don't check for errors because the user might have
5361 * some private files there that we don't want to delete) */
5362 Utf8Str strFullSnapshotFolder;
5363 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5364 Assert(!strFullSnapshotFolder.isEmpty());
5365 if (RTDirExists(strFullSnapshotFolder.c_str()))
5366 RTDirRemove(strFullSnapshotFolder.c_str());
5367
5368 // delete the directory that contains the settings file, but only
5369 // if it matches the VM name
5370 Utf8Str settingsDir;
5371 if (i_isInOwnDir(&settingsDir))
5372 RTDirRemove(settingsDir.c_str());
5373 }
5374
5375 alock.release();
5376
5377 mParent->i_saveModifiedRegistries();
5378 }
5379 catch (HRESULT aRC) { rc = aRC; }
5380
5381 return rc;
5382}
5383
5384HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5385{
5386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 ComObjPtr<Snapshot> pSnapshot;
5389 HRESULT rc;
5390
5391 if (aNameOrId.isEmpty())
5392 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5393 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5394 else
5395 {
5396 Guid uuid(aNameOrId);
5397 if (uuid.isValid())
5398 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5399 else
5400 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5401 }
5402 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5403
5404 return rc;
5405}
5406
5407HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5408{
5409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5410
5411 HRESULT rc = i_checkStateDependency(MutableStateDep);
5412 if (FAILED(rc)) return rc;
5413
5414 ComObjPtr<SharedFolder> sharedFolder;
5415 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5416 if (SUCCEEDED(rc))
5417 return setError(VBOX_E_OBJECT_IN_USE,
5418 tr("Shared folder named '%s' already exists"),
5419 aName.c_str());
5420
5421 sharedFolder.createObject();
5422 rc = sharedFolder->init(i_getMachine(),
5423 aName,
5424 aHostPath,
5425 !!aWritable,
5426 !!aAutomount,
5427 true /* fFailOnError */);
5428 if (FAILED(rc)) return rc;
5429
5430 i_setModified(IsModified_SharedFolders);
5431 mHWData.backup();
5432 mHWData->mSharedFolders.push_back(sharedFolder);
5433
5434 /* inform the direct session if any */
5435 alock.release();
5436 i_onSharedFolderChange();
5437
5438 return S_OK;
5439}
5440
5441HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5442{
5443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5444
5445 HRESULT rc = i_checkStateDependency(MutableStateDep);
5446 if (FAILED(rc)) return rc;
5447
5448 ComObjPtr<SharedFolder> sharedFolder;
5449 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5450 if (FAILED(rc)) return rc;
5451
5452 i_setModified(IsModified_SharedFolders);
5453 mHWData.backup();
5454 mHWData->mSharedFolders.remove(sharedFolder);
5455
5456 /* inform the direct session if any */
5457 alock.release();
5458 i_onSharedFolderChange();
5459
5460 return S_OK;
5461}
5462
5463HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5464{
5465 /* start with No */
5466 *aCanShow = FALSE;
5467
5468 ComPtr<IInternalSessionControl> directControl;
5469 {
5470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5471
5472 if (mData->mSession.mState != SessionState_Locked)
5473 return setError(VBOX_E_INVALID_VM_STATE,
5474 tr("Machine is not locked for session (session state: %s)"),
5475 Global::stringifySessionState(mData->mSession.mState));
5476
5477 directControl = mData->mSession.mDirectControl;
5478 }
5479
5480 /* ignore calls made after #OnSessionEnd() is called */
5481 if (!directControl)
5482 return S_OK;
5483
5484 LONG64 dummy;
5485 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5486}
5487
5488HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5489{
5490 ComPtr<IInternalSessionControl> directControl;
5491 {
5492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 if (mData->mSession.mState != SessionState_Locked)
5495 return setError(E_FAIL,
5496 tr("Machine is not locked for session (session state: %s)"),
5497 Global::stringifySessionState(mData->mSession.mState));
5498
5499 directControl = mData->mSession.mDirectControl;
5500 }
5501
5502 /* ignore calls made after #OnSessionEnd() is called */
5503 if (!directControl)
5504 return S_OK;
5505
5506 BOOL dummy;
5507 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5508}
5509
5510#ifdef VBOX_WITH_GUEST_PROPS
5511/**
5512 * Look up a guest property in VBoxSVC's internal structures.
5513 */
5514HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5515 com::Utf8Str &aValue,
5516 LONG64 *aTimestamp,
5517 com::Utf8Str &aFlags) const
5518{
5519 using namespace guestProp;
5520
5521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5522 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5523
5524 if (it != mHWData->mGuestProperties.end())
5525 {
5526 char szFlags[MAX_FLAGS_LEN + 1];
5527 aValue = it->second.strValue;
5528 *aTimestamp = it->second.mTimestamp;
5529 writeFlags(it->second.mFlags, szFlags);
5530 aFlags = Utf8Str(szFlags);
5531 }
5532
5533 return S_OK;
5534}
5535
5536/**
5537 * Query the VM that a guest property belongs to for the property.
5538 * @returns E_ACCESSDENIED if the VM process is not available or not
5539 * currently handling queries and the lookup should then be done in
5540 * VBoxSVC.
5541 */
5542HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5543 com::Utf8Str &aValue,
5544 LONG64 *aTimestamp,
5545 com::Utf8Str &aFlags) const
5546{
5547 HRESULT rc = S_OK;
5548 BSTR bValue = NULL;
5549 BSTR bFlags = NULL;
5550
5551 ComPtr<IInternalSessionControl> directControl;
5552 directControl = mData->mSession.mDirectControl;
5553
5554 /* fail if we were called after #OnSessionEnd() is called. This is a
5555 * silly race condition. */
5556
5557 /** @todo This code is bothering API clients (like python script clients) with
5558 * the AccessGuestProperty call, creating unncessary IPC. Need to
5559 * have a way of figuring out which kind of direct session it is... */
5560 if (!directControl)
5561 rc = E_ACCESSDENIED;
5562 else
5563 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5564 0 /* accessMode */,
5565 &bValue, aTimestamp, &bFlags);
5566
5567 aValue = bValue;
5568 aFlags = bFlags;
5569
5570 return rc;
5571}
5572#endif // VBOX_WITH_GUEST_PROPS
5573
5574HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5575 com::Utf8Str &aValue,
5576 LONG64 *aTimestamp,
5577 com::Utf8Str &aFlags)
5578{
5579#ifndef VBOX_WITH_GUEST_PROPS
5580 ReturnComNotImplemented();
5581#else // VBOX_WITH_GUEST_PROPS
5582
5583 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5584
5585 if (rc == E_ACCESSDENIED)
5586 /* The VM is not running or the service is not (yet) accessible */
5587 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5588 return rc;
5589#endif // VBOX_WITH_GUEST_PROPS
5590}
5591
5592HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5593{
5594 LONG64 dummyTimestamp;
5595 com::Utf8Str dummyFlags;
5596 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5597 return rc;
5598
5599}
5600HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5601{
5602 com::Utf8Str dummyFlags;
5603 com::Utf8Str dummyValue;
5604 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5605 return rc;
5606}
5607
5608#ifdef VBOX_WITH_GUEST_PROPS
5609/**
5610 * Set a guest property in VBoxSVC's internal structures.
5611 */
5612HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5613 const com::Utf8Str &aFlags, bool fDelete)
5614{
5615 using namespace guestProp;
5616
5617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5618 HRESULT rc = S_OK;
5619
5620 rc = i_checkStateDependency(MutableStateDep);
5621 if (FAILED(rc)) return rc;
5622
5623 try
5624 {
5625 uint32_t fFlags = NILFLAG;
5626 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5627 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5628
5629 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5630 if (it == mHWData->mGuestProperties.end())
5631 {
5632 if (!fDelete)
5633 {
5634 i_setModified(IsModified_MachineData);
5635 mHWData.backupEx();
5636
5637 RTTIMESPEC time;
5638 HWData::GuestProperty prop;
5639 prop.strValue = Bstr(aValue).raw();
5640 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5641 prop.mFlags = fFlags;
5642 mHWData->mGuestProperties[aName] = prop;
5643 }
5644 }
5645 else
5646 {
5647 if (it->second.mFlags & (RDONLYHOST))
5648 {
5649 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5650 }
5651 else
5652 {
5653 i_setModified(IsModified_MachineData);
5654 mHWData.backupEx();
5655
5656 /* The backupEx() operation invalidates our iterator,
5657 * so get a new one. */
5658 it = mHWData->mGuestProperties.find(aName);
5659 Assert(it != mHWData->mGuestProperties.end());
5660
5661 if (!fDelete)
5662 {
5663 RTTIMESPEC time;
5664 it->second.strValue = aValue;
5665 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5666 it->second.mFlags = fFlags;
5667 }
5668 else
5669 mHWData->mGuestProperties.erase(it);
5670 }
5671 }
5672
5673 if ( SUCCEEDED(rc)
5674 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5675 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5676 RTSTR_MAX,
5677 aName.c_str(),
5678 RTSTR_MAX,
5679 NULL)
5680 )
5681 )
5682 {
5683 alock.release();
5684
5685 mParent->i_onGuestPropertyChange(mData->mUuid,
5686 Bstr(aName).raw(),
5687 Bstr(aValue).raw(),
5688 Bstr(aFlags).raw());
5689 }
5690 }
5691 catch (std::bad_alloc &)
5692 {
5693 rc = E_OUTOFMEMORY;
5694 }
5695
5696 return rc;
5697}
5698
5699/**
5700 * Set a property on the VM that that property belongs to.
5701 * @returns E_ACCESSDENIED if the VM process is not available or not
5702 * currently handling queries and the setting should then be done in
5703 * VBoxSVC.
5704 */
5705HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5706 const com::Utf8Str &aFlags, bool fDelete)
5707{
5708 HRESULT rc;
5709
5710 try
5711 {
5712 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5713
5714 BSTR dummy = NULL; /* will not be changed (setter) */
5715 LONG64 dummy64;
5716 if (!directControl)
5717 rc = E_ACCESSDENIED;
5718 else
5719 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5720 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5721 fDelete? 2: 1 /* accessMode */,
5722 &dummy, &dummy64, &dummy);
5723 }
5724 catch (std::bad_alloc &)
5725 {
5726 rc = E_OUTOFMEMORY;
5727 }
5728
5729 return rc;
5730}
5731#endif // VBOX_WITH_GUEST_PROPS
5732
5733HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5734 const com::Utf8Str &aFlags)
5735{
5736#ifndef VBOX_WITH_GUEST_PROPS
5737 ReturnComNotImplemented();
5738#else // VBOX_WITH_GUEST_PROPS
5739 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5740 if (rc == E_ACCESSDENIED)
5741 /* The VM is not running or the service is not (yet) accessible */
5742 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5743 return rc;
5744#endif // VBOX_WITH_GUEST_PROPS
5745}
5746
5747HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5748{
5749 return setGuestProperty(aProperty, aValue, "");
5750}
5751
5752HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5753{
5754#ifndef VBOX_WITH_GUEST_PROPS
5755 ReturnComNotImplemented();
5756#else // VBOX_WITH_GUEST_PROPS
5757 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5758 if (rc == E_ACCESSDENIED)
5759 /* The VM is not running or the service is not (yet) accessible */
5760 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5761 return rc;
5762#endif // VBOX_WITH_GUEST_PROPS
5763}
5764
5765#ifdef VBOX_WITH_GUEST_PROPS
5766/**
5767 * Enumerate the guest properties in VBoxSVC's internal structures.
5768 */
5769HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5770 std::vector<com::Utf8Str> &aNames,
5771 std::vector<com::Utf8Str> &aValues,
5772 std::vector<LONG64> &aTimestamps,
5773 std::vector<com::Utf8Str> &aFlags)
5774{
5775 using namespace guestProp;
5776
5777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5778 Utf8Str strPatterns(aPatterns);
5779
5780 HWData::GuestPropertyMap propMap;
5781
5782 /*
5783 * Look for matching patterns and build up a list.
5784 */
5785 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5786 while (it != mHWData->mGuestProperties.end())
5787 {
5788 if ( strPatterns.isEmpty()
5789 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5790 RTSTR_MAX,
5791 it->first.c_str(),
5792 RTSTR_MAX,
5793 NULL)
5794 )
5795 propMap.insert(*it);
5796 it++;
5797 }
5798
5799 alock.release();
5800
5801 /*
5802 * And build up the arrays for returning the property information.
5803 */
5804 size_t cEntries = propMap.size();
5805
5806 aNames.resize(cEntries);
5807 aValues.resize(cEntries);
5808 aTimestamps.resize(cEntries);
5809 aFlags.resize(cEntries);
5810
5811 char szFlags[MAX_FLAGS_LEN + 1];
5812 size_t i= 0;
5813 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5814 {
5815 aNames[i] = it->first;
5816 aValues[i] = it->second.strValue;
5817 aTimestamps[i] = it->second.mTimestamp;
5818 writeFlags(it->second.mFlags, szFlags);
5819 aFlags[i] = Utf8Str(szFlags);
5820 }
5821
5822 return S_OK;
5823}
5824
5825/**
5826 * Enumerate the properties managed by a VM.
5827 * @returns E_ACCESSDENIED if the VM process is not available or not
5828 * currently handling queries and the setting should then be done in
5829 * VBoxSVC.
5830 */
5831HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5832 std::vector<com::Utf8Str> &aNames,
5833 std::vector<com::Utf8Str> &aValues,
5834 std::vector<LONG64> &aTimestamps,
5835 std::vector<com::Utf8Str> &aFlags)
5836{
5837 HRESULT rc;
5838 ComPtr<IInternalSessionControl> directControl;
5839 directControl = mData->mSession.mDirectControl;
5840
5841
5842 com::SafeArray<BSTR> bNames;
5843 com::SafeArray<BSTR> bValues;
5844 com::SafeArray<LONG64> bTimestamps;
5845 com::SafeArray<BSTR> bFlags;
5846
5847 if (!directControl)
5848 rc = E_ACCESSDENIED;
5849 else
5850 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5851 ComSafeArrayAsOutParam(bNames),
5852 ComSafeArrayAsOutParam(bValues),
5853 ComSafeArrayAsOutParam(bTimestamps),
5854 ComSafeArrayAsOutParam(bFlags));
5855 size_t i;
5856 aNames.resize(bNames.size());
5857 for (i = 0; i < bNames.size(); ++i)
5858 aNames[i] = Utf8Str(bNames[i]);
5859 aValues.resize(bValues.size());
5860 for (i = 0; i < bValues.size(); ++i)
5861 aValues[i] = Utf8Str(bValues[i]);
5862 aTimestamps.resize(bTimestamps.size());
5863 for (i = 0; i < bTimestamps.size(); ++i)
5864 aTimestamps[i] = bTimestamps[i];
5865 aFlags.resize(bFlags.size());
5866 for (i = 0; i < bFlags.size(); ++i)
5867 aFlags[i] = Utf8Str(bFlags[i]);
5868
5869 return rc;
5870}
5871#endif // VBOX_WITH_GUEST_PROPS
5872HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5873 std::vector<com::Utf8Str> &aNames,
5874 std::vector<com::Utf8Str> &aValues,
5875 std::vector<LONG64> &aTimestamps,
5876 std::vector<com::Utf8Str> &aFlags)
5877{
5878#ifndef VBOX_WITH_GUEST_PROPS
5879 ReturnComNotImplemented();
5880#else // VBOX_WITH_GUEST_PROPS
5881
5882 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5883
5884 if (rc == E_ACCESSDENIED)
5885 /* The VM is not running or the service is not (yet) accessible */
5886 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5887 return rc;
5888#endif // VBOX_WITH_GUEST_PROPS
5889}
5890
5891HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5892 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5893{
5894 MediaData::AttachmentList atts;
5895
5896 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5897 if (FAILED(rc)) return rc;
5898
5899 size_t i = 0;
5900 aMediumAttachments.resize(atts.size());
5901 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5902 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5903
5904 return S_OK;
5905}
5906
5907HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5908 LONG aControllerPort,
5909 LONG aDevice,
5910 ComPtr<IMediumAttachment> &aAttachment)
5911{
5912 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5913 aName.c_str(), aControllerPort, aDevice));
5914
5915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5916
5917 aAttachment = NULL;
5918
5919 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5920 Bstr(aName).raw(),
5921 aControllerPort,
5922 aDevice);
5923 if (pAttach.isNull())
5924 return setError(VBOX_E_OBJECT_NOT_FOUND,
5925 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5926 aDevice, aControllerPort, aName.c_str());
5927
5928 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5929
5930 return S_OK;
5931}
5932
5933
5934HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5935 StorageBus_T aConnectionType,
5936 ComPtr<IStorageController> &aController)
5937{
5938 if ( (aConnectionType <= StorageBus_Null)
5939 || (aConnectionType > StorageBus_USB))
5940 return setError(E_INVALIDARG,
5941 tr("Invalid connection type: %d"),
5942 aConnectionType);
5943
5944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5945
5946 HRESULT rc = i_checkStateDependency(MutableStateDep);
5947 if (FAILED(rc)) return rc;
5948
5949 /* try to find one with the name first. */
5950 ComObjPtr<StorageController> ctrl;
5951
5952 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5953 if (SUCCEEDED(rc))
5954 return setError(VBOX_E_OBJECT_IN_USE,
5955 tr("Storage controller named '%s' already exists"),
5956 aName.c_str());
5957
5958 ctrl.createObject();
5959
5960 /* get a new instance number for the storage controller */
5961 ULONG ulInstance = 0;
5962 bool fBootable = true;
5963 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5964 it != mStorageControllers->end();
5965 ++it)
5966 {
5967 if ((*it)->i_getStorageBus() == aConnectionType)
5968 {
5969 ULONG ulCurInst = (*it)->i_getInstance();
5970
5971 if (ulCurInst >= ulInstance)
5972 ulInstance = ulCurInst + 1;
5973
5974 /* Only one controller of each type can be marked as bootable. */
5975 if ((*it)->i_getBootable())
5976 fBootable = false;
5977 }
5978 }
5979
5980 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5981 if (FAILED(rc)) return rc;
5982
5983 i_setModified(IsModified_Storage);
5984 mStorageControllers.backup();
5985 mStorageControllers->push_back(ctrl);
5986
5987 ctrl.queryInterfaceTo(aController.asOutParam());
5988
5989 /* inform the direct session if any */
5990 alock.release();
5991 i_onStorageControllerChange();
5992
5993 return S_OK;
5994}
5995
5996HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5997 ComPtr<IStorageController> &aStorageController)
5998{
5999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6000
6001 ComObjPtr<StorageController> ctrl;
6002
6003 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6004 if (SUCCEEDED(rc))
6005 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6006
6007 return rc;
6008}
6009
6010HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6011 ComPtr<IStorageController> &aStorageController)
6012{
6013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6014
6015 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6016 it != mStorageControllers->end();
6017 ++it)
6018 {
6019 if ((*it)->i_getInstance() == aInstance)
6020 {
6021 (*it).queryInterfaceTo(aStorageController.asOutParam());
6022 return S_OK;
6023 }
6024 }
6025
6026 return setError(VBOX_E_OBJECT_NOT_FOUND,
6027 tr("Could not find a storage controller with instance number '%lu'"),
6028 aInstance);
6029}
6030
6031HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6032{
6033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 HRESULT rc = i_checkStateDependency(MutableStateDep);
6036 if (FAILED(rc)) return rc;
6037
6038 ComObjPtr<StorageController> ctrl;
6039
6040 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6041 if (SUCCEEDED(rc))
6042 {
6043 /* Ensure that only one controller of each type is marked as bootable. */
6044 if (aBootable == TRUE)
6045 {
6046 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6047 it != mStorageControllers->end();
6048 ++it)
6049 {
6050 ComObjPtr<StorageController> aCtrl = (*it);
6051
6052 if ( (aCtrl->i_getName() != aName)
6053 && aCtrl->i_getBootable() == TRUE
6054 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6055 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6056 {
6057 aCtrl->i_setBootable(FALSE);
6058 break;
6059 }
6060 }
6061 }
6062
6063 if (SUCCEEDED(rc))
6064 {
6065 ctrl->i_setBootable(aBootable);
6066 i_setModified(IsModified_Storage);
6067 }
6068 }
6069
6070 if (SUCCEEDED(rc))
6071 {
6072 /* inform the direct session if any */
6073 alock.release();
6074 i_onStorageControllerChange();
6075 }
6076
6077 return rc;
6078}
6079
6080HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6081{
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 HRESULT rc = i_checkStateDependency(MutableStateDep);
6085 if (FAILED(rc)) return rc;
6086
6087 ComObjPtr<StorageController> ctrl;
6088 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6089 if (FAILED(rc)) return rc;
6090
6091 {
6092 /* find all attached devices to the appropriate storage controller and detach them all */
6093 // make a temporary list because detachDevice invalidates iterators into
6094 // mMediaData->mAttachments
6095 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6096
6097 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6098 it != llAttachments2.end();
6099 ++it)
6100 {
6101 MediumAttachment *pAttachTemp = *it;
6102
6103 AutoCaller localAutoCaller(pAttachTemp);
6104 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6105
6106 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6107
6108 if (pAttachTemp->i_getControllerName() == aName)
6109 {
6110 rc = i_detachDevice(pAttachTemp, alock, NULL);
6111 if (FAILED(rc)) return rc;
6112 }
6113 }
6114 }
6115
6116 /* We can remove it now. */
6117 i_setModified(IsModified_Storage);
6118 mStorageControllers.backup();
6119
6120 ctrl->i_unshare();
6121
6122 mStorageControllers->remove(ctrl);
6123
6124 /* inform the direct session if any */
6125 alock.release();
6126 i_onStorageControllerChange();
6127
6128 return S_OK;
6129}
6130
6131HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6132 ComPtr<IUSBController> &aController)
6133{
6134 if ( (aType <= USBControllerType_Null)
6135 || (aType >= USBControllerType_Last))
6136 return setError(E_INVALIDARG,
6137 tr("Invalid USB controller type: %d"),
6138 aType);
6139
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 /* try to find one with the same type first. */
6146 ComObjPtr<USBController> ctrl;
6147
6148 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6149 if (SUCCEEDED(rc))
6150 return setError(VBOX_E_OBJECT_IN_USE,
6151 tr("USB controller named '%s' already exists"),
6152 aName.c_str());
6153
6154 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6155 ULONG maxInstances;
6156 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6157 if (FAILED(rc))
6158 return rc;
6159
6160 ULONG cInstances = i_getUSBControllerCountByType(aType);
6161 if (cInstances >= maxInstances)
6162 return setError(E_INVALIDARG,
6163 tr("Too many USB controllers of this type"));
6164
6165 ctrl.createObject();
6166
6167 rc = ctrl->init(this, aName, aType);
6168 if (FAILED(rc)) return rc;
6169
6170 i_setModified(IsModified_USB);
6171 mUSBControllers.backup();
6172 mUSBControllers->push_back(ctrl);
6173
6174 ctrl.queryInterfaceTo(aController.asOutParam());
6175
6176 /* inform the direct session if any */
6177 alock.release();
6178 i_onUSBControllerChange();
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6184{
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 ComObjPtr<USBController> ctrl;
6188
6189 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6190 if (SUCCEEDED(rc))
6191 ctrl.queryInterfaceTo(aController.asOutParam());
6192
6193 return rc;
6194}
6195
6196HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6197 ULONG *aControllers)
6198{
6199 if ( (aType <= USBControllerType_Null)
6200 || (aType >= USBControllerType_Last))
6201 return setError(E_INVALIDARG,
6202 tr("Invalid USB controller type: %d"),
6203 aType);
6204
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 ComObjPtr<USBController> ctrl;
6208
6209 *aControllers = i_getUSBControllerCountByType(aType);
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6215{
6216
6217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6218
6219 HRESULT rc = i_checkStateDependency(MutableStateDep);
6220 if (FAILED(rc)) return rc;
6221
6222 ComObjPtr<USBController> ctrl;
6223 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6224 if (FAILED(rc)) return rc;
6225
6226 i_setModified(IsModified_USB);
6227 mUSBControllers.backup();
6228
6229 ctrl->i_unshare();
6230
6231 mUSBControllers->remove(ctrl);
6232
6233 /* inform the direct session if any */
6234 alock.release();
6235 i_onUSBControllerChange();
6236
6237 return S_OK;
6238}
6239
6240HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6241 ULONG *aOriginX,
6242 ULONG *aOriginY,
6243 ULONG *aWidth,
6244 ULONG *aHeight,
6245 BOOL *aEnabled)
6246{
6247 uint32_t u32OriginX= 0;
6248 uint32_t u32OriginY= 0;
6249 uint32_t u32Width = 0;
6250 uint32_t u32Height = 0;
6251 uint16_t u16Flags = 0;
6252
6253 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6254 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6255 if (RT_FAILURE(vrc))
6256 {
6257#ifdef RT_OS_WINDOWS
6258 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6259 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6260 * So just assign fEnable to TRUE again.
6261 * The right fix would be to change GUI API wrappers to make sure that parameters
6262 * are changed only if API succeeds.
6263 */
6264 *aEnabled = TRUE;
6265#endif
6266 return setError(VBOX_E_IPRT_ERROR,
6267 tr("Saved guest size is not available (%Rrc)"),
6268 vrc);
6269 }
6270
6271 *aOriginX = u32OriginX;
6272 *aOriginY = u32OriginY;
6273 *aWidth = u32Width;
6274 *aHeight = u32Height;
6275 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6276
6277 return S_OK;
6278}
6279
6280HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6281{
6282 if (aScreenId != 0)
6283 return E_NOTIMPL;
6284
6285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6286
6287 uint8_t *pu8Data = NULL;
6288 uint32_t cbData = 0;
6289 uint32_t u32Width = 0;
6290 uint32_t u32Height = 0;
6291
6292 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6293
6294 if (RT_FAILURE(vrc))
6295 return setError(VBOX_E_IPRT_ERROR,
6296 tr("Saved screenshot data is not available (%Rrc)"),
6297 vrc);
6298
6299 *aSize = cbData;
6300 *aWidth = u32Width;
6301 *aHeight = u32Height;
6302
6303 freeSavedDisplayScreenshot(pu8Data);
6304
6305 return S_OK;
6306}
6307
6308HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6309 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6310{
6311 if (aScreenId != 0)
6312 return E_NOTIMPL;
6313
6314 if ( aBitmapFormat != BitmapFormat_BGR0
6315 && aBitmapFormat != BitmapFormat_BGRA
6316 && aBitmapFormat != BitmapFormat_RGBA
6317 && aBitmapFormat != BitmapFormat_PNG)
6318 return setError(E_NOTIMPL,
6319 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6320
6321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 uint8_t *pu8Data = NULL;
6324 uint32_t cbData = 0;
6325 uint32_t u32Width = 0;
6326 uint32_t u32Height = 0;
6327
6328 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6329
6330 if (RT_FAILURE(vrc))
6331 return setError(VBOX_E_IPRT_ERROR,
6332 tr("Saved thumbnail data is not available (%Rrc)"),
6333 vrc);
6334
6335 HRESULT hr = S_OK;
6336
6337 *aWidth = u32Width;
6338 *aHeight = u32Height;
6339
6340 if (cbData > 0)
6341 {
6342 /* Convert pixels to the format expected by the API caller. */
6343 if (aBitmapFormat == BitmapFormat_BGR0)
6344 {
6345 /* [0] B, [1] G, [2] R, [3] 0. */
6346 aData.resize(cbData);
6347 memcpy(&aData.front(), pu8Data, cbData);
6348 }
6349 else if (aBitmapFormat == BitmapFormat_BGRA)
6350 {
6351 /* [0] B, [1] G, [2] R, [3] A. */
6352 aData.resize(cbData);
6353 for (uint32_t i = 0; i < cbData; i += 4)
6354 {
6355 aData[i] = pu8Data[i];
6356 aData[i + 1] = pu8Data[i + 1];
6357 aData[i + 2] = pu8Data[i + 2];
6358 aData[i + 3] = 0xff;
6359 }
6360 }
6361 else if (aBitmapFormat == BitmapFormat_RGBA)
6362 {
6363 /* [0] R, [1] G, [2] B, [3] A. */
6364 aData.resize(cbData);
6365 for (uint32_t i = 0; i < cbData; i += 4)
6366 {
6367 aData[i] = pu8Data[i + 2];
6368 aData[i + 1] = pu8Data[i + 1];
6369 aData[i + 2] = pu8Data[i];
6370 aData[i + 3] = 0xff;
6371 }
6372 }
6373 else if (aBitmapFormat == BitmapFormat_PNG)
6374 {
6375 uint8_t *pu8PNG = NULL;
6376 uint32_t cbPNG = 0;
6377 uint32_t cxPNG = 0;
6378 uint32_t cyPNG = 0;
6379
6380 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6381
6382 if (RT_SUCCESS(vrc))
6383 {
6384 aData.resize(cbPNG);
6385 if (cbPNG)
6386 memcpy(&aData.front(), pu8PNG, cbPNG);
6387 }
6388 else
6389 hr = setError(VBOX_E_IPRT_ERROR,
6390 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6391 vrc);
6392
6393 RTMemFree(pu8PNG);
6394 }
6395 }
6396
6397 freeSavedDisplayScreenshot(pu8Data);
6398
6399 return hr;
6400}
6401
6402HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6403{
6404 if (aScreenId != 0)
6405 return E_NOTIMPL;
6406
6407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6408
6409 uint8_t *pu8Data = NULL;
6410 uint32_t cbData = 0;
6411 uint32_t u32Width = 0;
6412 uint32_t u32Height = 0;
6413
6414 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6415
6416 if (RT_FAILURE(vrc))
6417 return setError(VBOX_E_IPRT_ERROR,
6418 tr("Saved screenshot data is not available (%Rrc)"),
6419 vrc);
6420
6421 *aSize = cbData;
6422 *aWidth = u32Width;
6423 *aHeight = u32Height;
6424
6425 freeSavedDisplayScreenshot(pu8Data);
6426
6427 return S_OK;
6428}
6429
6430HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6431{
6432 if (aScreenId != 0)
6433 return E_NOTIMPL;
6434
6435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 uint8_t *pu8Data = NULL;
6438 uint32_t cbData = 0;
6439 uint32_t u32Width = 0;
6440 uint32_t u32Height = 0;
6441
6442 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6443
6444 if (RT_FAILURE(vrc))
6445 return setError(VBOX_E_IPRT_ERROR,
6446 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6447 vrc);
6448
6449 *aWidth = u32Width;
6450 *aHeight = u32Height;
6451
6452 aData.resize(cbData);
6453 if (cbData)
6454 memcpy(&aData.front(), pu8Data, cbData);
6455
6456 freeSavedDisplayScreenshot(pu8Data);
6457
6458 return S_OK;
6459}
6460
6461HRESULT Machine::hotPlugCPU(ULONG aCpu)
6462{
6463 HRESULT rc = S_OK;
6464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6465
6466 if (!mHWData->mCPUHotPlugEnabled)
6467 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6468
6469 if (aCpu >= mHWData->mCPUCount)
6470 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6471
6472 if (mHWData->mCPUAttached[aCpu])
6473 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6474
6475 alock.release();
6476 rc = i_onCPUChange(aCpu, false);
6477 alock.acquire();
6478 if (FAILED(rc)) return rc;
6479
6480 i_setModified(IsModified_MachineData);
6481 mHWData.backup();
6482 mHWData->mCPUAttached[aCpu] = true;
6483
6484 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6485 if (Global::IsOnline(mData->mMachineState))
6486 i_saveSettings(NULL);
6487
6488 return S_OK;
6489}
6490
6491HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6492{
6493 HRESULT rc = S_OK;
6494
6495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6496
6497 if (!mHWData->mCPUHotPlugEnabled)
6498 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6499
6500 if (aCpu >= SchemaDefs::MaxCPUCount)
6501 return setError(E_INVALIDARG,
6502 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6503 SchemaDefs::MaxCPUCount);
6504
6505 if (!mHWData->mCPUAttached[aCpu])
6506 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6507
6508 /* CPU 0 can't be detached */
6509 if (aCpu == 0)
6510 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6511
6512 alock.release();
6513 rc = i_onCPUChange(aCpu, true);
6514 alock.acquire();
6515 if (FAILED(rc)) return rc;
6516
6517 i_setModified(IsModified_MachineData);
6518 mHWData.backup();
6519 mHWData->mCPUAttached[aCpu] = false;
6520
6521 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6522 if (Global::IsOnline(mData->mMachineState))
6523 i_saveSettings(NULL);
6524
6525 return S_OK;
6526}
6527
6528HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6529{
6530 *aAttached = false;
6531
6532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6533
6534 /* If hotplug is enabled the CPU is always enabled. */
6535 if (!mHWData->mCPUHotPlugEnabled)
6536 {
6537 if (aCpu < mHWData->mCPUCount)
6538 *aAttached = true;
6539 }
6540 else
6541 {
6542 if (aCpu < SchemaDefs::MaxCPUCount)
6543 *aAttached = mHWData->mCPUAttached[aCpu];
6544 }
6545
6546 return S_OK;
6547}
6548
6549HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6550{
6551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 Utf8Str log = i_queryLogFilename(aIdx);
6554 if (!RTFileExists(log.c_str()))
6555 log.setNull();
6556 aFilename = log;
6557
6558 return S_OK;
6559}
6560
6561HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6562{
6563 if (aSize < 0)
6564 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6565
6566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6567
6568 HRESULT rc = S_OK;
6569 Utf8Str log = i_queryLogFilename(aIdx);
6570
6571 /* do not unnecessarily hold the lock while doing something which does
6572 * not need the lock and potentially takes a long time. */
6573 alock.release();
6574
6575 /* Limit the chunk size to 32K for now, as that gives better performance
6576 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6577 * One byte expands to approx. 25 bytes of breathtaking XML. */
6578 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6579 aData.resize(cbData);
6580
6581 RTFILE LogFile;
6582 int vrc = RTFileOpen(&LogFile, log.c_str(),
6583 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6584 if (RT_SUCCESS(vrc))
6585 {
6586 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6587 if (RT_SUCCESS(vrc))
6588 aData.resize(cbData);
6589 else
6590 rc = setError(VBOX_E_IPRT_ERROR,
6591 tr("Could not read log file '%s' (%Rrc)"),
6592 log.c_str(), vrc);
6593 RTFileClose(LogFile);
6594 }
6595 else
6596 rc = setError(VBOX_E_IPRT_ERROR,
6597 tr("Could not open log file '%s' (%Rrc)"),
6598 log.c_str(), vrc);
6599
6600 if (FAILED(rc))
6601 aData.resize(0);
6602
6603 return rc;
6604}
6605
6606
6607/**
6608 * Currently this method doesn't attach device to the running VM,
6609 * just makes sure it's plugged on next VM start.
6610 */
6611HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6612{
6613 // lock scope
6614 {
6615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6616
6617 HRESULT rc = i_checkStateDependency(MutableStateDep);
6618 if (FAILED(rc)) return rc;
6619
6620 ChipsetType_T aChipset = ChipsetType_PIIX3;
6621 COMGETTER(ChipsetType)(&aChipset);
6622
6623 if (aChipset != ChipsetType_ICH9)
6624 {
6625 return setError(E_INVALIDARG,
6626 tr("Host PCI attachment only supported with ICH9 chipset"));
6627 }
6628
6629 // check if device with this host PCI address already attached
6630 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6631 it != mHWData->mPCIDeviceAssignments.end();
6632 ++it)
6633 {
6634 LONG iHostAddress = -1;
6635 ComPtr<PCIDeviceAttachment> pAttach;
6636 pAttach = *it;
6637 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6638 if (iHostAddress == aHostAddress)
6639 return setError(E_INVALIDARG,
6640 tr("Device with host PCI address already attached to this VM"));
6641 }
6642
6643 ComObjPtr<PCIDeviceAttachment> pda;
6644 char name[32];
6645
6646 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6647 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6648 Bstr bname(name);
6649 pda.createObject();
6650 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6651 i_setModified(IsModified_MachineData);
6652 mHWData.backup();
6653 mHWData->mPCIDeviceAssignments.push_back(pda);
6654 }
6655
6656 return S_OK;
6657}
6658
6659/**
6660 * Currently this method doesn't detach device from the running VM,
6661 * just makes sure it's not plugged on next VM start.
6662 */
6663HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6664{
6665 ComObjPtr<PCIDeviceAttachment> pAttach;
6666 bool fRemoved = false;
6667 HRESULT rc;
6668
6669 // lock scope
6670 {
6671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 rc = i_checkStateDependency(MutableStateDep);
6674 if (FAILED(rc)) return rc;
6675
6676 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6677 it != mHWData->mPCIDeviceAssignments.end();
6678 ++it)
6679 {
6680 LONG iHostAddress = -1;
6681 pAttach = *it;
6682 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6683 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6684 {
6685 i_setModified(IsModified_MachineData);
6686 mHWData.backup();
6687 mHWData->mPCIDeviceAssignments.remove(pAttach);
6688 fRemoved = true;
6689 break;
6690 }
6691 }
6692 }
6693
6694
6695 /* Fire event outside of the lock */
6696 if (fRemoved)
6697 {
6698 Assert(!pAttach.isNull());
6699 ComPtr<IEventSource> es;
6700 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6701 Assert(SUCCEEDED(rc));
6702 Bstr mid;
6703 rc = this->COMGETTER(Id)(mid.asOutParam());
6704 Assert(SUCCEEDED(rc));
6705 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6706 }
6707
6708 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6709 tr("No host PCI device %08x attached"),
6710 aHostAddress
6711 );
6712}
6713
6714HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6715{
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6719
6720 size_t i = 0;
6721 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6722 it != mHWData->mPCIDeviceAssignments.end();
6723 ++i, ++it)
6724 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6725
6726 return S_OK;
6727}
6728
6729HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6730{
6731 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6732
6733 return S_OK;
6734}
6735
6736HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6737{
6738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6739
6740 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6741
6742 return S_OK;
6743}
6744
6745HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6746{
6747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6748 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6749 if (SUCCEEDED(hrc))
6750 {
6751 hrc = mHWData.backupEx();
6752 if (SUCCEEDED(hrc))
6753 {
6754 i_setModified(IsModified_MachineData);
6755 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6756 }
6757 }
6758 return hrc;
6759}
6760
6761HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6762{
6763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6764 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6765 return S_OK;
6766}
6767
6768HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6769{
6770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6771 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6772 if (SUCCEEDED(hrc))
6773 {
6774 hrc = mHWData.backupEx();
6775 if (SUCCEEDED(hrc))
6776 {
6777 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6778 if (SUCCEEDED(hrc))
6779 i_setModified(IsModified_MachineData);
6780 }
6781 }
6782 return hrc;
6783}
6784
6785HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6786{
6787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6788
6789 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6790
6791 return S_OK;
6792}
6793
6794HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6795{
6796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6797 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6798 if (SUCCEEDED(hrc))
6799 {
6800 hrc = mHWData.backupEx();
6801 if (SUCCEEDED(hrc))
6802 {
6803 i_setModified(IsModified_MachineData);
6804 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6805 }
6806 }
6807 return hrc;
6808}
6809
6810HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6811{
6812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6813
6814 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6820{
6821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6822
6823 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6824 if ( SUCCEEDED(hrc)
6825 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6826 {
6827 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6828 int vrc;
6829
6830 if (aAutostartEnabled)
6831 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6832 else
6833 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6834
6835 if (RT_SUCCESS(vrc))
6836 {
6837 hrc = mHWData.backupEx();
6838 if (SUCCEEDED(hrc))
6839 {
6840 i_setModified(IsModified_MachineData);
6841 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6842 }
6843 }
6844 else if (vrc == VERR_NOT_SUPPORTED)
6845 hrc = setError(VBOX_E_NOT_SUPPORTED,
6846 tr("The VM autostart feature is not supported on this platform"));
6847 else if (vrc == VERR_PATH_NOT_FOUND)
6848 hrc = setError(E_FAIL,
6849 tr("The path to the autostart database is not set"));
6850 else
6851 hrc = setError(E_UNEXPECTED,
6852 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6853 aAutostartEnabled ? "Adding" : "Removing",
6854 mUserData->s.strName.c_str(), vrc);
6855 }
6856 return hrc;
6857}
6858
6859HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6860{
6861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6862
6863 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6864
6865 return S_OK;
6866}
6867
6868HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6869{
6870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6871 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6872 if (SUCCEEDED(hrc))
6873 {
6874 hrc = mHWData.backupEx();
6875 if (SUCCEEDED(hrc))
6876 {
6877 i_setModified(IsModified_MachineData);
6878 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6879 }
6880 }
6881 return hrc;
6882}
6883
6884HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6885{
6886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6887
6888 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6889
6890 return S_OK;
6891}
6892
6893HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6894{
6895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6896 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6897 if ( SUCCEEDED(hrc)
6898 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6899 {
6900 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6901 int vrc;
6902
6903 if (aAutostopType != AutostopType_Disabled)
6904 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6905 else
6906 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6907
6908 if (RT_SUCCESS(vrc))
6909 {
6910 hrc = mHWData.backupEx();
6911 if (SUCCEEDED(hrc))
6912 {
6913 i_setModified(IsModified_MachineData);
6914 mHWData->mAutostart.enmAutostopType = aAutostopType;
6915 }
6916 }
6917 else if (vrc == VERR_NOT_SUPPORTED)
6918 hrc = setError(VBOX_E_NOT_SUPPORTED,
6919 tr("The VM autostop feature is not supported on this platform"));
6920 else if (vrc == VERR_PATH_NOT_FOUND)
6921 hrc = setError(E_FAIL,
6922 tr("The path to the autostart database is not set"));
6923 else
6924 hrc = setError(E_UNEXPECTED,
6925 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6926 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6927 mUserData->s.strName.c_str(), vrc);
6928 }
6929 return hrc;
6930}
6931
6932HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6933{
6934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6935
6936 aDefaultFrontend = mHWData->mDefaultFrontend;
6937
6938 return S_OK;
6939}
6940
6941HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6942{
6943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6944 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6945 if (SUCCEEDED(hrc))
6946 {
6947 hrc = mHWData.backupEx();
6948 if (SUCCEEDED(hrc))
6949 {
6950 i_setModified(IsModified_MachineData);
6951 mHWData->mDefaultFrontend = aDefaultFrontend;
6952 }
6953 }
6954 return hrc;
6955}
6956
6957HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6958{
6959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6960 size_t cbIcon = mUserData->mIcon.size();
6961 aIcon.resize(cbIcon);
6962 if (cbIcon)
6963 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6964 return S_OK;
6965}
6966
6967HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6968{
6969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6970 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6971 if (SUCCEEDED(hrc))
6972 {
6973 i_setModified(IsModified_MachineData);
6974 mUserData.backup();
6975 size_t cbIcon = aIcon.size();
6976 mUserData->mIcon.resize(cbIcon);
6977 if (cbIcon)
6978 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6979 }
6980 return hrc;
6981}
6982
6983HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6984{
6985#ifdef VBOX_WITH_USB
6986 *aUSBProxyAvailable = true;
6987#else
6988 *aUSBProxyAvailable = false;
6989#endif
6990 return S_OK;
6991}
6992
6993HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6994 ComPtr<IProgress> &aProgress)
6995{
6996 ComObjPtr<Progress> pP;
6997 Progress *ppP = pP;
6998 IProgress *iP = static_cast<IProgress *>(ppP);
6999 IProgress **pProgress = &iP;
7000
7001 IMachine *pTarget = aTarget;
7002
7003 /* Convert the options. */
7004 RTCList<CloneOptions_T> optList;
7005 if (aOptions.size())
7006 for (size_t i = 0; i < aOptions.size(); ++i)
7007 optList.append(aOptions[i]);
7008
7009 if (optList.contains(CloneOptions_Link))
7010 {
7011 if (!i_isSnapshotMachine())
7012 return setError(E_INVALIDARG,
7013 tr("Linked clone can only be created from a snapshot"));
7014 if (aMode != CloneMode_MachineState)
7015 return setError(E_INVALIDARG,
7016 tr("Linked clone can only be created for a single machine state"));
7017 }
7018 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7019
7020 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7021
7022 HRESULT rc = pWorker->start(pProgress);
7023
7024 pP = static_cast<Progress *>(*pProgress);
7025 pP.queryInterfaceTo(aProgress.asOutParam());
7026
7027 return rc;
7028
7029}
7030
7031// public methods for internal purposes
7032/////////////////////////////////////////////////////////////////////////////
7033
7034/**
7035 * Adds the given IsModified_* flag to the dirty flags of the machine.
7036 * This must be called either during i_loadSettings or under the machine write lock.
7037 * @param fl
7038 */
7039void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7040{
7041 mData->flModifications |= fl;
7042 if (fAllowStateModification && i_isStateModificationAllowed())
7043 mData->mCurrentStateModified = true;
7044}
7045
7046/**
7047 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7048 * care of the write locking.
7049 *
7050 * @param fModifications The flag to add.
7051 */
7052void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7053{
7054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7055 i_setModified(fModification, fAllowStateModification);
7056}
7057
7058/**
7059 * Saves the registry entry of this machine to the given configuration node.
7060 *
7061 * @param aEntryNode Node to save the registry entry to.
7062 *
7063 * @note locks this object for reading.
7064 */
7065HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7066{
7067 AutoLimitedCaller autoCaller(this);
7068 AssertComRCReturnRC(autoCaller.rc());
7069
7070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7071
7072 data.uuid = mData->mUuid;
7073 data.strSettingsFile = mData->m_strConfigFile;
7074
7075 return S_OK;
7076}
7077
7078/**
7079 * Calculates the absolute path of the given path taking the directory of the
7080 * machine settings file as the current directory.
7081 *
7082 * @param aPath Path to calculate the absolute path for.
7083 * @param aResult Where to put the result (used only on success, can be the
7084 * same Utf8Str instance as passed in @a aPath).
7085 * @return IPRT result.
7086 *
7087 * @note Locks this object for reading.
7088 */
7089int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7090{
7091 AutoCaller autoCaller(this);
7092 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7093
7094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7095
7096 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7097
7098 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7099
7100 strSettingsDir.stripFilename();
7101 char folder[RTPATH_MAX];
7102 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7103 if (RT_SUCCESS(vrc))
7104 aResult = folder;
7105
7106 return vrc;
7107}
7108
7109/**
7110 * Copies strSource to strTarget, making it relative to the machine folder
7111 * if it is a subdirectory thereof, or simply copying it otherwise.
7112 *
7113 * @param strSource Path to evaluate and copy.
7114 * @param strTarget Buffer to receive target path.
7115 *
7116 * @note Locks this object for reading.
7117 */
7118void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7119 Utf8Str &strTarget)
7120{
7121 AutoCaller autoCaller(this);
7122 AssertComRCReturn(autoCaller.rc(), (void)0);
7123
7124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7125
7126 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7127 // use strTarget as a temporary buffer to hold the machine settings dir
7128 strTarget = mData->m_strConfigFileFull;
7129 strTarget.stripFilename();
7130 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7131 {
7132 // is relative: then append what's left
7133 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7134 // for empty paths (only possible for subdirs) use "." to avoid
7135 // triggering default settings for not present config attributes.
7136 if (strTarget.isEmpty())
7137 strTarget = ".";
7138 }
7139 else
7140 // is not relative: then overwrite
7141 strTarget = strSource;
7142}
7143
7144/**
7145 * Returns the full path to the machine's log folder in the
7146 * \a aLogFolder argument.
7147 */
7148void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7149{
7150 AutoCaller autoCaller(this);
7151 AssertComRCReturnVoid(autoCaller.rc());
7152
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 char szTmp[RTPATH_MAX];
7156 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7157 if (RT_SUCCESS(vrc))
7158 {
7159 if (szTmp[0] && !mUserData.isNull())
7160 {
7161 char szTmp2[RTPATH_MAX];
7162 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7163 if (RT_SUCCESS(vrc))
7164 aLogFolder = BstrFmt("%s%c%s",
7165 szTmp2,
7166 RTPATH_DELIMITER,
7167 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7168 }
7169 else
7170 vrc = VERR_PATH_IS_RELATIVE;
7171 }
7172
7173 if (RT_FAILURE(vrc))
7174 {
7175 // fallback if VBOX_USER_LOGHOME is not set or invalid
7176 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7177 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7178 aLogFolder.append(RTPATH_DELIMITER);
7179 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7180 }
7181}
7182
7183/**
7184 * Returns the full path to the machine's log file for an given index.
7185 */
7186Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7187 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7188{
7189 Utf8Str logFolder;
7190 getLogFolder(logFolder);
7191 Assert(logFolder.length());
7192 Utf8Str log;
7193 if (idx == 0)
7194 log = Utf8StrFmt("%s%cVBox.log",
7195 logFolder.c_str(), RTPATH_DELIMITER);
7196 else
7197 log = Utf8StrFmt("%s%cVBox.log.%d",
7198 logFolder.c_str(), RTPATH_DELIMITER, idx);
7199 return log;
7200}
7201
7202/**
7203 * Returns the full path to the machine's (hardened) startup log file.
7204 */
7205Utf8Str Machine::i_getStartupLogFilename(void)
7206{
7207 Utf8Str strFilename;
7208 getLogFolder(strFilename);
7209 Assert(strFilename.length());
7210 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7211 return strFilename;
7212}
7213
7214
7215/**
7216 * Composes a unique saved state filename based on the current system time. The filename is
7217 * granular to the second so this will work so long as no more than one snapshot is taken on
7218 * a machine per second.
7219 *
7220 * Before version 4.1, we used this formula for saved state files:
7221 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7222 * which no longer works because saved state files can now be shared between the saved state of the
7223 * "saved" machine and an online snapshot, and the following would cause problems:
7224 * 1) save machine
7225 * 2) create online snapshot from that machine state --> reusing saved state file
7226 * 3) save machine again --> filename would be reused, breaking the online snapshot
7227 *
7228 * So instead we now use a timestamp.
7229 *
7230 * @param str
7231 */
7232
7233void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7234{
7235 AutoCaller autoCaller(this);
7236 AssertComRCReturnVoid(autoCaller.rc());
7237
7238 {
7239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7240 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7241 }
7242
7243 RTTIMESPEC ts;
7244 RTTimeNow(&ts);
7245 RTTIME time;
7246 RTTimeExplode(&time, &ts);
7247
7248 strStateFilePath += RTPATH_DELIMITER;
7249 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7250 time.i32Year, time.u8Month, time.u8MonthDay,
7251 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7252}
7253
7254/**
7255 * Returns the full path to the default video capture file.
7256 */
7257void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7258{
7259 AutoCaller autoCaller(this);
7260 AssertComRCReturnVoid(autoCaller.rc());
7261
7262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7263
7264 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7265 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7266 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7267}
7268
7269/**
7270 * Returns whether at least one USB controller is present for the VM.
7271 */
7272bool Machine::i_isUSBControllerPresent()
7273{
7274 AutoCaller autoCaller(this);
7275 AssertComRCReturn(autoCaller.rc(), false);
7276
7277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7278
7279 return (mUSBControllers->size() > 0);
7280}
7281
7282/**
7283 * @note Locks this object for writing, calls the client process
7284 * (inside the lock).
7285 */
7286HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7287 const Utf8Str &strFrontend,
7288 const Utf8Str &strEnvironment,
7289 ProgressProxy *aProgress)
7290{
7291 LogFlowThisFuncEnter();
7292
7293 AssertReturn(aControl, E_FAIL);
7294 AssertReturn(aProgress, E_FAIL);
7295 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7296
7297 AutoCaller autoCaller(this);
7298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7299
7300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7301
7302 if (!mData->mRegistered)
7303 return setError(E_UNEXPECTED,
7304 tr("The machine '%s' is not registered"),
7305 mUserData->s.strName.c_str());
7306
7307 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7308
7309 if ( mData->mSession.mState == SessionState_Locked
7310 || mData->mSession.mState == SessionState_Spawning
7311 || mData->mSession.mState == SessionState_Unlocking)
7312 return setError(VBOX_E_INVALID_OBJECT_STATE,
7313 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7314 mUserData->s.strName.c_str());
7315
7316 /* may not be busy */
7317 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7318
7319 /* get the path to the executable */
7320 char szPath[RTPATH_MAX];
7321 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7322 size_t cchBufLeft = strlen(szPath);
7323 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7324 szPath[cchBufLeft] = 0;
7325 char *pszNamePart = szPath + cchBufLeft;
7326 cchBufLeft = sizeof(szPath) - cchBufLeft;
7327
7328 int vrc = VINF_SUCCESS;
7329 RTPROCESS pid = NIL_RTPROCESS;
7330
7331 RTENV env = RTENV_DEFAULT;
7332
7333 if (!strEnvironment.isEmpty())
7334 {
7335 char *newEnvStr = NULL;
7336
7337 do
7338 {
7339 /* clone the current environment */
7340 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7341 AssertRCBreakStmt(vrc2, vrc = vrc2);
7342
7343 newEnvStr = RTStrDup(strEnvironment.c_str());
7344 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7345
7346 /* put new variables to the environment
7347 * (ignore empty variable names here since RTEnv API
7348 * intentionally doesn't do that) */
7349 char *var = newEnvStr;
7350 for (char *p = newEnvStr; *p; ++p)
7351 {
7352 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7353 {
7354 *p = '\0';
7355 if (*var)
7356 {
7357 char *val = strchr(var, '=');
7358 if (val)
7359 {
7360 *val++ = '\0';
7361 vrc2 = RTEnvSetEx(env, var, val);
7362 }
7363 else
7364 vrc2 = RTEnvUnsetEx(env, var);
7365 if (RT_FAILURE(vrc2))
7366 break;
7367 }
7368 var = p + 1;
7369 }
7370 }
7371 if (RT_SUCCESS(vrc2) && *var)
7372 vrc2 = RTEnvPutEx(env, var);
7373
7374 AssertRCBreakStmt(vrc2, vrc = vrc2);
7375 }
7376 while (0);
7377
7378 if (newEnvStr != NULL)
7379 RTStrFree(newEnvStr);
7380 }
7381
7382 /* Hardened startup logging */
7383#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7384 Utf8Str strSupStartLogArg("--sup-startup-log=");
7385 {
7386 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7387 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7388 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7389 {
7390 Utf8Str strStartupLogDir = strStartupLogFile;
7391 strStartupLogDir.stripFilename();
7392 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7393 file without stripping the file. */
7394 }
7395 strSupStartLogArg.append(strStartupLogFile);
7396 }
7397 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7398#else
7399 const char *pszSupStartupLogArg = NULL;
7400#endif
7401
7402
7403#ifdef VBOX_WITH_QTGUI
7404 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7405 {
7406# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7407 /* Modify the base path so that we don't need to use ".." below. */
7408 RTPathStripTrailingSlash(szPath);
7409 RTPathStripFilename(szPath);
7410 cchBufLeft = strlen(szPath);
7411 pszNamePart = szPath + cchBufLeft;
7412 cchBufLeft = sizeof(szPath) - cchBufLeft;
7413
7414# define OSX_APP_NAME "VirtualBoxVM"
7415# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7416
7417 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7418 if ( strAppOverride.contains(".")
7419 || strAppOverride.contains("/")
7420 || strAppOverride.contains("\\")
7421 || strAppOverride.contains(":"))
7422 strAppOverride.setNull();
7423 Utf8Str strAppPath;
7424 if (!strAppOverride.isEmpty())
7425 {
7426 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7427 Utf8Str strFullPath(szPath);
7428 strFullPath.append(strAppPath);
7429 /* there is a race, but people using this deserve the failure */
7430 if (!RTFileExists(strFullPath.c_str()))
7431 strAppOverride.setNull();
7432 }
7433 if (strAppOverride.isEmpty())
7434 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7435 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7436 strcpy(pszNamePart, strAppPath.c_str());
7437# else
7438 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7439 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7440 strcpy(pszNamePart, s_szVirtualBox_exe);
7441# endif
7442
7443 Utf8Str idStr = mData->mUuid.toString();
7444 const char *apszArgs[] =
7445 {
7446 szPath,
7447 "--comment", mUserData->s.strName.c_str(),
7448 "--startvm", idStr.c_str(),
7449 "--no-startvm-errormsgbox",
7450 pszSupStartupLogArg,
7451 NULL
7452 };
7453 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7454 }
7455#else /* !VBOX_WITH_QTGUI */
7456 if (0)
7457 ;
7458#endif /* VBOX_WITH_QTGUI */
7459
7460 else
7461
7462#ifdef VBOX_WITH_VBOXSDL
7463 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7464 {
7465 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7466 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7467 strcpy(pszNamePart, s_szVBoxSDL_exe);
7468
7469 Utf8Str idStr = mData->mUuid.toString();
7470 const char *apszArgs[] =
7471 {
7472 szPath,
7473 "--comment", mUserData->s.strName.c_str(),
7474 "--startvm", idStr.c_str(),
7475 pszSupStartupLogArg,
7476 NULL
7477 };
7478 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7479 }
7480#else /* !VBOX_WITH_VBOXSDL */
7481 if (0)
7482 ;
7483#endif /* !VBOX_WITH_VBOXSDL */
7484
7485 else
7486
7487#ifdef VBOX_WITH_HEADLESS
7488 if ( strFrontend == "headless"
7489 || strFrontend == "capture"
7490 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7491 )
7492 {
7493 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7494 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7495 * and a VM works even if the server has not been installed.
7496 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7497 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7498 * differently in 4.0 and 3.x.
7499 */
7500 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7501 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7502 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7503
7504 Utf8Str idStr = mData->mUuid.toString();
7505 const char *apszArgs[] =
7506 {
7507 szPath,
7508 "--comment", mUserData->s.strName.c_str(),
7509 "--startvm", idStr.c_str(),
7510 "--vrde", "config",
7511 0, /* For "--capture". */
7512 0, /* For "--sup-startup-log". */
7513 0
7514 };
7515 unsigned iArg = 7;
7516 if (strFrontend == "capture")
7517 apszArgs[iArg++] = "--capture";
7518 apszArgs[iArg++] = pszSupStartupLogArg;
7519
7520# ifdef RT_OS_WINDOWS
7521 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7522# else
7523 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7524# endif
7525 }
7526#else /* !VBOX_WITH_HEADLESS */
7527 if (0)
7528 ;
7529#endif /* !VBOX_WITH_HEADLESS */
7530 else
7531 {
7532 RTEnvDestroy(env);
7533 return setError(E_INVALIDARG,
7534 tr("Invalid frontend name: '%s'"),
7535 strFrontend.c_str());
7536 }
7537
7538 RTEnvDestroy(env);
7539
7540 if (RT_FAILURE(vrc))
7541 return setError(VBOX_E_IPRT_ERROR,
7542 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7543 mUserData->s.strName.c_str(), vrc);
7544
7545 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7546
7547 /*
7548 * Note that we don't release the lock here before calling the client,
7549 * because it doesn't need to call us back if called with a NULL argument.
7550 * Releasing the lock here is dangerous because we didn't prepare the
7551 * launch data yet, but the client we've just started may happen to be
7552 * too fast and call LockMachine() that will fail (because of PID, etc.),
7553 * so that the Machine will never get out of the Spawning session state.
7554 */
7555
7556 /* inform the session that it will be a remote one */
7557 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7558#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7559 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7560#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7561 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7562#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7563 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7564
7565 if (FAILED(rc))
7566 {
7567 /* restore the session state */
7568 mData->mSession.mState = SessionState_Unlocked;
7569 alock.release();
7570 mParent->i_addProcessToReap(pid);
7571 /* The failure may occur w/o any error info (from RPC), so provide one */
7572 return setError(VBOX_E_VM_ERROR,
7573 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7574 }
7575
7576 /* attach launch data to the machine */
7577 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7578 mData->mSession.mRemoteControls.push_back(aControl);
7579 mData->mSession.mProgress = aProgress;
7580 mData->mSession.mPID = pid;
7581 mData->mSession.mState = SessionState_Spawning;
7582 mData->mSession.mType = strFrontend;
7583
7584 alock.release();
7585 mParent->i_addProcessToReap(pid);
7586
7587 LogFlowThisFuncLeave();
7588 return S_OK;
7589}
7590
7591/**
7592 * Returns @c true if the given session machine instance has an open direct
7593 * session (and optionally also for direct sessions which are closing) and
7594 * returns the session control machine instance if so.
7595 *
7596 * Note that when the method returns @c false, the arguments remain unchanged.
7597 *
7598 * @param aMachine Session machine object.
7599 * @param aControl Direct session control object (optional).
7600 * @param aAllowClosing If true then additionally a session which is currently
7601 * being closed will also be allowed.
7602 *
7603 * @note locks this object for reading.
7604 */
7605bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7606 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7607 bool aAllowClosing /*= false*/)
7608{
7609 AutoLimitedCaller autoCaller(this);
7610 AssertComRCReturn(autoCaller.rc(), false);
7611
7612 /* just return false for inaccessible machines */
7613 if (getObjectState().getState() != ObjectState::Ready)
7614 return false;
7615
7616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7617
7618 if ( mData->mSession.mState == SessionState_Locked
7619 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7620 )
7621 {
7622 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7623
7624 aMachine = mData->mSession.mMachine;
7625
7626 if (aControl != NULL)
7627 *aControl = mData->mSession.mDirectControl;
7628
7629 return true;
7630 }
7631
7632 return false;
7633}
7634
7635/**
7636 * Returns @c true if the given machine has an spawning direct session.
7637 *
7638 * @note locks this object for reading.
7639 */
7640bool Machine::i_isSessionSpawning()
7641{
7642 AutoLimitedCaller autoCaller(this);
7643 AssertComRCReturn(autoCaller.rc(), false);
7644
7645 /* just return false for inaccessible machines */
7646 if (getObjectState().getState() != ObjectState::Ready)
7647 return false;
7648
7649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7650
7651 if (mData->mSession.mState == SessionState_Spawning)
7652 return true;
7653
7654 return false;
7655}
7656
7657/**
7658 * Called from the client watcher thread to check for unexpected client process
7659 * death during Session_Spawning state (e.g. before it successfully opened a
7660 * direct session).
7661 *
7662 * On Win32 and on OS/2, this method is called only when we've got the
7663 * direct client's process termination notification, so it always returns @c
7664 * true.
7665 *
7666 * On other platforms, this method returns @c true if the client process is
7667 * terminated and @c false if it's still alive.
7668 *
7669 * @note Locks this object for writing.
7670 */
7671bool Machine::i_checkForSpawnFailure()
7672{
7673 AutoCaller autoCaller(this);
7674 if (!autoCaller.isOk())
7675 {
7676 /* nothing to do */
7677 LogFlowThisFunc(("Already uninitialized!\n"));
7678 return true;
7679 }
7680
7681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7682
7683 if (mData->mSession.mState != SessionState_Spawning)
7684 {
7685 /* nothing to do */
7686 LogFlowThisFunc(("Not spawning any more!\n"));
7687 return true;
7688 }
7689
7690 HRESULT rc = S_OK;
7691
7692 /* PID not yet initialized, skip check. */
7693 if (mData->mSession.mPID == NIL_RTPROCESS)
7694 return false;
7695
7696 RTPROCSTATUS status;
7697 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7698
7699 if (vrc != VERR_PROCESS_RUNNING)
7700 {
7701 Utf8Str strExtraInfo;
7702
7703#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7704 /* If the startup logfile exists and is of non-zero length, tell the
7705 user to look there for more details to encourage them to attach it
7706 when reporting startup issues. */
7707 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7708 uint64_t cbStartupLogFile = 0;
7709 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7710 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7711 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7712#endif
7713
7714 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7715 rc = setError(E_FAIL,
7716 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7717 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7718 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7719 rc = setError(E_FAIL,
7720 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7721 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7722 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7723 rc = setError(E_FAIL,
7724 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7725 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7726 else
7727 rc = setError(E_FAIL,
7728 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7729 i_getName().c_str(), vrc, strExtraInfo.c_str());
7730 }
7731
7732 if (FAILED(rc))
7733 {
7734 /* Close the remote session, remove the remote control from the list
7735 * and reset session state to Closed (@note keep the code in sync with
7736 * the relevant part in LockMachine()). */
7737
7738 Assert(mData->mSession.mRemoteControls.size() == 1);
7739 if (mData->mSession.mRemoteControls.size() == 1)
7740 {
7741 ErrorInfoKeeper eik;
7742 mData->mSession.mRemoteControls.front()->Uninitialize();
7743 }
7744
7745 mData->mSession.mRemoteControls.clear();
7746 mData->mSession.mState = SessionState_Unlocked;
7747
7748 /* finalize the progress after setting the state */
7749 if (!mData->mSession.mProgress.isNull())
7750 {
7751 mData->mSession.mProgress->notifyComplete(rc);
7752 mData->mSession.mProgress.setNull();
7753 }
7754
7755 mData->mSession.mPID = NIL_RTPROCESS;
7756
7757 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7758 return true;
7759 }
7760
7761 return false;
7762}
7763
7764/**
7765 * Checks whether the machine can be registered. If so, commits and saves
7766 * all settings.
7767 *
7768 * @note Must be called from mParent's write lock. Locks this object and
7769 * children for writing.
7770 */
7771HRESULT Machine::i_prepareRegister()
7772{
7773 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7774
7775 AutoLimitedCaller autoCaller(this);
7776 AssertComRCReturnRC(autoCaller.rc());
7777
7778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7779
7780 /* wait for state dependents to drop to zero */
7781 i_ensureNoStateDependencies();
7782
7783 if (!mData->mAccessible)
7784 return setError(VBOX_E_INVALID_OBJECT_STATE,
7785 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7786 mUserData->s.strName.c_str(),
7787 mData->mUuid.toString().c_str());
7788
7789 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7790
7791 if (mData->mRegistered)
7792 return setError(VBOX_E_INVALID_OBJECT_STATE,
7793 tr("The machine '%s' with UUID {%s} is already registered"),
7794 mUserData->s.strName.c_str(),
7795 mData->mUuid.toString().c_str());
7796
7797 HRESULT rc = S_OK;
7798
7799 // Ensure the settings are saved. If we are going to be registered and
7800 // no config file exists yet, create it by calling i_saveSettings() too.
7801 if ( (mData->flModifications)
7802 || (!mData->pMachineConfigFile->fileExists())
7803 )
7804 {
7805 rc = i_saveSettings(NULL);
7806 // no need to check whether VirtualBox.xml needs saving too since
7807 // we can't have a machine XML file rename pending
7808 if (FAILED(rc)) return rc;
7809 }
7810
7811 /* more config checking goes here */
7812
7813 if (SUCCEEDED(rc))
7814 {
7815 /* we may have had implicit modifications we want to fix on success */
7816 i_commit();
7817
7818 mData->mRegistered = true;
7819 }
7820 else
7821 {
7822 /* we may have had implicit modifications we want to cancel on failure*/
7823 i_rollback(false /* aNotify */);
7824 }
7825
7826 return rc;
7827}
7828
7829/**
7830 * Increases the number of objects dependent on the machine state or on the
7831 * registered state. Guarantees that these two states will not change at least
7832 * until #releaseStateDependency() is called.
7833 *
7834 * Depending on the @a aDepType value, additional state checks may be made.
7835 * These checks will set extended error info on failure. See
7836 * #checkStateDependency() for more info.
7837 *
7838 * If this method returns a failure, the dependency is not added and the caller
7839 * is not allowed to rely on any particular machine state or registration state
7840 * value and may return the failed result code to the upper level.
7841 *
7842 * @param aDepType Dependency type to add.
7843 * @param aState Current machine state (NULL if not interested).
7844 * @param aRegistered Current registered state (NULL if not interested).
7845 *
7846 * @note Locks this object for writing.
7847 */
7848HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7849 MachineState_T *aState /* = NULL */,
7850 BOOL *aRegistered /* = NULL */)
7851{
7852 AutoCaller autoCaller(this);
7853 AssertComRCReturnRC(autoCaller.rc());
7854
7855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7856
7857 HRESULT rc = i_checkStateDependency(aDepType);
7858 if (FAILED(rc)) return rc;
7859
7860 {
7861 if (mData->mMachineStateChangePending != 0)
7862 {
7863 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7864 * drop to zero so don't add more. It may make sense to wait a bit
7865 * and retry before reporting an error (since the pending state
7866 * transition should be really quick) but let's just assert for
7867 * now to see if it ever happens on practice. */
7868
7869 AssertFailed();
7870
7871 return setError(E_ACCESSDENIED,
7872 tr("Machine state change is in progress. Please retry the operation later."));
7873 }
7874
7875 ++mData->mMachineStateDeps;
7876 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7877 }
7878
7879 if (aState)
7880 *aState = mData->mMachineState;
7881 if (aRegistered)
7882 *aRegistered = mData->mRegistered;
7883
7884 return S_OK;
7885}
7886
7887/**
7888 * Decreases the number of objects dependent on the machine state.
7889 * Must always complete the #addStateDependency() call after the state
7890 * dependency is no more necessary.
7891 */
7892void Machine::i_releaseStateDependency()
7893{
7894 AutoCaller autoCaller(this);
7895 AssertComRCReturnVoid(autoCaller.rc());
7896
7897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7898
7899 /* releaseStateDependency() w/o addStateDependency()? */
7900 AssertReturnVoid(mData->mMachineStateDeps != 0);
7901 -- mData->mMachineStateDeps;
7902
7903 if (mData->mMachineStateDeps == 0)
7904 {
7905 /* inform i_ensureNoStateDependencies() that there are no more deps */
7906 if (mData->mMachineStateChangePending != 0)
7907 {
7908 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7909 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7910 }
7911 }
7912}
7913
7914Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7915{
7916 /* start with nothing found */
7917 Utf8Str strResult("");
7918
7919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7920
7921 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7922 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7923 // found:
7924 strResult = it->second; // source is a Utf8Str
7925
7926 return strResult;
7927}
7928
7929// protected methods
7930/////////////////////////////////////////////////////////////////////////////
7931
7932/**
7933 * Performs machine state checks based on the @a aDepType value. If a check
7934 * fails, this method will set extended error info, otherwise it will return
7935 * S_OK. It is supposed, that on failure, the caller will immediately return
7936 * the return value of this method to the upper level.
7937 *
7938 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7939 *
7940 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7941 * current state of this machine object allows to change settings of the
7942 * machine (i.e. the machine is not registered, or registered but not running
7943 * and not saved). It is useful to call this method from Machine setters
7944 * before performing any change.
7945 *
7946 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7947 * as for MutableStateDep except that if the machine is saved, S_OK is also
7948 * returned. This is useful in setters which allow changing machine
7949 * properties when it is in the saved state.
7950 *
7951 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7952 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7953 * Aborted).
7954 *
7955 * @param aDepType Dependency type to check.
7956 *
7957 * @note Non Machine based classes should use #addStateDependency() and
7958 * #releaseStateDependency() methods or the smart AutoStateDependency
7959 * template.
7960 *
7961 * @note This method must be called from under this object's read or write
7962 * lock.
7963 */
7964HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7965{
7966 switch (aDepType)
7967 {
7968 case AnyStateDep:
7969 {
7970 break;
7971 }
7972 case MutableStateDep:
7973 {
7974 if ( mData->mRegistered
7975 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7976 Paused should actually be included here... (Live Migration) */
7977 || ( mData->mMachineState != MachineState_Paused
7978 && mData->mMachineState != MachineState_Running
7979 && mData->mMachineState != MachineState_Aborted
7980 && mData->mMachineState != MachineState_Teleported
7981 && mData->mMachineState != MachineState_PoweredOff
7982 )
7983 )
7984 )
7985 return setError(VBOX_E_INVALID_VM_STATE,
7986 tr("The machine is not mutable (state is %s)"),
7987 Global::stringifyMachineState(mData->mMachineState));
7988 break;
7989 }
7990 case MutableOrSavedStateDep:
7991 {
7992 if ( mData->mRegistered
7993 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7994 Paused should actually be included here... (Live Migration) */
7995 || ( mData->mMachineState != MachineState_Paused
7996 && mData->mMachineState != MachineState_Running
7997 && mData->mMachineState != MachineState_Aborted
7998 && mData->mMachineState != MachineState_Teleported
7999 && mData->mMachineState != MachineState_Saved
8000 && mData->mMachineState != MachineState_PoweredOff
8001 )
8002 )
8003 )
8004 return setError(VBOX_E_INVALID_VM_STATE,
8005 tr("The machine is not mutable (state is %s)"),
8006 Global::stringifyMachineState(mData->mMachineState));
8007 break;
8008 }
8009 case OfflineStateDep:
8010 {
8011 if ( mData->mRegistered
8012 && ( !i_isSessionMachine()
8013 || ( mData->mMachineState != MachineState_PoweredOff
8014 && mData->mMachineState != MachineState_Saved
8015 && mData->mMachineState != MachineState_Aborted
8016 && mData->mMachineState != MachineState_Teleported
8017 )
8018 )
8019 )
8020 return setError(VBOX_E_INVALID_VM_STATE,
8021 tr("The machine is not offline (state is %s)"),
8022 Global::stringifyMachineState(mData->mMachineState));
8023 break;
8024 }
8025 }
8026
8027 return S_OK;
8028}
8029
8030/**
8031 * Helper to initialize all associated child objects and allocate data
8032 * structures.
8033 *
8034 * This method must be called as a part of the object's initialization procedure
8035 * (usually done in the #init() method).
8036 *
8037 * @note Must be called only from #init() or from #registeredInit().
8038 */
8039HRESULT Machine::initDataAndChildObjects()
8040{
8041 AutoCaller autoCaller(this);
8042 AssertComRCReturnRC(autoCaller.rc());
8043 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8044 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8045
8046 AssertReturn(!mData->mAccessible, E_FAIL);
8047
8048 /* allocate data structures */
8049 mSSData.allocate();
8050 mUserData.allocate();
8051 mHWData.allocate();
8052 mMediaData.allocate();
8053 mStorageControllers.allocate();
8054 mUSBControllers.allocate();
8055
8056 /* initialize mOSTypeId */
8057 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8058
8059 /* create associated BIOS settings object */
8060 unconst(mBIOSSettings).createObject();
8061 mBIOSSettings->init(this);
8062
8063 /* create an associated VRDE object (default is disabled) */
8064 unconst(mVRDEServer).createObject();
8065 mVRDEServer->init(this);
8066
8067 /* create associated serial port objects */
8068 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8069 {
8070 unconst(mSerialPorts[slot]).createObject();
8071 mSerialPorts[slot]->init(this, slot);
8072 }
8073
8074 /* create associated parallel port objects */
8075 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8076 {
8077 unconst(mParallelPorts[slot]).createObject();
8078 mParallelPorts[slot]->init(this, slot);
8079 }
8080
8081 /* create the audio adapter object (always present, default is disabled) */
8082 unconst(mAudioAdapter).createObject();
8083 mAudioAdapter->init(this);
8084
8085 /* create the USB device filters object (always present) */
8086 unconst(mUSBDeviceFilters).createObject();
8087 mUSBDeviceFilters->init(this);
8088
8089 /* create associated network adapter objects */
8090 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8091 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8092 {
8093 unconst(mNetworkAdapters[slot]).createObject();
8094 mNetworkAdapters[slot]->init(this, slot);
8095 }
8096
8097 /* create the bandwidth control */
8098 unconst(mBandwidthControl).createObject();
8099 mBandwidthControl->init(this);
8100
8101 return S_OK;
8102}
8103
8104/**
8105 * Helper to uninitialize all associated child objects and to free all data
8106 * structures.
8107 *
8108 * This method must be called as a part of the object's uninitialization
8109 * procedure (usually done in the #uninit() method).
8110 *
8111 * @note Must be called only from #uninit() or from #registeredInit().
8112 */
8113void Machine::uninitDataAndChildObjects()
8114{
8115 AutoCaller autoCaller(this);
8116 AssertComRCReturnVoid(autoCaller.rc());
8117 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8118 || getObjectState().getState() == ObjectState::Limited);
8119
8120 /* tell all our other child objects we've been uninitialized */
8121 if (mBandwidthControl)
8122 {
8123 mBandwidthControl->uninit();
8124 unconst(mBandwidthControl).setNull();
8125 }
8126
8127 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8128 {
8129 if (mNetworkAdapters[slot])
8130 {
8131 mNetworkAdapters[slot]->uninit();
8132 unconst(mNetworkAdapters[slot]).setNull();
8133 }
8134 }
8135
8136 if (mUSBDeviceFilters)
8137 {
8138 mUSBDeviceFilters->uninit();
8139 unconst(mUSBDeviceFilters).setNull();
8140 }
8141
8142 if (mAudioAdapter)
8143 {
8144 mAudioAdapter->uninit();
8145 unconst(mAudioAdapter).setNull();
8146 }
8147
8148 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8149 {
8150 if (mParallelPorts[slot])
8151 {
8152 mParallelPorts[slot]->uninit();
8153 unconst(mParallelPorts[slot]).setNull();
8154 }
8155 }
8156
8157 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8158 {
8159 if (mSerialPorts[slot])
8160 {
8161 mSerialPorts[slot]->uninit();
8162 unconst(mSerialPorts[slot]).setNull();
8163 }
8164 }
8165
8166 if (mVRDEServer)
8167 {
8168 mVRDEServer->uninit();
8169 unconst(mVRDEServer).setNull();
8170 }
8171
8172 if (mBIOSSettings)
8173 {
8174 mBIOSSettings->uninit();
8175 unconst(mBIOSSettings).setNull();
8176 }
8177
8178 /* Deassociate media (only when a real Machine or a SnapshotMachine
8179 * instance is uninitialized; SessionMachine instances refer to real
8180 * Machine media). This is necessary for a clean re-initialization of
8181 * the VM after successfully re-checking the accessibility state. Note
8182 * that in case of normal Machine or SnapshotMachine uninitialization (as
8183 * a result of unregistering or deleting the snapshot), outdated media
8184 * attachments will already be uninitialized and deleted, so this
8185 * code will not affect them. */
8186 if ( !!mMediaData
8187 && (!i_isSessionMachine())
8188 )
8189 {
8190 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8191 it != mMediaData->mAttachments.end();
8192 ++it)
8193 {
8194 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8195 if (pMedium.isNull())
8196 continue;
8197 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8198 AssertComRC(rc);
8199 }
8200 }
8201
8202 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8203 {
8204 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8205 if (mData->mFirstSnapshot)
8206 {
8207 // snapshots tree is protected by machine write lock; strictly
8208 // this isn't necessary here since we're deleting the entire
8209 // machine, but otherwise we assert in Snapshot::uninit()
8210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8211 mData->mFirstSnapshot->uninit();
8212 mData->mFirstSnapshot.setNull();
8213 }
8214
8215 mData->mCurrentSnapshot.setNull();
8216 }
8217
8218 /* free data structures (the essential mData structure is not freed here
8219 * since it may be still in use) */
8220 mMediaData.free();
8221 mStorageControllers.free();
8222 mUSBControllers.free();
8223 mHWData.free();
8224 mUserData.free();
8225 mSSData.free();
8226}
8227
8228/**
8229 * Returns a pointer to the Machine object for this machine that acts like a
8230 * parent for complex machine data objects such as shared folders, etc.
8231 *
8232 * For primary Machine objects and for SnapshotMachine objects, returns this
8233 * object's pointer itself. For SessionMachine objects, returns the peer
8234 * (primary) machine pointer.
8235 */
8236Machine* Machine::i_getMachine()
8237{
8238 if (i_isSessionMachine())
8239 return (Machine*)mPeer;
8240 return this;
8241}
8242
8243/**
8244 * Makes sure that there are no machine state dependents. If necessary, waits
8245 * for the number of dependents to drop to zero.
8246 *
8247 * Make sure this method is called from under this object's write lock to
8248 * guarantee that no new dependents may be added when this method returns
8249 * control to the caller.
8250 *
8251 * @note Locks this object for writing. The lock will be released while waiting
8252 * (if necessary).
8253 *
8254 * @warning To be used only in methods that change the machine state!
8255 */
8256void Machine::i_ensureNoStateDependencies()
8257{
8258 AssertReturnVoid(isWriteLockOnCurrentThread());
8259
8260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8261
8262 /* Wait for all state dependents if necessary */
8263 if (mData->mMachineStateDeps != 0)
8264 {
8265 /* lazy semaphore creation */
8266 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8267 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8268
8269 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8270 mData->mMachineStateDeps));
8271
8272 ++mData->mMachineStateChangePending;
8273
8274 /* reset the semaphore before waiting, the last dependent will signal
8275 * it */
8276 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8277
8278 alock.release();
8279
8280 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8281
8282 alock.acquire();
8283
8284 -- mData->mMachineStateChangePending;
8285 }
8286}
8287
8288/**
8289 * Changes the machine state and informs callbacks.
8290 *
8291 * This method is not intended to fail so it either returns S_OK or asserts (and
8292 * returns a failure).
8293 *
8294 * @note Locks this object for writing.
8295 */
8296HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8297{
8298 LogFlowThisFuncEnter();
8299 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8300
8301 AutoCaller autoCaller(this);
8302 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8303
8304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8305
8306 /* wait for state dependents to drop to zero */
8307 i_ensureNoStateDependencies();
8308
8309 if (mData->mMachineState != aMachineState)
8310 {
8311 mData->mMachineState = aMachineState;
8312
8313 RTTimeNow(&mData->mLastStateChange);
8314
8315 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8316 }
8317
8318 LogFlowThisFuncLeave();
8319 return S_OK;
8320}
8321
8322/**
8323 * Searches for a shared folder with the given logical name
8324 * in the collection of shared folders.
8325 *
8326 * @param aName logical name of the shared folder
8327 * @param aSharedFolder where to return the found object
8328 * @param aSetError whether to set the error info if the folder is
8329 * not found
8330 * @return
8331 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8332 *
8333 * @note
8334 * must be called from under the object's lock!
8335 */
8336HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8337 ComObjPtr<SharedFolder> &aSharedFolder,
8338 bool aSetError /* = false */)
8339{
8340 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8341 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8342 it != mHWData->mSharedFolders.end();
8343 ++it)
8344 {
8345 SharedFolder *pSF = *it;
8346 AutoCaller autoCaller(pSF);
8347 if (pSF->i_getName() == aName)
8348 {
8349 aSharedFolder = pSF;
8350 rc = S_OK;
8351 break;
8352 }
8353 }
8354
8355 if (aSetError && FAILED(rc))
8356 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8357
8358 return rc;
8359}
8360
8361/**
8362 * Initializes all machine instance data from the given settings structures
8363 * from XML. The exception is the machine UUID which needs special handling
8364 * depending on the caller's use case, so the caller needs to set that herself.
8365 *
8366 * This gets called in several contexts during machine initialization:
8367 *
8368 * -- When machine XML exists on disk already and needs to be loaded into memory,
8369 * for example, from registeredInit() to load all registered machines on
8370 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8371 * attached to the machine should be part of some media registry already.
8372 *
8373 * -- During OVF import, when a machine config has been constructed from an
8374 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8375 * ensure that the media listed as attachments in the config (which have
8376 * been imported from the OVF) receive the correct registry ID.
8377 *
8378 * -- During VM cloning.
8379 *
8380 * @param config Machine settings from XML.
8381 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8382 * for each attached medium in the config.
8383 * @return
8384 */
8385HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8386 const Guid *puuidRegistry)
8387{
8388 // copy name, description, OS type, teleporter, UTC etc.
8389 mUserData->s = config.machineUserData;
8390
8391 // Decode the Icon overide data from config userdata and set onto Machine.
8392 #define DECODE_STR_MAX _1M
8393 const char* pszStr = config.machineUserData.ovIcon.c_str();
8394 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8395 if (cbOut > DECODE_STR_MAX)
8396 return setError(E_FAIL,
8397 tr("Icon Data too long.'%d' > '%d'"),
8398 cbOut,
8399 DECODE_STR_MAX);
8400 mUserData->mIcon.resize(cbOut);
8401 int vrc = VINF_SUCCESS;
8402 if (cbOut)
8403 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8404 if (RT_FAILURE(vrc))
8405 {
8406 mUserData->mIcon.resize(0);
8407 return setError(E_FAIL,
8408 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8409 pszStr,
8410 vrc);
8411 }
8412
8413 // look up the object by Id to check it is valid
8414 ComPtr<IGuestOSType> guestOSType;
8415 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8416 guestOSType.asOutParam());
8417 if (FAILED(rc)) return rc;
8418
8419 // stateFile (optional)
8420 if (config.strStateFile.isEmpty())
8421 mSSData->strStateFilePath.setNull();
8422 else
8423 {
8424 Utf8Str stateFilePathFull(config.strStateFile);
8425 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8426 if (RT_FAILURE(vrc))
8427 return setError(E_FAIL,
8428 tr("Invalid saved state file path '%s' (%Rrc)"),
8429 config.strStateFile.c_str(),
8430 vrc);
8431 mSSData->strStateFilePath = stateFilePathFull;
8432 }
8433
8434 // snapshot folder needs special processing so set it again
8435 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8436 if (FAILED(rc)) return rc;
8437
8438 /* Copy the extra data items (Not in any case config is already the same as
8439 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8440 * make sure the extra data map is copied). */
8441 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8442
8443 /* currentStateModified (optional, default is true) */
8444 mData->mCurrentStateModified = config.fCurrentStateModified;
8445
8446 mData->mLastStateChange = config.timeLastStateChange;
8447
8448 /*
8449 * note: all mUserData members must be assigned prior this point because
8450 * we need to commit changes in order to let mUserData be shared by all
8451 * snapshot machine instances.
8452 */
8453 mUserData.commitCopy();
8454
8455 // machine registry, if present (must be loaded before snapshots)
8456 if (config.canHaveOwnMediaRegistry())
8457 {
8458 // determine machine folder
8459 Utf8Str strMachineFolder = i_getSettingsFileFull();
8460 strMachineFolder.stripFilename();
8461 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8462 config.mediaRegistry,
8463 strMachineFolder);
8464 if (FAILED(rc)) return rc;
8465 }
8466
8467 /* Snapshot node (optional) */
8468 size_t cRootSnapshots;
8469 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8470 {
8471 // there must be only one root snapshot
8472 Assert(cRootSnapshots == 1);
8473
8474 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8475
8476 rc = i_loadSnapshot(snap,
8477 config.uuidCurrentSnapshot,
8478 NULL); // no parent == first snapshot
8479 if (FAILED(rc)) return rc;
8480 }
8481
8482 // hardware data
8483 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8484 if (FAILED(rc)) return rc;
8485
8486 // load storage controllers
8487 rc = i_loadStorageControllers(config.storageMachine,
8488 puuidRegistry,
8489 NULL /* puuidSnapshot */);
8490 if (FAILED(rc)) return rc;
8491
8492 /*
8493 * NOTE: the assignment below must be the last thing to do,
8494 * otherwise it will be not possible to change the settings
8495 * somewhere in the code above because all setters will be
8496 * blocked by i_checkStateDependency(MutableStateDep).
8497 */
8498
8499 /* set the machine state to Aborted or Saved when appropriate */
8500 if (config.fAborted)
8501 {
8502 mSSData->strStateFilePath.setNull();
8503
8504 /* no need to use i_setMachineState() during init() */
8505 mData->mMachineState = MachineState_Aborted;
8506 }
8507 else if (!mSSData->strStateFilePath.isEmpty())
8508 {
8509 /* no need to use i_setMachineState() during init() */
8510 mData->mMachineState = MachineState_Saved;
8511 }
8512
8513 // after loading settings, we are no longer different from the XML on disk
8514 mData->flModifications = 0;
8515
8516 return S_OK;
8517}
8518
8519/**
8520 * Recursively loads all snapshots starting from the given.
8521 *
8522 * @param aNode <Snapshot> node.
8523 * @param aCurSnapshotId Current snapshot ID from the settings file.
8524 * @param aParentSnapshot Parent snapshot.
8525 */
8526HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8527 const Guid &aCurSnapshotId,
8528 Snapshot *aParentSnapshot)
8529{
8530 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8531 AssertReturn(!i_isSessionMachine(), E_FAIL);
8532
8533 HRESULT rc = S_OK;
8534
8535 Utf8Str strStateFile;
8536 if (!data.strStateFile.isEmpty())
8537 {
8538 /* optional */
8539 strStateFile = data.strStateFile;
8540 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8541 if (RT_FAILURE(vrc))
8542 return setError(E_FAIL,
8543 tr("Invalid saved state file path '%s' (%Rrc)"),
8544 strStateFile.c_str(),
8545 vrc);
8546 }
8547
8548 /* create a snapshot machine object */
8549 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8550 pSnapshotMachine.createObject();
8551 rc = pSnapshotMachine->initFromSettings(this,
8552 data.hardware,
8553 &data.debugging,
8554 &data.autostart,
8555 data.storage,
8556 data.uuid.ref(),
8557 strStateFile);
8558 if (FAILED(rc)) return rc;
8559
8560 /* create a snapshot object */
8561 ComObjPtr<Snapshot> pSnapshot;
8562 pSnapshot.createObject();
8563 /* initialize the snapshot */
8564 rc = pSnapshot->init(mParent, // VirtualBox object
8565 data.uuid,
8566 data.strName,
8567 data.strDescription,
8568 data.timestamp,
8569 pSnapshotMachine,
8570 aParentSnapshot);
8571 if (FAILED(rc)) return rc;
8572
8573 /* memorize the first snapshot if necessary */
8574 if (!mData->mFirstSnapshot)
8575 mData->mFirstSnapshot = pSnapshot;
8576
8577 /* memorize the current snapshot when appropriate */
8578 if ( !mData->mCurrentSnapshot
8579 && pSnapshot->i_getId() == aCurSnapshotId
8580 )
8581 mData->mCurrentSnapshot = pSnapshot;
8582
8583 // now create the children
8584 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8585 it != data.llChildSnapshots.end();
8586 ++it)
8587 {
8588 const settings::Snapshot &childData = *it;
8589 // recurse
8590 rc = i_loadSnapshot(childData,
8591 aCurSnapshotId,
8592 pSnapshot); // parent = the one we created above
8593 if (FAILED(rc)) return rc;
8594 }
8595
8596 return rc;
8597}
8598
8599/**
8600 * Loads settings into mHWData.
8601 *
8602 * @param data Reference to the hardware settings.
8603 * @param pDbg Pointer to the debugging settings.
8604 * @param pAutostart Pointer to the autostart settings.
8605 */
8606HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8607 const settings::Autostart *pAutostart)
8608{
8609 AssertReturn(!i_isSessionMachine(), E_FAIL);
8610
8611 HRESULT rc = S_OK;
8612
8613 try
8614 {
8615 /* The hardware version attribute (optional). */
8616 mHWData->mHWVersion = data.strVersion;
8617 mHWData->mHardwareUUID = data.uuid;
8618
8619 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8620 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8621 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8622 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8623 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8624 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8625 mHWData->mPAEEnabled = data.fPAE;
8626 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8627 mHWData->mLongMode = data.enmLongMode;
8628 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8629 mHWData->mCPUCount = data.cCPUs;
8630 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8631 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8632
8633 // cpu
8634 if (mHWData->mCPUHotPlugEnabled)
8635 {
8636 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8637 it != data.llCpus.end();
8638 ++it)
8639 {
8640 const settings::Cpu &cpu = *it;
8641
8642 mHWData->mCPUAttached[cpu.ulId] = true;
8643 }
8644 }
8645
8646 // cpuid leafs
8647 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8648 it != data.llCpuIdLeafs.end();
8649 ++it)
8650 {
8651 const settings::CpuIdLeaf &leaf = *it;
8652
8653 switch (leaf.ulId)
8654 {
8655 case 0x0:
8656 case 0x1:
8657 case 0x2:
8658 case 0x3:
8659 case 0x4:
8660 case 0x5:
8661 case 0x6:
8662 case 0x7:
8663 case 0x8:
8664 case 0x9:
8665 case 0xA:
8666 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8667 break;
8668
8669 case 0x80000000:
8670 case 0x80000001:
8671 case 0x80000002:
8672 case 0x80000003:
8673 case 0x80000004:
8674 case 0x80000005:
8675 case 0x80000006:
8676 case 0x80000007:
8677 case 0x80000008:
8678 case 0x80000009:
8679 case 0x8000000A:
8680 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8681 break;
8682
8683 default:
8684 /* just ignore */
8685 break;
8686 }
8687 }
8688
8689 mHWData->mMemorySize = data.ulMemorySizeMB;
8690 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8691
8692 // boot order
8693 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8694 {
8695 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8696 if (it == data.mapBootOrder.end())
8697 mHWData->mBootOrder[i] = DeviceType_Null;
8698 else
8699 mHWData->mBootOrder[i] = it->second;
8700 }
8701
8702 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8703 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8704 mHWData->mMonitorCount = data.cMonitors;
8705 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8706 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8707 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8708 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8709 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8710 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8711 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8712 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8713 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8714 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8715 if (!data.strVideoCaptureFile.isEmpty())
8716 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8717 else
8718 mHWData->mVideoCaptureFile.setNull();
8719 mHWData->mFirmwareType = data.firmwareType;
8720 mHWData->mPointingHIDType = data.pointingHIDType;
8721 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8722 mHWData->mChipsetType = data.chipsetType;
8723 mHWData->mParavirtProvider = data.paravirtProvider;
8724 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8725 mHWData->mHPETEnabled = data.fHPETEnabled;
8726
8727 /* VRDEServer */
8728 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8729 if (FAILED(rc)) return rc;
8730
8731 /* BIOS */
8732 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8733 if (FAILED(rc)) return rc;
8734
8735 // Bandwidth control (must come before network adapters)
8736 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8737 if (FAILED(rc)) return rc;
8738
8739 /* Shared folders */
8740 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8741 it != data.usbSettings.llUSBControllers.end();
8742 ++it)
8743 {
8744 const settings::USBController &settingsCtrl = *it;
8745 ComObjPtr<USBController> newCtrl;
8746
8747 newCtrl.createObject();
8748 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8749 mUSBControllers->push_back(newCtrl);
8750 }
8751
8752 /* USB device filters */
8753 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8754 if (FAILED(rc)) return rc;
8755
8756 // network adapters
8757 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8758 size_t oldCount = mNetworkAdapters.size();
8759 if (newCount > oldCount)
8760 {
8761 mNetworkAdapters.resize(newCount);
8762 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8763 {
8764 unconst(mNetworkAdapters[slot]).createObject();
8765 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8766 }
8767 }
8768 else if (newCount < oldCount)
8769 mNetworkAdapters.resize(newCount);
8770 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8771 it != data.llNetworkAdapters.end();
8772 ++it)
8773 {
8774 const settings::NetworkAdapter &nic = *it;
8775
8776 /* slot unicity is guaranteed by XML Schema */
8777 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8778 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8779 if (FAILED(rc)) return rc;
8780 }
8781
8782 // serial ports
8783 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8784 it != data.llSerialPorts.end();
8785 ++it)
8786 {
8787 const settings::SerialPort &s = *it;
8788
8789 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8790 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8791 if (FAILED(rc)) return rc;
8792 }
8793
8794 // parallel ports (optional)
8795 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8796 it != data.llParallelPorts.end();
8797 ++it)
8798 {
8799 const settings::ParallelPort &p = *it;
8800
8801 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8802 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8803 if (FAILED(rc)) return rc;
8804 }
8805
8806 /* AudioAdapter */
8807 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8808 if (FAILED(rc)) return rc;
8809
8810 /* Shared folders */
8811 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8812 it != data.llSharedFolders.end();
8813 ++it)
8814 {
8815 const settings::SharedFolder &sf = *it;
8816
8817 ComObjPtr<SharedFolder> sharedFolder;
8818 /* Check for double entries. Not allowed! */
8819 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8820 if (SUCCEEDED(rc))
8821 return setError(VBOX_E_OBJECT_IN_USE,
8822 tr("Shared folder named '%s' already exists"),
8823 sf.strName.c_str());
8824
8825 /* Create the new shared folder. Don't break on error. This will be
8826 * reported when the machine starts. */
8827 sharedFolder.createObject();
8828 rc = sharedFolder->init(i_getMachine(),
8829 sf.strName,
8830 sf.strHostPath,
8831 RT_BOOL(sf.fWritable),
8832 RT_BOOL(sf.fAutoMount),
8833 false /* fFailOnError */);
8834 if (FAILED(rc)) return rc;
8835 mHWData->mSharedFolders.push_back(sharedFolder);
8836 }
8837
8838 // Clipboard
8839 mHWData->mClipboardMode = data.clipboardMode;
8840
8841 // drag'n'drop
8842 mHWData->mDnDMode = data.dndMode;
8843
8844 // guest settings
8845 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8846
8847 // IO settings
8848 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8849 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8850
8851 // Host PCI devices
8852 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8853 it != data.pciAttachments.end();
8854 ++it)
8855 {
8856 const settings::HostPCIDeviceAttachment &hpda = *it;
8857 ComObjPtr<PCIDeviceAttachment> pda;
8858
8859 pda.createObject();
8860 pda->i_loadSettings(this, hpda);
8861 mHWData->mPCIDeviceAssignments.push_back(pda);
8862 }
8863
8864 /*
8865 * (The following isn't really real hardware, but it lives in HWData
8866 * for reasons of convenience.)
8867 */
8868
8869#ifdef VBOX_WITH_GUEST_PROPS
8870 /* Guest properties (optional) */
8871 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8872 it != data.llGuestProperties.end();
8873 ++it)
8874 {
8875 const settings::GuestProperty &prop = *it;
8876 uint32_t fFlags = guestProp::NILFLAG;
8877 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8878 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8879 mHWData->mGuestProperties[prop.strName] = property;
8880 }
8881
8882 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8883#endif /* VBOX_WITH_GUEST_PROPS defined */
8884
8885 rc = i_loadDebugging(pDbg);
8886 if (FAILED(rc))
8887 return rc;
8888
8889 mHWData->mAutostart = *pAutostart;
8890
8891 /* default frontend */
8892 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8893 }
8894 catch(std::bad_alloc &)
8895 {
8896 return E_OUTOFMEMORY;
8897 }
8898
8899 AssertComRC(rc);
8900 return rc;
8901}
8902
8903/**
8904 * Called from Machine::loadHardware() to load the debugging settings of the
8905 * machine.
8906 *
8907 * @param pDbg Pointer to the settings.
8908 */
8909HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8910{
8911 mHWData->mDebugging = *pDbg;
8912 /* no more processing currently required, this will probably change. */
8913 return S_OK;
8914}
8915
8916/**
8917 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8918 *
8919 * @param data
8920 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8921 * @param puuidSnapshot
8922 * @return
8923 */
8924HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8925 const Guid *puuidRegistry,
8926 const Guid *puuidSnapshot)
8927{
8928 AssertReturn(!i_isSessionMachine(), E_FAIL);
8929
8930 HRESULT rc = S_OK;
8931
8932 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8933 it != data.llStorageControllers.end();
8934 ++it)
8935 {
8936 const settings::StorageController &ctlData = *it;
8937
8938 ComObjPtr<StorageController> pCtl;
8939 /* Try to find one with the name first. */
8940 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8941 if (SUCCEEDED(rc))
8942 return setError(VBOX_E_OBJECT_IN_USE,
8943 tr("Storage controller named '%s' already exists"),
8944 ctlData.strName.c_str());
8945
8946 pCtl.createObject();
8947 rc = pCtl->init(this,
8948 ctlData.strName,
8949 ctlData.storageBus,
8950 ctlData.ulInstance,
8951 ctlData.fBootable);
8952 if (FAILED(rc)) return rc;
8953
8954 mStorageControllers->push_back(pCtl);
8955
8956 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8957 if (FAILED(rc)) return rc;
8958
8959 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8960 if (FAILED(rc)) return rc;
8961
8962 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8963 if (FAILED(rc)) return rc;
8964
8965 /* Set IDE emulation settings (only for AHCI controller). */
8966 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8967 {
8968 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8969 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8970 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8971 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8972 )
8973 return rc;
8974 }
8975
8976 /* Load the attached devices now. */
8977 rc = i_loadStorageDevices(pCtl,
8978 ctlData,
8979 puuidRegistry,
8980 puuidSnapshot);
8981 if (FAILED(rc)) return rc;
8982 }
8983
8984 return S_OK;
8985}
8986
8987/**
8988 * Called from i_loadStorageControllers for a controller's devices.
8989 *
8990 * @param aStorageController
8991 * @param data
8992 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8993 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8994 * @return
8995 */
8996HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8997 const settings::StorageController &data,
8998 const Guid *puuidRegistry,
8999 const Guid *puuidSnapshot)
9000{
9001 HRESULT rc = S_OK;
9002
9003 /* paranoia: detect duplicate attachments */
9004 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9005 it != data.llAttachedDevices.end();
9006 ++it)
9007 {
9008 const settings::AttachedDevice &ad = *it;
9009
9010 for (settings::AttachedDevicesList::const_iterator it2 = it;
9011 it2 != data.llAttachedDevices.end();
9012 ++it2)
9013 {
9014 if (it == it2)
9015 continue;
9016
9017 const settings::AttachedDevice &ad2 = *it2;
9018
9019 if ( ad.lPort == ad2.lPort
9020 && ad.lDevice == ad2.lDevice)
9021 {
9022 return setError(E_FAIL,
9023 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9024 aStorageController->i_getName().c_str(),
9025 ad.lPort,
9026 ad.lDevice,
9027 mUserData->s.strName.c_str());
9028 }
9029 }
9030 }
9031
9032 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9033 it != data.llAttachedDevices.end();
9034 ++it)
9035 {
9036 const settings::AttachedDevice &dev = *it;
9037 ComObjPtr<Medium> medium;
9038
9039 switch (dev.deviceType)
9040 {
9041 case DeviceType_Floppy:
9042 case DeviceType_DVD:
9043 if (dev.strHostDriveSrc.isNotEmpty())
9044 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9045 false /* fRefresh */, medium);
9046 else
9047 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9048 dev.uuid,
9049 false /* fRefresh */,
9050 false /* aSetError */,
9051 medium);
9052 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9053 // This is not an error. The host drive or UUID might have vanished, so just go
9054 // ahead without this removeable medium attachment
9055 rc = S_OK;
9056 break;
9057
9058 case DeviceType_HardDisk:
9059 {
9060 /* find a hard disk by UUID */
9061 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9062 if (FAILED(rc))
9063 {
9064 if (i_isSnapshotMachine())
9065 {
9066 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9067 // so the user knows that the bad disk is in a snapshot somewhere
9068 com::ErrorInfo info;
9069 return setError(E_FAIL,
9070 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9071 puuidSnapshot->raw(),
9072 info.getText().raw());
9073 }
9074 else
9075 return rc;
9076 }
9077
9078 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9079
9080 if (medium->i_getType() == MediumType_Immutable)
9081 {
9082 if (i_isSnapshotMachine())
9083 return setError(E_FAIL,
9084 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9085 "of the virtual machine '%s' ('%s')"),
9086 medium->i_getLocationFull().c_str(),
9087 dev.uuid.raw(),
9088 puuidSnapshot->raw(),
9089 mUserData->s.strName.c_str(),
9090 mData->m_strConfigFileFull.c_str());
9091
9092 return setError(E_FAIL,
9093 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9094 medium->i_getLocationFull().c_str(),
9095 dev.uuid.raw(),
9096 mUserData->s.strName.c_str(),
9097 mData->m_strConfigFileFull.c_str());
9098 }
9099
9100 if (medium->i_getType() == MediumType_MultiAttach)
9101 {
9102 if (i_isSnapshotMachine())
9103 return setError(E_FAIL,
9104 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9105 "of the virtual machine '%s' ('%s')"),
9106 medium->i_getLocationFull().c_str(),
9107 dev.uuid.raw(),
9108 puuidSnapshot->raw(),
9109 mUserData->s.strName.c_str(),
9110 mData->m_strConfigFileFull.c_str());
9111
9112 return setError(E_FAIL,
9113 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9114 medium->i_getLocationFull().c_str(),
9115 dev.uuid.raw(),
9116 mUserData->s.strName.c_str(),
9117 mData->m_strConfigFileFull.c_str());
9118 }
9119
9120 if ( !i_isSnapshotMachine()
9121 && medium->i_getChildren().size() != 0
9122 )
9123 return setError(E_FAIL,
9124 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9125 "because it has %d differencing child hard disks"),
9126 medium->i_getLocationFull().c_str(),
9127 dev.uuid.raw(),
9128 mUserData->s.strName.c_str(),
9129 mData->m_strConfigFileFull.c_str(),
9130 medium->i_getChildren().size());
9131
9132 if (i_findAttachment(mMediaData->mAttachments,
9133 medium))
9134 return setError(E_FAIL,
9135 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9136 medium->i_getLocationFull().c_str(),
9137 dev.uuid.raw(),
9138 mUserData->s.strName.c_str(),
9139 mData->m_strConfigFileFull.c_str());
9140
9141 break;
9142 }
9143
9144 default:
9145 return setError(E_FAIL,
9146 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9147 medium->i_getLocationFull().c_str(),
9148 mUserData->s.strName.c_str(),
9149 mData->m_strConfigFileFull.c_str());
9150 }
9151
9152 if (FAILED(rc))
9153 break;
9154
9155 /* Bandwidth groups are loaded at this point. */
9156 ComObjPtr<BandwidthGroup> pBwGroup;
9157
9158 if (!dev.strBwGroup.isEmpty())
9159 {
9160 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9161 if (FAILED(rc))
9162 return setError(E_FAIL,
9163 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9164 medium->i_getLocationFull().c_str(),
9165 dev.strBwGroup.c_str(),
9166 mUserData->s.strName.c_str(),
9167 mData->m_strConfigFileFull.c_str());
9168 pBwGroup->i_reference();
9169 }
9170
9171 const Bstr controllerName = aStorageController->i_getName();
9172 ComObjPtr<MediumAttachment> pAttachment;
9173 pAttachment.createObject();
9174 rc = pAttachment->init(this,
9175 medium,
9176 controllerName,
9177 dev.lPort,
9178 dev.lDevice,
9179 dev.deviceType,
9180 false,
9181 dev.fPassThrough,
9182 dev.fTempEject,
9183 dev.fNonRotational,
9184 dev.fDiscard,
9185 dev.fHotPluggable,
9186 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9187 if (FAILED(rc)) break;
9188
9189 /* associate the medium with this machine and snapshot */
9190 if (!medium.isNull())
9191 {
9192 AutoCaller medCaller(medium);
9193 if (FAILED(medCaller.rc())) return medCaller.rc();
9194 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9195
9196 if (i_isSnapshotMachine())
9197 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9198 else
9199 rc = medium->i_addBackReference(mData->mUuid);
9200 /* If the medium->addBackReference fails it sets an appropriate
9201 * error message, so no need to do any guesswork here. */
9202
9203 if (puuidRegistry)
9204 // caller wants registry ID to be set on all attached media (OVF import case)
9205 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9206 }
9207
9208 if (FAILED(rc))
9209 break;
9210
9211 /* back up mMediaData to let registeredInit() properly rollback on failure
9212 * (= limited accessibility) */
9213 i_setModified(IsModified_Storage);
9214 mMediaData.backup();
9215 mMediaData->mAttachments.push_back(pAttachment);
9216 }
9217
9218 return rc;
9219}
9220
9221/**
9222 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9223 *
9224 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9225 * @param aSnapshot where to return the found snapshot
9226 * @param aSetError true to set extended error info on failure
9227 */
9228HRESULT Machine::i_findSnapshotById(const Guid &aId,
9229 ComObjPtr<Snapshot> &aSnapshot,
9230 bool aSetError /* = false */)
9231{
9232 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9233
9234 if (!mData->mFirstSnapshot)
9235 {
9236 if (aSetError)
9237 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9238 return E_FAIL;
9239 }
9240
9241 if (aId.isZero())
9242 aSnapshot = mData->mFirstSnapshot;
9243 else
9244 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9245
9246 if (!aSnapshot)
9247 {
9248 if (aSetError)
9249 return setError(E_FAIL,
9250 tr("Could not find a snapshot with UUID {%s}"),
9251 aId.toString().c_str());
9252 return E_FAIL;
9253 }
9254
9255 return S_OK;
9256}
9257
9258/**
9259 * Returns the snapshot with the given name or fails of no such snapshot.
9260 *
9261 * @param aName snapshot name to find
9262 * @param aSnapshot where to return the found snapshot
9263 * @param aSetError true to set extended error info on failure
9264 */
9265HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9266 ComObjPtr<Snapshot> &aSnapshot,
9267 bool aSetError /* = false */)
9268{
9269 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9270
9271 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9272
9273 if (!mData->mFirstSnapshot)
9274 {
9275 if (aSetError)
9276 return setError(VBOX_E_OBJECT_NOT_FOUND,
9277 tr("This machine does not have any snapshots"));
9278 return VBOX_E_OBJECT_NOT_FOUND;
9279 }
9280
9281 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9282
9283 if (!aSnapshot)
9284 {
9285 if (aSetError)
9286 return setError(VBOX_E_OBJECT_NOT_FOUND,
9287 tr("Could not find a snapshot named '%s'"), strName.c_str());
9288 return VBOX_E_OBJECT_NOT_FOUND;
9289 }
9290
9291 return S_OK;
9292}
9293
9294/**
9295 * Returns a storage controller object with the given name.
9296 *
9297 * @param aName storage controller name to find
9298 * @param aStorageController where to return the found storage controller
9299 * @param aSetError true to set extended error info on failure
9300 */
9301HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9302 ComObjPtr<StorageController> &aStorageController,
9303 bool aSetError /* = false */)
9304{
9305 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9306
9307 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9308 it != mStorageControllers->end();
9309 ++it)
9310 {
9311 if ((*it)->i_getName() == aName)
9312 {
9313 aStorageController = (*it);
9314 return S_OK;
9315 }
9316 }
9317
9318 if (aSetError)
9319 return setError(VBOX_E_OBJECT_NOT_FOUND,
9320 tr("Could not find a storage controller named '%s'"),
9321 aName.c_str());
9322 return VBOX_E_OBJECT_NOT_FOUND;
9323}
9324
9325/**
9326 * Returns a USB controller object with the given name.
9327 *
9328 * @param aName USB controller name to find
9329 * @param aUSBController where to return the found USB controller
9330 * @param aSetError true to set extended error info on failure
9331 */
9332HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9333 ComObjPtr<USBController> &aUSBController,
9334 bool aSetError /* = false */)
9335{
9336 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9337
9338 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9339 it != mUSBControllers->end();
9340 ++it)
9341 {
9342 if ((*it)->i_getName() == aName)
9343 {
9344 aUSBController = (*it);
9345 return S_OK;
9346 }
9347 }
9348
9349 if (aSetError)
9350 return setError(VBOX_E_OBJECT_NOT_FOUND,
9351 tr("Could not find a storage controller named '%s'"),
9352 aName.c_str());
9353 return VBOX_E_OBJECT_NOT_FOUND;
9354}
9355
9356/**
9357 * Returns the number of USB controller instance of the given type.
9358 *
9359 * @param enmType USB controller type.
9360 */
9361ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9362{
9363 ULONG cCtrls = 0;
9364
9365 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9366 it != mUSBControllers->end();
9367 ++it)
9368 {
9369 if ((*it)->i_getControllerType() == enmType)
9370 cCtrls++;
9371 }
9372
9373 return cCtrls;
9374}
9375
9376HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9377 MediaData::AttachmentList &atts)
9378{
9379 AutoCaller autoCaller(this);
9380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9381
9382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9383
9384 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9385 it != mMediaData->mAttachments.end();
9386 ++it)
9387 {
9388 const ComObjPtr<MediumAttachment> &pAtt = *it;
9389 // should never happen, but deal with NULL pointers in the list.
9390 AssertStmt(!pAtt.isNull(), continue);
9391
9392 // getControllerName() needs caller+read lock
9393 AutoCaller autoAttCaller(pAtt);
9394 if (FAILED(autoAttCaller.rc()))
9395 {
9396 atts.clear();
9397 return autoAttCaller.rc();
9398 }
9399 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9400
9401 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9402 atts.push_back(pAtt);
9403 }
9404
9405 return S_OK;
9406}
9407
9408
9409/**
9410 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9411 * file if the machine name was changed and about creating a new settings file
9412 * if this is a new machine.
9413 *
9414 * @note Must be never called directly but only from #saveSettings().
9415 */
9416HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9417{
9418 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9419
9420 HRESULT rc = S_OK;
9421
9422 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9423
9424 /// @todo need to handle primary group change, too
9425
9426 /* attempt to rename the settings file if machine name is changed */
9427 if ( mUserData->s.fNameSync
9428 && mUserData.isBackedUp()
9429 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9430 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9431 )
9432 {
9433 bool dirRenamed = false;
9434 bool fileRenamed = false;
9435
9436 Utf8Str configFile, newConfigFile;
9437 Utf8Str configFilePrev, newConfigFilePrev;
9438 Utf8Str configDir, newConfigDir;
9439
9440 do
9441 {
9442 int vrc = VINF_SUCCESS;
9443
9444 Utf8Str name = mUserData.backedUpData()->s.strName;
9445 Utf8Str newName = mUserData->s.strName;
9446 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9447 if (group == "/")
9448 group.setNull();
9449 Utf8Str newGroup = mUserData->s.llGroups.front();
9450 if (newGroup == "/")
9451 newGroup.setNull();
9452
9453 configFile = mData->m_strConfigFileFull;
9454
9455 /* first, rename the directory if it matches the group and machine name */
9456 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9457 group.c_str(), RTPATH_DELIMITER, name.c_str());
9458 /** @todo hack, make somehow use of ComposeMachineFilename */
9459 if (mUserData->s.fDirectoryIncludesUUID)
9460 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9461 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9462 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9463 /** @todo hack, make somehow use of ComposeMachineFilename */
9464 if (mUserData->s.fDirectoryIncludesUUID)
9465 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9466 configDir = configFile;
9467 configDir.stripFilename();
9468 newConfigDir = configDir;
9469 if ( configDir.length() >= groupPlusName.length()
9470 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9471 groupPlusName.c_str()))
9472 {
9473 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9474 Utf8Str newConfigBaseDir(newConfigDir);
9475 newConfigDir.append(newGroupPlusName);
9476 /* consistency: use \ if appropriate on the platform */
9477 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9478 /* new dir and old dir cannot be equal here because of 'if'
9479 * above and because name != newName */
9480 Assert(configDir != newConfigDir);
9481 if (!fSettingsFileIsNew)
9482 {
9483 /* perform real rename only if the machine is not new */
9484 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9485 if ( vrc == VERR_FILE_NOT_FOUND
9486 || vrc == VERR_PATH_NOT_FOUND)
9487 {
9488 /* create the parent directory, then retry renaming */
9489 Utf8Str parent(newConfigDir);
9490 parent.stripFilename();
9491 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9492 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9493 }
9494 if (RT_FAILURE(vrc))
9495 {
9496 rc = setError(E_FAIL,
9497 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9498 configDir.c_str(),
9499 newConfigDir.c_str(),
9500 vrc);
9501 break;
9502 }
9503 /* delete subdirectories which are no longer needed */
9504 Utf8Str dir(configDir);
9505 dir.stripFilename();
9506 while (dir != newConfigBaseDir && dir != ".")
9507 {
9508 vrc = RTDirRemove(dir.c_str());
9509 if (RT_FAILURE(vrc))
9510 break;
9511 dir.stripFilename();
9512 }
9513 dirRenamed = true;
9514 }
9515 }
9516
9517 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9518 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9519
9520 /* then try to rename the settings file itself */
9521 if (newConfigFile != configFile)
9522 {
9523 /* get the path to old settings file in renamed directory */
9524 configFile = Utf8StrFmt("%s%c%s",
9525 newConfigDir.c_str(),
9526 RTPATH_DELIMITER,
9527 RTPathFilename(configFile.c_str()));
9528 if (!fSettingsFileIsNew)
9529 {
9530 /* perform real rename only if the machine is not new */
9531 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9532 if (RT_FAILURE(vrc))
9533 {
9534 rc = setError(E_FAIL,
9535 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9536 configFile.c_str(),
9537 newConfigFile.c_str(),
9538 vrc);
9539 break;
9540 }
9541 fileRenamed = true;
9542 configFilePrev = configFile;
9543 configFilePrev += "-prev";
9544 newConfigFilePrev = newConfigFile;
9545 newConfigFilePrev += "-prev";
9546 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9547 }
9548 }
9549
9550 // update m_strConfigFileFull amd mConfigFile
9551 mData->m_strConfigFileFull = newConfigFile;
9552 // compute the relative path too
9553 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9554
9555 // store the old and new so that VirtualBox::i_saveSettings() can update
9556 // the media registry
9557 if ( mData->mRegistered
9558 && (configDir != newConfigDir || configFile != newConfigFile))
9559 {
9560 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9561
9562 if (pfNeedsGlobalSaveSettings)
9563 *pfNeedsGlobalSaveSettings = true;
9564 }
9565
9566 // in the saved state file path, replace the old directory with the new directory
9567 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9568 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9569
9570 // and do the same thing for the saved state file paths of all the online snapshots
9571 if (mData->mFirstSnapshot)
9572 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9573 newConfigDir.c_str());
9574 }
9575 while (0);
9576
9577 if (FAILED(rc))
9578 {
9579 /* silently try to rename everything back */
9580 if (fileRenamed)
9581 {
9582 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9583 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9584 }
9585 if (dirRenamed)
9586 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9587 }
9588
9589 if (FAILED(rc)) return rc;
9590 }
9591
9592 if (fSettingsFileIsNew)
9593 {
9594 /* create a virgin config file */
9595 int vrc = VINF_SUCCESS;
9596
9597 /* ensure the settings directory exists */
9598 Utf8Str path(mData->m_strConfigFileFull);
9599 path.stripFilename();
9600 if (!RTDirExists(path.c_str()))
9601 {
9602 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9603 if (RT_FAILURE(vrc))
9604 {
9605 return setError(E_FAIL,
9606 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9607 path.c_str(),
9608 vrc);
9609 }
9610 }
9611
9612 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9613 path = Utf8Str(mData->m_strConfigFileFull);
9614 RTFILE f = NIL_RTFILE;
9615 vrc = RTFileOpen(&f, path.c_str(),
9616 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9617 if (RT_FAILURE(vrc))
9618 return setError(E_FAIL,
9619 tr("Could not create the settings file '%s' (%Rrc)"),
9620 path.c_str(),
9621 vrc);
9622 RTFileClose(f);
9623 }
9624
9625 return rc;
9626}
9627
9628/**
9629 * Saves and commits machine data, user data and hardware data.
9630 *
9631 * Note that on failure, the data remains uncommitted.
9632 *
9633 * @a aFlags may combine the following flags:
9634 *
9635 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9636 * Used when saving settings after an operation that makes them 100%
9637 * correspond to the settings from the current snapshot.
9638 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9639 * #isReallyModified() returns false. This is necessary for cases when we
9640 * change machine data directly, not through the backup()/commit() mechanism.
9641 * - SaveS_Force: settings will be saved without doing a deep compare of the
9642 * settings structures. This is used when this is called because snapshots
9643 * have changed to avoid the overhead of the deep compare.
9644 *
9645 * @note Must be called from under this object's write lock. Locks children for
9646 * writing.
9647 *
9648 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9649 * initialized to false and that will be set to true by this function if
9650 * the caller must invoke VirtualBox::i_saveSettings() because the global
9651 * settings have changed. This will happen if a machine rename has been
9652 * saved and the global machine and media registries will therefore need
9653 * updating.
9654 */
9655HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9656 int aFlags /*= 0*/)
9657{
9658 LogFlowThisFuncEnter();
9659
9660 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9661
9662 /* make sure child objects are unable to modify the settings while we are
9663 * saving them */
9664 i_ensureNoStateDependencies();
9665
9666 AssertReturn(!i_isSnapshotMachine(),
9667 E_FAIL);
9668
9669 HRESULT rc = S_OK;
9670 bool fNeedsWrite = false;
9671
9672 /* First, prepare to save settings. It will care about renaming the
9673 * settings directory and file if the machine name was changed and about
9674 * creating a new settings file if this is a new machine. */
9675 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9676 if (FAILED(rc)) return rc;
9677
9678 // keep a pointer to the current settings structures
9679 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9680 settings::MachineConfigFile *pNewConfig = NULL;
9681
9682 try
9683 {
9684 // make a fresh one to have everyone write stuff into
9685 pNewConfig = new settings::MachineConfigFile(NULL);
9686 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9687
9688 // now go and copy all the settings data from COM to the settings structures
9689 // (this calles i_saveSettings() on all the COM objects in the machine)
9690 i_copyMachineDataToSettings(*pNewConfig);
9691
9692 if (aFlags & SaveS_ResetCurStateModified)
9693 {
9694 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9695 mData->mCurrentStateModified = FALSE;
9696 fNeedsWrite = true; // always, no need to compare
9697 }
9698 else if (aFlags & SaveS_Force)
9699 {
9700 fNeedsWrite = true; // always, no need to compare
9701 }
9702 else
9703 {
9704 if (!mData->mCurrentStateModified)
9705 {
9706 // do a deep compare of the settings that we just saved with the settings
9707 // previously stored in the config file; this invokes MachineConfigFile::operator==
9708 // which does a deep compare of all the settings, which is expensive but less expensive
9709 // than writing out XML in vain
9710 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9711
9712 // could still be modified if any settings changed
9713 mData->mCurrentStateModified = fAnySettingsChanged;
9714
9715 fNeedsWrite = fAnySettingsChanged;
9716 }
9717 else
9718 fNeedsWrite = true;
9719 }
9720
9721 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9722
9723 if (fNeedsWrite)
9724 // now spit it all out!
9725 pNewConfig->write(mData->m_strConfigFileFull);
9726
9727 mData->pMachineConfigFile = pNewConfig;
9728 delete pOldConfig;
9729 i_commit();
9730
9731 // after saving settings, we are no longer different from the XML on disk
9732 mData->flModifications = 0;
9733 }
9734 catch (HRESULT err)
9735 {
9736 // we assume that error info is set by the thrower
9737 rc = err;
9738
9739 // restore old config
9740 delete pNewConfig;
9741 mData->pMachineConfigFile = pOldConfig;
9742 }
9743 catch (...)
9744 {
9745 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9746 }
9747
9748 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9749 {
9750 /* Fire the data change event, even on failure (since we've already
9751 * committed all data). This is done only for SessionMachines because
9752 * mutable Machine instances are always not registered (i.e. private
9753 * to the client process that creates them) and thus don't need to
9754 * inform callbacks. */
9755 if (i_isSessionMachine())
9756 mParent->i_onMachineDataChange(mData->mUuid);
9757 }
9758
9759 LogFlowThisFunc(("rc=%08X\n", rc));
9760 LogFlowThisFuncLeave();
9761 return rc;
9762}
9763
9764/**
9765 * Implementation for saving the machine settings into the given
9766 * settings::MachineConfigFile instance. This copies machine extradata
9767 * from the previous machine config file in the instance data, if any.
9768 *
9769 * This gets called from two locations:
9770 *
9771 * -- Machine::i_saveSettings(), during the regular XML writing;
9772 *
9773 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9774 * exported to OVF and we write the VirtualBox proprietary XML
9775 * into a <vbox:Machine> tag.
9776 *
9777 * This routine fills all the fields in there, including snapshots, *except*
9778 * for the following:
9779 *
9780 * -- fCurrentStateModified. There is some special logic associated with that.
9781 *
9782 * The caller can then call MachineConfigFile::write() or do something else
9783 * with it.
9784 *
9785 * Caller must hold the machine lock!
9786 *
9787 * This throws XML errors and HRESULT, so the caller must have a catch block!
9788 */
9789void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9790{
9791 // deep copy extradata
9792 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9793
9794 config.uuid = mData->mUuid;
9795
9796 // copy name, description, OS type, teleport, UTC etc.
9797 config.machineUserData = mUserData->s;
9798
9799 // Encode the Icon Override data from Machine and store on config userdata.
9800 std::vector<BYTE> iconByte;
9801 getIcon(iconByte);
9802 ssize_t cbData = iconByte.size();
9803 if (cbData > 0)
9804 {
9805 ssize_t cchOut = RTBase64EncodedLength(cbData);
9806 Utf8Str strIconData;
9807 strIconData.reserve(cchOut+1);
9808 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9809 strIconData.mutableRaw(), strIconData.capacity(),
9810 NULL);
9811 if (RT_FAILURE(vrc))
9812 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9813 strIconData.jolt();
9814 config.machineUserData.ovIcon = strIconData;
9815 }
9816 else
9817 config.machineUserData.ovIcon.setNull();
9818
9819 if ( mData->mMachineState == MachineState_Saved
9820 || mData->mMachineState == MachineState_Restoring
9821 // when deleting a snapshot we may or may not have a saved state in the current state,
9822 // so let's not assert here please
9823 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9824 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9825 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9826 && (!mSSData->strStateFilePath.isEmpty())
9827 )
9828 )
9829 {
9830 Assert(!mSSData->strStateFilePath.isEmpty());
9831 /* try to make the file name relative to the settings file dir */
9832 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9833 }
9834 else
9835 {
9836 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9837 config.strStateFile.setNull();
9838 }
9839
9840 if (mData->mCurrentSnapshot)
9841 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9842 else
9843 config.uuidCurrentSnapshot.clear();
9844
9845 config.timeLastStateChange = mData->mLastStateChange;
9846 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9847 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9848
9849 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9850 if (FAILED(rc)) throw rc;
9851
9852 rc = i_saveStorageControllers(config.storageMachine);
9853 if (FAILED(rc)) throw rc;
9854
9855 // save machine's media registry if this is VirtualBox 4.0 or later
9856 if (config.canHaveOwnMediaRegistry())
9857 {
9858 // determine machine folder
9859 Utf8Str strMachineFolder = i_getSettingsFileFull();
9860 strMachineFolder.stripFilename();
9861 mParent->i_saveMediaRegistry(config.mediaRegistry,
9862 i_getId(), // only media with registry ID == machine UUID
9863 strMachineFolder);
9864 // this throws HRESULT
9865 }
9866
9867 // save snapshots
9868 rc = i_saveAllSnapshots(config);
9869 if (FAILED(rc)) throw rc;
9870}
9871
9872/**
9873 * Saves all snapshots of the machine into the given machine config file. Called
9874 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9875 * @param config
9876 * @return
9877 */
9878HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9879{
9880 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9881
9882 HRESULT rc = S_OK;
9883
9884 try
9885 {
9886 config.llFirstSnapshot.clear();
9887
9888 if (mData->mFirstSnapshot)
9889 {
9890 settings::Snapshot snapNew;
9891 config.llFirstSnapshot.push_back(snapNew);
9892
9893 // get reference to the fresh copy of the snapshot on the list and
9894 // work on that copy directly to avoid excessive copying later
9895 settings::Snapshot &snap = config.llFirstSnapshot.front();
9896
9897 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9898 if (FAILED(rc)) throw rc;
9899 }
9900
9901// if (mType == IsSessionMachine)
9902// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9903
9904 }
9905 catch (HRESULT err)
9906 {
9907 /* we assume that error info is set by the thrower */
9908 rc = err;
9909 }
9910 catch (...)
9911 {
9912 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9913 }
9914
9915 return rc;
9916}
9917
9918/**
9919 * Saves the VM hardware configuration. It is assumed that the
9920 * given node is empty.
9921 *
9922 * @param data Reference to the settings object for the hardware config.
9923 * @param pDbg Pointer to the settings object for the debugging config
9924 * which happens to live in mHWData.
9925 * @param pAutostart Pointer to the settings object for the autostart config
9926 * which happens to live in mHWData.
9927 */
9928HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9929 settings::Autostart *pAutostart)
9930{
9931 HRESULT rc = S_OK;
9932
9933 try
9934 {
9935 /* The hardware version attribute (optional).
9936 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9937 if ( mHWData->mHWVersion == "1"
9938 && mSSData->strStateFilePath.isEmpty()
9939 )
9940 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9941 other point needs to be found where this can be done. */
9942
9943 data.strVersion = mHWData->mHWVersion;
9944 data.uuid = mHWData->mHardwareUUID;
9945
9946 // CPU
9947 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9948 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9949 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9950 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9951 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9952 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9953 data.fPAE = !!mHWData->mPAEEnabled;
9954 data.enmLongMode = mHWData->mLongMode;
9955 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9956 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9957
9958 /* Standard and Extended CPUID leafs. */
9959 data.llCpuIdLeafs.clear();
9960 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9961 {
9962 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9963 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9964 }
9965 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9966 {
9967 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9968 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9969 }
9970
9971 data.cCPUs = mHWData->mCPUCount;
9972 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9973 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9974
9975 data.llCpus.clear();
9976 if (data.fCpuHotPlug)
9977 {
9978 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9979 {
9980 if (mHWData->mCPUAttached[idx])
9981 {
9982 settings::Cpu cpu;
9983 cpu.ulId = idx;
9984 data.llCpus.push_back(cpu);
9985 }
9986 }
9987 }
9988
9989 // memory
9990 data.ulMemorySizeMB = mHWData->mMemorySize;
9991 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9992
9993 // firmware
9994 data.firmwareType = mHWData->mFirmwareType;
9995
9996 // HID
9997 data.pointingHIDType = mHWData->mPointingHIDType;
9998 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9999
10000 // chipset
10001 data.chipsetType = mHWData->mChipsetType;
10002
10003 // paravirt
10004 data.paravirtProvider = mHWData->mParavirtProvider;
10005
10006
10007 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10008
10009 // HPET
10010 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10011
10012 // boot order
10013 data.mapBootOrder.clear();
10014 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10015 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10016
10017 // display
10018 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10019 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10020 data.cMonitors = mHWData->mMonitorCount;
10021 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10022 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10023 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10024 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10025 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10026 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10027 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10028 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10029 {
10030 if (mHWData->maVideoCaptureScreens[i])
10031 ASMBitSet(&data.u64VideoCaptureScreens, i);
10032 else
10033 ASMBitClear(&data.u64VideoCaptureScreens, i);
10034 }
10035 /* store relative video capture file if possible */
10036 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10037
10038 /* VRDEServer settings (optional) */
10039 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10040 if (FAILED(rc)) throw rc;
10041
10042 /* BIOS (required) */
10043 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10044 if (FAILED(rc)) throw rc;
10045
10046 /* USB Controller (required) */
10047 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10048 {
10049 ComObjPtr<USBController> ctrl = *it;
10050 settings::USBController settingsCtrl;
10051
10052 settingsCtrl.strName = ctrl->i_getName();
10053 settingsCtrl.enmType = ctrl->i_getControllerType();
10054
10055 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10056 }
10057
10058 /* USB device filters (required) */
10059 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10060 if (FAILED(rc)) throw rc;
10061
10062 /* Network adapters (required) */
10063 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10064 data.llNetworkAdapters.clear();
10065 /* Write out only the nominal number of network adapters for this
10066 * chipset type. Since Machine::commit() hasn't been called there
10067 * may be extra NIC settings in the vector. */
10068 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10069 {
10070 settings::NetworkAdapter nic;
10071 nic.ulSlot = (uint32_t)slot;
10072 /* paranoia check... must not be NULL, but must not crash either. */
10073 if (mNetworkAdapters[slot])
10074 {
10075 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10076 if (FAILED(rc)) throw rc;
10077
10078 data.llNetworkAdapters.push_back(nic);
10079 }
10080 }
10081
10082 /* Serial ports */
10083 data.llSerialPorts.clear();
10084 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10085 {
10086 settings::SerialPort s;
10087 s.ulSlot = slot;
10088 rc = mSerialPorts[slot]->i_saveSettings(s);
10089 if (FAILED(rc)) return rc;
10090
10091 data.llSerialPorts.push_back(s);
10092 }
10093
10094 /* Parallel ports */
10095 data.llParallelPorts.clear();
10096 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10097 {
10098 settings::ParallelPort p;
10099 p.ulSlot = slot;
10100 rc = mParallelPorts[slot]->i_saveSettings(p);
10101 if (FAILED(rc)) return rc;
10102
10103 data.llParallelPorts.push_back(p);
10104 }
10105
10106 /* Audio adapter */
10107 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10108 if (FAILED(rc)) return rc;
10109
10110 /* Shared folders */
10111 data.llSharedFolders.clear();
10112 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10113 it != mHWData->mSharedFolders.end();
10114 ++it)
10115 {
10116 SharedFolder *pSF = *it;
10117 AutoCaller sfCaller(pSF);
10118 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10119 settings::SharedFolder sf;
10120 sf.strName = pSF->i_getName();
10121 sf.strHostPath = pSF->i_getHostPath();
10122 sf.fWritable = !!pSF->i_isWritable();
10123 sf.fAutoMount = !!pSF->i_isAutoMounted();
10124
10125 data.llSharedFolders.push_back(sf);
10126 }
10127
10128 // clipboard
10129 data.clipboardMode = mHWData->mClipboardMode;
10130
10131 // drag'n'drop
10132 data.dndMode = mHWData->mDnDMode;
10133
10134 /* Guest */
10135 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10136
10137 // IO settings
10138 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10139 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10140
10141 /* BandwidthControl (required) */
10142 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10143 if (FAILED(rc)) throw rc;
10144
10145 /* Host PCI devices */
10146 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10147 it != mHWData->mPCIDeviceAssignments.end();
10148 ++it)
10149 {
10150 ComObjPtr<PCIDeviceAttachment> pda = *it;
10151 settings::HostPCIDeviceAttachment hpda;
10152
10153 rc = pda->i_saveSettings(hpda);
10154 if (FAILED(rc)) throw rc;
10155
10156 data.pciAttachments.push_back(hpda);
10157 }
10158
10159
10160 // guest properties
10161 data.llGuestProperties.clear();
10162#ifdef VBOX_WITH_GUEST_PROPS
10163 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10164 it != mHWData->mGuestProperties.end();
10165 ++it)
10166 {
10167 HWData::GuestProperty property = it->second;
10168
10169 /* Remove transient guest properties at shutdown unless we
10170 * are saving state */
10171 if ( ( mData->mMachineState == MachineState_PoweredOff
10172 || mData->mMachineState == MachineState_Aborted
10173 || mData->mMachineState == MachineState_Teleported)
10174 && ( property.mFlags & guestProp::TRANSIENT
10175 || property.mFlags & guestProp::TRANSRESET))
10176 continue;
10177 settings::GuestProperty prop;
10178 prop.strName = it->first;
10179 prop.strValue = property.strValue;
10180 prop.timestamp = property.mTimestamp;
10181 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10182 guestProp::writeFlags(property.mFlags, szFlags);
10183 prop.strFlags = szFlags;
10184
10185 data.llGuestProperties.push_back(prop);
10186 }
10187
10188 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10189 /* I presume this doesn't require a backup(). */
10190 mData->mGuestPropertiesModified = FALSE;
10191#endif /* VBOX_WITH_GUEST_PROPS defined */
10192
10193 *pDbg = mHWData->mDebugging;
10194 *pAutostart = mHWData->mAutostart;
10195
10196 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10197 }
10198 catch(std::bad_alloc &)
10199 {
10200 return E_OUTOFMEMORY;
10201 }
10202
10203 AssertComRC(rc);
10204 return rc;
10205}
10206
10207/**
10208 * Saves the storage controller configuration.
10209 *
10210 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10211 */
10212HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10213{
10214 data.llStorageControllers.clear();
10215
10216 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10217 it != mStorageControllers->end();
10218 ++it)
10219 {
10220 HRESULT rc;
10221 ComObjPtr<StorageController> pCtl = *it;
10222
10223 settings::StorageController ctl;
10224 ctl.strName = pCtl->i_getName();
10225 ctl.controllerType = pCtl->i_getControllerType();
10226 ctl.storageBus = pCtl->i_getStorageBus();
10227 ctl.ulInstance = pCtl->i_getInstance();
10228 ctl.fBootable = pCtl->i_getBootable();
10229
10230 /* Save the port count. */
10231 ULONG portCount;
10232 rc = pCtl->COMGETTER(PortCount)(&portCount);
10233 ComAssertComRCRet(rc, rc);
10234 ctl.ulPortCount = portCount;
10235
10236 /* Save fUseHostIOCache */
10237 BOOL fUseHostIOCache;
10238 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10239 ComAssertComRCRet(rc, rc);
10240 ctl.fUseHostIOCache = !!fUseHostIOCache;
10241
10242 /* Save IDE emulation settings. */
10243 if (ctl.controllerType == StorageControllerType_IntelAhci)
10244 {
10245 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10246 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10247 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10248 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10249 )
10250 ComAssertComRCRet(rc, rc);
10251 }
10252
10253 /* save the devices now. */
10254 rc = i_saveStorageDevices(pCtl, ctl);
10255 ComAssertComRCRet(rc, rc);
10256
10257 data.llStorageControllers.push_back(ctl);
10258 }
10259
10260 return S_OK;
10261}
10262
10263/**
10264 * Saves the hard disk configuration.
10265 */
10266HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10267 settings::StorageController &data)
10268{
10269 MediaData::AttachmentList atts;
10270
10271 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10272 if (FAILED(rc)) return rc;
10273
10274 data.llAttachedDevices.clear();
10275 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10276 it != atts.end();
10277 ++it)
10278 {
10279 settings::AttachedDevice dev;
10280 IMediumAttachment *iA = *it;
10281 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10282 Medium *pMedium = pAttach->i_getMedium();
10283
10284 dev.deviceType = pAttach->i_getType();
10285 dev.lPort = pAttach->i_getPort();
10286 dev.lDevice = pAttach->i_getDevice();
10287 dev.fPassThrough = pAttach->i_getPassthrough();
10288 dev.fHotPluggable = pAttach->i_getHotPluggable();
10289 if (pMedium)
10290 {
10291 if (pMedium->i_isHostDrive())
10292 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10293 else
10294 dev.uuid = pMedium->i_getId();
10295 dev.fTempEject = pAttach->i_getTempEject();
10296 dev.fNonRotational = pAttach->i_getNonRotational();
10297 dev.fDiscard = pAttach->i_getDiscard();
10298 }
10299
10300 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10301
10302 data.llAttachedDevices.push_back(dev);
10303 }
10304
10305 return S_OK;
10306}
10307
10308/**
10309 * Saves machine state settings as defined by aFlags
10310 * (SaveSTS_* values).
10311 *
10312 * @param aFlags Combination of SaveSTS_* flags.
10313 *
10314 * @note Locks objects for writing.
10315 */
10316HRESULT Machine::i_saveStateSettings(int aFlags)
10317{
10318 if (aFlags == 0)
10319 return S_OK;
10320
10321 AutoCaller autoCaller(this);
10322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10323
10324 /* This object's write lock is also necessary to serialize file access
10325 * (prevent concurrent reads and writes) */
10326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10327
10328 HRESULT rc = S_OK;
10329
10330 Assert(mData->pMachineConfigFile);
10331
10332 try
10333 {
10334 if (aFlags & SaveSTS_CurStateModified)
10335 mData->pMachineConfigFile->fCurrentStateModified = true;
10336
10337 if (aFlags & SaveSTS_StateFilePath)
10338 {
10339 if (!mSSData->strStateFilePath.isEmpty())
10340 /* try to make the file name relative to the settings file dir */
10341 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10342 else
10343 mData->pMachineConfigFile->strStateFile.setNull();
10344 }
10345
10346 if (aFlags & SaveSTS_StateTimeStamp)
10347 {
10348 Assert( mData->mMachineState != MachineState_Aborted
10349 || mSSData->strStateFilePath.isEmpty());
10350
10351 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10352
10353 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10354//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10355 }
10356
10357 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10358 }
10359 catch (...)
10360 {
10361 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10362 }
10363
10364 return rc;
10365}
10366
10367/**
10368 * Ensures that the given medium is added to a media registry. If this machine
10369 * was created with 4.0 or later, then the machine registry is used. Otherwise
10370 * the global VirtualBox media registry is used.
10371 *
10372 * Caller must NOT hold machine lock, media tree or any medium locks!
10373 *
10374 * @param pMedium
10375 */
10376void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10377{
10378 /* Paranoia checks: do not hold machine or media tree locks. */
10379 AssertReturnVoid(!isWriteLockOnCurrentThread());
10380 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10381
10382 ComObjPtr<Medium> pBase;
10383 {
10384 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10385 pBase = pMedium->i_getBase();
10386 }
10387
10388 /* Paranoia checks: do not hold medium locks. */
10389 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10390 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10391
10392 // decide which medium registry to use now that the medium is attached:
10393 Guid uuid;
10394 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10395 // machine XML is VirtualBox 4.0 or higher:
10396 uuid = i_getId(); // machine UUID
10397 else
10398 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10399
10400 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10401 mParent->i_markRegistryModified(uuid);
10402
10403 /* For more complex hard disk structures it can happen that the base
10404 * medium isn't yet associated with any medium registry. Do that now. */
10405 if (pMedium != pBase)
10406 {
10407 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10408 mParent->i_markRegistryModified(uuid);
10409 }
10410}
10411
10412/**
10413 * Creates differencing hard disks for all normal hard disks attached to this
10414 * machine and a new set of attachments to refer to created disks.
10415 *
10416 * Used when taking a snapshot or when deleting the current state. Gets called
10417 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10418 *
10419 * This method assumes that mMediaData contains the original hard disk attachments
10420 * it needs to create diffs for. On success, these attachments will be replaced
10421 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10422 * called to delete created diffs which will also rollback mMediaData and restore
10423 * whatever was backed up before calling this method.
10424 *
10425 * Attachments with non-normal hard disks are left as is.
10426 *
10427 * If @a aOnline is @c false then the original hard disks that require implicit
10428 * diffs will be locked for reading. Otherwise it is assumed that they are
10429 * already locked for writing (when the VM was started). Note that in the latter
10430 * case it is responsibility of the caller to lock the newly created diffs for
10431 * writing if this method succeeds.
10432 *
10433 * @param aProgress Progress object to run (must contain at least as
10434 * many operations left as the number of hard disks
10435 * attached).
10436 * @param aOnline Whether the VM was online prior to this operation.
10437 *
10438 * @note The progress object is not marked as completed, neither on success nor
10439 * on failure. This is a responsibility of the caller.
10440 *
10441 * @note Locks this object and the media tree for writing.
10442 */
10443HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10444 ULONG aWeight,
10445 bool aOnline)
10446{
10447 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10448
10449 AutoCaller autoCaller(this);
10450 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10451
10452 AutoMultiWriteLock2 alock(this->lockHandle(),
10453 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10454
10455 /* must be in a protective state because we release the lock below */
10456 AssertReturn( mData->mMachineState == MachineState_Saving
10457 || mData->mMachineState == MachineState_LiveSnapshotting
10458 || mData->mMachineState == MachineState_RestoringSnapshot
10459 || mData->mMachineState == MachineState_DeletingSnapshot
10460 , E_FAIL);
10461
10462 HRESULT rc = S_OK;
10463
10464 // use appropriate locked media map (online or offline)
10465 MediumLockListMap lockedMediaOffline;
10466 MediumLockListMap *lockedMediaMap;
10467 if (aOnline)
10468 lockedMediaMap = &mData->mSession.mLockedMedia;
10469 else
10470 lockedMediaMap = &lockedMediaOffline;
10471
10472 try
10473 {
10474 if (!aOnline)
10475 {
10476 /* lock all attached hard disks early to detect "in use"
10477 * situations before creating actual diffs */
10478 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10479 it != mMediaData->mAttachments.end();
10480 ++it)
10481 {
10482 MediumAttachment* pAtt = *it;
10483 if (pAtt->i_getType() == DeviceType_HardDisk)
10484 {
10485 Medium* pMedium = pAtt->i_getMedium();
10486 Assert(pMedium);
10487
10488 MediumLockList *pMediumLockList(new MediumLockList());
10489 alock.release();
10490 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10491 false /* fMediumLockWrite */,
10492 NULL,
10493 *pMediumLockList);
10494 alock.acquire();
10495 if (FAILED(rc))
10496 {
10497 delete pMediumLockList;
10498 throw rc;
10499 }
10500 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10501 if (FAILED(rc))
10502 {
10503 throw setError(rc,
10504 tr("Collecting locking information for all attached media failed"));
10505 }
10506 }
10507 }
10508
10509 /* Now lock all media. If this fails, nothing is locked. */
10510 alock.release();
10511 rc = lockedMediaMap->Lock();
10512 alock.acquire();
10513 if (FAILED(rc))
10514 {
10515 throw setError(rc,
10516 tr("Locking of attached media failed"));
10517 }
10518 }
10519
10520 /* remember the current list (note that we don't use backup() since
10521 * mMediaData may be already backed up) */
10522 MediaData::AttachmentList atts = mMediaData->mAttachments;
10523
10524 /* start from scratch */
10525 mMediaData->mAttachments.clear();
10526
10527 /* go through remembered attachments and create diffs for normal hard
10528 * disks and attach them */
10529 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10530 it != atts.end();
10531 ++it)
10532 {
10533 MediumAttachment* pAtt = *it;
10534
10535 DeviceType_T devType = pAtt->i_getType();
10536 Medium* pMedium = pAtt->i_getMedium();
10537
10538 if ( devType != DeviceType_HardDisk
10539 || pMedium == NULL
10540 || pMedium->i_getType() != MediumType_Normal)
10541 {
10542 /* copy the attachment as is */
10543
10544 /** @todo the progress object created in Console::TakeSnaphot
10545 * only expects operations for hard disks. Later other
10546 * device types need to show up in the progress as well. */
10547 if (devType == DeviceType_HardDisk)
10548 {
10549 if (pMedium == NULL)
10550 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10551 aWeight); // weight
10552 else
10553 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10554 pMedium->i_getBase()->i_getName().c_str()).raw(),
10555 aWeight); // weight
10556 }
10557
10558 mMediaData->mAttachments.push_back(pAtt);
10559 continue;
10560 }
10561
10562 /* need a diff */
10563 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10564 pMedium->i_getBase()->i_getName().c_str()).raw(),
10565 aWeight); // weight
10566
10567 Utf8Str strFullSnapshotFolder;
10568 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10569
10570 ComObjPtr<Medium> diff;
10571 diff.createObject();
10572 // store the diff in the same registry as the parent
10573 // (this cannot fail here because we can't create implicit diffs for
10574 // unregistered images)
10575 Guid uuidRegistryParent;
10576 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10577 Assert(fInRegistry); NOREF(fInRegistry);
10578 rc = diff->init(mParent,
10579 pMedium->i_getPreferredDiffFormat(),
10580 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10581 uuidRegistryParent);
10582 if (FAILED(rc)) throw rc;
10583
10584 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10585 * the push_back? Looks like we're going to release medium with the
10586 * wrong kind of lock (general issue with if we fail anywhere at all)
10587 * and an orphaned VDI in the snapshots folder. */
10588
10589 /* update the appropriate lock list */
10590 MediumLockList *pMediumLockList;
10591 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10592 AssertComRCThrowRC(rc);
10593 if (aOnline)
10594 {
10595 alock.release();
10596 /* The currently attached medium will be read-only, change
10597 * the lock type to read. */
10598 rc = pMediumLockList->Update(pMedium, false);
10599 alock.acquire();
10600 AssertComRCThrowRC(rc);
10601 }
10602
10603 /* release the locks before the potentially lengthy operation */
10604 alock.release();
10605 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10606 pMediumLockList,
10607 NULL /* aProgress */,
10608 true /* aWait */);
10609 alock.acquire();
10610 if (FAILED(rc)) throw rc;
10611
10612 /* actual lock list update is done in Medium::commitMedia */
10613
10614 rc = diff->i_addBackReference(mData->mUuid);
10615 AssertComRCThrowRC(rc);
10616
10617 /* add a new attachment */
10618 ComObjPtr<MediumAttachment> attachment;
10619 attachment.createObject();
10620 rc = attachment->init(this,
10621 diff,
10622 pAtt->i_getControllerName(),
10623 pAtt->i_getPort(),
10624 pAtt->i_getDevice(),
10625 DeviceType_HardDisk,
10626 true /* aImplicit */,
10627 false /* aPassthrough */,
10628 false /* aTempEject */,
10629 pAtt->i_getNonRotational(),
10630 pAtt->i_getDiscard(),
10631 pAtt->i_getHotPluggable(),
10632 pAtt->i_getBandwidthGroup());
10633 if (FAILED(rc)) throw rc;
10634
10635 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10636 AssertComRCThrowRC(rc);
10637 mMediaData->mAttachments.push_back(attachment);
10638 }
10639 }
10640 catch (HRESULT aRC) { rc = aRC; }
10641
10642 /* unlock all hard disks we locked when there is no VM */
10643 if (!aOnline)
10644 {
10645 ErrorInfoKeeper eik;
10646
10647 HRESULT rc1 = lockedMediaMap->Clear();
10648 AssertComRC(rc1);
10649 }
10650
10651 return rc;
10652}
10653
10654/**
10655 * Deletes implicit differencing hard disks created either by
10656 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10657 *
10658 * Note that to delete hard disks created by #AttachDevice() this method is
10659 * called from #fixupMedia() when the changes are rolled back.
10660 *
10661 * @note Locks this object and the media tree for writing.
10662 */
10663HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10664{
10665 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10666
10667 AutoCaller autoCaller(this);
10668 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10669
10670 AutoMultiWriteLock2 alock(this->lockHandle(),
10671 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10672
10673 /* We absolutely must have backed up state. */
10674 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10675
10676 /* Check if there are any implicitly created diff images. */
10677 bool fImplicitDiffs = false;
10678 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10679 it != mMediaData->mAttachments.end();
10680 ++it)
10681 {
10682 const ComObjPtr<MediumAttachment> &pAtt = *it;
10683 if (pAtt->i_isImplicit())
10684 {
10685 fImplicitDiffs = true;
10686 break;
10687 }
10688 }
10689 /* If there is nothing to do, leave early. This saves lots of image locking
10690 * effort. It also avoids a MachineStateChanged event without real reason.
10691 * This is important e.g. when loading a VM config, because there should be
10692 * no events. Otherwise API clients can become thoroughly confused for
10693 * inaccessible VMs (the code for loading VM configs uses this method for
10694 * cleanup if the config makes no sense), as they take such events as an
10695 * indication that the VM is alive, and they would force the VM config to
10696 * be reread, leading to an endless loop. */
10697 if (!fImplicitDiffs)
10698 return S_OK;
10699
10700 HRESULT rc = S_OK;
10701 MachineState_T oldState = mData->mMachineState;
10702
10703 /* will release the lock before the potentially lengthy operation,
10704 * so protect with the special state (unless already protected) */
10705 if ( oldState != MachineState_Saving
10706 && oldState != MachineState_LiveSnapshotting
10707 && oldState != MachineState_RestoringSnapshot
10708 && oldState != MachineState_DeletingSnapshot
10709 && oldState != MachineState_DeletingSnapshotOnline
10710 && oldState != MachineState_DeletingSnapshotPaused
10711 )
10712 i_setMachineState(MachineState_SettingUp);
10713
10714 // use appropriate locked media map (online or offline)
10715 MediumLockListMap lockedMediaOffline;
10716 MediumLockListMap *lockedMediaMap;
10717 if (aOnline)
10718 lockedMediaMap = &mData->mSession.mLockedMedia;
10719 else
10720 lockedMediaMap = &lockedMediaOffline;
10721
10722 try
10723 {
10724 if (!aOnline)
10725 {
10726 /* lock all attached hard disks early to detect "in use"
10727 * situations before deleting actual diffs */
10728 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10729 it != mMediaData->mAttachments.end();
10730 ++it)
10731 {
10732 MediumAttachment* pAtt = *it;
10733 if (pAtt->i_getType() == DeviceType_HardDisk)
10734 {
10735 Medium* pMedium = pAtt->i_getMedium();
10736 Assert(pMedium);
10737
10738 MediumLockList *pMediumLockList(new MediumLockList());
10739 alock.release();
10740 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10741 false /* fMediumLockWrite */,
10742 NULL,
10743 *pMediumLockList);
10744 alock.acquire();
10745
10746 if (FAILED(rc))
10747 {
10748 delete pMediumLockList;
10749 throw rc;
10750 }
10751
10752 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10753 if (FAILED(rc))
10754 throw rc;
10755 }
10756 }
10757
10758 if (FAILED(rc))
10759 throw rc;
10760 } // end of offline
10761
10762 /* Lock lists are now up to date and include implicitly created media */
10763
10764 /* Go through remembered attachments and delete all implicitly created
10765 * diffs and fix up the attachment information */
10766 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10767 MediaData::AttachmentList implicitAtts;
10768 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10769 it != mMediaData->mAttachments.end();
10770 ++it)
10771 {
10772 ComObjPtr<MediumAttachment> pAtt = *it;
10773 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10774 if (pMedium.isNull())
10775 continue;
10776
10777 // Implicit attachments go on the list for deletion and back references are removed.
10778 if (pAtt->i_isImplicit())
10779 {
10780 /* Deassociate and mark for deletion */
10781 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10782 rc = pMedium->i_removeBackReference(mData->mUuid);
10783 if (FAILED(rc))
10784 throw rc;
10785 implicitAtts.push_back(pAtt);
10786 continue;
10787 }
10788
10789 /* Was this medium attached before? */
10790 if (!i_findAttachment(oldAtts, pMedium))
10791 {
10792 /* no: de-associate */
10793 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10794 rc = pMedium->i_removeBackReference(mData->mUuid);
10795 if (FAILED(rc))
10796 throw rc;
10797 continue;
10798 }
10799 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10800 }
10801
10802 /* If there are implicit attachments to delete, throw away the lock
10803 * map contents (which will unlock all media) since the medium
10804 * attachments will be rolled back. Below we need to completely
10805 * recreate the lock map anyway since it is infinitely complex to
10806 * do this incrementally (would need reconstructing each attachment
10807 * change, which would be extremely hairy). */
10808 if (implicitAtts.size() != 0)
10809 {
10810 ErrorInfoKeeper eik;
10811
10812 HRESULT rc1 = lockedMediaMap->Clear();
10813 AssertComRC(rc1);
10814 }
10815
10816 /* rollback hard disk changes */
10817 mMediaData.rollback();
10818
10819 MultiResult mrc(S_OK);
10820
10821 // Delete unused implicit diffs.
10822 if (implicitAtts.size() != 0)
10823 {
10824 alock.release();
10825
10826 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10827 {
10828 // Remove medium associated with this attachment.
10829 ComObjPtr<MediumAttachment> pAtt = *it;
10830 Assert(pAtt);
10831 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10832 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10833 Assert(pMedium);
10834
10835 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10836 // continue on delete failure, just collect error messages
10837 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10838 pMedium->i_getLocationFull().c_str() ));
10839 mrc = rc;
10840 }
10841
10842 alock.acquire();
10843
10844 /* if there is a VM recreate media lock map as mentioned above,
10845 * otherwise it is a waste of time and we leave things unlocked */
10846 if (aOnline)
10847 {
10848 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10849 /* must never be NULL, but better safe than sorry */
10850 if (!pMachine.isNull())
10851 {
10852 alock.release();
10853 rc = mData->mSession.mMachine->i_lockMedia();
10854 alock.acquire();
10855 if (FAILED(rc))
10856 throw rc;
10857 }
10858 }
10859 }
10860 }
10861 catch (HRESULT aRC) {rc = aRC;}
10862
10863 if (mData->mMachineState == MachineState_SettingUp)
10864 i_setMachineState(oldState);
10865
10866 /* unlock all hard disks we locked when there is no VM */
10867 if (!aOnline)
10868 {
10869 ErrorInfoKeeper eik;
10870
10871 HRESULT rc1 = lockedMediaMap->Clear();
10872 AssertComRC(rc1);
10873 }
10874
10875 return rc;
10876}
10877
10878
10879/**
10880 * Looks through the given list of media attachments for one with the given parameters
10881 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10882 * can be searched as well if needed.
10883 *
10884 * @param list
10885 * @param aControllerName
10886 * @param aControllerPort
10887 * @param aDevice
10888 * @return
10889 */
10890MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10891 IN_BSTR aControllerName,
10892 LONG aControllerPort,
10893 LONG aDevice)
10894{
10895 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10896 {
10897 MediumAttachment *pAttach = *it;
10898 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10899 return pAttach;
10900 }
10901
10902 return NULL;
10903}
10904
10905/**
10906 * Looks through the given list of media attachments for one with the given parameters
10907 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10908 * can be searched as well if needed.
10909 *
10910 * @param list
10911 * @param aControllerName
10912 * @param aControllerPort
10913 * @param aDevice
10914 * @return
10915 */
10916MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10917 ComObjPtr<Medium> pMedium)
10918{
10919 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10920 {
10921 MediumAttachment *pAttach = *it;
10922 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10923 if (pMediumThis == pMedium)
10924 return pAttach;
10925 }
10926
10927 return NULL;
10928}
10929
10930/**
10931 * Looks through the given list of media attachments for one with the given parameters
10932 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10933 * can be searched as well if needed.
10934 *
10935 * @param list
10936 * @param aControllerName
10937 * @param aControllerPort
10938 * @param aDevice
10939 * @return
10940 */
10941MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10942 Guid &id)
10943{
10944 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10945 {
10946 MediumAttachment *pAttach = *it;
10947 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10948 if (pMediumThis->i_getId() == id)
10949 return pAttach;
10950 }
10951
10952 return NULL;
10953}
10954
10955/**
10956 * Main implementation for Machine::DetachDevice. This also gets called
10957 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10958 *
10959 * @param pAttach Medium attachment to detach.
10960 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10961 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10962 * SnapshotMachine, and this must be its snapshot.
10963 * @return
10964 */
10965HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10966 AutoWriteLock &writeLock,
10967 Snapshot *pSnapshot)
10968{
10969 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10970 DeviceType_T mediumType = pAttach->i_getType();
10971
10972 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10973
10974 if (pAttach->i_isImplicit())
10975 {
10976 /* attempt to implicitly delete the implicitly created diff */
10977
10978 /// @todo move the implicit flag from MediumAttachment to Medium
10979 /// and forbid any hard disk operation when it is implicit. Or maybe
10980 /// a special media state for it to make it even more simple.
10981
10982 Assert(mMediaData.isBackedUp());
10983
10984 /* will release the lock before the potentially lengthy operation, so
10985 * protect with the special state */
10986 MachineState_T oldState = mData->mMachineState;
10987 i_setMachineState(MachineState_SettingUp);
10988
10989 writeLock.release();
10990
10991 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10992 true /*aWait*/);
10993
10994 writeLock.acquire();
10995
10996 i_setMachineState(oldState);
10997
10998 if (FAILED(rc)) return rc;
10999 }
11000
11001 i_setModified(IsModified_Storage);
11002 mMediaData.backup();
11003 mMediaData->mAttachments.remove(pAttach);
11004
11005 if (!oldmedium.isNull())
11006 {
11007 // if this is from a snapshot, do not defer detachment to commitMedia()
11008 if (pSnapshot)
11009 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11010 // else if non-hard disk media, do not defer detachment to commitMedia() either
11011 else if (mediumType != DeviceType_HardDisk)
11012 oldmedium->i_removeBackReference(mData->mUuid);
11013 }
11014
11015 return S_OK;
11016}
11017
11018/**
11019 * Goes thru all media of the given list and
11020 *
11021 * 1) calls i_detachDevice() on each of them for this machine and
11022 * 2) adds all Medium objects found in the process to the given list,
11023 * depending on cleanupMode.
11024 *
11025 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11026 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11027 * media to the list.
11028 *
11029 * This gets called from Machine::Unregister, both for the actual Machine and
11030 * the SnapshotMachine objects that might be found in the snapshots.
11031 *
11032 * Requires caller and locking. The machine lock must be passed in because it
11033 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11034 *
11035 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11036 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11037 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11038 * Full, then all media get added;
11039 * otherwise no media get added.
11040 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11041 * @return
11042 */
11043HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11044 Snapshot *pSnapshot,
11045 CleanupMode_T cleanupMode,
11046 MediaList &llMedia)
11047{
11048 Assert(isWriteLockOnCurrentThread());
11049
11050 HRESULT rc;
11051
11052 // make a temporary list because i_detachDevice invalidates iterators into
11053 // mMediaData->mAttachments
11054 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11055
11056 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11057 {
11058 ComObjPtr<MediumAttachment> &pAttach = *it;
11059 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11060
11061 if (!pMedium.isNull())
11062 {
11063 AutoCaller mac(pMedium);
11064 if (FAILED(mac.rc())) return mac.rc();
11065 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11066 DeviceType_T devType = pMedium->i_getDeviceType();
11067 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11068 && devType == DeviceType_HardDisk)
11069 || (cleanupMode == CleanupMode_Full)
11070 )
11071 {
11072 llMedia.push_back(pMedium);
11073 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11074 /*
11075 * Search for medias which are not attached to any machine, but
11076 * in the chain to an attached disk. Mediums are only consided
11077 * if they are:
11078 * - have only one child
11079 * - no references to any machines
11080 * - are of normal medium type
11081 */
11082 while (!pParent.isNull())
11083 {
11084 AutoCaller mac1(pParent);
11085 if (FAILED(mac1.rc())) return mac1.rc();
11086 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11087 if (pParent->i_getChildren().size() == 1)
11088 {
11089 if ( pParent->i_getMachineBackRefCount() == 0
11090 && pParent->i_getType() == MediumType_Normal
11091 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11092 llMedia.push_back(pParent);
11093 }
11094 else
11095 break;
11096 pParent = pParent->i_getParent();
11097 }
11098 }
11099 }
11100
11101 // real machine: then we need to use the proper method
11102 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11103
11104 if (FAILED(rc))
11105 return rc;
11106 }
11107
11108 return S_OK;
11109}
11110
11111/**
11112 * Perform deferred hard disk detachments.
11113 *
11114 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11115 * backed up).
11116 *
11117 * If @a aOnline is @c true then this method will also unlock the old hard disks
11118 * for which the new implicit diffs were created and will lock these new diffs for
11119 * writing.
11120 *
11121 * @param aOnline Whether the VM was online prior to this operation.
11122 *
11123 * @note Locks this object for writing!
11124 */
11125void Machine::i_commitMedia(bool aOnline /*= false*/)
11126{
11127 AutoCaller autoCaller(this);
11128 AssertComRCReturnVoid(autoCaller.rc());
11129
11130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11131
11132 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11133
11134 HRESULT rc = S_OK;
11135
11136 /* no attach/detach operations -- nothing to do */
11137 if (!mMediaData.isBackedUp())
11138 return;
11139
11140 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11141 bool fMediaNeedsLocking = false;
11142
11143 /* enumerate new attachments */
11144 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11145 it != mMediaData->mAttachments.end();
11146 ++it)
11147 {
11148 MediumAttachment *pAttach = *it;
11149
11150 pAttach->i_commit();
11151
11152 Medium* pMedium = pAttach->i_getMedium();
11153 bool fImplicit = pAttach->i_isImplicit();
11154
11155 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11156 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11157 fImplicit));
11158
11159 /** @todo convert all this Machine-based voodoo to MediumAttachment
11160 * based commit logic. */
11161 if (fImplicit)
11162 {
11163 /* convert implicit attachment to normal */
11164 pAttach->i_setImplicit(false);
11165
11166 if ( aOnline
11167 && pMedium
11168 && pAttach->i_getType() == DeviceType_HardDisk
11169 )
11170 {
11171 ComObjPtr<Medium> parent = pMedium->i_getParent();
11172 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11173
11174 /* update the appropriate lock list */
11175 MediumLockList *pMediumLockList;
11176 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11177 AssertComRC(rc);
11178 if (pMediumLockList)
11179 {
11180 /* unlock if there's a need to change the locking */
11181 if (!fMediaNeedsLocking)
11182 {
11183 rc = mData->mSession.mLockedMedia.Unlock();
11184 AssertComRC(rc);
11185 fMediaNeedsLocking = true;
11186 }
11187 rc = pMediumLockList->Update(parent, false);
11188 AssertComRC(rc);
11189 rc = pMediumLockList->Append(pMedium, true);
11190 AssertComRC(rc);
11191 }
11192 }
11193
11194 continue;
11195 }
11196
11197 if (pMedium)
11198 {
11199 /* was this medium attached before? */
11200 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11201 {
11202 MediumAttachment *pOldAttach = *oldIt;
11203 if (pOldAttach->i_getMedium() == pMedium)
11204 {
11205 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11206
11207 /* yes: remove from old to avoid de-association */
11208 oldAtts.erase(oldIt);
11209 break;
11210 }
11211 }
11212 }
11213 }
11214
11215 /* enumerate remaining old attachments and de-associate from the
11216 * current machine state */
11217 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11218 {
11219 MediumAttachment *pAttach = *it;
11220 Medium* pMedium = pAttach->i_getMedium();
11221
11222 /* Detach only hard disks, since DVD/floppy media is detached
11223 * instantly in MountMedium. */
11224 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11225 {
11226 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11227
11228 /* now de-associate from the current machine state */
11229 rc = pMedium->i_removeBackReference(mData->mUuid);
11230 AssertComRC(rc);
11231
11232 if (aOnline)
11233 {
11234 /* unlock since medium is not used anymore */
11235 MediumLockList *pMediumLockList;
11236 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11237 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11238 {
11239 /* this happens for online snapshots, there the attachment
11240 * is changing, but only to a diff image created under
11241 * the old one, so there is no separate lock list */
11242 Assert(!pMediumLockList);
11243 }
11244 else
11245 {
11246 AssertComRC(rc);
11247 if (pMediumLockList)
11248 {
11249 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11250 AssertComRC(rc);
11251 }
11252 }
11253 }
11254 }
11255 }
11256
11257 /* take media locks again so that the locking state is consistent */
11258 if (fMediaNeedsLocking)
11259 {
11260 Assert(aOnline);
11261 rc = mData->mSession.mLockedMedia.Lock();
11262 AssertComRC(rc);
11263 }
11264
11265 /* commit the hard disk changes */
11266 mMediaData.commit();
11267
11268 if (i_isSessionMachine())
11269 {
11270 /*
11271 * Update the parent machine to point to the new owner.
11272 * This is necessary because the stored parent will point to the
11273 * session machine otherwise and cause crashes or errors later
11274 * when the session machine gets invalid.
11275 */
11276 /** @todo Change the MediumAttachment class to behave like any other
11277 * class in this regard by creating peer MediumAttachment
11278 * objects for session machines and share the data with the peer
11279 * machine.
11280 */
11281 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11282 it != mMediaData->mAttachments.end();
11283 ++it)
11284 (*it)->i_updateParentMachine(mPeer);
11285
11286 /* attach new data to the primary machine and reshare it */
11287 mPeer->mMediaData.attach(mMediaData);
11288 }
11289
11290 return;
11291}
11292
11293/**
11294 * Perform deferred deletion of implicitly created diffs.
11295 *
11296 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11297 * backed up).
11298 *
11299 * @note Locks this object for writing!
11300 */
11301void Machine::i_rollbackMedia()
11302{
11303 AutoCaller autoCaller(this);
11304 AssertComRCReturnVoid(autoCaller.rc());
11305
11306 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11307 LogFlowThisFunc(("Entering rollbackMedia\n"));
11308
11309 HRESULT rc = S_OK;
11310
11311 /* no attach/detach operations -- nothing to do */
11312 if (!mMediaData.isBackedUp())
11313 return;
11314
11315 /* enumerate new attachments */
11316 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11317 it != mMediaData->mAttachments.end();
11318 ++it)
11319 {
11320 MediumAttachment *pAttach = *it;
11321 /* Fix up the backrefs for DVD/floppy media. */
11322 if (pAttach->i_getType() != DeviceType_HardDisk)
11323 {
11324 Medium* pMedium = pAttach->i_getMedium();
11325 if (pMedium)
11326 {
11327 rc = pMedium->i_removeBackReference(mData->mUuid);
11328 AssertComRC(rc);
11329 }
11330 }
11331
11332 (*it)->i_rollback();
11333
11334 pAttach = *it;
11335 /* Fix up the backrefs for DVD/floppy media. */
11336 if (pAttach->i_getType() != DeviceType_HardDisk)
11337 {
11338 Medium* pMedium = pAttach->i_getMedium();
11339 if (pMedium)
11340 {
11341 rc = pMedium->i_addBackReference(mData->mUuid);
11342 AssertComRC(rc);
11343 }
11344 }
11345 }
11346
11347 /** @todo convert all this Machine-based voodoo to MediumAttachment
11348 * based rollback logic. */
11349 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11350
11351 return;
11352}
11353
11354/**
11355 * Returns true if the settings file is located in the directory named exactly
11356 * as the machine; this means, among other things, that the machine directory
11357 * should be auto-renamed.
11358 *
11359 * @param aSettingsDir if not NULL, the full machine settings file directory
11360 * name will be assigned there.
11361 *
11362 * @note Doesn't lock anything.
11363 * @note Not thread safe (must be called from this object's lock).
11364 */
11365bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11366{
11367 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11368 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11369 if (aSettingsDir)
11370 *aSettingsDir = strMachineDirName;
11371 strMachineDirName.stripPath(); // vmname
11372 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11373 strConfigFileOnly.stripPath() // vmname.vbox
11374 .stripSuffix(); // vmname
11375 /** @todo hack, make somehow use of ComposeMachineFilename */
11376 if (mUserData->s.fDirectoryIncludesUUID)
11377 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11378
11379 AssertReturn(!strMachineDirName.isEmpty(), false);
11380 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11381
11382 return strMachineDirName == strConfigFileOnly;
11383}
11384
11385/**
11386 * Discards all changes to machine settings.
11387 *
11388 * @param aNotify Whether to notify the direct session about changes or not.
11389 *
11390 * @note Locks objects for writing!
11391 */
11392void Machine::i_rollback(bool aNotify)
11393{
11394 AutoCaller autoCaller(this);
11395 AssertComRCReturn(autoCaller.rc(), (void)0);
11396
11397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11398
11399 if (!mStorageControllers.isNull())
11400 {
11401 if (mStorageControllers.isBackedUp())
11402 {
11403 /* unitialize all new devices (absent in the backed up list). */
11404 StorageControllerList::const_iterator it = mStorageControllers->begin();
11405 StorageControllerList *backedList = mStorageControllers.backedUpData();
11406 while (it != mStorageControllers->end())
11407 {
11408 if ( std::find(backedList->begin(), backedList->end(), *it)
11409 == backedList->end()
11410 )
11411 {
11412 (*it)->uninit();
11413 }
11414 ++it;
11415 }
11416
11417 /* restore the list */
11418 mStorageControllers.rollback();
11419 }
11420
11421 /* rollback any changes to devices after restoring the list */
11422 if (mData->flModifications & IsModified_Storage)
11423 {
11424 StorageControllerList::const_iterator it = mStorageControllers->begin();
11425 while (it != mStorageControllers->end())
11426 {
11427 (*it)->i_rollback();
11428 ++it;
11429 }
11430 }
11431 }
11432
11433 if (!mUSBControllers.isNull())
11434 {
11435 if (mUSBControllers.isBackedUp())
11436 {
11437 /* unitialize all new devices (absent in the backed up list). */
11438 USBControllerList::const_iterator it = mUSBControllers->begin();
11439 USBControllerList *backedList = mUSBControllers.backedUpData();
11440 while (it != mUSBControllers->end())
11441 {
11442 if ( std::find(backedList->begin(), backedList->end(), *it)
11443 == backedList->end()
11444 )
11445 {
11446 (*it)->uninit();
11447 }
11448 ++it;
11449 }
11450
11451 /* restore the list */
11452 mUSBControllers.rollback();
11453 }
11454
11455 /* rollback any changes to devices after restoring the list */
11456 if (mData->flModifications & IsModified_USB)
11457 {
11458 USBControllerList::const_iterator it = mUSBControllers->begin();
11459 while (it != mUSBControllers->end())
11460 {
11461 (*it)->i_rollback();
11462 ++it;
11463 }
11464 }
11465 }
11466
11467 mUserData.rollback();
11468
11469 mHWData.rollback();
11470
11471 if (mData->flModifications & IsModified_Storage)
11472 i_rollbackMedia();
11473
11474 if (mBIOSSettings)
11475 mBIOSSettings->i_rollback();
11476
11477 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11478 mVRDEServer->i_rollback();
11479
11480 if (mAudioAdapter)
11481 mAudioAdapter->i_rollback();
11482
11483 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11484 mUSBDeviceFilters->i_rollback();
11485
11486 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11487 mBandwidthControl->i_rollback();
11488
11489 if (!mHWData.isNull())
11490 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11491 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11492 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11493 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11494
11495 if (mData->flModifications & IsModified_NetworkAdapters)
11496 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11497 if ( mNetworkAdapters[slot]
11498 && mNetworkAdapters[slot]->i_isModified())
11499 {
11500 mNetworkAdapters[slot]->i_rollback();
11501 networkAdapters[slot] = mNetworkAdapters[slot];
11502 }
11503
11504 if (mData->flModifications & IsModified_SerialPorts)
11505 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11506 if ( mSerialPorts[slot]
11507 && mSerialPorts[slot]->i_isModified())
11508 {
11509 mSerialPorts[slot]->i_rollback();
11510 serialPorts[slot] = mSerialPorts[slot];
11511 }
11512
11513 if (mData->flModifications & IsModified_ParallelPorts)
11514 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11515 if ( mParallelPorts[slot]
11516 && mParallelPorts[slot]->i_isModified())
11517 {
11518 mParallelPorts[slot]->i_rollback();
11519 parallelPorts[slot] = mParallelPorts[slot];
11520 }
11521
11522 if (aNotify)
11523 {
11524 /* inform the direct session about changes */
11525
11526 ComObjPtr<Machine> that = this;
11527 uint32_t flModifications = mData->flModifications;
11528 alock.release();
11529
11530 if (flModifications & IsModified_SharedFolders)
11531 that->i_onSharedFolderChange();
11532
11533 if (flModifications & IsModified_VRDEServer)
11534 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11535 if (flModifications & IsModified_USB)
11536 that->i_onUSBControllerChange();
11537
11538 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11539 if (networkAdapters[slot])
11540 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11541 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11542 if (serialPorts[slot])
11543 that->i_onSerialPortChange(serialPorts[slot]);
11544 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11545 if (parallelPorts[slot])
11546 that->i_onParallelPortChange(parallelPorts[slot]);
11547
11548 if (flModifications & IsModified_Storage)
11549 that->i_onStorageControllerChange();
11550
11551#if 0
11552 if (flModifications & IsModified_BandwidthControl)
11553 that->onBandwidthControlChange();
11554#endif
11555 }
11556}
11557
11558/**
11559 * Commits all the changes to machine settings.
11560 *
11561 * Note that this operation is supposed to never fail.
11562 *
11563 * @note Locks this object and children for writing.
11564 */
11565void Machine::i_commit()
11566{
11567 AutoCaller autoCaller(this);
11568 AssertComRCReturnVoid(autoCaller.rc());
11569
11570 AutoCaller peerCaller(mPeer);
11571 AssertComRCReturnVoid(peerCaller.rc());
11572
11573 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11574
11575 /*
11576 * use safe commit to ensure Snapshot machines (that share mUserData)
11577 * will still refer to a valid memory location
11578 */
11579 mUserData.commitCopy();
11580
11581 mHWData.commit();
11582
11583 if (mMediaData.isBackedUp())
11584 i_commitMedia(Global::IsOnline(mData->mMachineState));
11585
11586 mBIOSSettings->i_commit();
11587 mVRDEServer->i_commit();
11588 mAudioAdapter->i_commit();
11589 mUSBDeviceFilters->i_commit();
11590 mBandwidthControl->i_commit();
11591
11592 /* Since mNetworkAdapters is a list which might have been changed (resized)
11593 * without using the Backupable<> template we need to handle the copying
11594 * of the list entries manually, including the creation of peers for the
11595 * new objects. */
11596 bool commitNetworkAdapters = false;
11597 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11598 if (mPeer)
11599 {
11600 /* commit everything, even the ones which will go away */
11601 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11602 mNetworkAdapters[slot]->i_commit();
11603 /* copy over the new entries, creating a peer and uninit the original */
11604 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11605 for (size_t slot = 0; slot < newSize; slot++)
11606 {
11607 /* look if this adapter has a peer device */
11608 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11609 if (!peer)
11610 {
11611 /* no peer means the adapter is a newly created one;
11612 * create a peer owning data this data share it with */
11613 peer.createObject();
11614 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11615 }
11616 mPeer->mNetworkAdapters[slot] = peer;
11617 }
11618 /* uninit any no longer needed network adapters */
11619 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11620 mNetworkAdapters[slot]->uninit();
11621 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11622 {
11623 if (mPeer->mNetworkAdapters[slot])
11624 mPeer->mNetworkAdapters[slot]->uninit();
11625 }
11626 /* Keep the original network adapter count until this point, so that
11627 * discarding a chipset type change will not lose settings. */
11628 mNetworkAdapters.resize(newSize);
11629 mPeer->mNetworkAdapters.resize(newSize);
11630 }
11631 else
11632 {
11633 /* we have no peer (our parent is the newly created machine);
11634 * just commit changes to the network adapters */
11635 commitNetworkAdapters = true;
11636 }
11637 if (commitNetworkAdapters)
11638 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11639 mNetworkAdapters[slot]->i_commit();
11640
11641 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11642 mSerialPorts[slot]->i_commit();
11643 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11644 mParallelPorts[slot]->i_commit();
11645
11646 bool commitStorageControllers = false;
11647
11648 if (mStorageControllers.isBackedUp())
11649 {
11650 mStorageControllers.commit();
11651
11652 if (mPeer)
11653 {
11654 /* Commit all changes to new controllers (this will reshare data with
11655 * peers for those who have peers) */
11656 StorageControllerList *newList = new StorageControllerList();
11657 StorageControllerList::const_iterator it = mStorageControllers->begin();
11658 while (it != mStorageControllers->end())
11659 {
11660 (*it)->i_commit();
11661
11662 /* look if this controller has a peer device */
11663 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11664 if (!peer)
11665 {
11666 /* no peer means the device is a newly created one;
11667 * create a peer owning data this device share it with */
11668 peer.createObject();
11669 peer->init(mPeer, *it, true /* aReshare */);
11670 }
11671 else
11672 {
11673 /* remove peer from the old list */
11674 mPeer->mStorageControllers->remove(peer);
11675 }
11676 /* and add it to the new list */
11677 newList->push_back(peer);
11678
11679 ++it;
11680 }
11681
11682 /* uninit old peer's controllers that are left */
11683 it = mPeer->mStorageControllers->begin();
11684 while (it != mPeer->mStorageControllers->end())
11685 {
11686 (*it)->uninit();
11687 ++it;
11688 }
11689
11690 /* attach new list of controllers to our peer */
11691 mPeer->mStorageControllers.attach(newList);
11692 }
11693 else
11694 {
11695 /* we have no peer (our parent is the newly created machine);
11696 * just commit changes to devices */
11697 commitStorageControllers = true;
11698 }
11699 }
11700 else
11701 {
11702 /* the list of controllers itself is not changed,
11703 * just commit changes to controllers themselves */
11704 commitStorageControllers = true;
11705 }
11706
11707 if (commitStorageControllers)
11708 {
11709 StorageControllerList::const_iterator it = mStorageControllers->begin();
11710 while (it != mStorageControllers->end())
11711 {
11712 (*it)->i_commit();
11713 ++it;
11714 }
11715 }
11716
11717 bool commitUSBControllers = false;
11718
11719 if (mUSBControllers.isBackedUp())
11720 {
11721 mUSBControllers.commit();
11722
11723 if (mPeer)
11724 {
11725 /* Commit all changes to new controllers (this will reshare data with
11726 * peers for those who have peers) */
11727 USBControllerList *newList = new USBControllerList();
11728 USBControllerList::const_iterator it = mUSBControllers->begin();
11729 while (it != mUSBControllers->end())
11730 {
11731 (*it)->i_commit();
11732
11733 /* look if this controller has a peer device */
11734 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11735 if (!peer)
11736 {
11737 /* no peer means the device is a newly created one;
11738 * create a peer owning data this device share it with */
11739 peer.createObject();
11740 peer->init(mPeer, *it, true /* aReshare */);
11741 }
11742 else
11743 {
11744 /* remove peer from the old list */
11745 mPeer->mUSBControllers->remove(peer);
11746 }
11747 /* and add it to the new list */
11748 newList->push_back(peer);
11749
11750 ++it;
11751 }
11752
11753 /* uninit old peer's controllers that are left */
11754 it = mPeer->mUSBControllers->begin();
11755 while (it != mPeer->mUSBControllers->end())
11756 {
11757 (*it)->uninit();
11758 ++it;
11759 }
11760
11761 /* attach new list of controllers to our peer */
11762 mPeer->mUSBControllers.attach(newList);
11763 }
11764 else
11765 {
11766 /* we have no peer (our parent is the newly created machine);
11767 * just commit changes to devices */
11768 commitUSBControllers = true;
11769 }
11770 }
11771 else
11772 {
11773 /* the list of controllers itself is not changed,
11774 * just commit changes to controllers themselves */
11775 commitUSBControllers = true;
11776 }
11777
11778 if (commitUSBControllers)
11779 {
11780 USBControllerList::const_iterator it = mUSBControllers->begin();
11781 while (it != mUSBControllers->end())
11782 {
11783 (*it)->i_commit();
11784 ++it;
11785 }
11786 }
11787
11788 if (i_isSessionMachine())
11789 {
11790 /* attach new data to the primary machine and reshare it */
11791 mPeer->mUserData.attach(mUserData);
11792 mPeer->mHWData.attach(mHWData);
11793 /* mMediaData is reshared by fixupMedia */
11794 // mPeer->mMediaData.attach(mMediaData);
11795 Assert(mPeer->mMediaData.data() == mMediaData.data());
11796 }
11797}
11798
11799/**
11800 * Copies all the hardware data from the given machine.
11801 *
11802 * Currently, only called when the VM is being restored from a snapshot. In
11803 * particular, this implies that the VM is not running during this method's
11804 * call.
11805 *
11806 * @note This method must be called from under this object's lock.
11807 *
11808 * @note This method doesn't call #commit(), so all data remains backed up and
11809 * unsaved.
11810 */
11811void Machine::i_copyFrom(Machine *aThat)
11812{
11813 AssertReturnVoid(!i_isSnapshotMachine());
11814 AssertReturnVoid(aThat->i_isSnapshotMachine());
11815
11816 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11817
11818 mHWData.assignCopy(aThat->mHWData);
11819
11820 // create copies of all shared folders (mHWData after attaching a copy
11821 // contains just references to original objects)
11822 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11823 it != mHWData->mSharedFolders.end();
11824 ++it)
11825 {
11826 ComObjPtr<SharedFolder> folder;
11827 folder.createObject();
11828 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11829 AssertComRC(rc);
11830 *it = folder;
11831 }
11832
11833 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11834 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11835 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11836 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11837 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11838
11839 /* create private copies of all controllers */
11840 mStorageControllers.backup();
11841 mStorageControllers->clear();
11842 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11843 it != aThat->mStorageControllers->end();
11844 ++it)
11845 {
11846 ComObjPtr<StorageController> ctrl;
11847 ctrl.createObject();
11848 ctrl->initCopy(this, *it);
11849 mStorageControllers->push_back(ctrl);
11850 }
11851
11852 /* create private copies of all USB controllers */
11853 mUSBControllers.backup();
11854 mUSBControllers->clear();
11855 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11856 it != aThat->mUSBControllers->end();
11857 ++it)
11858 {
11859 ComObjPtr<USBController> ctrl;
11860 ctrl.createObject();
11861 ctrl->initCopy(this, *it);
11862 mUSBControllers->push_back(ctrl);
11863 }
11864
11865 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11866 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11867 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11868 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11869 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11870 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11871 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11872}
11873
11874/**
11875 * Returns whether the given storage controller is hotplug capable.
11876 *
11877 * @returns true if the controller supports hotplugging
11878 * false otherwise.
11879 * @param enmCtrlType The controller type to check for.
11880 */
11881bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11882{
11883 ComPtr<ISystemProperties> systemProperties;
11884 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11885 if (FAILED(rc))
11886 return false;
11887
11888 BOOL aHotplugCapable = FALSE;
11889 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11890
11891 return RT_BOOL(aHotplugCapable);
11892}
11893
11894#ifdef VBOX_WITH_RESOURCE_USAGE_API
11895
11896void Machine::i_getDiskList(MediaList &list)
11897{
11898 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11899 it != mMediaData->mAttachments.end();
11900 ++it)
11901 {
11902 MediumAttachment* pAttach = *it;
11903 /* just in case */
11904 AssertStmt(pAttach, continue);
11905
11906 AutoCaller localAutoCallerA(pAttach);
11907 if (FAILED(localAutoCallerA.rc())) continue;
11908
11909 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11910
11911 if (pAttach->i_getType() == DeviceType_HardDisk)
11912 list.push_back(pAttach->i_getMedium());
11913 }
11914}
11915
11916void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11917{
11918 AssertReturnVoid(isWriteLockOnCurrentThread());
11919 AssertPtrReturnVoid(aCollector);
11920
11921 pm::CollectorHAL *hal = aCollector->getHAL();
11922 /* Create sub metrics */
11923 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11924 "Percentage of processor time spent in user mode by the VM process.");
11925 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11926 "Percentage of processor time spent in kernel mode by the VM process.");
11927 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11928 "Size of resident portion of VM process in memory.");
11929 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11930 "Actual size of all VM disks combined.");
11931 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11932 "Network receive rate.");
11933 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11934 "Network transmit rate.");
11935 /* Create and register base metrics */
11936 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11937 cpuLoadUser, cpuLoadKernel);
11938 aCollector->registerBaseMetric(cpuLoad);
11939 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11940 ramUsageUsed);
11941 aCollector->registerBaseMetric(ramUsage);
11942 MediaList disks;
11943 i_getDiskList(disks);
11944 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11945 diskUsageUsed);
11946 aCollector->registerBaseMetric(diskUsage);
11947
11948 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11949 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11950 new pm::AggregateAvg()));
11951 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11952 new pm::AggregateMin()));
11953 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11954 new pm::AggregateMax()));
11955 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11956 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11957 new pm::AggregateAvg()));
11958 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11959 new pm::AggregateMin()));
11960 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11961 new pm::AggregateMax()));
11962
11963 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11964 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11965 new pm::AggregateAvg()));
11966 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11967 new pm::AggregateMin()));
11968 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11969 new pm::AggregateMax()));
11970
11971 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11972 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11973 new pm::AggregateAvg()));
11974 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11975 new pm::AggregateMin()));
11976 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11977 new pm::AggregateMax()));
11978
11979
11980 /* Guest metrics collector */
11981 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11982 aCollector->registerGuest(mCollectorGuest);
11983 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11984 this, __PRETTY_FUNCTION__, mCollectorGuest));
11985
11986 /* Create sub metrics */
11987 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11988 "Percentage of processor time spent in user mode as seen by the guest.");
11989 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11990 "Percentage of processor time spent in kernel mode as seen by the guest.");
11991 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11992 "Percentage of processor time spent idling as seen by the guest.");
11993
11994 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11995 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11996 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11997 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11998 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11999 pm::SubMetric *guestMemCache = new pm::SubMetric(
12000 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12001
12002 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12003 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12004
12005 /* Create and register base metrics */
12006 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12007 machineNetRx, machineNetTx);
12008 aCollector->registerBaseMetric(machineNetRate);
12009
12010 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12011 guestLoadUser, guestLoadKernel, guestLoadIdle);
12012 aCollector->registerBaseMetric(guestCpuLoad);
12013
12014 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12015 guestMemTotal, guestMemFree,
12016 guestMemBalloon, guestMemShared,
12017 guestMemCache, guestPagedTotal);
12018 aCollector->registerBaseMetric(guestCpuMem);
12019
12020 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12021 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12022 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12023 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12024
12025 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12026 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12027 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12028 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12029
12030 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12031 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12032 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12033 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12034
12035 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12036 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12037 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12038 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12039
12040 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12041 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12042 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12043 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12044
12045 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12046 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12047 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12048 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12049
12050 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12051 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12052 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12053 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12054
12055 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12056 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12058 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12059
12060 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12061 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12063 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12064
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12066 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12069
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12074}
12075
12076void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12077{
12078 AssertReturnVoid(isWriteLockOnCurrentThread());
12079
12080 if (aCollector)
12081 {
12082 aCollector->unregisterMetricsFor(aMachine);
12083 aCollector->unregisterBaseMetricsFor(aMachine);
12084 }
12085}
12086
12087#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12088
12089
12090////////////////////////////////////////////////////////////////////////////////
12091
12092DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12093
12094HRESULT SessionMachine::FinalConstruct()
12095{
12096 LogFlowThisFunc(("\n"));
12097
12098 mClientToken = NULL;
12099
12100 return BaseFinalConstruct();
12101}
12102
12103void SessionMachine::FinalRelease()
12104{
12105 LogFlowThisFunc(("\n"));
12106
12107 Assert(!mClientToken);
12108 /* paranoia, should not hang around any more */
12109 if (mClientToken)
12110 {
12111 delete mClientToken;
12112 mClientToken = NULL;
12113 }
12114
12115 uninit(Uninit::Unexpected);
12116
12117 BaseFinalRelease();
12118}
12119
12120/**
12121 * @note Must be called only by Machine::LockMachine() from its own write lock.
12122 */
12123HRESULT SessionMachine::init(Machine *aMachine)
12124{
12125 LogFlowThisFuncEnter();
12126 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12127
12128 AssertReturn(aMachine, E_INVALIDARG);
12129
12130 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12131
12132 /* Enclose the state transition NotReady->InInit->Ready */
12133 AutoInitSpan autoInitSpan(this);
12134 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12135
12136 HRESULT rc = S_OK;
12137
12138 /* create the machine client token */
12139 try
12140 {
12141 mClientToken = new ClientToken(aMachine, this);
12142 if (!mClientToken->isReady())
12143 {
12144 delete mClientToken;
12145 mClientToken = NULL;
12146 rc = E_FAIL;
12147 }
12148 }
12149 catch (std::bad_alloc &)
12150 {
12151 rc = E_OUTOFMEMORY;
12152 }
12153 if (FAILED(rc))
12154 return rc;
12155
12156 /* memorize the peer Machine */
12157 unconst(mPeer) = aMachine;
12158 /* share the parent pointer */
12159 unconst(mParent) = aMachine->mParent;
12160
12161 /* take the pointers to data to share */
12162 mData.share(aMachine->mData);
12163 mSSData.share(aMachine->mSSData);
12164
12165 mUserData.share(aMachine->mUserData);
12166 mHWData.share(aMachine->mHWData);
12167 mMediaData.share(aMachine->mMediaData);
12168
12169 mStorageControllers.allocate();
12170 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12171 it != aMachine->mStorageControllers->end();
12172 ++it)
12173 {
12174 ComObjPtr<StorageController> ctl;
12175 ctl.createObject();
12176 ctl->init(this, *it);
12177 mStorageControllers->push_back(ctl);
12178 }
12179
12180 mUSBControllers.allocate();
12181 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12182 it != aMachine->mUSBControllers->end();
12183 ++it)
12184 {
12185 ComObjPtr<USBController> ctl;
12186 ctl.createObject();
12187 ctl->init(this, *it);
12188 mUSBControllers->push_back(ctl);
12189 }
12190
12191 unconst(mBIOSSettings).createObject();
12192 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12193 /* create another VRDEServer object that will be mutable */
12194 unconst(mVRDEServer).createObject();
12195 mVRDEServer->init(this, aMachine->mVRDEServer);
12196 /* create another audio adapter object that will be mutable */
12197 unconst(mAudioAdapter).createObject();
12198 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12199 /* create a list of serial ports that will be mutable */
12200 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12201 {
12202 unconst(mSerialPorts[slot]).createObject();
12203 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12204 }
12205 /* create a list of parallel ports that will be mutable */
12206 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12207 {
12208 unconst(mParallelPorts[slot]).createObject();
12209 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12210 }
12211
12212 /* create another USB device filters object that will be mutable */
12213 unconst(mUSBDeviceFilters).createObject();
12214 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12215
12216 /* create a list of network adapters that will be mutable */
12217 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12218 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12219 {
12220 unconst(mNetworkAdapters[slot]).createObject();
12221 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12222 }
12223
12224 /* create another bandwidth control object that will be mutable */
12225 unconst(mBandwidthControl).createObject();
12226 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12227
12228 /* default is to delete saved state on Saved -> PoweredOff transition */
12229 mRemoveSavedState = true;
12230
12231 /* Confirm a successful initialization when it's the case */
12232 autoInitSpan.setSucceeded();
12233
12234 miNATNetworksStarted = 0;
12235
12236 LogFlowThisFuncLeave();
12237 return rc;
12238}
12239
12240/**
12241 * Uninitializes this session object. If the reason is other than
12242 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12243 * or the client watcher code.
12244 *
12245 * @param aReason uninitialization reason
12246 *
12247 * @note Locks mParent + this object for writing.
12248 */
12249void SessionMachine::uninit(Uninit::Reason aReason)
12250{
12251 LogFlowThisFuncEnter();
12252 LogFlowThisFunc(("reason=%d\n", aReason));
12253
12254 /*
12255 * Strongly reference ourselves to prevent this object deletion after
12256 * mData->mSession.mMachine.setNull() below (which can release the last
12257 * reference and call the destructor). Important: this must be done before
12258 * accessing any members (and before AutoUninitSpan that does it as well).
12259 * This self reference will be released as the very last step on return.
12260 */
12261 ComObjPtr<SessionMachine> selfRef = this;
12262
12263 /* Enclose the state transition Ready->InUninit->NotReady */
12264 AutoUninitSpan autoUninitSpan(this);
12265 if (autoUninitSpan.uninitDone())
12266 {
12267 LogFlowThisFunc(("Already uninitialized\n"));
12268 LogFlowThisFuncLeave();
12269 return;
12270 }
12271
12272 if (autoUninitSpan.initFailed())
12273 {
12274 /* We've been called by init() because it's failed. It's not really
12275 * necessary (nor it's safe) to perform the regular uninit sequence
12276 * below, the following is enough.
12277 */
12278 LogFlowThisFunc(("Initialization failed.\n"));
12279 /* destroy the machine client token */
12280 if (mClientToken)
12281 {
12282 delete mClientToken;
12283 mClientToken = NULL;
12284 }
12285 uninitDataAndChildObjects();
12286 mData.free();
12287 unconst(mParent) = NULL;
12288 unconst(mPeer) = NULL;
12289 LogFlowThisFuncLeave();
12290 return;
12291 }
12292
12293 MachineState_T lastState;
12294 {
12295 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12296 lastState = mData->mMachineState;
12297 }
12298 NOREF(lastState);
12299
12300#ifdef VBOX_WITH_USB
12301 // release all captured USB devices, but do this before requesting the locks below
12302 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12303 {
12304 /* Console::captureUSBDevices() is called in the VM process only after
12305 * setting the machine state to Starting or Restoring.
12306 * Console::detachAllUSBDevices() will be called upon successful
12307 * termination. So, we need to release USB devices only if there was
12308 * an abnormal termination of a running VM.
12309 *
12310 * This is identical to SessionMachine::DetachAllUSBDevices except
12311 * for the aAbnormal argument. */
12312 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12313 AssertComRC(rc);
12314 NOREF(rc);
12315
12316 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12317 if (service)
12318 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12319 }
12320#endif /* VBOX_WITH_USB */
12321
12322 // we need to lock this object in uninit() because the lock is shared
12323 // with mPeer (as well as data we modify below). mParent lock is needed
12324 // by several calls to it, and USB needs host lock.
12325 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12326
12327#ifdef VBOX_WITH_RESOURCE_USAGE_API
12328 /*
12329 * It is safe to call Machine::i_unregisterMetrics() here because
12330 * PerformanceCollector::samplerCallback no longer accesses guest methods
12331 * holding the lock.
12332 */
12333 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12334 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12335 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12336 this, __PRETTY_FUNCTION__, mCollectorGuest));
12337 if (mCollectorGuest)
12338 {
12339 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12340 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12341 mCollectorGuest = NULL;
12342 }
12343#endif
12344
12345 if (aReason == Uninit::Abnormal)
12346 {
12347 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12348 Global::IsOnlineOrTransient(lastState)));
12349
12350 /* reset the state to Aborted */
12351 if (mData->mMachineState != MachineState_Aborted)
12352 i_setMachineState(MachineState_Aborted);
12353 }
12354
12355 // any machine settings modified?
12356 if (mData->flModifications)
12357 {
12358 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12359 i_rollback(false /* aNotify */);
12360 }
12361
12362 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12363 || !mConsoleTaskData.mSnapshot);
12364 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12365 {
12366 LogWarningThisFunc(("canceling failed save state request!\n"));
12367 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12368 }
12369 else if (!mConsoleTaskData.mSnapshot.isNull())
12370 {
12371 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12372
12373 /* delete all differencing hard disks created (this will also attach
12374 * their parents back by rolling back mMediaData) */
12375 i_rollbackMedia();
12376
12377 // delete the saved state file (it might have been already created)
12378 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12379 // think it's still in use
12380 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12381 mConsoleTaskData.mSnapshot->uninit();
12382 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12383 }
12384
12385 mData->mSession.mPID = NIL_RTPROCESS;
12386
12387 if (aReason == Uninit::Unexpected)
12388 {
12389 /* Uninitialization didn't come from #checkForDeath(), so tell the
12390 * client watcher thread to update the set of machines that have open
12391 * sessions. */
12392 mParent->i_updateClientWatcher();
12393 }
12394
12395 /* uninitialize all remote controls */
12396 if (mData->mSession.mRemoteControls.size())
12397 {
12398 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12399 mData->mSession.mRemoteControls.size()));
12400
12401 Data::Session::RemoteControlList::iterator it =
12402 mData->mSession.mRemoteControls.begin();
12403 while (it != mData->mSession.mRemoteControls.end())
12404 {
12405 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12406 HRESULT rc = (*it)->Uninitialize();
12407 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12408 if (FAILED(rc))
12409 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12410 ++it;
12411 }
12412 mData->mSession.mRemoteControls.clear();
12413 }
12414
12415 /* Remove all references to the NAT network service. The service will stop
12416 * if all references (also from other VMs) are removed. */
12417 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12418 {
12419 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12420 {
12421 NetworkAttachmentType_T type;
12422 HRESULT hrc;
12423
12424 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12425 if ( SUCCEEDED(hrc)
12426 && type == NetworkAttachmentType_NATNetwork)
12427 {
12428 Bstr name;
12429 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12430 if (SUCCEEDED(hrc))
12431 {
12432 multilock.release();
12433 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12434 mUserData->s.strName.c_str(), name.raw()));
12435 mParent->i_natNetworkRefDec(name.raw());
12436 multilock.acquire();
12437 }
12438 }
12439 }
12440 }
12441
12442 /*
12443 * An expected uninitialization can come only from #checkForDeath().
12444 * Otherwise it means that something's gone really wrong (for example,
12445 * the Session implementation has released the VirtualBox reference
12446 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12447 * etc). However, it's also possible, that the client releases the IPC
12448 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12449 * but the VirtualBox release event comes first to the server process.
12450 * This case is practically possible, so we should not assert on an
12451 * unexpected uninit, just log a warning.
12452 */
12453
12454 if ((aReason == Uninit::Unexpected))
12455 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12456
12457 if (aReason != Uninit::Normal)
12458 {
12459 mData->mSession.mDirectControl.setNull();
12460 }
12461 else
12462 {
12463 /* this must be null here (see #OnSessionEnd()) */
12464 Assert(mData->mSession.mDirectControl.isNull());
12465 Assert(mData->mSession.mState == SessionState_Unlocking);
12466 Assert(!mData->mSession.mProgress.isNull());
12467 }
12468 if (mData->mSession.mProgress)
12469 {
12470 if (aReason == Uninit::Normal)
12471 mData->mSession.mProgress->i_notifyComplete(S_OK);
12472 else
12473 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12474 COM_IIDOF(ISession),
12475 getComponentName(),
12476 tr("The VM session was aborted"));
12477 mData->mSession.mProgress.setNull();
12478 }
12479
12480 /* remove the association between the peer machine and this session machine */
12481 Assert( (SessionMachine*)mData->mSession.mMachine == this
12482 || aReason == Uninit::Unexpected);
12483
12484 /* reset the rest of session data */
12485 mData->mSession.mMachine.setNull();
12486 mData->mSession.mState = SessionState_Unlocked;
12487 mData->mSession.mType.setNull();
12488
12489 /* destroy the machine client token before leaving the exclusive lock */
12490 if (mClientToken)
12491 {
12492 delete mClientToken;
12493 mClientToken = NULL;
12494 }
12495
12496 /* fire an event */
12497 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12498
12499 uninitDataAndChildObjects();
12500
12501 /* free the essential data structure last */
12502 mData.free();
12503
12504 /* release the exclusive lock before setting the below two to NULL */
12505 multilock.release();
12506
12507 unconst(mParent) = NULL;
12508 unconst(mPeer) = NULL;
12509
12510 LogFlowThisFuncLeave();
12511}
12512
12513// util::Lockable interface
12514////////////////////////////////////////////////////////////////////////////////
12515
12516/**
12517 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12518 * with the primary Machine instance (mPeer).
12519 */
12520RWLockHandle *SessionMachine::lockHandle() const
12521{
12522 AssertReturn(mPeer != NULL, NULL);
12523 return mPeer->lockHandle();
12524}
12525
12526// IInternalMachineControl methods
12527////////////////////////////////////////////////////////////////////////////////
12528
12529/**
12530 * Passes collected guest statistics to performance collector object
12531 */
12532HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12533 ULONG aCpuKernel, ULONG aCpuIdle,
12534 ULONG aMemTotal, ULONG aMemFree,
12535 ULONG aMemBalloon, ULONG aMemShared,
12536 ULONG aMemCache, ULONG aPageTotal,
12537 ULONG aAllocVMM, ULONG aFreeVMM,
12538 ULONG aBalloonedVMM, ULONG aSharedVMM,
12539 ULONG aVmNetRx, ULONG aVmNetTx)
12540{
12541#ifdef VBOX_WITH_RESOURCE_USAGE_API
12542 if (mCollectorGuest)
12543 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12544 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12545 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12546 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12547
12548 return S_OK;
12549#else
12550 NOREF(aValidStats);
12551 NOREF(aCpuUser);
12552 NOREF(aCpuKernel);
12553 NOREF(aCpuIdle);
12554 NOREF(aMemTotal);
12555 NOREF(aMemFree);
12556 NOREF(aMemBalloon);
12557 NOREF(aMemShared);
12558 NOREF(aMemCache);
12559 NOREF(aPageTotal);
12560 NOREF(aAllocVMM);
12561 NOREF(aFreeVMM);
12562 NOREF(aBalloonedVMM);
12563 NOREF(aSharedVMM);
12564 NOREF(aVmNetRx);
12565 NOREF(aVmNetTx);
12566 return E_NOTIMPL;
12567#endif
12568}
12569
12570/**
12571 * @note Locks this object for writing.
12572 */
12573HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12574{
12575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12576
12577 mRemoveSavedState = RT_BOOL(aRemove);
12578
12579 return S_OK;
12580}
12581
12582/**
12583 * @note Locks the same as #i_setMachineState() does.
12584 */
12585HRESULT SessionMachine::updateState(MachineState_T aState)
12586{
12587 return i_setMachineState(aState);
12588}
12589
12590/**
12591 * @note Locks this object for writing.
12592 */
12593HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12594{
12595 IProgress* pProgress(aProgress);
12596
12597 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12598
12599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12600
12601 if (mData->mSession.mState != SessionState_Locked)
12602 return VBOX_E_INVALID_OBJECT_STATE;
12603
12604 if (!mData->mSession.mProgress.isNull())
12605 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12606
12607 /* If we didn't reference the NAT network service yet, add a reference to
12608 * force a start */
12609 if (miNATNetworksStarted < 1)
12610 {
12611 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12612 {
12613 NetworkAttachmentType_T type;
12614 HRESULT hrc;
12615 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12616 if ( SUCCEEDED(hrc)
12617 && type == NetworkAttachmentType_NATNetwork)
12618 {
12619 Bstr name;
12620 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12621 if (SUCCEEDED(hrc))
12622 {
12623 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12624 mUserData->s.strName.c_str(), name.raw()));
12625 mPeer->lockHandle()->unlockWrite();
12626 mParent->i_natNetworkRefInc(name.raw());
12627#ifdef RT_LOCK_STRICT
12628 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12629#else
12630 mPeer->lockHandle()->lockWrite();
12631#endif
12632 }
12633 }
12634 }
12635 miNATNetworksStarted++;
12636 }
12637
12638 LogFlowThisFunc(("returns S_OK.\n"));
12639 return S_OK;
12640}
12641
12642/**
12643 * @note Locks this object for writing.
12644 */
12645HRESULT SessionMachine::endPowerUp(LONG aResult)
12646{
12647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12648
12649 if (mData->mSession.mState != SessionState_Locked)
12650 return VBOX_E_INVALID_OBJECT_STATE;
12651
12652 /* Finalize the LaunchVMProcess progress object. */
12653 if (mData->mSession.mProgress)
12654 {
12655 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12656 mData->mSession.mProgress.setNull();
12657 }
12658
12659 if (SUCCEEDED((HRESULT)aResult))
12660 {
12661#ifdef VBOX_WITH_RESOURCE_USAGE_API
12662 /* The VM has been powered up successfully, so it makes sense
12663 * now to offer the performance metrics for a running machine
12664 * object. Doing it earlier wouldn't be safe. */
12665 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12666 mData->mSession.mPID);
12667#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12668 }
12669
12670 return S_OK;
12671}
12672
12673/**
12674 * @note Locks this object for writing.
12675 */
12676HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12677{
12678 LogFlowThisFuncEnter();
12679
12680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12681
12682 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12683 E_FAIL);
12684
12685 /* create a progress object to track operation completion */
12686 ComObjPtr<Progress> pProgress;
12687 pProgress.createObject();
12688 pProgress->init(i_getVirtualBox(),
12689 static_cast<IMachine *>(this) /* aInitiator */,
12690 Bstr(tr("Stopping the virtual machine")).raw(),
12691 FALSE /* aCancelable */);
12692
12693 /* fill in the console task data */
12694 mConsoleTaskData.mLastState = mData->mMachineState;
12695 mConsoleTaskData.mProgress = pProgress;
12696
12697 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12698 i_setMachineState(MachineState_Stopping);
12699
12700 pProgress.queryInterfaceTo(aProgress.asOutParam());
12701
12702 return S_OK;
12703}
12704
12705/**
12706 * @note Locks this object for writing.
12707 */
12708HRESULT SessionMachine::endPoweringDown(LONG aResult,
12709 const com::Utf8Str &aErrMsg)
12710{
12711 LogFlowThisFuncEnter();
12712
12713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12714
12715 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12716 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12717 && mConsoleTaskData.mLastState != MachineState_Null,
12718 E_FAIL);
12719
12720 /*
12721 * On failure, set the state to the state we had when BeginPoweringDown()
12722 * was called (this is expected by Console::PowerDown() and the associated
12723 * task). On success the VM process already changed the state to
12724 * MachineState_PoweredOff, so no need to do anything.
12725 */
12726 if (FAILED(aResult))
12727 i_setMachineState(mConsoleTaskData.mLastState);
12728
12729 /* notify the progress object about operation completion */
12730 Assert(mConsoleTaskData.mProgress);
12731 if (SUCCEEDED(aResult))
12732 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12733 else
12734 {
12735 if (aErrMsg.length())
12736 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12737 COM_IIDOF(ISession),
12738 getComponentName(),
12739 aErrMsg.c_str());
12740 else
12741 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12742 }
12743
12744 /* clear out the temporary saved state data */
12745 mConsoleTaskData.mLastState = MachineState_Null;
12746 mConsoleTaskData.mProgress.setNull();
12747
12748 LogFlowThisFuncLeave();
12749 return S_OK;
12750}
12751
12752
12753/**
12754 * Goes through the USB filters of the given machine to see if the given
12755 * device matches any filter or not.
12756 *
12757 * @note Locks the same as USBController::hasMatchingFilter() does.
12758 */
12759HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12760 BOOL *aMatched,
12761 ULONG *aMaskedInterfaces)
12762{
12763 LogFlowThisFunc(("\n"));
12764
12765#ifdef VBOX_WITH_USB
12766 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12767#else
12768 NOREF(aDevice);
12769 NOREF(aMaskedInterfaces);
12770 *aMatched = FALSE;
12771#endif
12772
12773 return S_OK;
12774}
12775
12776/**
12777 * @note Locks the same as Host::captureUSBDevice() does.
12778 */
12779HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId)
12780{
12781 LogFlowThisFunc(("\n"));
12782
12783#ifdef VBOX_WITH_USB
12784 /* if captureDeviceForVM() fails, it must have set extended error info */
12785 clearError();
12786 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12787 if (FAILED(rc)) return rc;
12788
12789 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12790 AssertReturn(service, E_FAIL);
12791 return service->captureDeviceForVM(this, aId.ref());
12792#else
12793 NOREF(aId);
12794 return E_NOTIMPL;
12795#endif
12796}
12797
12798/**
12799 * @note Locks the same as Host::detachUSBDevice() does.
12800 */
12801HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12802 BOOL aDone)
12803{
12804 LogFlowThisFunc(("\n"));
12805
12806#ifdef VBOX_WITH_USB
12807 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12808 AssertReturn(service, E_FAIL);
12809 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12810#else
12811 NOREF(aId);
12812 NOREF(aDone);
12813 return E_NOTIMPL;
12814#endif
12815}
12816
12817/**
12818 * Inserts all machine filters to the USB proxy service and then calls
12819 * Host::autoCaptureUSBDevices().
12820 *
12821 * Called by Console from the VM process upon VM startup.
12822 *
12823 * @note Locks what called methods lock.
12824 */
12825HRESULT SessionMachine::autoCaptureUSBDevices()
12826{
12827 LogFlowThisFunc(("\n"));
12828
12829#ifdef VBOX_WITH_USB
12830 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12831 AssertComRC(rc);
12832 NOREF(rc);
12833
12834 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12835 AssertReturn(service, E_FAIL);
12836 return service->autoCaptureDevicesForVM(this);
12837#else
12838 return S_OK;
12839#endif
12840}
12841
12842/**
12843 * Removes all machine filters from the USB proxy service and then calls
12844 * Host::detachAllUSBDevices().
12845 *
12846 * Called by Console from the VM process upon normal VM termination or by
12847 * SessionMachine::uninit() upon abnormal VM termination (from under the
12848 * Machine/SessionMachine lock).
12849 *
12850 * @note Locks what called methods lock.
12851 */
12852HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12853{
12854 LogFlowThisFunc(("\n"));
12855
12856#ifdef VBOX_WITH_USB
12857 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12858 AssertComRC(rc);
12859 NOREF(rc);
12860
12861 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12862 AssertReturn(service, E_FAIL);
12863 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12864#else
12865 NOREF(aDone);
12866 return S_OK;
12867#endif
12868}
12869
12870/**
12871 * @note Locks this object for writing.
12872 */
12873HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12874 ComPtr<IProgress> &aProgress)
12875{
12876 LogFlowThisFuncEnter();
12877
12878 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12879 /*
12880 * We don't assert below because it might happen that a non-direct session
12881 * informs us it is closed right after we've been uninitialized -- it's ok.
12882 */
12883
12884 /* get IInternalSessionControl interface */
12885 ComPtr<IInternalSessionControl> control(aSession);
12886
12887 ComAssertRet(!control.isNull(), E_INVALIDARG);
12888
12889 /* Creating a Progress object requires the VirtualBox lock, and
12890 * thus locking it here is required by the lock order rules. */
12891 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12892
12893 if (control == mData->mSession.mDirectControl)
12894 {
12895 /* The direct session is being normally closed by the client process
12896 * ----------------------------------------------------------------- */
12897
12898 /* go to the closing state (essential for all open*Session() calls and
12899 * for #checkForDeath()) */
12900 Assert(mData->mSession.mState == SessionState_Locked);
12901 mData->mSession.mState = SessionState_Unlocking;
12902
12903 /* set direct control to NULL to release the remote instance */
12904 mData->mSession.mDirectControl.setNull();
12905 LogFlowThisFunc(("Direct control is set to NULL\n"));
12906
12907 if (mData->mSession.mProgress)
12908 {
12909 /* finalize the progress, someone might wait if a frontend
12910 * closes the session before powering on the VM. */
12911 mData->mSession.mProgress->notifyComplete(E_FAIL,
12912 COM_IIDOF(ISession),
12913 getComponentName(),
12914 tr("The VM session was closed before any attempt to power it on"));
12915 mData->mSession.mProgress.setNull();
12916 }
12917
12918 /* Create the progress object the client will use to wait until
12919 * #checkForDeath() is called to uninitialize this session object after
12920 * it releases the IPC semaphore.
12921 * Note! Because we're "reusing" mProgress here, this must be a proxy
12922 * object just like for LaunchVMProcess. */
12923 Assert(mData->mSession.mProgress.isNull());
12924 ComObjPtr<ProgressProxy> progress;
12925 progress.createObject();
12926 ComPtr<IUnknown> pPeer(mPeer);
12927 progress->init(mParent, pPeer,
12928 Bstr(tr("Closing session")).raw(),
12929 FALSE /* aCancelable */);
12930 progress.queryInterfaceTo(aProgress.asOutParam());
12931 mData->mSession.mProgress = progress;
12932 }
12933 else
12934 {
12935 /* the remote session is being normally closed */
12936 Data::Session::RemoteControlList::iterator it =
12937 mData->mSession.mRemoteControls.begin();
12938 while (it != mData->mSession.mRemoteControls.end())
12939 {
12940 if (control == *it)
12941 break;
12942 ++it;
12943 }
12944 BOOL found = it != mData->mSession.mRemoteControls.end();
12945 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12946 E_INVALIDARG);
12947 // This MUST be erase(it), not remove(*it) as the latter triggers a
12948 // very nasty use after free due to the place where the value "lives".
12949 mData->mSession.mRemoteControls.erase(it);
12950 }
12951
12952 /* signal the client watcher thread, because the client is going away */
12953 mParent->i_updateClientWatcher();
12954
12955 LogFlowThisFuncLeave();
12956 return S_OK;
12957}
12958
12959/**
12960 * @note Locks this object for writing.
12961 */
12962HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12963 com::Utf8Str &aStateFilePath)
12964{
12965 LogFlowThisFuncEnter();
12966
12967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12968
12969 AssertReturn( mData->mMachineState == MachineState_Paused
12970 && mConsoleTaskData.mLastState == MachineState_Null
12971 && mConsoleTaskData.strStateFilePath.isEmpty(),
12972 E_FAIL);
12973
12974 /* create a progress object to track operation completion */
12975 ComObjPtr<Progress> pProgress;
12976 pProgress.createObject();
12977 pProgress->init(i_getVirtualBox(),
12978 static_cast<IMachine *>(this) /* aInitiator */,
12979 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12980 FALSE /* aCancelable */);
12981
12982 /* stateFilePath is null when the machine is not running */
12983 if (mData->mMachineState == MachineState_Paused)
12984 i_composeSavedStateFilename(aStateFilePath);
12985
12986 /* fill in the console task data */
12987 mConsoleTaskData.mLastState = mData->mMachineState;
12988 mConsoleTaskData.strStateFilePath = aStateFilePath;
12989 mConsoleTaskData.mProgress = pProgress;
12990
12991 /* set the state to Saving (this is expected by Console::SaveState()) */
12992 i_setMachineState(MachineState_Saving);
12993
12994 pProgress.queryInterfaceTo(aProgress.asOutParam());
12995
12996 return S_OK;
12997}
12998
12999/**
13000 * @note Locks mParent + this object for writing.
13001 */
13002HRESULT SessionMachine::endSavingState(LONG aResult,
13003 const com::Utf8Str &aErrMsg)
13004{
13005 LogFlowThisFunc(("\n"));
13006
13007 /* endSavingState() need mParent lock */
13008 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13009
13010 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13011 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13012 && mConsoleTaskData.mLastState != MachineState_Null
13013 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13014 E_FAIL);
13015
13016 /*
13017 * On failure, set the state to the state we had when BeginSavingState()
13018 * was called (this is expected by Console::SaveState() and the associated
13019 * task). On success the VM process already changed the state to
13020 * MachineState_Saved, so no need to do anything.
13021 */
13022 if (FAILED(aResult))
13023 i_setMachineState(mConsoleTaskData.mLastState);
13024
13025 return i_endSavingState(aResult, aErrMsg);
13026}
13027
13028/**
13029 * @note Locks this object for writing.
13030 */
13031HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13032{
13033 LogFlowThisFunc(("\n"));
13034
13035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13036
13037 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13038 || mData->mMachineState == MachineState_Teleported
13039 || mData->mMachineState == MachineState_Aborted
13040 , E_FAIL); /** @todo setError. */
13041
13042 com::Utf8Str stateFilePathFull;
13043 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13044 if (RT_FAILURE(vrc))
13045 return setError(VBOX_E_FILE_ERROR,
13046 tr("Invalid saved state file path '%s' (%Rrc)"),
13047 aSavedStateFile.c_str(),
13048 vrc);
13049
13050 mSSData->strStateFilePath = stateFilePathFull;
13051
13052 /* The below i_setMachineState() will detect the state transition and will
13053 * update the settings file */
13054
13055 return i_setMachineState(MachineState_Saved);
13056}
13057
13058HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13059 std::vector<com::Utf8Str> &aValues,
13060 std::vector<LONG64> &aTimestamps,
13061 std::vector<com::Utf8Str> &aFlags)
13062{
13063 LogFlowThisFunc(("\n"));
13064
13065#ifdef VBOX_WITH_GUEST_PROPS
13066 using namespace guestProp;
13067
13068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13069
13070 size_t cEntries = mHWData->mGuestProperties.size();
13071 aNames.resize(cEntries);
13072 aValues.resize(cEntries);
13073 aTimestamps.resize(cEntries);
13074 aFlags.resize(cEntries);
13075
13076 size_t i = 0;
13077 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13078 it != mHWData->mGuestProperties.end();
13079 ++it, ++i)
13080 {
13081 char szFlags[MAX_FLAGS_LEN + 1];
13082 aNames[i] = it->first;
13083 aValues[i] = it->second.strValue;
13084 aTimestamps[i] = it->second.mTimestamp;
13085
13086 /* If it is NULL, keep it NULL. */
13087 if (it->second.mFlags)
13088 {
13089 writeFlags(it->second.mFlags, szFlags);
13090 aFlags[i] = szFlags;
13091 }
13092 else
13093 aFlags[i] = "";
13094 }
13095 return S_OK;
13096#else
13097 ReturnComNotImplemented();
13098#endif
13099}
13100
13101HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13102 const com::Utf8Str &aValue,
13103 LONG64 aTimestamp,
13104 const com::Utf8Str &aFlags)
13105{
13106 LogFlowThisFunc(("\n"));
13107
13108#ifdef VBOX_WITH_GUEST_PROPS
13109 using namespace guestProp;
13110
13111 try
13112 {
13113 /*
13114 * Convert input up front.
13115 */
13116 uint32_t fFlags = NILFLAG;
13117 if (aFlags.length())
13118 {
13119 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13120 AssertRCReturn(vrc, E_INVALIDARG);
13121 }
13122
13123 /*
13124 * Now grab the object lock, validate the state and do the update.
13125 */
13126
13127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13128
13129 switch (mData->mMachineState)
13130 {
13131 case MachineState_Paused:
13132 case MachineState_Running:
13133 case MachineState_Teleporting:
13134 case MachineState_TeleportingPausedVM:
13135 case MachineState_LiveSnapshotting:
13136 case MachineState_DeletingSnapshotOnline:
13137 case MachineState_DeletingSnapshotPaused:
13138 case MachineState_Saving:
13139 case MachineState_Stopping:
13140 break;
13141
13142 default:
13143 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13144 VBOX_E_INVALID_VM_STATE);
13145 }
13146
13147 i_setModified(IsModified_MachineData);
13148 mHWData.backup();
13149
13150 bool fDelete = !aValue.length();
13151 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13152 if (it != mHWData->mGuestProperties.end())
13153 {
13154 if (!fDelete)
13155 {
13156 it->second.strValue = aValue;
13157 it->second.mTimestamp = aTimestamp;
13158 it->second.mFlags = fFlags;
13159 }
13160 else
13161 mHWData->mGuestProperties.erase(it);
13162
13163 mData->mGuestPropertiesModified = TRUE;
13164 }
13165 else if (!fDelete)
13166 {
13167 HWData::GuestProperty prop;
13168 prop.strValue = aValue;
13169 prop.mTimestamp = aTimestamp;
13170 prop.mFlags = fFlags;
13171
13172 mHWData->mGuestProperties[aName] = prop;
13173 mData->mGuestPropertiesModified = TRUE;
13174 }
13175
13176 /*
13177 * Send a callback notification if appropriate
13178 */
13179 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13180 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13181 RTSTR_MAX,
13182 aName.c_str(),
13183 RTSTR_MAX, NULL)
13184 )
13185 {
13186 alock.release();
13187
13188 mParent->i_onGuestPropertyChange(mData->mUuid,
13189 Bstr(aName).raw(),
13190 Bstr(aValue).raw(),
13191 Bstr(aFlags).raw());
13192 }
13193 }
13194 catch (...)
13195 {
13196 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13197 }
13198 return S_OK;
13199#else
13200 ReturnComNotImplemented();
13201#endif
13202}
13203
13204
13205HRESULT SessionMachine::lockMedia()
13206{
13207 AutoMultiWriteLock2 alock(this->lockHandle(),
13208 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13209
13210 AssertReturn( mData->mMachineState == MachineState_Starting
13211 || mData->mMachineState == MachineState_Restoring
13212 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13213
13214 clearError();
13215 alock.release();
13216 return i_lockMedia();
13217}
13218
13219HRESULT SessionMachine::unlockMedia()
13220{
13221 HRESULT hrc = i_unlockMedia();
13222 return hrc;
13223}
13224
13225HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13226 ComPtr<IMediumAttachment> &aNewAttachment)
13227{
13228 // request the host lock first, since might be calling Host methods for getting host drives;
13229 // next, protect the media tree all the while we're in here, as well as our member variables
13230 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13231 this->lockHandle(),
13232 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13233
13234 IMediumAttachment *iAttach = aAttachment;
13235 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13236
13237 Bstr ctrlName;
13238 LONG lPort;
13239 LONG lDevice;
13240 bool fTempEject;
13241 {
13242 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13243
13244 /* Need to query the details first, as the IMediumAttachment reference
13245 * might be to the original settings, which we are going to change. */
13246 ctrlName = pAttach->i_getControllerName();
13247 lPort = pAttach->i_getPort();
13248 lDevice = pAttach->i_getDevice();
13249 fTempEject = pAttach->i_getTempEject();
13250 }
13251
13252 if (!fTempEject)
13253 {
13254 /* Remember previously mounted medium. The medium before taking the
13255 * backup is not necessarily the same thing. */
13256 ComObjPtr<Medium> oldmedium;
13257 oldmedium = pAttach->i_getMedium();
13258
13259 i_setModified(IsModified_Storage);
13260 mMediaData.backup();
13261
13262 // The backup operation makes the pAttach reference point to the
13263 // old settings. Re-get the correct reference.
13264 pAttach = i_findAttachment(mMediaData->mAttachments,
13265 ctrlName.raw(),
13266 lPort,
13267 lDevice);
13268
13269 {
13270 AutoCaller autoAttachCaller(this);
13271 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13272
13273 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13274 if (!oldmedium.isNull())
13275 oldmedium->i_removeBackReference(mData->mUuid);
13276
13277 pAttach->i_updateMedium(NULL);
13278 pAttach->i_updateEjected();
13279 }
13280
13281 i_setModified(IsModified_Storage);
13282 }
13283 else
13284 {
13285 {
13286 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13287 pAttach->i_updateEjected();
13288 }
13289 }
13290
13291 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13292
13293 return S_OK;
13294}
13295
13296// public methods only for internal purposes
13297/////////////////////////////////////////////////////////////////////////////
13298
13299#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13300/**
13301 * Called from the client watcher thread to check for expected or unexpected
13302 * death of the client process that has a direct session to this machine.
13303 *
13304 * On Win32 and on OS/2, this method is called only when we've got the
13305 * mutex (i.e. the client has either died or terminated normally) so it always
13306 * returns @c true (the client is terminated, the session machine is
13307 * uninitialized).
13308 *
13309 * On other platforms, the method returns @c true if the client process has
13310 * terminated normally or abnormally and the session machine was uninitialized,
13311 * and @c false if the client process is still alive.
13312 *
13313 * @note Locks this object for writing.
13314 */
13315bool SessionMachine::i_checkForDeath()
13316{
13317 Uninit::Reason reason;
13318 bool terminated = false;
13319
13320 /* Enclose autoCaller with a block because calling uninit() from under it
13321 * will deadlock. */
13322 {
13323 AutoCaller autoCaller(this);
13324 if (!autoCaller.isOk())
13325 {
13326 /* return true if not ready, to cause the client watcher to exclude
13327 * the corresponding session from watching */
13328 LogFlowThisFunc(("Already uninitialized!\n"));
13329 return true;
13330 }
13331
13332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13333
13334 /* Determine the reason of death: if the session state is Closing here,
13335 * everything is fine. Otherwise it means that the client did not call
13336 * OnSessionEnd() before it released the IPC semaphore. This may happen
13337 * either because the client process has abnormally terminated, or
13338 * because it simply forgot to call ISession::Close() before exiting. We
13339 * threat the latter also as an abnormal termination (see
13340 * Session::uninit() for details). */
13341 reason = mData->mSession.mState == SessionState_Unlocking ?
13342 Uninit::Normal :
13343 Uninit::Abnormal;
13344
13345 if (mClientToken)
13346 terminated = mClientToken->release();
13347 } /* AutoCaller block */
13348
13349 if (terminated)
13350 uninit(reason);
13351
13352 return terminated;
13353}
13354
13355void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359 strTokenId.setNull();
13360
13361 AutoCaller autoCaller(this);
13362 AssertComRCReturnVoid(autoCaller.rc());
13363
13364 Assert(mClientToken);
13365 if (mClientToken)
13366 mClientToken->getId(strTokenId);
13367}
13368#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13369IToken *SessionMachine::i_getToken()
13370{
13371 LogFlowThisFunc(("\n"));
13372
13373 AutoCaller autoCaller(this);
13374 AssertComRCReturn(autoCaller.rc(), NULL);
13375
13376 Assert(mClientToken);
13377 if (mClientToken)
13378 return mClientToken->getToken();
13379 else
13380 return NULL;
13381}
13382#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13383
13384Machine::ClientToken *SessionMachine::i_getClientToken()
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388 AutoCaller autoCaller(this);
13389 AssertComRCReturn(autoCaller.rc(), NULL);
13390
13391 return mClientToken;
13392}
13393
13394
13395/**
13396 * @note Locks this object for reading.
13397 */
13398HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13399{
13400 LogFlowThisFunc(("\n"));
13401
13402 AutoCaller autoCaller(this);
13403 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13404
13405 ComPtr<IInternalSessionControl> directControl;
13406 {
13407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13408 directControl = mData->mSession.mDirectControl;
13409 }
13410
13411 /* ignore notifications sent after #OnSessionEnd() is called */
13412 if (!directControl)
13413 return S_OK;
13414
13415 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13416}
13417
13418/**
13419 * @note Locks this object for reading.
13420 */
13421HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13422 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13423 IN_BSTR aGuestIp, LONG aGuestPort)
13424{
13425 LogFlowThisFunc(("\n"));
13426
13427 AutoCaller autoCaller(this);
13428 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13429
13430 ComPtr<IInternalSessionControl> directControl;
13431 {
13432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13433 directControl = mData->mSession.mDirectControl;
13434 }
13435
13436 /* ignore notifications sent after #OnSessionEnd() is called */
13437 if (!directControl)
13438 return S_OK;
13439 /*
13440 * instead acting like callback we ask IVirtualBox deliver corresponding event
13441 */
13442
13443 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13444 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13445 return S_OK;
13446}
13447
13448/**
13449 * @note Locks this object for reading.
13450 */
13451HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13452{
13453 LogFlowThisFunc(("\n"));
13454
13455 AutoCaller autoCaller(this);
13456 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13457
13458 ComPtr<IInternalSessionControl> directControl;
13459 {
13460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13461 directControl = mData->mSession.mDirectControl;
13462 }
13463
13464 /* ignore notifications sent after #OnSessionEnd() is called */
13465 if (!directControl)
13466 return S_OK;
13467
13468 return directControl->OnSerialPortChange(serialPort);
13469}
13470
13471/**
13472 * @note Locks this object for reading.
13473 */
13474HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13475{
13476 LogFlowThisFunc(("\n"));
13477
13478 AutoCaller autoCaller(this);
13479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13480
13481 ComPtr<IInternalSessionControl> directControl;
13482 {
13483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13484 directControl = mData->mSession.mDirectControl;
13485 }
13486
13487 /* ignore notifications sent after #OnSessionEnd() is called */
13488 if (!directControl)
13489 return S_OK;
13490
13491 return directControl->OnParallelPortChange(parallelPort);
13492}
13493
13494/**
13495 * @note Locks this object for reading.
13496 */
13497HRESULT SessionMachine::i_onStorageControllerChange()
13498{
13499 LogFlowThisFunc(("\n"));
13500
13501 AutoCaller autoCaller(this);
13502 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13503
13504 ComPtr<IInternalSessionControl> directControl;
13505 {
13506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13507 directControl = mData->mSession.mDirectControl;
13508 }
13509
13510 /* ignore notifications sent after #OnSessionEnd() is called */
13511 if (!directControl)
13512 return S_OK;
13513
13514 return directControl->OnStorageControllerChange();
13515}
13516
13517/**
13518 * @note Locks this object for reading.
13519 */
13520HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13521{
13522 LogFlowThisFunc(("\n"));
13523
13524 AutoCaller autoCaller(this);
13525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13526
13527 ComPtr<IInternalSessionControl> directControl;
13528 {
13529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13530 directControl = mData->mSession.mDirectControl;
13531 }
13532
13533 /* ignore notifications sent after #OnSessionEnd() is called */
13534 if (!directControl)
13535 return S_OK;
13536
13537 return directControl->OnMediumChange(aAttachment, aForce);
13538}
13539
13540/**
13541 * @note Locks this object for reading.
13542 */
13543HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13544{
13545 LogFlowThisFunc(("\n"));
13546
13547 AutoCaller autoCaller(this);
13548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13549
13550 ComPtr<IInternalSessionControl> directControl;
13551 {
13552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13553 directControl = mData->mSession.mDirectControl;
13554 }
13555
13556 /* ignore notifications sent after #OnSessionEnd() is called */
13557 if (!directControl)
13558 return S_OK;
13559
13560 return directControl->OnCPUChange(aCPU, aRemove);
13561}
13562
13563HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13564{
13565 LogFlowThisFunc(("\n"));
13566
13567 AutoCaller autoCaller(this);
13568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13569
13570 ComPtr<IInternalSessionControl> directControl;
13571 {
13572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13573 directControl = mData->mSession.mDirectControl;
13574 }
13575
13576 /* ignore notifications sent after #OnSessionEnd() is called */
13577 if (!directControl)
13578 return S_OK;
13579
13580 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13581}
13582
13583/**
13584 * @note Locks this object for reading.
13585 */
13586HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13587{
13588 LogFlowThisFunc(("\n"));
13589
13590 AutoCaller autoCaller(this);
13591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13592
13593 ComPtr<IInternalSessionControl> directControl;
13594 {
13595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13596 directControl = mData->mSession.mDirectControl;
13597 }
13598
13599 /* ignore notifications sent after #OnSessionEnd() is called */
13600 if (!directControl)
13601 return S_OK;
13602
13603 return directControl->OnVRDEServerChange(aRestart);
13604}
13605
13606/**
13607 * @note Locks this object for reading.
13608 */
13609HRESULT SessionMachine::i_onVideoCaptureChange()
13610{
13611 LogFlowThisFunc(("\n"));
13612
13613 AutoCaller autoCaller(this);
13614 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13615
13616 ComPtr<IInternalSessionControl> directControl;
13617 {
13618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13619 directControl = mData->mSession.mDirectControl;
13620 }
13621
13622 /* ignore notifications sent after #OnSessionEnd() is called */
13623 if (!directControl)
13624 return S_OK;
13625
13626 return directControl->OnVideoCaptureChange();
13627}
13628
13629/**
13630 * @note Locks this object for reading.
13631 */
13632HRESULT SessionMachine::i_onUSBControllerChange()
13633{
13634 LogFlowThisFunc(("\n"));
13635
13636 AutoCaller autoCaller(this);
13637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13638
13639 ComPtr<IInternalSessionControl> directControl;
13640 {
13641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13642 directControl = mData->mSession.mDirectControl;
13643 }
13644
13645 /* ignore notifications sent after #OnSessionEnd() is called */
13646 if (!directControl)
13647 return S_OK;
13648
13649 return directControl->OnUSBControllerChange();
13650}
13651
13652/**
13653 * @note Locks this object for reading.
13654 */
13655HRESULT SessionMachine::i_onSharedFolderChange()
13656{
13657 LogFlowThisFunc(("\n"));
13658
13659 AutoCaller autoCaller(this);
13660 AssertComRCReturnRC(autoCaller.rc());
13661
13662 ComPtr<IInternalSessionControl> directControl;
13663 {
13664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13665 directControl = mData->mSession.mDirectControl;
13666 }
13667
13668 /* ignore notifications sent after #OnSessionEnd() is called */
13669 if (!directControl)
13670 return S_OK;
13671
13672 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13673}
13674
13675/**
13676 * @note Locks this object for reading.
13677 */
13678HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13679{
13680 LogFlowThisFunc(("\n"));
13681
13682 AutoCaller autoCaller(this);
13683 AssertComRCReturnRC(autoCaller.rc());
13684
13685 ComPtr<IInternalSessionControl> directControl;
13686 {
13687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13688 directControl = mData->mSession.mDirectControl;
13689 }
13690
13691 /* ignore notifications sent after #OnSessionEnd() is called */
13692 if (!directControl)
13693 return S_OK;
13694
13695 return directControl->OnClipboardModeChange(aClipboardMode);
13696}
13697
13698/**
13699 * @note Locks this object for reading.
13700 */
13701HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13702{
13703 LogFlowThisFunc(("\n"));
13704
13705 AutoCaller autoCaller(this);
13706 AssertComRCReturnRC(autoCaller.rc());
13707
13708 ComPtr<IInternalSessionControl> directControl;
13709 {
13710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13711 directControl = mData->mSession.mDirectControl;
13712 }
13713
13714 /* ignore notifications sent after #OnSessionEnd() is called */
13715 if (!directControl)
13716 return S_OK;
13717
13718 return directControl->OnDnDModeChange(aDnDMode);
13719}
13720
13721/**
13722 * @note Locks this object for reading.
13723 */
13724HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13725{
13726 LogFlowThisFunc(("\n"));
13727
13728 AutoCaller autoCaller(this);
13729 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13730
13731 ComPtr<IInternalSessionControl> directControl;
13732 {
13733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13734 directControl = mData->mSession.mDirectControl;
13735 }
13736
13737 /* ignore notifications sent after #OnSessionEnd() is called */
13738 if (!directControl)
13739 return S_OK;
13740
13741 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13742}
13743
13744/**
13745 * @note Locks this object for reading.
13746 */
13747HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751 AutoCaller autoCaller(this);
13752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13753
13754 ComPtr<IInternalSessionControl> directControl;
13755 {
13756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13757 directControl = mData->mSession.mDirectControl;
13758 }
13759
13760 /* ignore notifications sent after #OnSessionEnd() is called */
13761 if (!directControl)
13762 return S_OK;
13763
13764 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13765}
13766
13767/**
13768 * Returns @c true if this machine's USB controller reports it has a matching
13769 * filter for the given USB device and @c false otherwise.
13770 *
13771 * @note locks this object for reading.
13772 */
13773bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13774{
13775 AutoCaller autoCaller(this);
13776 /* silently return if not ready -- this method may be called after the
13777 * direct machine session has been called */
13778 if (!autoCaller.isOk())
13779 return false;
13780
13781#ifdef VBOX_WITH_USB
13782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13783
13784 switch (mData->mMachineState)
13785 {
13786 case MachineState_Starting:
13787 case MachineState_Restoring:
13788 case MachineState_TeleportingIn:
13789 case MachineState_Paused:
13790 case MachineState_Running:
13791 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13792 * elsewhere... */
13793 alock.release();
13794 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13795 default: break;
13796 }
13797#else
13798 NOREF(aDevice);
13799 NOREF(aMaskedIfs);
13800#endif
13801 return false;
13802}
13803
13804/**
13805 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13806 */
13807HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13808 IVirtualBoxErrorInfo *aError,
13809 ULONG aMaskedIfs)
13810{
13811 LogFlowThisFunc(("\n"));
13812
13813 AutoCaller autoCaller(this);
13814
13815 /* This notification may happen after the machine object has been
13816 * uninitialized (the session was closed), so don't assert. */
13817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13818
13819 ComPtr<IInternalSessionControl> directControl;
13820 {
13821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13822 directControl = mData->mSession.mDirectControl;
13823 }
13824
13825 /* fail on notifications sent after #OnSessionEnd() is called, it is
13826 * expected by the caller */
13827 if (!directControl)
13828 return E_FAIL;
13829
13830 /* No locks should be held at this point. */
13831 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13832 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13833
13834 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13835}
13836
13837/**
13838 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13839 */
13840HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13841 IVirtualBoxErrorInfo *aError)
13842{
13843 LogFlowThisFunc(("\n"));
13844
13845 AutoCaller autoCaller(this);
13846
13847 /* This notification may happen after the machine object has been
13848 * uninitialized (the session was closed), so don't assert. */
13849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13850
13851 ComPtr<IInternalSessionControl> directControl;
13852 {
13853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13854 directControl = mData->mSession.mDirectControl;
13855 }
13856
13857 /* fail on notifications sent after #OnSessionEnd() is called, it is
13858 * expected by the caller */
13859 if (!directControl)
13860 return E_FAIL;
13861
13862 /* No locks should be held at this point. */
13863 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13864 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13865
13866 return directControl->OnUSBDeviceDetach(aId, aError);
13867}
13868
13869// protected methods
13870/////////////////////////////////////////////////////////////////////////////
13871
13872/**
13873 * Helper method to finalize saving the state.
13874 *
13875 * @note Must be called from under this object's lock.
13876 *
13877 * @param aRc S_OK if the snapshot has been taken successfully
13878 * @param aErrMsg human readable error message for failure
13879 *
13880 * @note Locks mParent + this objects for writing.
13881 */
13882HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13883{
13884 LogFlowThisFuncEnter();
13885
13886 AutoCaller autoCaller(this);
13887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13888
13889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13890
13891 HRESULT rc = S_OK;
13892
13893 if (SUCCEEDED(aRc))
13894 {
13895 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13896
13897 /* save all VM settings */
13898 rc = i_saveSettings(NULL);
13899 // no need to check whether VirtualBox.xml needs saving also since
13900 // we can't have a name change pending at this point
13901 }
13902 else
13903 {
13904 // delete the saved state file (it might have been already created);
13905 // we need not check whether this is shared with a snapshot here because
13906 // we certainly created this saved state file here anew
13907 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13908 }
13909
13910 /* notify the progress object about operation completion */
13911 Assert(mConsoleTaskData.mProgress);
13912 if (SUCCEEDED(aRc))
13913 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13914 else
13915 {
13916 if (aErrMsg.length())
13917 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13918 COM_IIDOF(ISession),
13919 getComponentName(),
13920 aErrMsg.c_str());
13921 else
13922 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13923 }
13924
13925 /* clear out the temporary saved state data */
13926 mConsoleTaskData.mLastState = MachineState_Null;
13927 mConsoleTaskData.strStateFilePath.setNull();
13928 mConsoleTaskData.mProgress.setNull();
13929
13930 LogFlowThisFuncLeave();
13931 return rc;
13932}
13933
13934/**
13935 * Deletes the given file if it is no longer in use by either the current machine state
13936 * (if the machine is "saved") or any of the machine's snapshots.
13937 *
13938 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13939 * but is different for each SnapshotMachine. When calling this, the order of calling this
13940 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13941 * is therefore critical. I know, it's all rather messy.
13942 *
13943 * @param strStateFile
13944 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13945 * the test for whether the saved state file is in use.
13946 */
13947void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13948 Snapshot *pSnapshotToIgnore)
13949{
13950 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13951 if ( (strStateFile.isNotEmpty())
13952 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13953 )
13954 // ... and it must also not be shared with other snapshots
13955 if ( !mData->mFirstSnapshot
13956 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13957 // this checks the SnapshotMachine's state file paths
13958 )
13959 RTFileDelete(strStateFile.c_str());
13960}
13961
13962/**
13963 * Locks the attached media.
13964 *
13965 * All attached hard disks are locked for writing and DVD/floppy are locked for
13966 * reading. Parents of attached hard disks (if any) are locked for reading.
13967 *
13968 * This method also performs accessibility check of all media it locks: if some
13969 * media is inaccessible, the method will return a failure and a bunch of
13970 * extended error info objects per each inaccessible medium.
13971 *
13972 * Note that this method is atomic: if it returns a success, all media are
13973 * locked as described above; on failure no media is locked at all (all
13974 * succeeded individual locks will be undone).
13975 *
13976 * The caller is responsible for doing the necessary state sanity checks.
13977 *
13978 * The locks made by this method must be undone by calling #unlockMedia() when
13979 * no more needed.
13980 */
13981HRESULT SessionMachine::i_lockMedia()
13982{
13983 AutoCaller autoCaller(this);
13984 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13985
13986 AutoMultiWriteLock2 alock(this->lockHandle(),
13987 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13988
13989 /* bail out if trying to lock things with already set up locking */
13990 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13991
13992 MultiResult mrc(S_OK);
13993
13994 /* Collect locking information for all medium objects attached to the VM. */
13995 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13996 it != mMediaData->mAttachments.end();
13997 ++it)
13998 {
13999 MediumAttachment* pAtt = *it;
14000 DeviceType_T devType = pAtt->i_getType();
14001 Medium *pMedium = pAtt->i_getMedium();
14002
14003 MediumLockList *pMediumLockList(new MediumLockList());
14004 // There can be attachments without a medium (floppy/dvd), and thus
14005 // it's impossible to create a medium lock list. It still makes sense
14006 // to have the empty medium lock list in the map in case a medium is
14007 // attached later.
14008 if (pMedium != NULL)
14009 {
14010 MediumType_T mediumType = pMedium->i_getType();
14011 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14012 || mediumType == MediumType_Shareable;
14013 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14014
14015 alock.release();
14016 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14017 !fIsReadOnlyLock /* fMediumLockWrite */,
14018 NULL,
14019 *pMediumLockList);
14020 alock.acquire();
14021 if (FAILED(mrc))
14022 {
14023 delete pMediumLockList;
14024 mData->mSession.mLockedMedia.Clear();
14025 break;
14026 }
14027 }
14028
14029 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14030 if (FAILED(rc))
14031 {
14032 mData->mSession.mLockedMedia.Clear();
14033 mrc = setError(rc,
14034 tr("Collecting locking information for all attached media failed"));
14035 break;
14036 }
14037 }
14038
14039 if (SUCCEEDED(mrc))
14040 {
14041 /* Now lock all media. If this fails, nothing is locked. */
14042 alock.release();
14043 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14044 alock.acquire();
14045 if (FAILED(rc))
14046 {
14047 mrc = setError(rc,
14048 tr("Locking of attached media failed"));
14049 }
14050 }
14051
14052 return mrc;
14053}
14054
14055/**
14056 * Undoes the locks made by by #lockMedia().
14057 */
14058HRESULT SessionMachine::i_unlockMedia()
14059{
14060 AutoCaller autoCaller(this);
14061 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14062
14063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14064
14065 /* we may be holding important error info on the current thread;
14066 * preserve it */
14067 ErrorInfoKeeper eik;
14068
14069 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14070 AssertComRC(rc);
14071 return rc;
14072}
14073
14074/**
14075 * Helper to change the machine state (reimplementation).
14076 *
14077 * @note Locks this object for writing.
14078 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14079 * it can cause crashes in random places due to unexpectedly committing
14080 * the current settings. The caller is responsible for that. The call
14081 * to saveStateSettings is fine, because this method does not commit.
14082 */
14083HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14084{
14085 LogFlowThisFuncEnter();
14086 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14087
14088 AutoCaller autoCaller(this);
14089 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14090
14091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14092
14093 MachineState_T oldMachineState = mData->mMachineState;
14094
14095 AssertMsgReturn(oldMachineState != aMachineState,
14096 ("oldMachineState=%s, aMachineState=%s\n",
14097 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14098 E_FAIL);
14099
14100 HRESULT rc = S_OK;
14101
14102 int stsFlags = 0;
14103 bool deleteSavedState = false;
14104
14105 /* detect some state transitions */
14106
14107 if ( ( oldMachineState == MachineState_Saved
14108 && aMachineState == MachineState_Restoring)
14109 || ( ( oldMachineState == MachineState_PoweredOff
14110 || oldMachineState == MachineState_Teleported
14111 || oldMachineState == MachineState_Aborted
14112 )
14113 && ( aMachineState == MachineState_TeleportingIn
14114 || aMachineState == MachineState_Starting
14115 )
14116 )
14117 )
14118 {
14119 /* The EMT thread is about to start */
14120
14121 /* Nothing to do here for now... */
14122
14123 /// @todo NEWMEDIA don't let mDVDDrive and other children
14124 /// change anything when in the Starting/Restoring state
14125 }
14126 else if ( ( oldMachineState == MachineState_Running
14127 || oldMachineState == MachineState_Paused
14128 || oldMachineState == MachineState_Teleporting
14129 || oldMachineState == MachineState_LiveSnapshotting
14130 || oldMachineState == MachineState_Stuck
14131 || oldMachineState == MachineState_Starting
14132 || oldMachineState == MachineState_Stopping
14133 || oldMachineState == MachineState_Saving
14134 || oldMachineState == MachineState_Restoring
14135 || oldMachineState == MachineState_TeleportingPausedVM
14136 || oldMachineState == MachineState_TeleportingIn
14137 )
14138 && ( aMachineState == MachineState_PoweredOff
14139 || aMachineState == MachineState_Saved
14140 || aMachineState == MachineState_Teleported
14141 || aMachineState == MachineState_Aborted
14142 )
14143 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14144 * snapshot */
14145 && ( mConsoleTaskData.mSnapshot.isNull()
14146 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14147 )
14148 )
14149 {
14150 /* The EMT thread has just stopped, unlock attached media. Note that as
14151 * opposed to locking that is done from Console, we do unlocking here
14152 * because the VM process may have aborted before having a chance to
14153 * properly unlock all media it locked. */
14154
14155 unlockMedia();
14156 }
14157
14158 if (oldMachineState == MachineState_Restoring)
14159 {
14160 if (aMachineState != MachineState_Saved)
14161 {
14162 /*
14163 * delete the saved state file once the machine has finished
14164 * restoring from it (note that Console sets the state from
14165 * Restoring to Saved if the VM couldn't restore successfully,
14166 * to give the user an ability to fix an error and retry --
14167 * we keep the saved state file in this case)
14168 */
14169 deleteSavedState = true;
14170 }
14171 }
14172 else if ( oldMachineState == MachineState_Saved
14173 && ( aMachineState == MachineState_PoweredOff
14174 || aMachineState == MachineState_Aborted
14175 || aMachineState == MachineState_Teleported
14176 )
14177 )
14178 {
14179 /*
14180 * delete the saved state after Console::ForgetSavedState() is called
14181 * or if the VM process (owning a direct VM session) crashed while the
14182 * VM was Saved
14183 */
14184
14185 /// @todo (dmik)
14186 // Not sure that deleting the saved state file just because of the
14187 // client death before it attempted to restore the VM is a good
14188 // thing. But when it crashes we need to go to the Aborted state
14189 // which cannot have the saved state file associated... The only
14190 // way to fix this is to make the Aborted condition not a VM state
14191 // but a bool flag: i.e., when a crash occurs, set it to true and
14192 // change the state to PoweredOff or Saved depending on the
14193 // saved state presence.
14194
14195 deleteSavedState = true;
14196 mData->mCurrentStateModified = TRUE;
14197 stsFlags |= SaveSTS_CurStateModified;
14198 }
14199
14200 if ( aMachineState == MachineState_Starting
14201 || aMachineState == MachineState_Restoring
14202 || aMachineState == MachineState_TeleportingIn
14203 )
14204 {
14205 /* set the current state modified flag to indicate that the current
14206 * state is no more identical to the state in the
14207 * current snapshot */
14208 if (!mData->mCurrentSnapshot.isNull())
14209 {
14210 mData->mCurrentStateModified = TRUE;
14211 stsFlags |= SaveSTS_CurStateModified;
14212 }
14213 }
14214
14215 if (deleteSavedState)
14216 {
14217 if (mRemoveSavedState)
14218 {
14219 Assert(!mSSData->strStateFilePath.isEmpty());
14220
14221 // it is safe to delete the saved state file if ...
14222 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14223 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14224 // ... none of the snapshots share the saved state file
14225 )
14226 RTFileDelete(mSSData->strStateFilePath.c_str());
14227 }
14228
14229 mSSData->strStateFilePath.setNull();
14230 stsFlags |= SaveSTS_StateFilePath;
14231 }
14232
14233 /* redirect to the underlying peer machine */
14234 mPeer->i_setMachineState(aMachineState);
14235
14236 if ( aMachineState == MachineState_PoweredOff
14237 || aMachineState == MachineState_Teleported
14238 || aMachineState == MachineState_Aborted
14239 || aMachineState == MachineState_Saved)
14240 {
14241 /* the machine has stopped execution
14242 * (or the saved state file was adopted) */
14243 stsFlags |= SaveSTS_StateTimeStamp;
14244 }
14245
14246 if ( ( oldMachineState == MachineState_PoweredOff
14247 || oldMachineState == MachineState_Aborted
14248 || oldMachineState == MachineState_Teleported
14249 )
14250 && aMachineState == MachineState_Saved)
14251 {
14252 /* the saved state file was adopted */
14253 Assert(!mSSData->strStateFilePath.isEmpty());
14254 stsFlags |= SaveSTS_StateFilePath;
14255 }
14256
14257#ifdef VBOX_WITH_GUEST_PROPS
14258 if ( aMachineState == MachineState_PoweredOff
14259 || aMachineState == MachineState_Aborted
14260 || aMachineState == MachineState_Teleported)
14261 {
14262 /* Make sure any transient guest properties get removed from the
14263 * property store on shutdown. */
14264
14265 HWData::GuestPropertyMap::const_iterator it;
14266 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14267 if (!fNeedsSaving)
14268 for (it = mHWData->mGuestProperties.begin();
14269 it != mHWData->mGuestProperties.end(); ++it)
14270 if ( (it->second.mFlags & guestProp::TRANSIENT)
14271 || (it->second.mFlags & guestProp::TRANSRESET))
14272 {
14273 fNeedsSaving = true;
14274 break;
14275 }
14276 if (fNeedsSaving)
14277 {
14278 mData->mCurrentStateModified = TRUE;
14279 stsFlags |= SaveSTS_CurStateModified;
14280 }
14281 }
14282#endif
14283
14284 rc = i_saveStateSettings(stsFlags);
14285
14286 if ( ( oldMachineState != MachineState_PoweredOff
14287 && oldMachineState != MachineState_Aborted
14288 && oldMachineState != MachineState_Teleported
14289 )
14290 && ( aMachineState == MachineState_PoweredOff
14291 || aMachineState == MachineState_Aborted
14292 || aMachineState == MachineState_Teleported
14293 )
14294 )
14295 {
14296 /* we've been shut down for any reason */
14297 /* no special action so far */
14298 }
14299
14300 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14301 LogFlowThisFuncLeave();
14302 return rc;
14303}
14304
14305/**
14306 * Sends the current machine state value to the VM process.
14307 *
14308 * @note Locks this object for reading, then calls a client process.
14309 */
14310HRESULT SessionMachine::i_updateMachineStateOnClient()
14311{
14312 AutoCaller autoCaller(this);
14313 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14314
14315 ComPtr<IInternalSessionControl> directControl;
14316 {
14317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14318 AssertReturn(!!mData, E_FAIL);
14319 directControl = mData->mSession.mDirectControl;
14320
14321 /* directControl may be already set to NULL here in #OnSessionEnd()
14322 * called too early by the direct session process while there is still
14323 * some operation (like deleting the snapshot) in progress. The client
14324 * process in this case is waiting inside Session::close() for the
14325 * "end session" process object to complete, while #uninit() called by
14326 * #checkForDeath() on the Watcher thread is waiting for the pending
14327 * operation to complete. For now, we accept this inconsistent behavior
14328 * and simply do nothing here. */
14329
14330 if (mData->mSession.mState == SessionState_Unlocking)
14331 return S_OK;
14332
14333 AssertReturn(!directControl.isNull(), E_FAIL);
14334 }
14335
14336 return directControl->UpdateMachineState(mData->mMachineState);
14337}
14338
14339HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14340{
14341 NOREF(aRemove);
14342 ReturnComNotImplemented();
14343}
14344
14345HRESULT Machine::updateState(MachineState_T aState)
14346{
14347 NOREF(aState);
14348 ReturnComNotImplemented();
14349}
14350
14351HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14352{
14353 NOREF(aProgress);
14354 ReturnComNotImplemented();
14355}
14356
14357HRESULT Machine::endPowerUp(LONG aResult)
14358{
14359 NOREF(aResult);
14360 ReturnComNotImplemented();
14361}
14362
14363HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14364{
14365 NOREF(aProgress);
14366 ReturnComNotImplemented();
14367}
14368
14369HRESULT Machine::endPoweringDown(LONG aResult,
14370 const com::Utf8Str &aErrMsg)
14371{
14372 NOREF(aResult);
14373 NOREF(aErrMsg);
14374 ReturnComNotImplemented();
14375}
14376
14377HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14378 BOOL *aMatched,
14379 ULONG *aMaskedInterfaces)
14380{
14381 NOREF(aDevice);
14382 NOREF(aMatched);
14383 NOREF(aMaskedInterfaces);
14384 ReturnComNotImplemented();
14385
14386}
14387
14388HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14389{
14390 NOREF(aId);
14391 ReturnComNotImplemented();
14392}
14393
14394HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14395 BOOL aDone)
14396{
14397 NOREF(aId);
14398 NOREF(aDone);
14399 ReturnComNotImplemented();
14400}
14401
14402HRESULT Machine::autoCaptureUSBDevices()
14403{
14404 ReturnComNotImplemented();
14405}
14406
14407HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14408{
14409 NOREF(aDone);
14410 ReturnComNotImplemented();
14411}
14412
14413HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14414 ComPtr<IProgress> &aProgress)
14415{
14416 NOREF(aSession);
14417 NOREF(aProgress);
14418 ReturnComNotImplemented();
14419}
14420
14421HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14422 com::Utf8Str &aStateFilePath)
14423{
14424 NOREF(aProgress);
14425 NOREF(aStateFilePath);
14426 ReturnComNotImplemented();
14427}
14428
14429HRESULT Machine::endSavingState(LONG aResult,
14430 const com::Utf8Str &aErrMsg)
14431{
14432 NOREF(aResult);
14433 NOREF(aErrMsg);
14434 ReturnComNotImplemented();
14435}
14436
14437HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14438{
14439 NOREF(aSavedStateFile);
14440 ReturnComNotImplemented();
14441}
14442
14443HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14444 const com::Utf8Str &aName,
14445 const com::Utf8Str &aDescription,
14446 const ComPtr<IProgress> &aConsoleProgress,
14447 BOOL aFTakingSnapshotOnline,
14448 com::Utf8Str &aStateFilePath)
14449{
14450 NOREF(aInitiator);
14451 NOREF(aName);
14452 NOREF(aDescription);
14453 NOREF(aConsoleProgress);
14454 NOREF(aFTakingSnapshotOnline);
14455 NOREF(aStateFilePath);
14456 ReturnComNotImplemented();
14457}
14458
14459HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14460{
14461 NOREF(aSuccess);
14462 ReturnComNotImplemented();
14463}
14464
14465HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14466 const com::Guid &aStartId,
14467 const com::Guid &aEndId,
14468 BOOL aDeleteAllChildren,
14469 MachineState_T *aMachineState,
14470 ComPtr<IProgress> &aProgress)
14471{
14472 NOREF(aInitiator);
14473 NOREF(aStartId);
14474 NOREF(aEndId);
14475 NOREF(aDeleteAllChildren);
14476 NOREF(aMachineState);
14477 NOREF(aProgress);
14478 ReturnComNotImplemented();
14479}
14480
14481HRESULT Machine::finishOnlineMergeMedium()
14482{
14483 ReturnComNotImplemented();
14484}
14485
14486HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14487 const ComPtr<ISnapshot> &aSnapshot,
14488 MachineState_T *aMachineState,
14489 ComPtr<IProgress> &aProgress)
14490{
14491 NOREF(aInitiator);
14492 NOREF(aSnapshot);
14493 NOREF(aMachineState);
14494 NOREF(aProgress);
14495 ReturnComNotImplemented();
14496}
14497
14498HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14499 std::vector<com::Utf8Str> &aValues,
14500 std::vector<LONG64> &aTimestamps,
14501 std::vector<com::Utf8Str> &aFlags)
14502{
14503 NOREF(aNames);
14504 NOREF(aValues);
14505 NOREF(aTimestamps);
14506 NOREF(aFlags);
14507 ReturnComNotImplemented();
14508}
14509
14510HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14511 const com::Utf8Str &aValue,
14512 LONG64 aTimestamp,
14513 const com::Utf8Str &aFlags)
14514{
14515 NOREF(aName);
14516 NOREF(aValue);
14517 NOREF(aTimestamp);
14518 NOREF(aFlags);
14519 ReturnComNotImplemented();
14520}
14521
14522HRESULT Machine::lockMedia()
14523{
14524 ReturnComNotImplemented();
14525}
14526
14527HRESULT Machine::unlockMedia()
14528{
14529 ReturnComNotImplemented();
14530}
14531
14532HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14533 ComPtr<IMediumAttachment> &aNewAttachment)
14534{
14535 NOREF(aAttachment);
14536 NOREF(aNewAttachment);
14537 ReturnComNotImplemented();
14538}
14539
14540HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14541 ULONG aCpuUser,
14542 ULONG aCpuKernel,
14543 ULONG aCpuIdle,
14544 ULONG aMemTotal,
14545 ULONG aMemFree,
14546 ULONG aMemBalloon,
14547 ULONG aMemShared,
14548 ULONG aMemCache,
14549 ULONG aPagedTotal,
14550 ULONG aMemAllocTotal,
14551 ULONG aMemFreeTotal,
14552 ULONG aMemBalloonTotal,
14553 ULONG aMemSharedTotal,
14554 ULONG aVmNetRx,
14555 ULONG aVmNetTx)
14556{
14557 NOREF(aValidStats);
14558 NOREF(aCpuUser);
14559 NOREF(aCpuKernel);
14560 NOREF(aCpuIdle);
14561 NOREF(aMemTotal);
14562 NOREF(aMemFree);
14563 NOREF(aMemBalloon);
14564 NOREF(aMemShared);
14565 NOREF(aMemCache);
14566 NOREF(aPagedTotal);
14567 NOREF(aMemAllocTotal);
14568 NOREF(aMemFreeTotal);
14569 NOREF(aMemBalloonTotal);
14570 NOREF(aMemSharedTotal);
14571 NOREF(aVmNetRx);
14572 NOREF(aVmNetTx);
14573 ReturnComNotImplemented();
14574}
Note: See TracBrowser for help on using the repository browser.

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