VirtualBox

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

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

Main/MachineImpl: Enable Hyper-V GIM for new Windows VMs.

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