VirtualBox

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

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

Main,Frontend: Windows 10 guest os type.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 498.3 KB
Line 
1/* $Id: MachineImpl.cpp 53012 2014-10-09 17:40:56Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "Windows10"
1284 || mUserData->s.strOsType == "Windows10_64"
1285 || mUserData->s.strOsType == "Windows81"
1286 || mUserData->s.strOsType == "Windows81_64"
1287 || mUserData->s.strOsType == "Windows8"
1288 || mUserData->s.strOsType == "Windows8_64"
1289 || mUserData->s.strOsType == "Windows7"
1290 || mUserData->s.strOsType == "Windows7_64"
1291 || mUserData->s.strOsType == "WindowsVista"
1292 || mUserData->s.strOsType == "WindowsVista_64"
1293 || mUserData->s.strOsType == "Windows2012"
1294 || mUserData->s.strOsType == "Windows2012_64"
1295 || mUserData->s.strOsType == "Windows2008"
1296 || mUserData->s.strOsType == "Windows2008_64")
1297 {
1298 *aParavirtProvider = ParavirtProvider_HyperV;
1299 }
1300 else
1301 *aParavirtProvider = ParavirtProvider_None;
1302 break;
1303 }
1304 }
1305 break;
1306 }
1307 }
1308
1309 Assert( *aParavirtProvider == ParavirtProvider_None
1310 || *aParavirtProvider == ParavirtProvider_Minimal
1311 || *aParavirtProvider == ParavirtProvider_HyperV);
1312 return S_OK;
1313}
1314
1315HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1316{
1317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1318
1319 aHardwareVersion = mHWData->mHWVersion;
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1325{
1326 /* check known version */
1327 Utf8Str hwVersion = aHardwareVersion;
1328 if ( hwVersion.compare("1") != 0
1329 && hwVersion.compare("2") != 0)
1330 return setError(E_INVALIDARG,
1331 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1332
1333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 HRESULT rc = i_checkStateDependency(MutableStateDep);
1336 if (FAILED(rc)) return rc;
1337
1338 i_setModified(IsModified_MachineData);
1339 mHWData.backup();
1340 mHWData->mHWVersion = aHardwareVersion;
1341
1342 return S_OK;
1343}
1344
1345HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1346{
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 aHardwareUUID = mHWData->mHardwareUUID;
1351 else
1352 aHardwareUUID = mData->mUuid;
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1358{
1359 if (!aHardwareUUID.isValid())
1360 return E_INVALIDARG;
1361
1362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1363
1364 HRESULT rc = i_checkStateDependency(MutableStateDep);
1365 if (FAILED(rc)) return rc;
1366
1367 i_setModified(IsModified_MachineData);
1368 mHWData.backup();
1369 if (aHardwareUUID == mData->mUuid)
1370 mHWData->mHardwareUUID.clear();
1371 else
1372 mHWData->mHardwareUUID = aHardwareUUID;
1373
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 *aMemorySize = mHWData->mMemorySize;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setMemorySize(ULONG aMemorySize)
1387{
1388 /* check RAM limits */
1389 if ( aMemorySize < MM_RAM_MIN_IN_MB
1390 || aMemorySize > MM_RAM_MAX_IN_MB
1391 )
1392 return setError(E_INVALIDARG,
1393 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1394 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 mHWData->mMemorySize = aMemorySize;
1404
1405 return S_OK;
1406}
1407
1408HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1409{
1410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 *aCPUCount = mHWData->mCPUCount;
1413
1414 return S_OK;
1415}
1416
1417HRESULT Machine::setCPUCount(ULONG aCPUCount)
1418{
1419 /* check CPU limits */
1420 if ( aCPUCount < SchemaDefs::MinCPUCount
1421 || aCPUCount > SchemaDefs::MaxCPUCount
1422 )
1423 return setError(E_INVALIDARG,
1424 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1425 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1426
1427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1430 if (mHWData->mCPUHotPlugEnabled)
1431 {
1432 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1433 {
1434 if (mHWData->mCPUAttached[idx])
1435 return setError(E_INVALIDARG,
1436 tr("There is still a CPU attached to socket %lu."
1437 "Detach the CPU before removing the socket"),
1438 aCPUCount, idx+1);
1439 }
1440 }
1441
1442 HRESULT rc = i_checkStateDependency(MutableStateDep);
1443 if (FAILED(rc)) return rc;
1444
1445 i_setModified(IsModified_MachineData);
1446 mHWData.backup();
1447 mHWData->mCPUCount = aCPUCount;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1462{
1463 HRESULT rc = S_OK;
1464
1465 /* check throttle limits */
1466 if ( aCPUExecutionCap < 1
1467 || aCPUExecutionCap > 100
1468 )
1469 return setError(E_INVALIDARG,
1470 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1471 aCPUExecutionCap, 1, 100);
1472
1473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 alock.release();
1476 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1477 alock.acquire();
1478 if (FAILED(rc)) return rc;
1479
1480 i_setModified(IsModified_MachineData);
1481 mHWData.backup();
1482 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1483
1484 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1485 if (Global::IsOnline(mData->mMachineState))
1486 i_saveSettings(NULL);
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1492{
1493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
1495 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1496
1497 return S_OK;
1498}
1499
1500HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1501{
1502 HRESULT rc = S_OK;
1503
1504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1505
1506 rc = i_checkStateDependency(MutableStateDep);
1507 if (FAILED(rc)) return rc;
1508
1509 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1510 {
1511 if (aCPUHotPlugEnabled)
1512 {
1513 i_setModified(IsModified_MachineData);
1514 mHWData.backup();
1515
1516 /* Add the amount of CPUs currently attached */
1517 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1518 mHWData->mCPUAttached[i] = true;
1519 }
1520 else
1521 {
1522 /*
1523 * We can disable hotplug only if the amount of maximum CPUs is equal
1524 * to the amount of attached CPUs
1525 */
1526 unsigned cCpusAttached = 0;
1527 unsigned iHighestId = 0;
1528
1529 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1530 {
1531 if (mHWData->mCPUAttached[i])
1532 {
1533 cCpusAttached++;
1534 iHighestId = i;
1535 }
1536 }
1537
1538 if ( (cCpusAttached != mHWData->mCPUCount)
1539 || (iHighestId >= mHWData->mCPUCount))
1540 return setError(E_INVALIDARG,
1541 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1542
1543 i_setModified(IsModified_MachineData);
1544 mHWData.backup();
1545 }
1546 }
1547
1548 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1549
1550 return rc;
1551}
1552
1553HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1554{
1555#ifdef VBOX_WITH_USB_CARDREADER
1556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1557
1558 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1559
1560 return S_OK;
1561#else
1562 NOREF(aEmulatedUSBCardReaderEnabled);
1563 return E_NOTIMPL;
1564#endif
1565}
1566
1567HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1568{
1569#ifdef VBOX_WITH_USB_CARDREADER
1570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 HRESULT rc = i_checkStateDependency(MutableStateDep);
1573 if (FAILED(rc)) return rc;
1574
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1578
1579 return S_OK;
1580#else
1581 NOREF(aEmulatedUSBCardReaderEnabled);
1582 return E_NOTIMPL;
1583#endif
1584}
1585
1586HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1587{
1588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1589
1590 *aHPETEnabled = mHWData->mHPETEnabled;
1591
1592 return S_OK;
1593}
1594
1595HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1596{
1597 HRESULT rc = S_OK;
1598
1599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 rc = i_checkStateDependency(MutableStateDep);
1602 if (FAILED(rc)) return rc;
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606
1607 mHWData->mHPETEnabled = aHPETEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1621{
1622 HRESULT rc = S_OK;
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 i_setModified(IsModified_MachineData);
1627 mHWData.backup();
1628 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1629
1630 alock.release();
1631 rc = i_onVideoCaptureChange();
1632 alock.acquire();
1633 if (FAILED(rc))
1634 {
1635 /*
1636 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1637 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1638 * determine if it should start or stop capturing. Therefore we need to manually
1639 * undo change.
1640 */
1641 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1642 return rc;
1643 }
1644
1645 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1646 if (Global::IsOnline(mData->mMachineState))
1647 i_saveSettings(NULL);
1648
1649 return rc;
1650}
1651
1652HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1656 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1657 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1658 return S_OK;
1659}
1660
1661HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1662{
1663 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1664 bool fChanged = false;
1665
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667
1668 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1669 {
1670 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1671 {
1672 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1673 fChanged = true;
1674 }
1675 }
1676 if (fChanged)
1677 {
1678 alock.release();
1679 HRESULT rc = i_onVideoCaptureChange();
1680 alock.acquire();
1681 if (FAILED(rc)) return rc;
1682 i_setModified(IsModified_MachineData);
1683
1684 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1685 if (Global::IsOnline(mData->mMachineState))
1686 i_saveSettings(NULL);
1687 }
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1693{
1694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1695 if (mHWData->mVideoCaptureFile.isEmpty())
1696 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1697 else
1698 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1699 return S_OK;
1700}
1701
1702HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1703{
1704 Utf8Str strFile(aVideoCaptureFile);
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 if ( Global::IsOnline(mData->mMachineState)
1708 && mHWData->mVideoCaptureEnabled)
1709 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1710
1711 if (!RTPathStartsWithRoot(strFile.c_str()))
1712 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1713
1714 if (!strFile.isEmpty())
1715 {
1716 Utf8Str defaultFile;
1717 i_getDefaultVideoCaptureFile(defaultFile);
1718 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1719 strFile.setNull();
1720 }
1721
1722 i_setModified(IsModified_MachineData);
1723 mHWData.backup();
1724 mHWData->mVideoCaptureFile = strFile;
1725
1726 return S_OK;
1727}
1728
1729HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1730{
1731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1732 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1737{
1738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 if ( Global::IsOnline(mData->mMachineState)
1741 && mHWData->mVideoCaptureEnabled)
1742 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1743
1744 i_setModified(IsModified_MachineData);
1745 mHWData.backup();
1746 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1747
1748 return S_OK;
1749}
1750
1751HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1752{
1753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1754 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1755 return S_OK;
1756}
1757
1758HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1759{
1760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 if ( Global::IsOnline(mData->mMachineState)
1763 && mHWData->mVideoCaptureEnabled)
1764 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1765
1766 i_setModified(IsModified_MachineData);
1767 mHWData.backup();
1768 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1769
1770 return S_OK;
1771}
1772
1773HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1774{
1775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1776 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1777 return S_OK;
1778}
1779
1780HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1781{
1782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 if ( Global::IsOnline(mData->mMachineState)
1785 && mHWData->mVideoCaptureEnabled)
1786 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1787
1788 i_setModified(IsModified_MachineData);
1789 mHWData.backup();
1790 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1791
1792 return S_OK;
1793}
1794
1795HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1796{
1797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1798 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1799 return S_OK;
1800}
1801
1802HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1803{
1804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1805
1806 if ( Global::IsOnline(mData->mMachineState)
1807 && mHWData->mVideoCaptureEnabled)
1808 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1809
1810 i_setModified(IsModified_MachineData);
1811 mHWData.backup();
1812 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1813
1814 return S_OK;
1815}
1816
1817HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1818{
1819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1820 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1821 return S_OK;
1822}
1823
1824HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1825{
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 if ( Global::IsOnline(mData->mMachineState)
1829 && mHWData->mVideoCaptureEnabled)
1830 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1831
1832 i_setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1835
1836 return S_OK;
1837}
1838
1839HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1840{
1841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1842 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1843 return S_OK;
1844}
1845
1846HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1847{
1848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 if ( Global::IsOnline(mData->mMachineState)
1851 && mHWData->mVideoCaptureEnabled)
1852 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1853
1854 i_setModified(IsModified_MachineData);
1855 mHWData.backup();
1856 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1857
1858 return S_OK;
1859}
1860
1861HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1862{
1863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1864
1865 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 if ( Global::IsOnline(mData->mMachineState)
1874 && mHWData->mVideoCaptureEnabled)
1875 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1876
1877 i_setModified(IsModified_MachineData);
1878 mHWData.backup();
1879 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1889
1890 return S_OK;
1891}
1892
1893HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1894{
1895 switch (aGraphicsControllerType)
1896 {
1897 case GraphicsControllerType_Null:
1898 case GraphicsControllerType_VBoxVGA:
1899#ifdef VBOX_WITH_VMSVGA
1900 case GraphicsControllerType_VMSVGA:
1901#endif
1902 break;
1903 default:
1904 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1905 }
1906
1907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 HRESULT rc = i_checkStateDependency(MutableStateDep);
1910 if (FAILED(rc)) return rc;
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1920{
1921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1922
1923 *aVRAMSize = mHWData->mVRAMSize;
1924
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1929{
1930 /* check VRAM limits */
1931 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1932 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1933 return setError(E_INVALIDARG,
1934 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1935 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1936
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 HRESULT rc = i_checkStateDependency(MutableStateDep);
1940 if (FAILED(rc)) return rc;
1941
1942 i_setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mVRAMSize = aVRAMSize;
1945
1946 return S_OK;
1947}
1948
1949/** @todo this method should not be public */
1950HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1955
1956 return S_OK;
1957}
1958
1959/**
1960 * Set the memory balloon size.
1961 *
1962 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1963 * we have to make sure that we never call IGuest from here.
1964 */
1965HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1966{
1967 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1968#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1969 /* check limits */
1970 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1971 return setError(E_INVALIDARG,
1972 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1973 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1974
1975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1976
1977 i_setModified(IsModified_MachineData);
1978 mHWData.backup();
1979 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1980
1981 return S_OK;
1982#else
1983 NOREF(aMemoryBalloonSize);
1984 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1985#endif
1986}
1987
1988HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1989{
1990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1993 return S_OK;
1994}
1995
1996HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1997{
1998#ifdef VBOX_WITH_PAGE_SHARING
1999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2000
2001 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2005 return S_OK;
2006#else
2007 NOREF(aPageFusionEnabled);
2008 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2009#endif
2010}
2011
2012HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2013{
2014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2015
2016 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2017
2018 return S_OK;
2019}
2020
2021HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2022{
2023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2024
2025 HRESULT rc = i_checkStateDependency(MutableStateDep);
2026 if (FAILED(rc)) return rc;
2027
2028 /** @todo check validity! */
2029
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2033
2034 return S_OK;
2035}
2036
2037
2038HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2039{
2040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2041
2042 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2043
2044 return S_OK;
2045}
2046
2047HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2048{
2049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2050
2051 HRESULT rc = i_checkStateDependency(MutableStateDep);
2052 if (FAILED(rc)) return rc;
2053
2054 /** @todo check validity! */
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2058
2059 return S_OK;
2060}
2061
2062HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2063{
2064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2065
2066 *aMonitorCount = mHWData->mMonitorCount;
2067
2068 return S_OK;
2069}
2070
2071HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2072{
2073 /* make sure monitor count is a sensible number */
2074 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2075 return setError(E_INVALIDARG,
2076 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2077 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2078
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 HRESULT rc = i_checkStateDependency(MutableStateDep);
2082 if (FAILED(rc)) return rc;
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMonitorCount = aMonitorCount;
2087
2088 return S_OK;
2089}
2090
2091HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2092{
2093 /* mBIOSSettings is constant during life time, no need to lock */
2094 aBIOSSettings = mBIOSSettings;
2095
2096 return S_OK;
2097}
2098
2099HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2100{
2101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 switch (aProperty)
2104 {
2105 case CPUPropertyType_PAE:
2106 *aValue = mHWData->mPAEEnabled;
2107 break;
2108
2109 case CPUPropertyType_Synthetic:
2110 *aValue = mHWData->mSyntheticCpu;
2111 break;
2112
2113 case CPUPropertyType_LongMode:
2114 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2115 *aValue = TRUE;
2116 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2117 *aValue = FALSE;
2118#if HC_ARCH_BITS == 64
2119 else
2120 *aValue = TRUE;
2121#else
2122 else
2123 {
2124 *aValue = FALSE;
2125
2126 ComPtr<IGuestOSType> ptrGuestOSType;
2127 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2128 if (SUCCEEDED(hrc2))
2129 {
2130 BOOL fIs64Bit = FALSE;
2131 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2132 if (SUCCEEDED(hrc2) && fIs64Bit)
2133 {
2134 ComObjPtr<Host> ptrHost = mParent->i_host();
2135 alock.release();
2136
2137 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2138 if (FAILED(hrc2))
2139 *aValue = FALSE;
2140 }
2141 }
2142 }
2143#endif
2144 break;
2145
2146 case CPUPropertyType_TripleFaultReset:
2147 *aValue = mHWData->mTripleFaultReset;
2148 break;
2149
2150 default:
2151 return E_INVALIDARG;
2152 }
2153 return S_OK;
2154}
2155
2156HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2157{
2158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 HRESULT rc = i_checkStateDependency(MutableStateDep);
2161 if (FAILED(rc)) return rc;
2162
2163 switch (aProperty)
2164 {
2165 case CPUPropertyType_PAE:
2166 i_setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mPAEEnabled = !!aValue;
2169 break;
2170
2171 case CPUPropertyType_Synthetic:
2172 i_setModified(IsModified_MachineData);
2173 mHWData.backup();
2174 mHWData->mSyntheticCpu = !!aValue;
2175 break;
2176
2177 case CPUPropertyType_LongMode:
2178 i_setModified(IsModified_MachineData);
2179 mHWData.backup();
2180 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2181 break;
2182
2183 case CPUPropertyType_TripleFaultReset:
2184 i_setModified(IsModified_MachineData);
2185 mHWData.backup();
2186 mHWData->mTripleFaultReset = !!aValue;
2187 break;
2188
2189 default:
2190 return E_INVALIDARG;
2191 }
2192 return S_OK;
2193}
2194
2195HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2196{
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 switch(aId)
2200 {
2201 case 0x0:
2202 case 0x1:
2203 case 0x2:
2204 case 0x3:
2205 case 0x4:
2206 case 0x5:
2207 case 0x6:
2208 case 0x7:
2209 case 0x8:
2210 case 0x9:
2211 case 0xA:
2212 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2213 return E_INVALIDARG;
2214
2215 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2216 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2217 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2218 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2219 break;
2220
2221 case 0x80000000:
2222 case 0x80000001:
2223 case 0x80000002:
2224 case 0x80000003:
2225 case 0x80000004:
2226 case 0x80000005:
2227 case 0x80000006:
2228 case 0x80000007:
2229 case 0x80000008:
2230 case 0x80000009:
2231 case 0x8000000A:
2232 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2233 return E_INVALIDARG;
2234
2235 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2236 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2237 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2238 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2239 break;
2240
2241 default:
2242 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2243 }
2244 return S_OK;
2245}
2246
2247
2248HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2249{
2250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2251
2252 HRESULT rc = i_checkStateDependency(MutableStateDep);
2253 if (FAILED(rc)) return rc;
2254
2255 switch(aId)
2256 {
2257 case 0x0:
2258 case 0x1:
2259 case 0x2:
2260 case 0x3:
2261 case 0x4:
2262 case 0x5:
2263 case 0x6:
2264 case 0x7:
2265 case 0x8:
2266 case 0x9:
2267 case 0xA:
2268 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2269 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2270 i_setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2273 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2274 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2275 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2276 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2277 break;
2278
2279 case 0x80000000:
2280 case 0x80000001:
2281 case 0x80000002:
2282 case 0x80000003:
2283 case 0x80000004:
2284 case 0x80000005:
2285 case 0x80000006:
2286 case 0x80000007:
2287 case 0x80000008:
2288 case 0x80000009:
2289 case 0x8000000A:
2290 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2291 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2292 i_setModified(IsModified_MachineData);
2293 mHWData.backup();
2294 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2295 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2296 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2297 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2298 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2299 break;
2300
2301 default:
2302 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2303 }
2304 return S_OK;
2305}
2306
2307HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2308{
2309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
2311 HRESULT rc = i_checkStateDependency(MutableStateDep);
2312 if (FAILED(rc)) return rc;
2313
2314 switch(aId)
2315 {
2316 case 0x0:
2317 case 0x1:
2318 case 0x2:
2319 case 0x3:
2320 case 0x4:
2321 case 0x5:
2322 case 0x6:
2323 case 0x7:
2324 case 0x8:
2325 case 0x9:
2326 case 0xA:
2327 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2328 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 /* Invalidate leaf. */
2332 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2333 break;
2334
2335 case 0x80000000:
2336 case 0x80000001:
2337 case 0x80000002:
2338 case 0x80000003:
2339 case 0x80000004:
2340 case 0x80000005:
2341 case 0x80000006:
2342 case 0x80000007:
2343 case 0x80000008:
2344 case 0x80000009:
2345 case 0x8000000A:
2346 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2347 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2348 i_setModified(IsModified_MachineData);
2349 mHWData.backup();
2350 /* Invalidate leaf. */
2351 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2352 break;
2353
2354 default:
2355 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2356 }
2357 return S_OK;
2358}
2359
2360HRESULT Machine::removeAllCPUIDLeaves()
2361{
2362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 HRESULT rc = i_checkStateDependency(MutableStateDep);
2365 if (FAILED(rc)) return rc;
2366
2367 i_setModified(IsModified_MachineData);
2368 mHWData.backup();
2369
2370 /* Invalidate all standard leafs. */
2371 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2372 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2373
2374 /* Invalidate all extended leafs. */
2375 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2376 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2377
2378 return S_OK;
2379}
2380HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2381{
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 switch(aProperty)
2385 {
2386 case HWVirtExPropertyType_Enabled:
2387 *aValue = mHWData->mHWVirtExEnabled;
2388 break;
2389
2390 case HWVirtExPropertyType_VPID:
2391 *aValue = mHWData->mHWVirtExVPIDEnabled;
2392 break;
2393
2394 case HWVirtExPropertyType_NestedPaging:
2395 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2396 break;
2397
2398 case HWVirtExPropertyType_UnrestrictedExecution:
2399 *aValue = mHWData->mHWVirtExUXEnabled;
2400 break;
2401
2402 case HWVirtExPropertyType_LargePages:
2403 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2404#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2405 *aValue = FALSE;
2406#endif
2407 break;
2408
2409 case HWVirtExPropertyType_Force:
2410 *aValue = mHWData->mHWVirtExForceEnabled;
2411 break;
2412
2413 default:
2414 return E_INVALIDARG;
2415 }
2416 return S_OK;
2417}
2418
2419HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2420{
2421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 HRESULT rc = i_checkStateDependency(MutableStateDep);
2424 if (FAILED(rc)) return rc;
2425
2426 switch(aProperty)
2427 {
2428 case HWVirtExPropertyType_Enabled:
2429 i_setModified(IsModified_MachineData);
2430 mHWData.backup();
2431 mHWData->mHWVirtExEnabled = !!aValue;
2432 break;
2433
2434 case HWVirtExPropertyType_VPID:
2435 i_setModified(IsModified_MachineData);
2436 mHWData.backup();
2437 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2438 break;
2439
2440 case HWVirtExPropertyType_NestedPaging:
2441 i_setModified(IsModified_MachineData);
2442 mHWData.backup();
2443 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2444 break;
2445
2446 case HWVirtExPropertyType_UnrestrictedExecution:
2447 i_setModified(IsModified_MachineData);
2448 mHWData.backup();
2449 mHWData->mHWVirtExUXEnabled = !!aValue;
2450 break;
2451
2452 case HWVirtExPropertyType_LargePages:
2453 i_setModified(IsModified_MachineData);
2454 mHWData.backup();
2455 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2456 break;
2457
2458 case HWVirtExPropertyType_Force:
2459 i_setModified(IsModified_MachineData);
2460 mHWData.backup();
2461 mHWData->mHWVirtExForceEnabled = !!aValue;
2462 break;
2463
2464 default:
2465 return E_INVALIDARG;
2466 }
2467
2468 return S_OK;
2469}
2470
2471HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2472{
2473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2474
2475 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2476
2477 return S_OK;
2478}
2479
2480HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2481{
2482 /* @todo (r=dmik):
2483 * 1. Allow to change the name of the snapshot folder containing snapshots
2484 * 2. Rename the folder on disk instead of just changing the property
2485 * value (to be smart and not to leave garbage). Note that it cannot be
2486 * done here because the change may be rolled back. Thus, the right
2487 * place is #saveSettings().
2488 */
2489
2490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 HRESULT rc = i_checkStateDependency(MutableStateDep);
2493 if (FAILED(rc)) return rc;
2494
2495 if (!mData->mCurrentSnapshot.isNull())
2496 return setError(E_FAIL,
2497 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2498
2499 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2500
2501 if (strSnapshotFolder.isEmpty())
2502 strSnapshotFolder = "Snapshots";
2503 int vrc = i_calculateFullPath(strSnapshotFolder,
2504 strSnapshotFolder);
2505 if (RT_FAILURE(vrc))
2506 return setError(E_FAIL,
2507 tr("Invalid snapshot folder '%s' (%Rrc)"),
2508 strSnapshotFolder.c_str(), vrc);
2509
2510 i_setModified(IsModified_MachineData);
2511 mUserData.backup();
2512
2513 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2514
2515 return S_OK;
2516}
2517
2518HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2519{
2520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2521
2522 aMediumAttachments.resize(mMediaData->mAttachments.size());
2523 size_t i = 0;
2524 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2525 it != mMediaData->mAttachments.end(); ++it, ++i)
2526 aMediumAttachments[i] = *it;
2527
2528 return S_OK;
2529}
2530
2531HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 Assert(!!mVRDEServer);
2536
2537 aVRDEServer = mVRDEServer;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2543{
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 aAudioAdapter = mAudioAdapter;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2552{
2553#ifdef VBOX_WITH_VUSB
2554 clearError();
2555 MultiResult rc(S_OK);
2556
2557# ifdef VBOX_WITH_USB
2558 rc = mParent->i_host()->i_checkUSBProxyService();
2559 if (FAILED(rc)) return rc;
2560# endif
2561
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 USBControllerList data = *mUSBControllers.data();
2565 aUSBControllers.resize(data.size());
2566 size_t i = 0;
2567 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2568 aUSBControllers[i] = *it;
2569
2570 return S_OK;
2571#else
2572 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2573 * extended error info to indicate that USB is simply not available
2574 * (w/o treating it as a failure), for example, as in OSE */
2575 NOREF(aUSBControllers);
2576 ReturnComNotImplemented();
2577#endif /* VBOX_WITH_VUSB */
2578}
2579
2580HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2581{
2582#ifdef VBOX_WITH_VUSB
2583 clearError();
2584 MultiResult rc(S_OK);
2585
2586# ifdef VBOX_WITH_USB
2587 rc = mParent->i_host()->i_checkUSBProxyService();
2588 if (FAILED(rc)) return rc;
2589# endif
2590
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 aUSBDeviceFilters = mUSBDeviceFilters;
2594 return rc;
2595#else
2596 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2597 * extended error info to indicate that USB is simply not available
2598 * (w/o treating it as a failure), for example, as in OSE */
2599 NOREF(aUSBDeviceFilters);
2600 ReturnComNotImplemented();
2601#endif /* VBOX_WITH_VUSB */
2602}
2603
2604HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 aSettingsFilePath = mData->m_strConfigFileFull;
2609
2610 return S_OK;
2611}
2612
2613HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2614{
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 HRESULT rc = i_checkStateDependency(MutableStateDep);
2618 if (FAILED(rc)) return rc;
2619
2620 if (!mData->pMachineConfigFile->fileExists())
2621 // this is a new machine, and no config file exists yet:
2622 *aSettingsModified = TRUE;
2623 else
2624 *aSettingsModified = (mData->flModifications != 0);
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2630{
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 *aSessionState = mData->mSession.mState;
2635
2636 return S_OK;
2637}
2638
2639HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 aSessionType = mData->mSession.mType;
2644
2645 return S_OK;
2646}
2647
2648HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2649{
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 *aSessionPID = mData->mSession.mPID;
2653
2654 return S_OK;
2655}
2656
2657HRESULT Machine::getState(MachineState_T *aState)
2658{
2659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 *aState = mData->mMachineState;
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2667{
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 aStateFilePath = mSSData->strStateFilePath;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 i_getLogFolder(aLogFolder);
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aCurrentSnapshot = mData->mCurrentSnapshot;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2707 ? 0
2708 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2714{
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 /* Note: for machines with no snapshots, we always return FALSE
2718 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2719 * reasons :) */
2720
2721 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2722 ? FALSE
2723 : mData->mCurrentStateModified;
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aSharedFolders.resize(mHWData->mSharedFolders.size());
2733 size_t i = 0;
2734 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2735 it != mHWData->mSharedFolders.end(); ++i, ++it)
2736 aSharedFolders[i] = *it;
2737
2738 return S_OK;
2739}
2740
2741HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2742{
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 *aClipboardMode = mHWData->mClipboardMode;
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2751{
2752 HRESULT rc = S_OK;
2753
2754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 alock.release();
2757 rc = i_onClipboardModeChange(aClipboardMode);
2758 alock.acquire();
2759 if (FAILED(rc)) return rc;
2760
2761 i_setModified(IsModified_MachineData);
2762 mHWData.backup();
2763 mHWData->mClipboardMode = aClipboardMode;
2764
2765 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2766 if (Global::IsOnline(mData->mMachineState))
2767 i_saveSettings(NULL);
2768
2769 return S_OK;
2770}
2771
2772HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 *aDnDMode = mHWData->mDnDMode;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2782{
2783 HRESULT rc = S_OK;
2784
2785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 alock.release();
2788 rc = i_onDnDModeChange(aDnDMode);
2789
2790 alock.acquire();
2791 if (FAILED(rc)) return rc;
2792
2793 i_setModified(IsModified_MachineData);
2794 mHWData.backup();
2795 mHWData->mDnDMode = aDnDMode;
2796
2797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2798 if (Global::IsOnline(mData->mMachineState))
2799 i_saveSettings(NULL);
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 try
2809 {
2810 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2811 }
2812 catch (...)
2813 {
2814 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2815 }
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2821{
2822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 HRESULT rc = i_checkStateDependency(MutableStateDep);
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2830 return rc;
2831}
2832
2833HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2834{
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836 StorageControllerList data = *mStorageControllers.data();
2837 size_t i = 0;
2838 aStorageControllers.resize(data.size());
2839 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2840 aStorageControllers[i] = *it;
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aEnabled = mUserData->s.fTeleporterEnabled;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 /* Only allow it to be set to true when PoweredOff or Aborted.
2858 (Clearing it is always permitted.) */
2859 if ( aTeleporterEnabled
2860 && mData->mRegistered
2861 && ( !i_isSessionMachine()
2862 || ( mData->mMachineState != MachineState_PoweredOff
2863 && mData->mMachineState != MachineState_Teleported
2864 && mData->mMachineState != MachineState_Aborted
2865 )
2866 )
2867 )
2868 return setError(VBOX_E_INVALID_VM_STATE,
2869 tr("The machine is not powered off (state is %s)"),
2870 Global::stringifyMachineState(mData->mMachineState));
2871
2872 i_setModified(IsModified_MachineData);
2873 mUserData.backup();
2874 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2884
2885 return S_OK;
2886}
2887
2888HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2889{
2890 if (aTeleporterPort >= _64K)
2891 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2892
2893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 HRESULT rc = i_checkStateDependency(MutableStateDep);
2896 if (FAILED(rc)) return rc;
2897
2898 i_setModified(IsModified_MachineData);
2899 mUserData.backup();
2900 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2906{
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2915{
2916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 HRESULT rc = i_checkStateDependency(MutableStateDep);
2919 if (FAILED(rc)) return rc;
2920
2921 i_setModified(IsModified_MachineData);
2922 mUserData.backup();
2923 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2924
2925 return S_OK;
2926}
2927
2928HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2929{
2930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2931 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2937{
2938 /*
2939 * Hash the password first.
2940 */
2941 com::Utf8Str aT = aTeleporterPassword;
2942
2943 if (!aT.isEmpty())
2944 {
2945 if (VBoxIsPasswordHashed(&aT))
2946 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2947 VBoxHashPassword(&aT);
2948 }
2949
2950 /*
2951 * Do the update.
2952 */
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2955 if (SUCCEEDED(hrc))
2956 {
2957 i_setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterPassword = aT;
2960 }
2961
2962 return hrc;
2963}
2964
2965HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2966{
2967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2970 return S_OK;
2971}
2972
2973HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2974{
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 /* @todo deal with running state change. */
2978 HRESULT rc = i_checkStateDependency(MutableStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mUserData.backup();
2983 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2984 return S_OK;
2985}
2986
2987HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2988{
2989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2990
2991 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2992 return S_OK;
2993}
2994
2995HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2996{
2997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 /* @todo deal with running state change. */
3000 HRESULT rc = i_checkStateDependency(MutableStateDep);
3001 if (FAILED(rc)) return rc;
3002
3003 i_setModified(IsModified_MachineData);
3004 mUserData.backup();
3005 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3006 return S_OK;
3007}
3008
3009HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3010{
3011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3014 return S_OK;
3015}
3016
3017HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3018{
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 /* @todo deal with running state change. */
3022 HRESULT rc = i_checkStateDependency(MutableStateDep);
3023 if (FAILED(rc)) return rc;
3024
3025 i_setModified(IsModified_MachineData);
3026 mUserData.backup();
3027 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3028 return S_OK;
3029}
3030
3031HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3032{
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3036
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3041{
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = i_checkStateDependency(MutableStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3064{
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 /* @todo deal with running state change. */
3068 HRESULT rc = i_checkStateDependency(MutableStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 i_setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3074 return S_OK;
3075}
3076
3077HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3078{
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3082
3083 return S_OK;
3084}
3085
3086HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3087{
3088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 /* Only allow it to be set to true when PoweredOff or Aborted.
3091 (Clearing it is always permitted.) */
3092 if ( aRTCUseUTC
3093 && mData->mRegistered
3094 && ( !i_isSessionMachine()
3095 || ( mData->mMachineState != MachineState_PoweredOff
3096 && mData->mMachineState != MachineState_Teleported
3097 && mData->mMachineState != MachineState_Aborted
3098 )
3099 )
3100 )
3101 return setError(VBOX_E_INVALID_VM_STATE,
3102 tr("The machine is not powered off (state is %s)"),
3103 Global::stringifyMachineState(mData->mMachineState));
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3117
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 HRESULT rc = i_checkStateDependency(MutableStateDep);
3126 if (FAILED(rc)) return rc;
3127
3128 i_setModified(IsModified_MachineData);
3129 mHWData.backup();
3130 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3131
3132 return S_OK;
3133}
3134
3135HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3136{
3137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 *aIOCacheSize = mHWData->mIOCacheSize;
3140
3141 return S_OK;
3142}
3143
3144HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3145{
3146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3147
3148 HRESULT rc = i_checkStateDependency(MutableStateDep);
3149 if (FAILED(rc)) return rc;
3150
3151 i_setModified(IsModified_MachineData);
3152 mHWData.backup();
3153 mHWData->mIOCacheSize = aIOCacheSize;
3154
3155 return S_OK;
3156}
3157
3158
3159/**
3160 * @note Locks objects!
3161 */
3162HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3163 LockType_T aLockType)
3164
3165{
3166 /* check the session state */
3167 SessionState_T state;
3168 HRESULT rc = aSession->COMGETTER(State)(&state);
3169 if (FAILED(rc)) return rc;
3170
3171 if (state != SessionState_Unlocked)
3172 return setError(VBOX_E_INVALID_OBJECT_STATE,
3173 tr("The given session is busy"));
3174
3175 // get the client's IInternalSessionControl interface
3176 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3177 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3178 E_INVALIDARG);
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 if (!mData->mRegistered)
3183 return setError(E_UNEXPECTED,
3184 tr("The machine '%s' is not registered"),
3185 mUserData->s.strName.c_str());
3186
3187 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3188
3189 SessionState_T oldState = mData->mSession.mState;
3190 /* Hack: in case the session is closing and there is a progress object
3191 * which allows waiting for the session to be closed, take the opportunity
3192 * and do a limited wait (max. 1 second). This helps a lot when the system
3193 * is busy and thus session closing can take a little while. */
3194 if ( mData->mSession.mState == SessionState_Unlocking
3195 && mData->mSession.mProgress)
3196 {
3197 alock.release();
3198 mData->mSession.mProgress->WaitForCompletion(1000);
3199 alock.acquire();
3200 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3201 }
3202
3203 // try again now
3204 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3205 // (i.e. session machine exists)
3206 && (aLockType == LockType_Shared) // caller wants a shared link to the
3207 // existing session that holds the write lock:
3208 )
3209 {
3210 // OK, share the session... we are now dealing with three processes:
3211 // 1) VBoxSVC (where this code runs);
3212 // 2) process C: the caller's client process (who wants a shared session);
3213 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3214
3215 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3216 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3217 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3218 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3219 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3220
3221 /*
3222 * Release the lock before calling the client process. It's safe here
3223 * since the only thing to do after we get the lock again is to add
3224 * the remote control to the list (which doesn't directly influence
3225 * anything).
3226 */
3227 alock.release();
3228
3229 // get the console of the session holding the write lock (this is a remote call)
3230 ComPtr<IConsole> pConsoleW;
3231 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3232 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3233 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3234 if (FAILED(rc))
3235 // the failure may occur w/o any error info (from RPC), so provide one
3236 return setError(VBOX_E_VM_ERROR,
3237 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3238
3239 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3240
3241 // share the session machine and W's console with the caller's session
3242 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3243 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3244 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3245
3246 if (FAILED(rc))
3247 // the failure may occur w/o any error info (from RPC), so provide one
3248 return setError(VBOX_E_VM_ERROR,
3249 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3250 alock.acquire();
3251
3252 // need to revalidate the state after acquiring the lock again
3253 if (mData->mSession.mState != SessionState_Locked)
3254 {
3255 pSessionControl->Uninitialize();
3256 return setError(VBOX_E_INVALID_SESSION_STATE,
3257 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3258 mUserData->s.strName.c_str());
3259 }
3260
3261 // add the caller's session to the list
3262 mData->mSession.mRemoteControls.push_back(pSessionControl);
3263 }
3264 else if ( mData->mSession.mState == SessionState_Locked
3265 || mData->mSession.mState == SessionState_Unlocking
3266 )
3267 {
3268 // sharing not permitted, or machine still unlocking:
3269 return setError(VBOX_E_INVALID_OBJECT_STATE,
3270 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3271 mUserData->s.strName.c_str());
3272 }
3273 else
3274 {
3275 // machine is not locked: then write-lock the machine (create the session machine)
3276
3277 // must not be busy
3278 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3279
3280 // get the caller's session PID
3281 RTPROCESS pid = NIL_RTPROCESS;
3282 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3283 pSessionControl->GetPID((ULONG*)&pid);
3284 Assert(pid != NIL_RTPROCESS);
3285
3286 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3287
3288 if (fLaunchingVMProcess)
3289 {
3290 if (mData->mSession.mPID == NIL_RTPROCESS)
3291 {
3292 // two or more clients racing for a lock, the one which set the
3293 // session state to Spawning will win, the others will get an
3294 // error as we can't decide here if waiting a little would help
3295 // (only for shared locks this would avoid an error)
3296 return setError(VBOX_E_INVALID_OBJECT_STATE,
3297 tr("The machine '%s' already has a lock request pending"),
3298 mUserData->s.strName.c_str());
3299 }
3300
3301 // this machine is awaiting for a spawning session to be opened:
3302 // then the calling process must be the one that got started by
3303 // LaunchVMProcess()
3304
3305 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3306 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3307
3308#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3309 /* Hardened windows builds spawns three processes when a VM is
3310 launched, the 3rd one is the one that will end up here. */
3311 RTPROCESS ppid;
3312 int rc = RTProcQueryParent(pid, &ppid);
3313 if (RT_SUCCESS(rc))
3314 rc = RTProcQueryParent(ppid, &ppid);
3315 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3316 || rc == VERR_ACCESS_DENIED)
3317 {
3318 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3319 mData->mSession.mPID = pid;
3320 }
3321#endif
3322
3323 if (mData->mSession.mPID != pid)
3324 return setError(E_ACCESSDENIED,
3325 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3326 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3327 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3328 }
3329
3330 // create the mutable SessionMachine from the current machine
3331 ComObjPtr<SessionMachine> sessionMachine;
3332 sessionMachine.createObject();
3333 rc = sessionMachine->init(this);
3334 AssertComRC(rc);
3335
3336 /* NOTE: doing return from this function after this point but
3337 * before the end is forbidden since it may call SessionMachine::uninit()
3338 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3339 * lock while still holding the Machine lock in alock so that a deadlock
3340 * is possible due to the wrong lock order. */
3341
3342 if (SUCCEEDED(rc))
3343 {
3344 /*
3345 * Set the session state to Spawning to protect against subsequent
3346 * attempts to open a session and to unregister the machine after
3347 * we release the lock.
3348 */
3349 SessionState_T origState = mData->mSession.mState;
3350 mData->mSession.mState = SessionState_Spawning;
3351
3352#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3353 /* Get the client token ID to be passed to the client process */
3354 Utf8Str strTokenId;
3355 sessionMachine->i_getTokenId(strTokenId);
3356 Assert(!strTokenId.isEmpty());
3357#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3358 /* Get the client token to be passed to the client process */
3359 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3360 /* The token is now "owned" by pToken, fix refcount */
3361 if (!pToken.isNull())
3362 pToken->Release();
3363#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3364
3365 /*
3366 * Release the lock before calling the client process -- it will call
3367 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3368 * because the state is Spawning, so that LaunchVMProcess() and
3369 * LockMachine() calls will fail. This method, called before we
3370 * acquire the lock again, will fail because of the wrong PID.
3371 *
3372 * Note that mData->mSession.mRemoteControls accessed outside
3373 * the lock may not be modified when state is Spawning, so it's safe.
3374 */
3375 alock.release();
3376
3377 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3378#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3379 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3380#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3381 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3382 /* Now the token is owned by the client process. */
3383 pToken.setNull();
3384#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3385 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3386
3387 /* The failure may occur w/o any error info (from RPC), so provide one */
3388 if (FAILED(rc))
3389 setError(VBOX_E_VM_ERROR,
3390 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3391
3392 if ( SUCCEEDED(rc)
3393 && fLaunchingVMProcess
3394 )
3395 {
3396 /* complete the remote session initialization */
3397
3398 /* get the console from the direct session */
3399 ComPtr<IConsole> console;
3400 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3401 ComAssertComRC(rc);
3402
3403 if (SUCCEEDED(rc) && !console)
3404 {
3405 ComAssert(!!console);
3406 rc = E_FAIL;
3407 }
3408
3409 /* assign machine & console to the remote session */
3410 if (SUCCEEDED(rc))
3411 {
3412 /*
3413 * after LaunchVMProcess(), the first and the only
3414 * entry in remoteControls is that remote session
3415 */
3416 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3417 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3418 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3419
3420 /* The failure may occur w/o any error info (from RPC), so provide one */
3421 if (FAILED(rc))
3422 setError(VBOX_E_VM_ERROR,
3423 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3424 }
3425
3426 if (FAILED(rc))
3427 pSessionControl->Uninitialize();
3428 }
3429
3430 /* acquire the lock again */
3431 alock.acquire();
3432
3433 /* Restore the session state */
3434 mData->mSession.mState = origState;
3435 }
3436
3437 // finalize spawning anyway (this is why we don't return on errors above)
3438 if (fLaunchingVMProcess)
3439 {
3440 /* Note that the progress object is finalized later */
3441 /** @todo Consider checking mData->mSession.mProgress for cancellation
3442 * around here. */
3443
3444 /* We don't reset mSession.mPID here because it is necessary for
3445 * SessionMachine::uninit() to reap the child process later. */
3446
3447 if (FAILED(rc))
3448 {
3449 /* Close the remote session, remove the remote control from the list
3450 * and reset session state to Closed (@note keep the code in sync
3451 * with the relevant part in checkForSpawnFailure()). */
3452
3453 Assert(mData->mSession.mRemoteControls.size() == 1);
3454 if (mData->mSession.mRemoteControls.size() == 1)
3455 {
3456 ErrorInfoKeeper eik;
3457 mData->mSession.mRemoteControls.front()->Uninitialize();
3458 }
3459
3460 mData->mSession.mRemoteControls.clear();
3461 mData->mSession.mState = SessionState_Unlocked;
3462 }
3463 }
3464 else
3465 {
3466 /* memorize PID of the directly opened session */
3467 if (SUCCEEDED(rc))
3468 mData->mSession.mPID = pid;
3469 }
3470
3471 if (SUCCEEDED(rc))
3472 {
3473 /* memorize the direct session control and cache IUnknown for it */
3474 mData->mSession.mDirectControl = pSessionControl;
3475 mData->mSession.mState = SessionState_Locked;
3476 /* associate the SessionMachine with this Machine */
3477 mData->mSession.mMachine = sessionMachine;
3478
3479 /* request an IUnknown pointer early from the remote party for later
3480 * identity checks (it will be internally cached within mDirectControl
3481 * at least on XPCOM) */
3482 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3483 NOREF(unk);
3484 }
3485
3486 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3487 * would break the lock order */
3488 alock.release();
3489
3490 /* uninitialize the created session machine on failure */
3491 if (FAILED(rc))
3492 sessionMachine->uninit();
3493
3494 }
3495
3496 if (SUCCEEDED(rc))
3497 {
3498 /*
3499 * tell the client watcher thread to update the set of
3500 * machines that have open sessions
3501 */
3502 mParent->i_updateClientWatcher();
3503
3504 if (oldState != SessionState_Locked)
3505 /* fire an event */
3506 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3507 }
3508
3509 return rc;
3510}
3511
3512/**
3513 * @note Locks objects!
3514 */
3515HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3516 const com::Utf8Str &aType,
3517 const com::Utf8Str &aEnvironment,
3518 ComPtr<IProgress> &aProgress)
3519{
3520 Utf8Str strFrontend(aType);
3521 /* "emergencystop" doesn't need the session, so skip the checks/interface
3522 * retrieval. This code doesn't quite fit in here, but introducing a
3523 * special API method would be even more effort, and would require explicit
3524 * support by every API client. It's better to hide the feature a bit. */
3525 if (strFrontend != "emergencystop")
3526 CheckComArgNotNull(aSession);
3527
3528 HRESULT rc = S_OK;
3529 if (strFrontend.isEmpty())
3530 {
3531 Bstr bstrFrontend;
3532 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3533 if (FAILED(rc))
3534 return rc;
3535 strFrontend = bstrFrontend;
3536 if (strFrontend.isEmpty())
3537 {
3538 ComPtr<ISystemProperties> systemProperties;
3539 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3540 if (FAILED(rc))
3541 return rc;
3542 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3543 if (FAILED(rc))
3544 return rc;
3545 strFrontend = bstrFrontend;
3546 }
3547 /* paranoia - emergencystop is not a valid default */
3548 if (strFrontend == "emergencystop")
3549 strFrontend = Utf8Str::Empty;
3550 }
3551 /* default frontend: Qt GUI */
3552 if (strFrontend.isEmpty())
3553 strFrontend = "GUI/Qt";
3554
3555 if (strFrontend != "emergencystop")
3556 {
3557 /* check the session state */
3558 SessionState_T state;
3559 rc = aSession->COMGETTER(State)(&state);
3560 if (FAILED(rc))
3561 return rc;
3562
3563 if (state != SessionState_Unlocked)
3564 return setError(VBOX_E_INVALID_OBJECT_STATE,
3565 tr("The given session is busy"));
3566
3567 /* get the IInternalSessionControl interface */
3568 ComPtr<IInternalSessionControl> control(aSession);
3569 ComAssertMsgRet(!control.isNull(),
3570 ("No IInternalSessionControl interface"),
3571 E_INVALIDARG);
3572
3573 /* get the teleporter enable state for the progress object init. */
3574 BOOL fTeleporterEnabled;
3575 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3576 if (FAILED(rc))
3577 return rc;
3578
3579 /* create a progress object */
3580 ComObjPtr<ProgressProxy> progress;
3581 progress.createObject();
3582 rc = progress->init(mParent,
3583 static_cast<IMachine*>(this),
3584 Bstr(tr("Starting VM")).raw(),
3585 TRUE /* aCancelable */,
3586 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3587 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3588 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3589 2 /* uFirstOperationWeight */,
3590 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3591
3592 if (SUCCEEDED(rc))
3593 {
3594 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3595 if (SUCCEEDED(rc))
3596 {
3597 aProgress = progress;
3598
3599 /* signal the client watcher thread */
3600 mParent->i_updateClientWatcher();
3601
3602 /* fire an event */
3603 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3604 }
3605 }
3606 }
3607 else
3608 {
3609 /* no progress object - either instant success or failure */
3610 aProgress = NULL;
3611
3612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3613
3614 if (mData->mSession.mState != SessionState_Locked)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("The machine '%s' is not locked by a session"),
3617 mUserData->s.strName.c_str());
3618
3619 /* must have a VM process associated - do not kill normal API clients
3620 * with an open session */
3621 if (!Global::IsOnline(mData->mMachineState))
3622 return setError(VBOX_E_INVALID_OBJECT_STATE,
3623 tr("The machine '%s' does not have a VM process"),
3624 mUserData->s.strName.c_str());
3625
3626 /* forcibly terminate the VM process */
3627 if (mData->mSession.mPID != NIL_RTPROCESS)
3628 RTProcTerminate(mData->mSession.mPID);
3629
3630 /* signal the client watcher thread, as most likely the client has
3631 * been terminated */
3632 mParent->i_updateClientWatcher();
3633 }
3634
3635 return rc;
3636}
3637
3638HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3639{
3640 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3641 return setError(E_INVALIDARG,
3642 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3643 aPosition, SchemaDefs::MaxBootPosition);
3644
3645 if (aDevice == DeviceType_USB)
3646 return setError(E_NOTIMPL,
3647 tr("Booting from USB device is currently not supported"));
3648
3649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3650
3651 HRESULT rc = i_checkStateDependency(MutableStateDep);
3652 if (FAILED(rc)) return rc;
3653
3654 i_setModified(IsModified_MachineData);
3655 mHWData.backup();
3656 mHWData->mBootOrder[aPosition - 1] = aDevice;
3657
3658 return S_OK;
3659}
3660
3661HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3662{
3663 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3664 return setError(E_INVALIDARG,
3665 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3666 aPosition, SchemaDefs::MaxBootPosition);
3667
3668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3669
3670 *aDevice = mHWData->mBootOrder[aPosition - 1];
3671
3672 return S_OK;
3673}
3674
3675HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3676 LONG aControllerPort,
3677 LONG aDevice,
3678 DeviceType_T aType,
3679 const ComPtr<IMedium> &aMedium)
3680{
3681 IMedium *aM = aMedium;
3682 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3683 aName.c_str(), aControllerPort, aDevice, aType, aM));
3684
3685 // request the host lock first, since might be calling Host methods for getting host drives;
3686 // next, protect the media tree all the while we're in here, as well as our member variables
3687 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3688 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3689
3690 HRESULT rc = i_checkStateDependency(MutableStateDep);
3691 if (FAILED(rc)) return rc;
3692
3693 /// @todo NEWMEDIA implicit machine registration
3694 if (!mData->mRegistered)
3695 return setError(VBOX_E_INVALID_OBJECT_STATE,
3696 tr("Cannot attach storage devices to an unregistered machine"));
3697
3698 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3699
3700 /* Check for an existing controller. */
3701 ComObjPtr<StorageController> ctl;
3702 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3703 if (FAILED(rc)) return rc;
3704
3705 StorageControllerType_T ctrlType;
3706 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3707 if (FAILED(rc))
3708 return setError(E_FAIL,
3709 tr("Could not get type of controller '%s'"),
3710 aName.c_str());
3711
3712 bool fSilent = false;
3713 Utf8Str strReconfig;
3714
3715 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3716 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3717 if ( mData->mMachineState == MachineState_Paused
3718 && strReconfig == "1")
3719 fSilent = true;
3720
3721 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3722 bool fHotplug = false;
3723 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3724 fHotplug = true;
3725
3726 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3727 return setError(VBOX_E_INVALID_VM_STATE,
3728 tr("Controller '%s' does not support hotplugging"),
3729 aName.c_str());
3730
3731 // check that the port and device are not out of range
3732 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3733 if (FAILED(rc)) return rc;
3734
3735 /* check if the device slot is already busy */
3736 MediumAttachment *pAttachTemp;
3737 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3738 Bstr(aName).raw(),
3739 aControllerPort,
3740 aDevice)))
3741 {
3742 Medium *pMedium = pAttachTemp->i_getMedium();
3743 if (pMedium)
3744 {
3745 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3746 return setError(VBOX_E_OBJECT_IN_USE,
3747 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3748 pMedium->i_getLocationFull().c_str(),
3749 aControllerPort,
3750 aDevice,
3751 aName.c_str());
3752 }
3753 else
3754 return setError(VBOX_E_OBJECT_IN_USE,
3755 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3756 aControllerPort, aDevice, aName.c_str());
3757 }
3758
3759 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3760 if (aMedium && medium.isNull())
3761 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3762
3763 AutoCaller mediumCaller(medium);
3764 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3765
3766 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3767
3768 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3769 && !medium.isNull()
3770 )
3771 return setError(VBOX_E_OBJECT_IN_USE,
3772 tr("Medium '%s' is already attached to this virtual machine"),
3773 medium->i_getLocationFull().c_str());
3774
3775 if (!medium.isNull())
3776 {
3777 MediumType_T mtype = medium->i_getType();
3778 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3779 // For DVDs it's not written to the config file, so needs no global config
3780 // version bump. For floppies it's a new attribute "type", which is ignored
3781 // by older VirtualBox version, so needs no global config version bump either.
3782 // For hard disks this type is not accepted.
3783 if (mtype == MediumType_MultiAttach)
3784 {
3785 // This type is new with VirtualBox 4.0 and therefore requires settings
3786 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3787 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3788 // two reasons: The medium type is a property of the media registry tree, which
3789 // can reside in the global config file (for pre-4.0 media); we would therefore
3790 // possibly need to bump the global config version. We don't want to do that though
3791 // because that might make downgrading to pre-4.0 impossible.
3792 // As a result, we can only use these two new types if the medium is NOT in the
3793 // global registry:
3794 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3795 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3796 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3797 )
3798 return setError(VBOX_E_INVALID_OBJECT_STATE,
3799 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3800 "to machines that were created with VirtualBox 4.0 or later"),
3801 medium->i_getLocationFull().c_str());
3802 }
3803 }
3804
3805 bool fIndirect = false;
3806 if (!medium.isNull())
3807 fIndirect = medium->i_isReadOnly();
3808 bool associate = true;
3809
3810 do
3811 {
3812 if ( aType == DeviceType_HardDisk
3813 && mMediaData.isBackedUp())
3814 {
3815 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3816
3817 /* check if the medium was attached to the VM before we started
3818 * changing attachments in which case the attachment just needs to
3819 * be restored */
3820 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3821 {
3822 AssertReturn(!fIndirect, E_FAIL);
3823
3824 /* see if it's the same bus/channel/device */
3825 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3826 {
3827 /* the simplest case: restore the whole attachment
3828 * and return, nothing else to do */
3829 mMediaData->mAttachments.push_back(pAttachTemp);
3830
3831 /* Reattach the medium to the VM. */
3832 if (fHotplug || fSilent)
3833 {
3834 mediumLock.release();
3835 treeLock.release();
3836 alock.release();
3837
3838 MediumLockList *pMediumLockList(new MediumLockList());
3839
3840 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3841 true /* fMediumLockWrite */,
3842 NULL,
3843 *pMediumLockList);
3844 alock.acquire();
3845 if (FAILED(rc))
3846 delete pMediumLockList;
3847 else
3848 {
3849 mData->mSession.mLockedMedia.Unlock();
3850 alock.release();
3851 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3852 mData->mSession.mLockedMedia.Lock();
3853 alock.acquire();
3854 }
3855 alock.release();
3856
3857 if (SUCCEEDED(rc))
3858 {
3859 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3860 /* Remove lock list in case of error. */
3861 if (FAILED(rc))
3862 {
3863 mData->mSession.mLockedMedia.Unlock();
3864 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3865 mData->mSession.mLockedMedia.Lock();
3866 }
3867 }
3868 }
3869
3870 return S_OK;
3871 }
3872
3873 /* bus/channel/device differ; we need a new attachment object,
3874 * but don't try to associate it again */
3875 associate = false;
3876 break;
3877 }
3878 }
3879
3880 /* go further only if the attachment is to be indirect */
3881 if (!fIndirect)
3882 break;
3883
3884 /* perform the so called smart attachment logic for indirect
3885 * attachments. Note that smart attachment is only applicable to base
3886 * hard disks. */
3887
3888 if (medium->i_getParent().isNull())
3889 {
3890 /* first, investigate the backup copy of the current hard disk
3891 * attachments to make it possible to re-attach existing diffs to
3892 * another device slot w/o losing their contents */
3893 if (mMediaData.isBackedUp())
3894 {
3895 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3896
3897 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3898 uint32_t foundLevel = 0;
3899
3900 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3901 {
3902 uint32_t level = 0;
3903 MediumAttachment *pAttach = *it;
3904 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3905 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3906 if (pMedium.isNull())
3907 continue;
3908
3909 if (pMedium->i_getBase(&level) == medium)
3910 {
3911 /* skip the hard disk if its currently attached (we
3912 * cannot attach the same hard disk twice) */
3913 if (i_findAttachment(mMediaData->mAttachments,
3914 pMedium))
3915 continue;
3916
3917 /* matched device, channel and bus (i.e. attached to the
3918 * same place) will win and immediately stop the search;
3919 * otherwise the attachment that has the youngest
3920 * descendant of medium will be used
3921 */
3922 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3923 {
3924 /* the simplest case: restore the whole attachment
3925 * and return, nothing else to do */
3926 mMediaData->mAttachments.push_back(*it);
3927
3928 /* Reattach the medium to the VM. */
3929 if (fHotplug || fSilent)
3930 {
3931 mediumLock.release();
3932 treeLock.release();
3933 alock.release();
3934
3935 MediumLockList *pMediumLockList(new MediumLockList());
3936
3937 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3938 true /* fMediumLockWrite */,
3939 NULL,
3940 *pMediumLockList);
3941 alock.acquire();
3942 if (FAILED(rc))
3943 delete pMediumLockList;
3944 else
3945 {
3946 mData->mSession.mLockedMedia.Unlock();
3947 alock.release();
3948 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3949 mData->mSession.mLockedMedia.Lock();
3950 alock.acquire();
3951 }
3952 alock.release();
3953
3954 if (SUCCEEDED(rc))
3955 {
3956 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3957 /* Remove lock list in case of error. */
3958 if (FAILED(rc))
3959 {
3960 mData->mSession.mLockedMedia.Unlock();
3961 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3962 mData->mSession.mLockedMedia.Lock();
3963 }
3964 }
3965 }
3966
3967 return S_OK;
3968 }
3969 else if ( foundIt == oldAtts.end()
3970 || level > foundLevel /* prefer younger */
3971 )
3972 {
3973 foundIt = it;
3974 foundLevel = level;
3975 }
3976 }
3977 }
3978
3979 if (foundIt != oldAtts.end())
3980 {
3981 /* use the previously attached hard disk */
3982 medium = (*foundIt)->i_getMedium();
3983 mediumCaller.attach(medium);
3984 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3985 mediumLock.attach(medium);
3986 /* not implicit, doesn't require association with this VM */
3987 fIndirect = false;
3988 associate = false;
3989 /* go right to the MediumAttachment creation */
3990 break;
3991 }
3992 }
3993
3994 /* must give up the medium lock and medium tree lock as below we
3995 * go over snapshots, which needs a lock with higher lock order. */
3996 mediumLock.release();
3997 treeLock.release();
3998
3999 /* then, search through snapshots for the best diff in the given
4000 * hard disk's chain to base the new diff on */
4001
4002 ComObjPtr<Medium> base;
4003 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4004 while (snap)
4005 {
4006 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4007
4008 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4009
4010 MediumAttachment *pAttachFound = NULL;
4011 uint32_t foundLevel = 0;
4012
4013 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4014 {
4015 MediumAttachment *pAttach = *it;
4016 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4017 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4018 if (pMedium.isNull())
4019 continue;
4020
4021 uint32_t level = 0;
4022 if (pMedium->i_getBase(&level) == medium)
4023 {
4024 /* matched device, channel and bus (i.e. attached to the
4025 * same place) will win and immediately stop the search;
4026 * otherwise the attachment that has the youngest
4027 * descendant of medium will be used
4028 */
4029 if ( pAttach->i_getDevice() == aDevice
4030 && pAttach->i_getPort() == aControllerPort
4031 && pAttach->i_getControllerName() == aName
4032 )
4033 {
4034 pAttachFound = pAttach;
4035 break;
4036 }
4037 else if ( !pAttachFound
4038 || level > foundLevel /* prefer younger */
4039 )
4040 {
4041 pAttachFound = pAttach;
4042 foundLevel = level;
4043 }
4044 }
4045 }
4046
4047 if (pAttachFound)
4048 {
4049 base = pAttachFound->i_getMedium();
4050 break;
4051 }
4052
4053 snap = snap->i_getParent();
4054 }
4055
4056 /* re-lock medium tree and the medium, as we need it below */
4057 treeLock.acquire();
4058 mediumLock.acquire();
4059
4060 /* found a suitable diff, use it as a base */
4061 if (!base.isNull())
4062 {
4063 medium = base;
4064 mediumCaller.attach(medium);
4065 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4066 mediumLock.attach(medium);
4067 }
4068 }
4069
4070 Utf8Str strFullSnapshotFolder;
4071 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4072
4073 ComObjPtr<Medium> diff;
4074 diff.createObject();
4075 // store this diff in the same registry as the parent
4076 Guid uuidRegistryParent;
4077 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4078 {
4079 // parent image has no registry: this can happen if we're attaching a new immutable
4080 // image that has not yet been attached (medium then points to the base and we're
4081 // creating the diff image for the immutable, and the parent is not yet registered);
4082 // put the parent in the machine registry then
4083 mediumLock.release();
4084 treeLock.release();
4085 alock.release();
4086 i_addMediumToRegistry(medium);
4087 alock.acquire();
4088 treeLock.acquire();
4089 mediumLock.acquire();
4090 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4091 }
4092 rc = diff->init(mParent,
4093 medium->i_getPreferredDiffFormat(),
4094 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4095 uuidRegistryParent);
4096 if (FAILED(rc)) return rc;
4097
4098 /* Apply the normal locking logic to the entire chain. */
4099 MediumLockList *pMediumLockList(new MediumLockList());
4100 mediumLock.release();
4101 treeLock.release();
4102 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4103 true /* fMediumLockWrite */,
4104 medium,
4105 *pMediumLockList);
4106 treeLock.acquire();
4107 mediumLock.acquire();
4108 if (SUCCEEDED(rc))
4109 {
4110 mediumLock.release();
4111 treeLock.release();
4112 rc = pMediumLockList->Lock();
4113 treeLock.acquire();
4114 mediumLock.acquire();
4115 if (FAILED(rc))
4116 setError(rc,
4117 tr("Could not lock medium when creating diff '%s'"),
4118 diff->i_getLocationFull().c_str());
4119 else
4120 {
4121 /* will release the lock before the potentially lengthy
4122 * operation, so protect with the special state */
4123 MachineState_T oldState = mData->mMachineState;
4124 i_setMachineState(MachineState_SettingUp);
4125
4126 mediumLock.release();
4127 treeLock.release();
4128 alock.release();
4129
4130 rc = medium->i_createDiffStorage(diff,
4131 MediumVariant_Standard,
4132 pMediumLockList,
4133 NULL /* aProgress */,
4134 true /* aWait */);
4135
4136 alock.acquire();
4137 treeLock.acquire();
4138 mediumLock.acquire();
4139
4140 i_setMachineState(oldState);
4141 }
4142 }
4143
4144 /* Unlock the media and free the associated memory. */
4145 delete pMediumLockList;
4146
4147 if (FAILED(rc)) return rc;
4148
4149 /* use the created diff for the actual attachment */
4150 medium = diff;
4151 mediumCaller.attach(medium);
4152 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4153 mediumLock.attach(medium);
4154 }
4155 while (0);
4156
4157 ComObjPtr<MediumAttachment> attachment;
4158 attachment.createObject();
4159 rc = attachment->init(this,
4160 medium,
4161 aName,
4162 aControllerPort,
4163 aDevice,
4164 aType,
4165 fIndirect,
4166 false /* fPassthrough */,
4167 false /* fTempEject */,
4168 false /* fNonRotational */,
4169 false /* fDiscard */,
4170 fHotplug /* fHotPluggable */,
4171 Utf8Str::Empty);
4172 if (FAILED(rc)) return rc;
4173
4174 if (associate && !medium.isNull())
4175 {
4176 // as the last step, associate the medium to the VM
4177 rc = medium->i_addBackReference(mData->mUuid);
4178 // here we can fail because of Deleting, or being in process of creating a Diff
4179 if (FAILED(rc)) return rc;
4180
4181 mediumLock.release();
4182 treeLock.release();
4183 alock.release();
4184 i_addMediumToRegistry(medium);
4185 alock.acquire();
4186 treeLock.acquire();
4187 mediumLock.acquire();
4188 }
4189
4190 /* success: finally remember the attachment */
4191 i_setModified(IsModified_Storage);
4192 mMediaData.backup();
4193 mMediaData->mAttachments.push_back(attachment);
4194
4195 mediumLock.release();
4196 treeLock.release();
4197 alock.release();
4198
4199 if (fHotplug || fSilent)
4200 {
4201 if (!medium.isNull())
4202 {
4203 MediumLockList *pMediumLockList(new MediumLockList());
4204
4205 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4206 true /* fMediumLockWrite */,
4207 NULL,
4208 *pMediumLockList);
4209 alock.acquire();
4210 if (FAILED(rc))
4211 delete pMediumLockList;
4212 else
4213 {
4214 mData->mSession.mLockedMedia.Unlock();
4215 alock.release();
4216 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4217 mData->mSession.mLockedMedia.Lock();
4218 alock.acquire();
4219 }
4220 alock.release();
4221 }
4222
4223 if (SUCCEEDED(rc))
4224 {
4225 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4226 /* Remove lock list in case of error. */
4227 if (FAILED(rc))
4228 {
4229 mData->mSession.mLockedMedia.Unlock();
4230 mData->mSession.mLockedMedia.Remove(attachment);
4231 mData->mSession.mLockedMedia.Lock();
4232 }
4233 }
4234 }
4235
4236 mParent->i_saveModifiedRegistries();
4237
4238 return rc;
4239}
4240
4241HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4242 LONG aDevice)
4243{
4244 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4245 aName.c_str(), aControllerPort, aDevice));
4246
4247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4248
4249 HRESULT rc = i_checkStateDependency(MutableStateDep);
4250 if (FAILED(rc)) return rc;
4251
4252 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4253
4254 /* Check for an existing controller. */
4255 ComObjPtr<StorageController> ctl;
4256 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4257 if (FAILED(rc)) return rc;
4258
4259 StorageControllerType_T ctrlType;
4260 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4261 if (FAILED(rc))
4262 return setError(E_FAIL,
4263 tr("Could not get type of controller '%s'"),
4264 aName.c_str());
4265
4266 bool fSilent = false;
4267 Utf8Str strReconfig;
4268
4269 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4270 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4271 if ( mData->mMachineState == MachineState_Paused
4272 && strReconfig == "1")
4273 fSilent = true;
4274
4275 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4276 bool fHotplug = false;
4277 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4278 fHotplug = true;
4279
4280 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4281 return setError(VBOX_E_INVALID_VM_STATE,
4282 tr("Controller '%s' does not support hotplugging"),
4283 aName.c_str());
4284
4285 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4286 Bstr(aName).raw(),
4287 aControllerPort,
4288 aDevice);
4289 if (!pAttach)
4290 return setError(VBOX_E_OBJECT_NOT_FOUND,
4291 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4292 aDevice, aControllerPort, aName.c_str());
4293
4294 if (fHotplug && !pAttach->i_getHotPluggable())
4295 return setError(VBOX_E_NOT_SUPPORTED,
4296 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4297 aDevice, aControllerPort, aName.c_str());
4298
4299 /*
4300 * The VM has to detach the device before we delete any implicit diffs.
4301 * If this fails we can roll back without loosing data.
4302 */
4303 if (fHotplug || fSilent)
4304 {
4305 alock.release();
4306 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4307 alock.acquire();
4308 }
4309 if (FAILED(rc)) return rc;
4310
4311 /* If we are here everything went well and we can delete the implicit now. */
4312 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4313
4314 alock.release();
4315
4316 mParent->i_saveModifiedRegistries();
4317
4318 return rc;
4319}
4320
4321HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4322 LONG aDevice, BOOL aPassthrough)
4323{
4324 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4325 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4326
4327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4328
4329 HRESULT rc = i_checkStateDependency(MutableStateDep);
4330 if (FAILED(rc)) return rc;
4331
4332 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4333
4334 if (Global::IsOnlineOrTransient(mData->mMachineState))
4335 return setError(VBOX_E_INVALID_VM_STATE,
4336 tr("Invalid machine state: %s"),
4337 Global::stringifyMachineState(mData->mMachineState));
4338
4339 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4340 Bstr(aName).raw(),
4341 aControllerPort,
4342 aDevice);
4343 if (!pAttach)
4344 return setError(VBOX_E_OBJECT_NOT_FOUND,
4345 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4346 aDevice, aControllerPort, aName.c_str());
4347
4348
4349 i_setModified(IsModified_Storage);
4350 mMediaData.backup();
4351
4352 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4353
4354 if (pAttach->i_getType() != DeviceType_DVD)
4355 return setError(E_INVALIDARG,
4356 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4357 aDevice, aControllerPort, aName.c_str());
4358 pAttach->i_updatePassthrough(!!aPassthrough);
4359
4360 return S_OK;
4361}
4362
4363HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4364 LONG aDevice, BOOL aTemporaryEject)
4365{
4366
4367 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4368 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4369
4370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4371
4372 HRESULT rc = i_checkStateDependency(MutableStateDep);
4373 if (FAILED(rc)) return rc;
4374
4375 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4376 Bstr(aName).raw(),
4377 aControllerPort,
4378 aDevice);
4379 if (!pAttach)
4380 return setError(VBOX_E_OBJECT_NOT_FOUND,
4381 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4382 aDevice, aControllerPort, aName.c_str());
4383
4384
4385 i_setModified(IsModified_Storage);
4386 mMediaData.backup();
4387
4388 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4389
4390 if (pAttach->i_getType() != DeviceType_DVD)
4391 return setError(E_INVALIDARG,
4392 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4393 aDevice, aControllerPort, aName.c_str());
4394 pAttach->i_updateTempEject(!!aTemporaryEject);
4395
4396 return S_OK;
4397}
4398
4399HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4400 LONG aDevice, BOOL aNonRotational)
4401{
4402
4403 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4404 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4405
4406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4407
4408 HRESULT rc = i_checkStateDependency(MutableStateDep);
4409 if (FAILED(rc)) return rc;
4410
4411 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4412
4413 if (Global::IsOnlineOrTransient(mData->mMachineState))
4414 return setError(VBOX_E_INVALID_VM_STATE,
4415 tr("Invalid machine state: %s"),
4416 Global::stringifyMachineState(mData->mMachineState));
4417
4418 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4419 Bstr(aName).raw(),
4420 aControllerPort,
4421 aDevice);
4422 if (!pAttach)
4423 return setError(VBOX_E_OBJECT_NOT_FOUND,
4424 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4425 aDevice, aControllerPort, aName.c_str());
4426
4427
4428 i_setModified(IsModified_Storage);
4429 mMediaData.backup();
4430
4431 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4432
4433 if (pAttach->i_getType() != DeviceType_HardDisk)
4434 return setError(E_INVALIDARG,
4435 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"),
4436 aDevice, aControllerPort, aName.c_str());
4437 pAttach->i_updateNonRotational(!!aNonRotational);
4438
4439 return S_OK;
4440}
4441
4442HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4443 LONG aDevice, BOOL aDiscard)
4444{
4445
4446 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4447 aName.c_str(), aControllerPort, aDevice, aDiscard));
4448
4449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4450
4451 HRESULT rc = i_checkStateDependency(MutableStateDep);
4452 if (FAILED(rc)) return rc;
4453
4454 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4455
4456 if (Global::IsOnlineOrTransient(mData->mMachineState))
4457 return setError(VBOX_E_INVALID_VM_STATE,
4458 tr("Invalid machine state: %s"),
4459 Global::stringifyMachineState(mData->mMachineState));
4460
4461 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4462 Bstr(aName).raw(),
4463 aControllerPort,
4464 aDevice);
4465 if (!pAttach)
4466 return setError(VBOX_E_OBJECT_NOT_FOUND,
4467 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4468 aDevice, aControllerPort, aName.c_str());
4469
4470
4471 i_setModified(IsModified_Storage);
4472 mMediaData.backup();
4473
4474 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4475
4476 if (pAttach->i_getType() != DeviceType_HardDisk)
4477 return setError(E_INVALIDARG,
4478 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"),
4479 aDevice, aControllerPort, aName.c_str());
4480 pAttach->i_updateDiscard(!!aDiscard);
4481
4482 return S_OK;
4483}
4484
4485HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4486 LONG aDevice, BOOL aHotPluggable)
4487{
4488 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4489 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4490
4491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4492
4493 HRESULT rc = i_checkStateDependency(MutableStateDep);
4494 if (FAILED(rc)) return rc;
4495
4496 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4497
4498 if (Global::IsOnlineOrTransient(mData->mMachineState))
4499 return setError(VBOX_E_INVALID_VM_STATE,
4500 tr("Invalid machine state: %s"),
4501 Global::stringifyMachineState(mData->mMachineState));
4502
4503 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4504 Bstr(aName).raw(),
4505 aControllerPort,
4506 aDevice);
4507 if (!pAttach)
4508 return setError(VBOX_E_OBJECT_NOT_FOUND,
4509 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4510 aDevice, aControllerPort, aName.c_str());
4511
4512 /* Check for an existing controller. */
4513 ComObjPtr<StorageController> ctl;
4514 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4515 if (FAILED(rc)) return rc;
4516
4517 StorageControllerType_T ctrlType;
4518 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4519 if (FAILED(rc))
4520 return setError(E_FAIL,
4521 tr("Could not get type of controller '%s'"),
4522 aName.c_str());
4523
4524 if (!i_isControllerHotplugCapable(ctrlType))
4525 return setError(VBOX_E_NOT_SUPPORTED,
4526 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4527 aName.c_str());
4528
4529 i_setModified(IsModified_Storage);
4530 mMediaData.backup();
4531
4532 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4533
4534 if (pAttach->i_getType() == DeviceType_Floppy)
4535 return setError(E_INVALIDARG,
4536 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"),
4537 aDevice, aControllerPort, aName.c_str());
4538 pAttach->i_updateHotPluggable(!!aHotPluggable);
4539
4540 return S_OK;
4541}
4542
4543HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4544 LONG aDevice)
4545{
4546 int rc = S_OK;
4547 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4548 aName.c_str(), aControllerPort, aDevice));
4549
4550 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4551
4552 return rc;
4553}
4554
4555HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4556 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4557{
4558 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4559 aName.c_str(), aControllerPort, aDevice));
4560
4561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4562
4563 HRESULT rc = i_checkStateDependency(MutableStateDep);
4564 if (FAILED(rc)) return rc;
4565
4566 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4567
4568 if (Global::IsOnlineOrTransient(mData->mMachineState))
4569 return setError(VBOX_E_INVALID_VM_STATE,
4570 tr("Invalid machine state: %s"),
4571 Global::stringifyMachineState(mData->mMachineState));
4572
4573 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4574 Bstr(aName).raw(),
4575 aControllerPort,
4576 aDevice);
4577 if (!pAttach)
4578 return setError(VBOX_E_OBJECT_NOT_FOUND,
4579 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4580 aDevice, aControllerPort, aName.c_str());
4581
4582
4583 i_setModified(IsModified_Storage);
4584 mMediaData.backup();
4585
4586 IBandwidthGroup *iB = aBandwidthGroup;
4587 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4588 if (aBandwidthGroup && group.isNull())
4589 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4590
4591 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4592
4593 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4594 if (strBandwidthGroupOld.isNotEmpty())
4595 {
4596 /* Get the bandwidth group object and release it - this must not fail. */
4597 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4598 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4599 Assert(SUCCEEDED(rc));
4600
4601 pBandwidthGroupOld->i_release();
4602 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4603 }
4604
4605 if (!group.isNull())
4606 {
4607 group->i_reference();
4608 pAttach->i_updateBandwidthGroup(group->i_getName());
4609 }
4610
4611 return S_OK;
4612}
4613
4614HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4615 LONG aControllerPort,
4616 LONG aDevice,
4617 DeviceType_T aType)
4618{
4619 HRESULT rc = S_OK;
4620
4621 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4622 aName.c_str(), aControllerPort, aDevice, aType));
4623
4624 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4625
4626 return rc;
4627}
4628
4629
4630HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4631 LONG aControllerPort,
4632 LONG aDevice,
4633 BOOL aForce)
4634{
4635 int rc = S_OK;
4636 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4637 aName.c_str(), aControllerPort, aForce));
4638
4639 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4640
4641 return rc;
4642}
4643
4644HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4645 LONG aControllerPort,
4646 LONG aDevice,
4647 const ComPtr<IMedium> &aMedium,
4648 BOOL aForce)
4649{
4650 int rc = S_OK;
4651 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4652 aName.c_str(), aControllerPort, aDevice, aForce));
4653
4654 // request the host lock first, since might be calling Host methods for getting host drives;
4655 // next, protect the media tree all the while we're in here, as well as our member variables
4656 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4657 this->lockHandle(),
4658 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4659
4660 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4661 Bstr(aName).raw(),
4662 aControllerPort,
4663 aDevice);
4664 if (pAttach.isNull())
4665 return setError(VBOX_E_OBJECT_NOT_FOUND,
4666 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4667 aDevice, aControllerPort, aName.c_str());
4668
4669 /* Remember previously mounted medium. The medium before taking the
4670 * backup is not necessarily the same thing. */
4671 ComObjPtr<Medium> oldmedium;
4672 oldmedium = pAttach->i_getMedium();
4673
4674 IMedium *iM = aMedium;
4675 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4676 if (aMedium && pMedium.isNull())
4677 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4678
4679 AutoCaller mediumCaller(pMedium);
4680 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4681
4682 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4683 if (pMedium)
4684 {
4685 DeviceType_T mediumType = pAttach->i_getType();
4686 switch (mediumType)
4687 {
4688 case DeviceType_DVD:
4689 case DeviceType_Floppy:
4690 break;
4691
4692 default:
4693 return setError(VBOX_E_INVALID_OBJECT_STATE,
4694 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4695 aControllerPort,
4696 aDevice,
4697 aName.c_str());
4698 }
4699 }
4700
4701 i_setModified(IsModified_Storage);
4702 mMediaData.backup();
4703
4704 {
4705 // The backup operation makes the pAttach reference point to the
4706 // old settings. Re-get the correct reference.
4707 pAttach = i_findAttachment(mMediaData->mAttachments,
4708 Bstr(aName).raw(),
4709 aControllerPort,
4710 aDevice);
4711 if (!oldmedium.isNull())
4712 oldmedium->i_removeBackReference(mData->mUuid);
4713 if (!pMedium.isNull())
4714 {
4715 pMedium->i_addBackReference(mData->mUuid);
4716
4717 mediumLock.release();
4718 multiLock.release();
4719 i_addMediumToRegistry(pMedium);
4720 multiLock.acquire();
4721 mediumLock.acquire();
4722 }
4723
4724 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4725 pAttach->i_updateMedium(pMedium);
4726 }
4727
4728 i_setModified(IsModified_Storage);
4729
4730 mediumLock.release();
4731 multiLock.release();
4732 rc = i_onMediumChange(pAttach, aForce);
4733 multiLock.acquire();
4734 mediumLock.acquire();
4735
4736 /* On error roll back this change only. */
4737 if (FAILED(rc))
4738 {
4739 if (!pMedium.isNull())
4740 pMedium->i_removeBackReference(mData->mUuid);
4741 pAttach = i_findAttachment(mMediaData->mAttachments,
4742 Bstr(aName).raw(),
4743 aControllerPort,
4744 aDevice);
4745 /* If the attachment is gone in the meantime, bail out. */
4746 if (pAttach.isNull())
4747 return rc;
4748 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4749 if (!oldmedium.isNull())
4750 oldmedium->i_addBackReference(mData->mUuid);
4751 pAttach->i_updateMedium(oldmedium);
4752 }
4753
4754 mediumLock.release();
4755 multiLock.release();
4756
4757 mParent->i_saveModifiedRegistries();
4758
4759 return rc;
4760}
4761HRESULT Machine::getMedium(const com::Utf8Str &aName,
4762 LONG aControllerPort,
4763 LONG aDevice,
4764 ComPtr<IMedium> &aMedium)
4765{
4766 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4767 aName.c_str(), aControllerPort, aDevice));
4768
4769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4770
4771 aMedium = NULL;
4772
4773 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4774 Bstr(aName).raw(),
4775 aControllerPort,
4776 aDevice);
4777 if (pAttach.isNull())
4778 return setError(VBOX_E_OBJECT_NOT_FOUND,
4779 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4780 aDevice, aControllerPort, aName.c_str());
4781
4782 aMedium = pAttach->i_getMedium();
4783
4784 return S_OK;
4785}
4786
4787HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4788{
4789
4790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4791
4792 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4793
4794 return S_OK;
4795}
4796
4797HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4798{
4799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4800
4801 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4802
4803 return S_OK;
4804}
4805
4806HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4807{
4808 /* Do not assert if slot is out of range, just return the advertised
4809 status. testdriver/vbox.py triggers this in logVmInfo. */
4810 if (aSlot >= mNetworkAdapters.size())
4811 return setError(E_INVALIDARG,
4812 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4813 aSlot, mNetworkAdapters.size());
4814
4815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4818
4819 return S_OK;
4820}
4821
4822HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4823{
4824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
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 // This saving of settings is tricky: there is no "old state" for the
4918 // extradata items at all (unlike all other settings), so the old/new
4919 // settings comparison would give a wrong result!
4920 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4921
4922 if (fNeedsGlobalSaveSettings)
4923 {
4924 // save the global settings; for that we should hold only the VirtualBox lock
4925 alock.release();
4926 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4927 mParent->i_saveSettings();
4928 }
4929 }
4930
4931 // fire notification outside the lock
4932 if (fChanged)
4933 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4934
4935 return S_OK;
4936}
4937
4938HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4939{
4940 aProgress = NULL;
4941 NOREF(aSettingsFilePath);
4942 ReturnComNotImplemented();
4943}
4944
4945HRESULT Machine::saveSettings()
4946{
4947 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4948
4949 /* when there was auto-conversion, we want to save the file even if
4950 * the VM is saved */
4951 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4952 if (FAILED(rc)) return rc;
4953
4954 /* the settings file path may never be null */
4955 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4956
4957 /* save all VM data excluding snapshots */
4958 bool fNeedsGlobalSaveSettings = false;
4959 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4960 mlock.release();
4961
4962 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4963 {
4964 // save the global settings; for that we should hold only the VirtualBox lock
4965 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4966 rc = mParent->i_saveSettings();
4967 }
4968
4969 return rc;
4970}
4971
4972
4973HRESULT Machine::discardSettings()
4974{
4975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4976
4977 HRESULT rc = i_checkStateDependency(MutableStateDep);
4978 if (FAILED(rc)) return rc;
4979
4980 /*
4981 * during this rollback, the session will be notified if data has
4982 * been actually changed
4983 */
4984 i_rollback(true /* aNotify */);
4985
4986 return S_OK;
4987}
4988
4989/** @note Locks objects! */
4990HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4991 std::vector<ComPtr<IMedium> > &aMedia)
4992{
4993 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4994 AutoLimitedCaller autoCaller(this);
4995 AssertComRCReturnRC(autoCaller.rc());
4996
4997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4998
4999 Guid id(i_getId());
5000
5001 if (mData->mSession.mState != SessionState_Unlocked)
5002 return setError(VBOX_E_INVALID_OBJECT_STATE,
5003 tr("Cannot unregister the machine '%s' while it is locked"),
5004 mUserData->s.strName.c_str());
5005
5006 // wait for state dependents to drop to zero
5007 i_ensureNoStateDependencies();
5008
5009 if (!mData->mAccessible)
5010 {
5011 // inaccessible maschines can only be unregistered; uninitialize ourselves
5012 // here because currently there may be no unregistered that are inaccessible
5013 // (this state combination is not supported). Note releasing the caller and
5014 // leaving the lock before calling uninit()
5015 alock.release();
5016 autoCaller.release();
5017
5018 uninit();
5019
5020 mParent->i_unregisterMachine(this, id);
5021 // calls VirtualBox::i_saveSettings()
5022
5023 return S_OK;
5024 }
5025
5026 HRESULT rc = S_OK;
5027
5028 // discard saved state
5029 if (mData->mMachineState == MachineState_Saved)
5030 {
5031 // add the saved state file to the list of files the caller should delete
5032 Assert(!mSSData->strStateFilePath.isEmpty());
5033 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5034
5035 mSSData->strStateFilePath.setNull();
5036
5037 // unconditionally set the machine state to powered off, we now
5038 // know no session has locked the machine
5039 mData->mMachineState = MachineState_PoweredOff;
5040 }
5041
5042 size_t cSnapshots = 0;
5043 if (mData->mFirstSnapshot)
5044 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5045 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5046 // fail now before we start detaching media
5047 return setError(VBOX_E_INVALID_OBJECT_STATE,
5048 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5049 mUserData->s.strName.c_str(), cSnapshots);
5050
5051 // This list collects the medium objects from all medium attachments
5052 // which we will detach from the machine and its snapshots, in a specific
5053 // order which allows for closing all media without getting "media in use"
5054 // errors, simply by going through the list from the front to the back:
5055 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5056 // and must be closed before the parent media from the snapshots, or closing the parents
5057 // will fail because they still have children);
5058 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5059 // the root ("first") snapshot of the machine.
5060 MediaList llMedia;
5061
5062 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5063 && mMediaData->mAttachments.size()
5064 )
5065 {
5066 // we have media attachments: detach them all and add the Medium objects to our list
5067 if (aCleanupMode != CleanupMode_UnregisterOnly)
5068 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5069 else
5070 return setError(VBOX_E_INVALID_OBJECT_STATE,
5071 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5072 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5073 }
5074
5075 if (cSnapshots)
5076 {
5077 // autoCleanup must be true here, or we would have failed above
5078
5079 // add the media from the medium attachments of the snapshots to llMedia
5080 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5081 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5082 // into the children first
5083
5084 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5085 MachineState_T oldState = mData->mMachineState;
5086 mData->mMachineState = MachineState_DeletingSnapshot;
5087
5088 // make a copy of the first snapshot so the refcount does not drop to 0
5089 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5090 // because of the AutoCaller voodoo)
5091 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5092
5093 // GO!
5094 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5095
5096 mData->mMachineState = oldState;
5097 }
5098
5099 if (FAILED(rc))
5100 {
5101 i_rollbackMedia();
5102 return rc;
5103 }
5104
5105 // commit all the media changes made above
5106 i_commitMedia();
5107
5108 mData->mRegistered = false;
5109
5110 // machine lock no longer needed
5111 alock.release();
5112
5113 // return media to caller
5114 size_t i = 0;
5115 aMedia.resize(llMedia.size());
5116 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5117 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5118
5119 mParent->i_unregisterMachine(this, id);
5120 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5121
5122 return S_OK;
5123}
5124
5125struct Machine::DeleteTask
5126{
5127 ComObjPtr<Machine> pMachine;
5128 RTCList<ComPtr<IMedium> > llMediums;
5129 StringsList llFilesToDelete;
5130 ComObjPtr<Progress> pProgress;
5131};
5132
5133HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5134{
5135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5136
5137 HRESULT rc = i_checkStateDependency(MutableStateDep);
5138 if (FAILED(rc)) return rc;
5139
5140 if (mData->mRegistered)
5141 return setError(VBOX_E_INVALID_VM_STATE,
5142 tr("Cannot delete settings of a registered machine"));
5143
5144 DeleteTask *pTask = new DeleteTask;
5145 pTask->pMachine = this;
5146
5147 // collect files to delete
5148 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5149
5150 for (size_t i = 0; i < aMedia.size(); ++i)
5151 {
5152 IMedium *pIMedium(aMedia[i]);
5153 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5154 if (pMedium.isNull())
5155 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5156 SafeArray<BSTR> ids;
5157 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5158 if (FAILED(rc)) return rc;
5159 /* At this point the medium should not have any back references
5160 * anymore. If it has it is attached to another VM and *must* not
5161 * deleted. */
5162 if (ids.size() < 1)
5163 pTask->llMediums.append(pMedium);
5164 }
5165 if (mData->pMachineConfigFile->fileExists())
5166 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5167
5168 pTask->pProgress.createObject();
5169 pTask->pProgress->init(i_getVirtualBox(),
5170 static_cast<IMachine*>(this) /* aInitiator */,
5171 Bstr(tr("Deleting files")).raw(),
5172 true /* fCancellable */,
5173 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5174 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5175
5176 int vrc = RTThreadCreate(NULL,
5177 Machine::deleteThread,
5178 (void*)pTask,
5179 0,
5180 RTTHREADTYPE_MAIN_WORKER,
5181 0,
5182 "MachineDelete");
5183
5184 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5185
5186 if (RT_FAILURE(vrc))
5187 {
5188 delete pTask;
5189 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5190 }
5191
5192 LogFlowFuncLeave();
5193
5194 return S_OK;
5195}
5196
5197/**
5198 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5199 * calls Machine::deleteTaskWorker() on the actual machine object.
5200 * @param Thread
5201 * @param pvUser
5202 * @return
5203 */
5204/*static*/
5205DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5206{
5207 LogFlowFuncEnter();
5208
5209 DeleteTask *pTask = (DeleteTask*)pvUser;
5210 Assert(pTask);
5211 Assert(pTask->pMachine);
5212 Assert(pTask->pProgress);
5213
5214 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5215 pTask->pProgress->i_notifyComplete(rc);
5216
5217 delete pTask;
5218
5219 LogFlowFuncLeave();
5220
5221 NOREF(Thread);
5222
5223 return VINF_SUCCESS;
5224}
5225
5226/**
5227 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5228 * @param task
5229 * @return
5230 */
5231HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5232{
5233 AutoCaller autoCaller(this);
5234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5235
5236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5237
5238 HRESULT rc = S_OK;
5239
5240 try
5241 {
5242 ULONG uLogHistoryCount = 3;
5243 ComPtr<ISystemProperties> systemProperties;
5244 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5245 if (FAILED(rc)) throw rc;
5246
5247 if (!systemProperties.isNull())
5248 {
5249 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5250 if (FAILED(rc)) throw rc;
5251 }
5252
5253 MachineState_T oldState = mData->mMachineState;
5254 i_setMachineState(MachineState_SettingUp);
5255 alock.release();
5256 for (size_t i = 0; i < task.llMediums.size(); ++i)
5257 {
5258 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5259 {
5260 AutoCaller mac(pMedium);
5261 if (FAILED(mac.rc())) throw mac.rc();
5262 Utf8Str strLocation = pMedium->i_getLocationFull();
5263 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5264 if (FAILED(rc)) throw rc;
5265 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5266 }
5267 ComPtr<IProgress> pProgress2;
5268 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5269 if (FAILED(rc)) throw rc;
5270 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5271 if (FAILED(rc)) throw rc;
5272 /* Check the result of the asynchronous process. */
5273 LONG iRc;
5274 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5275 if (FAILED(rc)) throw rc;
5276 /* If the thread of the progress object has an error, then
5277 * retrieve the error info from there, or it'll be lost. */
5278 if (FAILED(iRc))
5279 throw setError(ProgressErrorInfo(pProgress2));
5280
5281 /* Close the medium, deliberately without checking the return
5282- * code, and without leaving any trace in the error info, as
5283- * a failure here is a very minor issue, which shouldn't happen
5284- * as above we even managed to delete the medium. */
5285 {
5286 ErrorInfoKeeper eik;
5287 pMedium->Close();
5288 }
5289 }
5290 i_setMachineState(oldState);
5291 alock.acquire();
5292
5293 // delete the files pushed on the task list by Machine::Delete()
5294 // (this includes saved states of the machine and snapshots and
5295 // medium storage files from the IMedium list passed in, and the
5296 // machine XML file)
5297 StringsList::const_iterator it = task.llFilesToDelete.begin();
5298 while (it != task.llFilesToDelete.end())
5299 {
5300 const Utf8Str &strFile = *it;
5301 LogFunc(("Deleting file %s\n", strFile.c_str()));
5302 int vrc = RTFileDelete(strFile.c_str());
5303 if (RT_FAILURE(vrc))
5304 throw setError(VBOX_E_IPRT_ERROR,
5305 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5306
5307 ++it;
5308 if (it == task.llFilesToDelete.end())
5309 {
5310 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5311 if (FAILED(rc)) throw rc;
5312 break;
5313 }
5314
5315 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5316 if (FAILED(rc)) throw rc;
5317 }
5318
5319 /* delete the settings only when the file actually exists */
5320 if (mData->pMachineConfigFile->fileExists())
5321 {
5322 /* Delete any backup or uncommitted XML files. Ignore failures.
5323 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5324 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5325 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5326 RTFileDelete(otherXml.c_str());
5327 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5328 RTFileDelete(otherXml.c_str());
5329
5330 /* delete the Logs folder, nothing important should be left
5331 * there (we don't check for errors because the user might have
5332 * some private files there that we don't want to delete) */
5333 Utf8Str logFolder;
5334 getLogFolder(logFolder);
5335 Assert(logFolder.length());
5336 if (RTDirExists(logFolder.c_str()))
5337 {
5338 /* Delete all VBox.log[.N] files from the Logs folder
5339 * (this must be in sync with the rotation logic in
5340 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5341 * files that may have been created by the GUI. */
5342 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5343 logFolder.c_str(), RTPATH_DELIMITER);
5344 RTFileDelete(log.c_str());
5345 log = Utf8StrFmt("%s%cVBox.png",
5346 logFolder.c_str(), RTPATH_DELIMITER);
5347 RTFileDelete(log.c_str());
5348 for (int i = uLogHistoryCount; i > 0; i--)
5349 {
5350 log = Utf8StrFmt("%s%cVBox.log.%d",
5351 logFolder.c_str(), RTPATH_DELIMITER, i);
5352 RTFileDelete(log.c_str());
5353 log = Utf8StrFmt("%s%cVBox.png.%d",
5354 logFolder.c_str(), RTPATH_DELIMITER, i);
5355 RTFileDelete(log.c_str());
5356 }
5357#if defined(RT_OS_WINDOWS)
5358 log = Utf8StrFmt("%s%cVBoxStartup.log",
5359 logFolder.c_str(), RTPATH_DELIMITER);
5360 RTFileDelete(log.c_str());
5361#endif
5362
5363 RTDirRemove(logFolder.c_str());
5364 }
5365
5366 /* delete the Snapshots folder, nothing important should be left
5367 * there (we don't check for errors because the user might have
5368 * some private files there that we don't want to delete) */
5369 Utf8Str strFullSnapshotFolder;
5370 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5371 Assert(!strFullSnapshotFolder.isEmpty());
5372 if (RTDirExists(strFullSnapshotFolder.c_str()))
5373 RTDirRemove(strFullSnapshotFolder.c_str());
5374
5375 // delete the directory that contains the settings file, but only
5376 // if it matches the VM name
5377 Utf8Str settingsDir;
5378 if (i_isInOwnDir(&settingsDir))
5379 RTDirRemove(settingsDir.c_str());
5380 }
5381
5382 alock.release();
5383
5384 mParent->i_saveModifiedRegistries();
5385 }
5386 catch (HRESULT aRC) { rc = aRC; }
5387
5388 return rc;
5389}
5390
5391HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5392{
5393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5394
5395 ComObjPtr<Snapshot> pSnapshot;
5396 HRESULT rc;
5397
5398 if (aNameOrId.isEmpty())
5399 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5400 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5401 else
5402 {
5403 Guid uuid(aNameOrId);
5404 if (uuid.isValid())
5405 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5406 else
5407 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5408 }
5409 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5410
5411 return rc;
5412}
5413
5414HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5415{
5416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5417
5418 HRESULT rc = i_checkStateDependency(MutableStateDep);
5419 if (FAILED(rc)) return rc;
5420
5421 ComObjPtr<SharedFolder> sharedFolder;
5422 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5423 if (SUCCEEDED(rc))
5424 return setError(VBOX_E_OBJECT_IN_USE,
5425 tr("Shared folder named '%s' already exists"),
5426 aName.c_str());
5427
5428 sharedFolder.createObject();
5429 rc = sharedFolder->init(i_getMachine(),
5430 aName,
5431 aHostPath,
5432 !!aWritable,
5433 !!aAutomount,
5434 true /* fFailOnError */);
5435 if (FAILED(rc)) return rc;
5436
5437 i_setModified(IsModified_SharedFolders);
5438 mHWData.backup();
5439 mHWData->mSharedFolders.push_back(sharedFolder);
5440
5441 /* inform the direct session if any */
5442 alock.release();
5443 i_onSharedFolderChange();
5444
5445 return S_OK;
5446}
5447
5448HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5449{
5450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5451
5452 HRESULT rc = i_checkStateDependency(MutableStateDep);
5453 if (FAILED(rc)) return rc;
5454
5455 ComObjPtr<SharedFolder> sharedFolder;
5456 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5457 if (FAILED(rc)) return rc;
5458
5459 i_setModified(IsModified_SharedFolders);
5460 mHWData.backup();
5461 mHWData->mSharedFolders.remove(sharedFolder);
5462
5463 /* inform the direct session if any */
5464 alock.release();
5465 i_onSharedFolderChange();
5466
5467 return S_OK;
5468}
5469
5470HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5471{
5472 /* start with No */
5473 *aCanShow = FALSE;
5474
5475 ComPtr<IInternalSessionControl> directControl;
5476 {
5477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5478
5479 if (mData->mSession.mState != SessionState_Locked)
5480 return setError(VBOX_E_INVALID_VM_STATE,
5481 tr("Machine is not locked for session (session state: %s)"),
5482 Global::stringifySessionState(mData->mSession.mState));
5483
5484 directControl = mData->mSession.mDirectControl;
5485 }
5486
5487 /* ignore calls made after #OnSessionEnd() is called */
5488 if (!directControl)
5489 return S_OK;
5490
5491 LONG64 dummy;
5492 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5493}
5494
5495HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5496{
5497 ComPtr<IInternalSessionControl> directControl;
5498 {
5499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5500
5501 if (mData->mSession.mState != SessionState_Locked)
5502 return setError(E_FAIL,
5503 tr("Machine is not locked for session (session state: %s)"),
5504 Global::stringifySessionState(mData->mSession.mState));
5505
5506 directControl = mData->mSession.mDirectControl;
5507 }
5508
5509 /* ignore calls made after #OnSessionEnd() is called */
5510 if (!directControl)
5511 return S_OK;
5512
5513 BOOL dummy;
5514 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5515}
5516
5517#ifdef VBOX_WITH_GUEST_PROPS
5518/**
5519 * Look up a guest property in VBoxSVC's internal structures.
5520 */
5521HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5522 com::Utf8Str &aValue,
5523 LONG64 *aTimestamp,
5524 com::Utf8Str &aFlags) const
5525{
5526 using namespace guestProp;
5527
5528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5529 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5530
5531 if (it != mHWData->mGuestProperties.end())
5532 {
5533 char szFlags[MAX_FLAGS_LEN + 1];
5534 aValue = it->second.strValue;
5535 *aTimestamp = it->second.mTimestamp;
5536 writeFlags(it->second.mFlags, szFlags);
5537 aFlags = Utf8Str(szFlags);
5538 }
5539
5540 return S_OK;
5541}
5542
5543/**
5544 * Query the VM that a guest property belongs to for the property.
5545 * @returns E_ACCESSDENIED if the VM process is not available or not
5546 * currently handling queries and the lookup should then be done in
5547 * VBoxSVC.
5548 */
5549HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5550 com::Utf8Str &aValue,
5551 LONG64 *aTimestamp,
5552 com::Utf8Str &aFlags) const
5553{
5554 HRESULT rc = S_OK;
5555 BSTR bValue = NULL;
5556 BSTR bFlags = NULL;
5557
5558 ComPtr<IInternalSessionControl> directControl;
5559 directControl = mData->mSession.mDirectControl;
5560
5561 /* fail if we were called after #OnSessionEnd() is called. This is a
5562 * silly race condition. */
5563
5564 /** @todo This code is bothering API clients (like python script clients) with
5565 * the AccessGuestProperty call, creating unncessary IPC. Need to
5566 * have a way of figuring out which kind of direct session it is... */
5567 if (!directControl)
5568 rc = E_ACCESSDENIED;
5569 else
5570 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5571 0 /* accessMode */,
5572 &bValue, aTimestamp, &bFlags);
5573
5574 aValue = bValue;
5575 aFlags = bFlags;
5576
5577 return rc;
5578}
5579#endif // VBOX_WITH_GUEST_PROPS
5580
5581HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5582 com::Utf8Str &aValue,
5583 LONG64 *aTimestamp,
5584 com::Utf8Str &aFlags)
5585{
5586#ifndef VBOX_WITH_GUEST_PROPS
5587 ReturnComNotImplemented();
5588#else // VBOX_WITH_GUEST_PROPS
5589
5590 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5591
5592 if (rc == E_ACCESSDENIED)
5593 /* The VM is not running or the service is not (yet) accessible */
5594 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5595 return rc;
5596#endif // VBOX_WITH_GUEST_PROPS
5597}
5598
5599HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5600{
5601 LONG64 dummyTimestamp;
5602 com::Utf8Str dummyFlags;
5603 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5604 return rc;
5605
5606}
5607HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5608{
5609 com::Utf8Str dummyFlags;
5610 com::Utf8Str dummyValue;
5611 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5612 return rc;
5613}
5614
5615#ifdef VBOX_WITH_GUEST_PROPS
5616/**
5617 * Set a guest property in VBoxSVC's internal structures.
5618 */
5619HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5620 const com::Utf8Str &aFlags, bool fDelete)
5621{
5622 using namespace guestProp;
5623
5624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5625 HRESULT rc = S_OK;
5626
5627 rc = i_checkStateDependency(MutableStateDep);
5628 if (FAILED(rc)) return rc;
5629
5630 try
5631 {
5632 uint32_t fFlags = NILFLAG;
5633 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5634 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5635
5636 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5637 if (it == mHWData->mGuestProperties.end())
5638 {
5639 if (!fDelete)
5640 {
5641 i_setModified(IsModified_MachineData);
5642 mHWData.backupEx();
5643
5644 RTTIMESPEC time;
5645 HWData::GuestProperty prop;
5646 prop.strValue = Bstr(aValue).raw();
5647 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5648 prop.mFlags = fFlags;
5649 mHWData->mGuestProperties[aName] = prop;
5650 }
5651 }
5652 else
5653 {
5654 if (it->second.mFlags & (RDONLYHOST))
5655 {
5656 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5657 }
5658 else
5659 {
5660 i_setModified(IsModified_MachineData);
5661 mHWData.backupEx();
5662
5663 /* The backupEx() operation invalidates our iterator,
5664 * so get a new one. */
5665 it = mHWData->mGuestProperties.find(aName);
5666 Assert(it != mHWData->mGuestProperties.end());
5667
5668 if (!fDelete)
5669 {
5670 RTTIMESPEC time;
5671 it->second.strValue = aValue;
5672 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5673 it->second.mFlags = fFlags;
5674 }
5675 else
5676 mHWData->mGuestProperties.erase(it);
5677 }
5678 }
5679
5680 if ( SUCCEEDED(rc)
5681 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5682 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5683 RTSTR_MAX,
5684 aName.c_str(),
5685 RTSTR_MAX,
5686 NULL)
5687 )
5688 )
5689 {
5690 alock.release();
5691
5692 mParent->i_onGuestPropertyChange(mData->mUuid,
5693 Bstr(aName).raw(),
5694 Bstr(aValue).raw(),
5695 Bstr(aFlags).raw());
5696 }
5697 }
5698 catch (std::bad_alloc &)
5699 {
5700 rc = E_OUTOFMEMORY;
5701 }
5702
5703 return rc;
5704}
5705
5706/**
5707 * Set a property on the VM that that property belongs to.
5708 * @returns E_ACCESSDENIED if the VM process is not available or not
5709 * currently handling queries and the setting should then be done in
5710 * VBoxSVC.
5711 */
5712HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5713 const com::Utf8Str &aFlags, bool fDelete)
5714{
5715 HRESULT rc;
5716
5717 try
5718 {
5719 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5720
5721 BSTR dummy = NULL; /* will not be changed (setter) */
5722 LONG64 dummy64;
5723 if (!directControl)
5724 rc = E_ACCESSDENIED;
5725 else
5726 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5727 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5728 fDelete? 2: 1 /* accessMode */,
5729 &dummy, &dummy64, &dummy);
5730 }
5731 catch (std::bad_alloc &)
5732 {
5733 rc = E_OUTOFMEMORY;
5734 }
5735
5736 return rc;
5737}
5738#endif // VBOX_WITH_GUEST_PROPS
5739
5740HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5741 const com::Utf8Str &aFlags)
5742{
5743#ifndef VBOX_WITH_GUEST_PROPS
5744 ReturnComNotImplemented();
5745#else // VBOX_WITH_GUEST_PROPS
5746 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5747 if (rc == E_ACCESSDENIED)
5748 /* The VM is not running or the service is not (yet) accessible */
5749 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5750 return rc;
5751#endif // VBOX_WITH_GUEST_PROPS
5752}
5753
5754HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5755{
5756 return setGuestProperty(aProperty, aValue, "");
5757}
5758
5759HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5760{
5761#ifndef VBOX_WITH_GUEST_PROPS
5762 ReturnComNotImplemented();
5763#else // VBOX_WITH_GUEST_PROPS
5764 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5765 if (rc == E_ACCESSDENIED)
5766 /* The VM is not running or the service is not (yet) accessible */
5767 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5768 return rc;
5769#endif // VBOX_WITH_GUEST_PROPS
5770}
5771
5772#ifdef VBOX_WITH_GUEST_PROPS
5773/**
5774 * Enumerate the guest properties in VBoxSVC's internal structures.
5775 */
5776HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5777 std::vector<com::Utf8Str> &aNames,
5778 std::vector<com::Utf8Str> &aValues,
5779 std::vector<LONG64> &aTimestamps,
5780 std::vector<com::Utf8Str> &aFlags)
5781{
5782 using namespace guestProp;
5783
5784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5785 Utf8Str strPatterns(aPatterns);
5786
5787 HWData::GuestPropertyMap propMap;
5788
5789 /*
5790 * Look for matching patterns and build up a list.
5791 */
5792 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5793 while (it != mHWData->mGuestProperties.end())
5794 {
5795 if ( strPatterns.isEmpty()
5796 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5797 RTSTR_MAX,
5798 it->first.c_str(),
5799 RTSTR_MAX,
5800 NULL)
5801 )
5802 propMap.insert(*it);
5803 it++;
5804 }
5805
5806 alock.release();
5807
5808 /*
5809 * And build up the arrays for returning the property information.
5810 */
5811 size_t cEntries = propMap.size();
5812
5813 aNames.resize(cEntries);
5814 aValues.resize(cEntries);
5815 aTimestamps.resize(cEntries);
5816 aFlags.resize(cEntries);
5817
5818 char szFlags[MAX_FLAGS_LEN + 1];
5819 size_t i= 0;
5820 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5821 {
5822 aNames[i] = it->first;
5823 aValues[i] = it->second.strValue;
5824 aTimestamps[i] = it->second.mTimestamp;
5825 writeFlags(it->second.mFlags, szFlags);
5826 aFlags[i] = Utf8Str(szFlags);
5827 }
5828
5829 return S_OK;
5830}
5831
5832/**
5833 * Enumerate the properties managed by a VM.
5834 * @returns E_ACCESSDENIED if the VM process is not available or not
5835 * currently handling queries and the setting should then be done in
5836 * VBoxSVC.
5837 */
5838HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5839 std::vector<com::Utf8Str> &aNames,
5840 std::vector<com::Utf8Str> &aValues,
5841 std::vector<LONG64> &aTimestamps,
5842 std::vector<com::Utf8Str> &aFlags)
5843{
5844 HRESULT rc;
5845 ComPtr<IInternalSessionControl> directControl;
5846 directControl = mData->mSession.mDirectControl;
5847
5848
5849 com::SafeArray<BSTR> bNames;
5850 com::SafeArray<BSTR> bValues;
5851 com::SafeArray<LONG64> bTimestamps;
5852 com::SafeArray<BSTR> bFlags;
5853
5854 if (!directControl)
5855 rc = E_ACCESSDENIED;
5856 else
5857 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5858 ComSafeArrayAsOutParam(bNames),
5859 ComSafeArrayAsOutParam(bValues),
5860 ComSafeArrayAsOutParam(bTimestamps),
5861 ComSafeArrayAsOutParam(bFlags));
5862 size_t i;
5863 aNames.resize(bNames.size());
5864 for (i = 0; i < bNames.size(); ++i)
5865 aNames[i] = Utf8Str(bNames[i]);
5866 aValues.resize(bValues.size());
5867 for (i = 0; i < bValues.size(); ++i)
5868 aValues[i] = Utf8Str(bValues[i]);
5869 aTimestamps.resize(bTimestamps.size());
5870 for (i = 0; i < bTimestamps.size(); ++i)
5871 aTimestamps[i] = bTimestamps[i];
5872 aFlags.resize(bFlags.size());
5873 for (i = 0; i < bFlags.size(); ++i)
5874 aFlags[i] = Utf8Str(bFlags[i]);
5875
5876 return rc;
5877}
5878#endif // VBOX_WITH_GUEST_PROPS
5879HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5880 std::vector<com::Utf8Str> &aNames,
5881 std::vector<com::Utf8Str> &aValues,
5882 std::vector<LONG64> &aTimestamps,
5883 std::vector<com::Utf8Str> &aFlags)
5884{
5885#ifndef VBOX_WITH_GUEST_PROPS
5886 ReturnComNotImplemented();
5887#else // VBOX_WITH_GUEST_PROPS
5888
5889 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5890
5891 if (rc == E_ACCESSDENIED)
5892 /* The VM is not running or the service is not (yet) accessible */
5893 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5894 return rc;
5895#endif // VBOX_WITH_GUEST_PROPS
5896}
5897
5898HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5899 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5900{
5901 MediaData::AttachmentList atts;
5902
5903 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5904 if (FAILED(rc)) return rc;
5905
5906 size_t i = 0;
5907 aMediumAttachments.resize(atts.size());
5908 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5909 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5910
5911 return S_OK;
5912}
5913
5914HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5915 LONG aControllerPort,
5916 LONG aDevice,
5917 ComPtr<IMediumAttachment> &aAttachment)
5918{
5919 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5920 aName.c_str(), aControllerPort, aDevice));
5921
5922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5923
5924 aAttachment = NULL;
5925
5926 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5927 Bstr(aName).raw(),
5928 aControllerPort,
5929 aDevice);
5930 if (pAttach.isNull())
5931 return setError(VBOX_E_OBJECT_NOT_FOUND,
5932 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5933 aDevice, aControllerPort, aName.c_str());
5934
5935 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5936
5937 return S_OK;
5938}
5939
5940
5941HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5942 StorageBus_T aConnectionType,
5943 ComPtr<IStorageController> &aController)
5944{
5945 if ( (aConnectionType <= StorageBus_Null)
5946 || (aConnectionType > StorageBus_USB))
5947 return setError(E_INVALIDARG,
5948 tr("Invalid connection type: %d"),
5949 aConnectionType);
5950
5951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5952
5953 HRESULT rc = i_checkStateDependency(MutableStateDep);
5954 if (FAILED(rc)) return rc;
5955
5956 /* try to find one with the name first. */
5957 ComObjPtr<StorageController> ctrl;
5958
5959 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5960 if (SUCCEEDED(rc))
5961 return setError(VBOX_E_OBJECT_IN_USE,
5962 tr("Storage controller named '%s' already exists"),
5963 aName.c_str());
5964
5965 ctrl.createObject();
5966
5967 /* get a new instance number for the storage controller */
5968 ULONG ulInstance = 0;
5969 bool fBootable = true;
5970 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5971 it != mStorageControllers->end();
5972 ++it)
5973 {
5974 if ((*it)->i_getStorageBus() == aConnectionType)
5975 {
5976 ULONG ulCurInst = (*it)->i_getInstance();
5977
5978 if (ulCurInst >= ulInstance)
5979 ulInstance = ulCurInst + 1;
5980
5981 /* Only one controller of each type can be marked as bootable. */
5982 if ((*it)->i_getBootable())
5983 fBootable = false;
5984 }
5985 }
5986
5987 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5988 if (FAILED(rc)) return rc;
5989
5990 i_setModified(IsModified_Storage);
5991 mStorageControllers.backup();
5992 mStorageControllers->push_back(ctrl);
5993
5994 ctrl.queryInterfaceTo(aController.asOutParam());
5995
5996 /* inform the direct session if any */
5997 alock.release();
5998 i_onStorageControllerChange();
5999
6000 return S_OK;
6001}
6002
6003HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6004 ComPtr<IStorageController> &aStorageController)
6005{
6006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6007
6008 ComObjPtr<StorageController> ctrl;
6009
6010 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6011 if (SUCCEEDED(rc))
6012 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6013
6014 return rc;
6015}
6016
6017HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6018 ComPtr<IStorageController> &aStorageController)
6019{
6020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6021
6022 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6023 it != mStorageControllers->end();
6024 ++it)
6025 {
6026 if ((*it)->i_getInstance() == aInstance)
6027 {
6028 (*it).queryInterfaceTo(aStorageController.asOutParam());
6029 return S_OK;
6030 }
6031 }
6032
6033 return setError(VBOX_E_OBJECT_NOT_FOUND,
6034 tr("Could not find a storage controller with instance number '%lu'"),
6035 aInstance);
6036}
6037
6038HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6039{
6040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6041
6042 HRESULT rc = i_checkStateDependency(MutableStateDep);
6043 if (FAILED(rc)) return rc;
6044
6045 ComObjPtr<StorageController> ctrl;
6046
6047 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6048 if (SUCCEEDED(rc))
6049 {
6050 /* Ensure that only one controller of each type is marked as bootable. */
6051 if (aBootable == TRUE)
6052 {
6053 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6054 it != mStorageControllers->end();
6055 ++it)
6056 {
6057 ComObjPtr<StorageController> aCtrl = (*it);
6058
6059 if ( (aCtrl->i_getName() != aName)
6060 && aCtrl->i_getBootable() == TRUE
6061 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6062 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6063 {
6064 aCtrl->i_setBootable(FALSE);
6065 break;
6066 }
6067 }
6068 }
6069
6070 if (SUCCEEDED(rc))
6071 {
6072 ctrl->i_setBootable(aBootable);
6073 i_setModified(IsModified_Storage);
6074 }
6075 }
6076
6077 if (SUCCEEDED(rc))
6078 {
6079 /* inform the direct session if any */
6080 alock.release();
6081 i_onStorageControllerChange();
6082 }
6083
6084 return rc;
6085}
6086
6087HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6088{
6089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6090
6091 HRESULT rc = i_checkStateDependency(MutableStateDep);
6092 if (FAILED(rc)) return rc;
6093
6094 ComObjPtr<StorageController> ctrl;
6095 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6096 if (FAILED(rc)) return rc;
6097
6098 {
6099 /* find all attached devices to the appropriate storage controller and detach them all */
6100 // make a temporary list because detachDevice invalidates iterators into
6101 // mMediaData->mAttachments
6102 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6103
6104 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6105 it != llAttachments2.end();
6106 ++it)
6107 {
6108 MediumAttachment *pAttachTemp = *it;
6109
6110 AutoCaller localAutoCaller(pAttachTemp);
6111 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6112
6113 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6114
6115 if (pAttachTemp->i_getControllerName() == aName)
6116 {
6117 rc = i_detachDevice(pAttachTemp, alock, NULL);
6118 if (FAILED(rc)) return rc;
6119 }
6120 }
6121 }
6122
6123 /* We can remove it now. */
6124 i_setModified(IsModified_Storage);
6125 mStorageControllers.backup();
6126
6127 ctrl->i_unshare();
6128
6129 mStorageControllers->remove(ctrl);
6130
6131 /* inform the direct session if any */
6132 alock.release();
6133 i_onStorageControllerChange();
6134
6135 return S_OK;
6136}
6137
6138HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6139 ComPtr<IUSBController> &aController)
6140{
6141 if ( (aType <= USBControllerType_Null)
6142 || (aType >= USBControllerType_Last))
6143 return setError(E_INVALIDARG,
6144 tr("Invalid USB controller type: %d"),
6145 aType);
6146
6147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6148
6149 HRESULT rc = i_checkStateDependency(MutableStateDep);
6150 if (FAILED(rc)) return rc;
6151
6152 /* try to find one with the same type first. */
6153 ComObjPtr<USBController> ctrl;
6154
6155 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6156 if (SUCCEEDED(rc))
6157 return setError(VBOX_E_OBJECT_IN_USE,
6158 tr("USB controller named '%s' already exists"),
6159 aName.c_str());
6160
6161 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6162 ULONG maxInstances;
6163 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6164 if (FAILED(rc))
6165 return rc;
6166
6167 ULONG cInstances = i_getUSBControllerCountByType(aType);
6168 if (cInstances >= maxInstances)
6169 return setError(E_INVALIDARG,
6170 tr("Too many USB controllers of this type"));
6171
6172 ctrl.createObject();
6173
6174 rc = ctrl->init(this, aName, aType);
6175 if (FAILED(rc)) return rc;
6176
6177 i_setModified(IsModified_USB);
6178 mUSBControllers.backup();
6179 mUSBControllers->push_back(ctrl);
6180
6181 ctrl.queryInterfaceTo(aController.asOutParam());
6182
6183 /* inform the direct session if any */
6184 alock.release();
6185 i_onUSBControllerChange();
6186
6187 return S_OK;
6188}
6189
6190HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6191{
6192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6193
6194 ComObjPtr<USBController> ctrl;
6195
6196 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6197 if (SUCCEEDED(rc))
6198 ctrl.queryInterfaceTo(aController.asOutParam());
6199
6200 return rc;
6201}
6202
6203HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6204 ULONG *aControllers)
6205{
6206 if ( (aType <= USBControllerType_Null)
6207 || (aType >= USBControllerType_Last))
6208 return setError(E_INVALIDARG,
6209 tr("Invalid USB controller type: %d"),
6210 aType);
6211
6212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6213
6214 ComObjPtr<USBController> ctrl;
6215
6216 *aControllers = i_getUSBControllerCountByType(aType);
6217
6218 return S_OK;
6219}
6220
6221HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6222{
6223
6224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 HRESULT rc = i_checkStateDependency(MutableStateDep);
6227 if (FAILED(rc)) return rc;
6228
6229 ComObjPtr<USBController> ctrl;
6230 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6231 if (FAILED(rc)) return rc;
6232
6233 i_setModified(IsModified_USB);
6234 mUSBControllers.backup();
6235
6236 ctrl->i_unshare();
6237
6238 mUSBControllers->remove(ctrl);
6239
6240 /* inform the direct session if any */
6241 alock.release();
6242 i_onUSBControllerChange();
6243
6244 return S_OK;
6245}
6246
6247HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6248 ULONG *aOriginX,
6249 ULONG *aOriginY,
6250 ULONG *aWidth,
6251 ULONG *aHeight,
6252 BOOL *aEnabled)
6253{
6254 uint32_t u32OriginX= 0;
6255 uint32_t u32OriginY= 0;
6256 uint32_t u32Width = 0;
6257 uint32_t u32Height = 0;
6258 uint16_t u16Flags = 0;
6259
6260 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6261 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6262 if (RT_FAILURE(vrc))
6263 {
6264#ifdef RT_OS_WINDOWS
6265 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6266 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6267 * So just assign fEnable to TRUE again.
6268 * The right fix would be to change GUI API wrappers to make sure that parameters
6269 * are changed only if API succeeds.
6270 */
6271 *aEnabled = TRUE;
6272#endif
6273 return setError(VBOX_E_IPRT_ERROR,
6274 tr("Saved guest size is not available (%Rrc)"),
6275 vrc);
6276 }
6277
6278 *aOriginX = u32OriginX;
6279 *aOriginY = u32OriginY;
6280 *aWidth = u32Width;
6281 *aHeight = u32Height;
6282 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6283
6284 return S_OK;
6285}
6286
6287HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6288{
6289 if (aScreenId != 0)
6290 return E_NOTIMPL;
6291
6292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6293
6294 uint8_t *pu8Data = NULL;
6295 uint32_t cbData = 0;
6296 uint32_t u32Width = 0;
6297 uint32_t u32Height = 0;
6298
6299 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6300
6301 if (RT_FAILURE(vrc))
6302 return setError(VBOX_E_IPRT_ERROR,
6303 tr("Saved screenshot data is not available (%Rrc)"),
6304 vrc);
6305
6306 *aSize = cbData;
6307 *aWidth = u32Width;
6308 *aHeight = u32Height;
6309
6310 freeSavedDisplayScreenshot(pu8Data);
6311
6312 return S_OK;
6313}
6314
6315HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6316 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6317{
6318 if (aScreenId != 0)
6319 return E_NOTIMPL;
6320
6321 if ( aBitmapFormat != BitmapFormat_BGR0
6322 && aBitmapFormat != BitmapFormat_BGRA
6323 && aBitmapFormat != BitmapFormat_RGBA
6324 && aBitmapFormat != BitmapFormat_PNG)
6325 return setError(E_NOTIMPL,
6326 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6327
6328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6329
6330 uint8_t *pu8Data = NULL;
6331 uint32_t cbData = 0;
6332 uint32_t u32Width = 0;
6333 uint32_t u32Height = 0;
6334
6335 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6336
6337 if (RT_FAILURE(vrc))
6338 return setError(VBOX_E_IPRT_ERROR,
6339 tr("Saved thumbnail data is not available (%Rrc)"),
6340 vrc);
6341
6342 HRESULT hr = S_OK;
6343
6344 *aWidth = u32Width;
6345 *aHeight = u32Height;
6346
6347 if (cbData > 0)
6348 {
6349 /* Convert pixels to the format expected by the API caller. */
6350 if (aBitmapFormat == BitmapFormat_BGR0)
6351 {
6352 /* [0] B, [1] G, [2] R, [3] 0. */
6353 aData.resize(cbData);
6354 memcpy(&aData.front(), pu8Data, cbData);
6355 }
6356 else if (aBitmapFormat == BitmapFormat_BGRA)
6357 {
6358 /* [0] B, [1] G, [2] R, [3] A. */
6359 aData.resize(cbData);
6360 for (uint32_t i = 0; i < cbData; i += 4)
6361 {
6362 aData[i] = pu8Data[i];
6363 aData[i + 1] = pu8Data[i + 1];
6364 aData[i + 2] = pu8Data[i + 2];
6365 aData[i + 3] = 0xff;
6366 }
6367 }
6368 else if (aBitmapFormat == BitmapFormat_RGBA)
6369 {
6370 /* [0] R, [1] G, [2] B, [3] A. */
6371 aData.resize(cbData);
6372 for (uint32_t i = 0; i < cbData; i += 4)
6373 {
6374 aData[i] = pu8Data[i + 2];
6375 aData[i + 1] = pu8Data[i + 1];
6376 aData[i + 2] = pu8Data[i];
6377 aData[i + 3] = 0xff;
6378 }
6379 }
6380 else if (aBitmapFormat == BitmapFormat_PNG)
6381 {
6382 uint8_t *pu8PNG = NULL;
6383 uint32_t cbPNG = 0;
6384 uint32_t cxPNG = 0;
6385 uint32_t cyPNG = 0;
6386
6387 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6388
6389 if (RT_SUCCESS(vrc))
6390 {
6391 aData.resize(cbPNG);
6392 if (cbPNG)
6393 memcpy(&aData.front(), pu8PNG, cbPNG);
6394 }
6395 else
6396 hr = setError(VBOX_E_IPRT_ERROR,
6397 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6398 vrc);
6399
6400 RTMemFree(pu8PNG);
6401 }
6402 }
6403
6404 freeSavedDisplayScreenshot(pu8Data);
6405
6406 return hr;
6407}
6408
6409HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6410{
6411 if (aScreenId != 0)
6412 return E_NOTIMPL;
6413
6414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 uint8_t *pu8Data = NULL;
6417 uint32_t cbData = 0;
6418 uint32_t u32Width = 0;
6419 uint32_t u32Height = 0;
6420
6421 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6422
6423 if (RT_FAILURE(vrc))
6424 return setError(VBOX_E_IPRT_ERROR,
6425 tr("Saved screenshot data is not available (%Rrc)"),
6426 vrc);
6427
6428 *aSize = cbData;
6429 *aWidth = u32Width;
6430 *aHeight = u32Height;
6431
6432 freeSavedDisplayScreenshot(pu8Data);
6433
6434 return S_OK;
6435}
6436
6437HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6438{
6439 if (aScreenId != 0)
6440 return E_NOTIMPL;
6441
6442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6443
6444 uint8_t *pu8Data = NULL;
6445 uint32_t cbData = 0;
6446 uint32_t u32Width = 0;
6447 uint32_t u32Height = 0;
6448
6449 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6450
6451 if (RT_FAILURE(vrc))
6452 return setError(VBOX_E_IPRT_ERROR,
6453 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6454 vrc);
6455
6456 *aWidth = u32Width;
6457 *aHeight = u32Height;
6458
6459 aData.resize(cbData);
6460 if (cbData)
6461 memcpy(&aData.front(), pu8Data, cbData);
6462
6463 freeSavedDisplayScreenshot(pu8Data);
6464
6465 return S_OK;
6466}
6467
6468HRESULT Machine::hotPlugCPU(ULONG aCpu)
6469{
6470 HRESULT rc = S_OK;
6471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6472
6473 if (!mHWData->mCPUHotPlugEnabled)
6474 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6475
6476 if (aCpu >= mHWData->mCPUCount)
6477 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6478
6479 if (mHWData->mCPUAttached[aCpu])
6480 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6481
6482 alock.release();
6483 rc = i_onCPUChange(aCpu, false);
6484 alock.acquire();
6485 if (FAILED(rc)) return rc;
6486
6487 i_setModified(IsModified_MachineData);
6488 mHWData.backup();
6489 mHWData->mCPUAttached[aCpu] = true;
6490
6491 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6492 if (Global::IsOnline(mData->mMachineState))
6493 i_saveSettings(NULL);
6494
6495 return S_OK;
6496}
6497
6498HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6499{
6500 HRESULT rc = S_OK;
6501
6502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6503
6504 if (!mHWData->mCPUHotPlugEnabled)
6505 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6506
6507 if (aCpu >= SchemaDefs::MaxCPUCount)
6508 return setError(E_INVALIDARG,
6509 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6510 SchemaDefs::MaxCPUCount);
6511
6512 if (!mHWData->mCPUAttached[aCpu])
6513 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6514
6515 /* CPU 0 can't be detached */
6516 if (aCpu == 0)
6517 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6518
6519 alock.release();
6520 rc = i_onCPUChange(aCpu, true);
6521 alock.acquire();
6522 if (FAILED(rc)) return rc;
6523
6524 i_setModified(IsModified_MachineData);
6525 mHWData.backup();
6526 mHWData->mCPUAttached[aCpu] = false;
6527
6528 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6529 if (Global::IsOnline(mData->mMachineState))
6530 i_saveSettings(NULL);
6531
6532 return S_OK;
6533}
6534
6535HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6536{
6537 *aAttached = false;
6538
6539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6540
6541 /* If hotplug is enabled the CPU is always enabled. */
6542 if (!mHWData->mCPUHotPlugEnabled)
6543 {
6544 if (aCpu < mHWData->mCPUCount)
6545 *aAttached = true;
6546 }
6547 else
6548 {
6549 if (aCpu < SchemaDefs::MaxCPUCount)
6550 *aAttached = mHWData->mCPUAttached[aCpu];
6551 }
6552
6553 return S_OK;
6554}
6555
6556HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6557{
6558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6559
6560 Utf8Str log = i_queryLogFilename(aIdx);
6561 if (!RTFileExists(log.c_str()))
6562 log.setNull();
6563 aFilename = log;
6564
6565 return S_OK;
6566}
6567
6568HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6569{
6570 if (aSize < 0)
6571 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6572
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 HRESULT rc = S_OK;
6576 Utf8Str log = i_queryLogFilename(aIdx);
6577
6578 /* do not unnecessarily hold the lock while doing something which does
6579 * not need the lock and potentially takes a long time. */
6580 alock.release();
6581
6582 /* Limit the chunk size to 32K for now, as that gives better performance
6583 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6584 * One byte expands to approx. 25 bytes of breathtaking XML. */
6585 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6586 aData.resize(cbData);
6587
6588 RTFILE LogFile;
6589 int vrc = RTFileOpen(&LogFile, log.c_str(),
6590 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6591 if (RT_SUCCESS(vrc))
6592 {
6593 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6594 if (RT_SUCCESS(vrc))
6595 aData.resize(cbData);
6596 else
6597 rc = setError(VBOX_E_IPRT_ERROR,
6598 tr("Could not read log file '%s' (%Rrc)"),
6599 log.c_str(), vrc);
6600 RTFileClose(LogFile);
6601 }
6602 else
6603 rc = setError(VBOX_E_IPRT_ERROR,
6604 tr("Could not open log file '%s' (%Rrc)"),
6605 log.c_str(), vrc);
6606
6607 if (FAILED(rc))
6608 aData.resize(0);
6609
6610 return rc;
6611}
6612
6613
6614/**
6615 * Currently this method doesn't attach device to the running VM,
6616 * just makes sure it's plugged on next VM start.
6617 */
6618HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6619{
6620 // lock scope
6621 {
6622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6623
6624 HRESULT rc = i_checkStateDependency(MutableStateDep);
6625 if (FAILED(rc)) return rc;
6626
6627 ChipsetType_T aChipset = ChipsetType_PIIX3;
6628 COMGETTER(ChipsetType)(&aChipset);
6629
6630 if (aChipset != ChipsetType_ICH9)
6631 {
6632 return setError(E_INVALIDARG,
6633 tr("Host PCI attachment only supported with ICH9 chipset"));
6634 }
6635
6636 // check if device with this host PCI address already attached
6637 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6638 it != mHWData->mPCIDeviceAssignments.end();
6639 ++it)
6640 {
6641 LONG iHostAddress = -1;
6642 ComPtr<PCIDeviceAttachment> pAttach;
6643 pAttach = *it;
6644 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6645 if (iHostAddress == aHostAddress)
6646 return setError(E_INVALIDARG,
6647 tr("Device with host PCI address already attached to this VM"));
6648 }
6649
6650 ComObjPtr<PCIDeviceAttachment> pda;
6651 char name[32];
6652
6653 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6654 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6655 Bstr bname(name);
6656 pda.createObject();
6657 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6658 i_setModified(IsModified_MachineData);
6659 mHWData.backup();
6660 mHWData->mPCIDeviceAssignments.push_back(pda);
6661 }
6662
6663 return S_OK;
6664}
6665
6666/**
6667 * Currently this method doesn't detach device from the running VM,
6668 * just makes sure it's not plugged on next VM start.
6669 */
6670HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6671{
6672 ComObjPtr<PCIDeviceAttachment> pAttach;
6673 bool fRemoved = false;
6674 HRESULT rc;
6675
6676 // lock scope
6677 {
6678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6679
6680 rc = i_checkStateDependency(MutableStateDep);
6681 if (FAILED(rc)) return rc;
6682
6683 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6684 it != mHWData->mPCIDeviceAssignments.end();
6685 ++it)
6686 {
6687 LONG iHostAddress = -1;
6688 pAttach = *it;
6689 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6690 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6691 {
6692 i_setModified(IsModified_MachineData);
6693 mHWData.backup();
6694 mHWData->mPCIDeviceAssignments.remove(pAttach);
6695 fRemoved = true;
6696 break;
6697 }
6698 }
6699 }
6700
6701
6702 /* Fire event outside of the lock */
6703 if (fRemoved)
6704 {
6705 Assert(!pAttach.isNull());
6706 ComPtr<IEventSource> es;
6707 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6708 Assert(SUCCEEDED(rc));
6709 Bstr mid;
6710 rc = this->COMGETTER(Id)(mid.asOutParam());
6711 Assert(SUCCEEDED(rc));
6712 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6713 }
6714
6715 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6716 tr("No host PCI device %08x attached"),
6717 aHostAddress
6718 );
6719}
6720
6721HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6722{
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6726
6727 size_t i = 0;
6728 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6729 it != mHWData->mPCIDeviceAssignments.end();
6730 ++i, ++it)
6731 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6732
6733 return S_OK;
6734}
6735
6736HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6737{
6738 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6739
6740 return S_OK;
6741}
6742
6743HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6744{
6745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6746
6747 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6748
6749 return S_OK;
6750}
6751
6752HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6753{
6754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6755 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6756 if (SUCCEEDED(hrc))
6757 {
6758 hrc = mHWData.backupEx();
6759 if (SUCCEEDED(hrc))
6760 {
6761 i_setModified(IsModified_MachineData);
6762 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6763 }
6764 }
6765 return hrc;
6766}
6767
6768HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6769{
6770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6771 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6772 return S_OK;
6773}
6774
6775HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6776{
6777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6778 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6779 if (SUCCEEDED(hrc))
6780 {
6781 hrc = mHWData.backupEx();
6782 if (SUCCEEDED(hrc))
6783 {
6784 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6785 if (SUCCEEDED(hrc))
6786 i_setModified(IsModified_MachineData);
6787 }
6788 }
6789 return hrc;
6790}
6791
6792HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6793{
6794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6795
6796 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6797
6798 return S_OK;
6799}
6800
6801HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6802{
6803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6804 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6805 if (SUCCEEDED(hrc))
6806 {
6807 hrc = mHWData.backupEx();
6808 if (SUCCEEDED(hrc))
6809 {
6810 i_setModified(IsModified_MachineData);
6811 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6812 }
6813 }
6814 return hrc;
6815}
6816
6817HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6818{
6819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6820
6821 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6827{
6828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6831 if ( SUCCEEDED(hrc)
6832 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6833 {
6834 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6835 int vrc;
6836
6837 if (aAutostartEnabled)
6838 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6839 else
6840 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6841
6842 if (RT_SUCCESS(vrc))
6843 {
6844 hrc = mHWData.backupEx();
6845 if (SUCCEEDED(hrc))
6846 {
6847 i_setModified(IsModified_MachineData);
6848 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6849 }
6850 }
6851 else if (vrc == VERR_NOT_SUPPORTED)
6852 hrc = setError(VBOX_E_NOT_SUPPORTED,
6853 tr("The VM autostart feature is not supported on this platform"));
6854 else if (vrc == VERR_PATH_NOT_FOUND)
6855 hrc = setError(E_FAIL,
6856 tr("The path to the autostart database is not set"));
6857 else
6858 hrc = setError(E_UNEXPECTED,
6859 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6860 aAutostartEnabled ? "Adding" : "Removing",
6861 mUserData->s.strName.c_str(), vrc);
6862 }
6863 return hrc;
6864}
6865
6866HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6867{
6868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6869
6870 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6871
6872 return S_OK;
6873}
6874
6875HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6876{
6877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6878 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6879 if (SUCCEEDED(hrc))
6880 {
6881 hrc = mHWData.backupEx();
6882 if (SUCCEEDED(hrc))
6883 {
6884 i_setModified(IsModified_MachineData);
6885 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6886 }
6887 }
6888 return hrc;
6889}
6890
6891HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6892{
6893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6894
6895 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6896
6897 return S_OK;
6898}
6899
6900HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6901{
6902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6903 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6904 if ( SUCCEEDED(hrc)
6905 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6906 {
6907 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6908 int vrc;
6909
6910 if (aAutostopType != AutostopType_Disabled)
6911 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6912 else
6913 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6914
6915 if (RT_SUCCESS(vrc))
6916 {
6917 hrc = mHWData.backupEx();
6918 if (SUCCEEDED(hrc))
6919 {
6920 i_setModified(IsModified_MachineData);
6921 mHWData->mAutostart.enmAutostopType = aAutostopType;
6922 }
6923 }
6924 else if (vrc == VERR_NOT_SUPPORTED)
6925 hrc = setError(VBOX_E_NOT_SUPPORTED,
6926 tr("The VM autostop feature is not supported on this platform"));
6927 else if (vrc == VERR_PATH_NOT_FOUND)
6928 hrc = setError(E_FAIL,
6929 tr("The path to the autostart database is not set"));
6930 else
6931 hrc = setError(E_UNEXPECTED,
6932 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6933 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6934 mUserData->s.strName.c_str(), vrc);
6935 }
6936 return hrc;
6937}
6938
6939HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6940{
6941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6942
6943 aDefaultFrontend = mHWData->mDefaultFrontend;
6944
6945 return S_OK;
6946}
6947
6948HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6949{
6950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6951 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6952 if (SUCCEEDED(hrc))
6953 {
6954 hrc = mHWData.backupEx();
6955 if (SUCCEEDED(hrc))
6956 {
6957 i_setModified(IsModified_MachineData);
6958 mHWData->mDefaultFrontend = aDefaultFrontend;
6959 }
6960 }
6961 return hrc;
6962}
6963
6964HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6965{
6966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6967 size_t cbIcon = mUserData->mIcon.size();
6968 aIcon.resize(cbIcon);
6969 if (cbIcon)
6970 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6971 return S_OK;
6972}
6973
6974HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6975{
6976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6977 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6978 if (SUCCEEDED(hrc))
6979 {
6980 i_setModified(IsModified_MachineData);
6981 mUserData.backup();
6982 size_t cbIcon = aIcon.size();
6983 mUserData->mIcon.resize(cbIcon);
6984 if (cbIcon)
6985 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6986 }
6987 return hrc;
6988}
6989
6990HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6991{
6992#ifdef VBOX_WITH_USB
6993 *aUSBProxyAvailable = true;
6994#else
6995 *aUSBProxyAvailable = false;
6996#endif
6997 return S_OK;
6998}
6999
7000HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7001 ComPtr<IProgress> &aProgress)
7002{
7003 ComObjPtr<Progress> pP;
7004 Progress *ppP = pP;
7005 IProgress *iP = static_cast<IProgress *>(ppP);
7006 IProgress **pProgress = &iP;
7007
7008 IMachine *pTarget = aTarget;
7009
7010 /* Convert the options. */
7011 RTCList<CloneOptions_T> optList;
7012 if (aOptions.size())
7013 for (size_t i = 0; i < aOptions.size(); ++i)
7014 optList.append(aOptions[i]);
7015
7016 if (optList.contains(CloneOptions_Link))
7017 {
7018 if (!i_isSnapshotMachine())
7019 return setError(E_INVALIDARG,
7020 tr("Linked clone can only be created from a snapshot"));
7021 if (aMode != CloneMode_MachineState)
7022 return setError(E_INVALIDARG,
7023 tr("Linked clone can only be created for a single machine state"));
7024 }
7025 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7026
7027 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7028
7029 HRESULT rc = pWorker->start(pProgress);
7030
7031 pP = static_cast<Progress *>(*pProgress);
7032 pP.queryInterfaceTo(aProgress.asOutParam());
7033
7034 return rc;
7035
7036}
7037
7038// public methods for internal purposes
7039/////////////////////////////////////////////////////////////////////////////
7040
7041/**
7042 * Adds the given IsModified_* flag to the dirty flags of the machine.
7043 * This must be called either during i_loadSettings or under the machine write lock.
7044 * @param fl
7045 */
7046void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7047{
7048 mData->flModifications |= fl;
7049 if (fAllowStateModification && i_isStateModificationAllowed())
7050 mData->mCurrentStateModified = true;
7051}
7052
7053/**
7054 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7055 * care of the write locking.
7056 *
7057 * @param fModifications The flag to add.
7058 */
7059void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7060{
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062 i_setModified(fModification, fAllowStateModification);
7063}
7064
7065/**
7066 * Saves the registry entry of this machine to the given configuration node.
7067 *
7068 * @param aEntryNode Node to save the registry entry to.
7069 *
7070 * @note locks this object for reading.
7071 */
7072HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7073{
7074 AutoLimitedCaller autoCaller(this);
7075 AssertComRCReturnRC(autoCaller.rc());
7076
7077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7078
7079 data.uuid = mData->mUuid;
7080 data.strSettingsFile = mData->m_strConfigFile;
7081
7082 return S_OK;
7083}
7084
7085/**
7086 * Calculates the absolute path of the given path taking the directory of the
7087 * machine settings file as the current directory.
7088 *
7089 * @param aPath Path to calculate the absolute path for.
7090 * @param aResult Where to put the result (used only on success, can be the
7091 * same Utf8Str instance as passed in @a aPath).
7092 * @return IPRT result.
7093 *
7094 * @note Locks this object for reading.
7095 */
7096int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7097{
7098 AutoCaller autoCaller(this);
7099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7100
7101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7102
7103 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7104
7105 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7106
7107 strSettingsDir.stripFilename();
7108 char folder[RTPATH_MAX];
7109 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7110 if (RT_SUCCESS(vrc))
7111 aResult = folder;
7112
7113 return vrc;
7114}
7115
7116/**
7117 * Copies strSource to strTarget, making it relative to the machine folder
7118 * if it is a subdirectory thereof, or simply copying it otherwise.
7119 *
7120 * @param strSource Path to evaluate and copy.
7121 * @param strTarget Buffer to receive target path.
7122 *
7123 * @note Locks this object for reading.
7124 */
7125void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7126 Utf8Str &strTarget)
7127{
7128 AutoCaller autoCaller(this);
7129 AssertComRCReturn(autoCaller.rc(), (void)0);
7130
7131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7132
7133 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7134 // use strTarget as a temporary buffer to hold the machine settings dir
7135 strTarget = mData->m_strConfigFileFull;
7136 strTarget.stripFilename();
7137 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7138 {
7139 // is relative: then append what's left
7140 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7141 // for empty paths (only possible for subdirs) use "." to avoid
7142 // triggering default settings for not present config attributes.
7143 if (strTarget.isEmpty())
7144 strTarget = ".";
7145 }
7146 else
7147 // is not relative: then overwrite
7148 strTarget = strSource;
7149}
7150
7151/**
7152 * Returns the full path to the machine's log folder in the
7153 * \a aLogFolder argument.
7154 */
7155void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7156{
7157 AutoCaller autoCaller(this);
7158 AssertComRCReturnVoid(autoCaller.rc());
7159
7160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7161
7162 char szTmp[RTPATH_MAX];
7163 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7164 if (RT_SUCCESS(vrc))
7165 {
7166 if (szTmp[0] && !mUserData.isNull())
7167 {
7168 char szTmp2[RTPATH_MAX];
7169 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7170 if (RT_SUCCESS(vrc))
7171 aLogFolder = BstrFmt("%s%c%s",
7172 szTmp2,
7173 RTPATH_DELIMITER,
7174 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7175 }
7176 else
7177 vrc = VERR_PATH_IS_RELATIVE;
7178 }
7179
7180 if (RT_FAILURE(vrc))
7181 {
7182 // fallback if VBOX_USER_LOGHOME is not set or invalid
7183 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7184 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7185 aLogFolder.append(RTPATH_DELIMITER);
7186 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7187 }
7188}
7189
7190/**
7191 * Returns the full path to the machine's log file for an given index.
7192 */
7193Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7194 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7195{
7196 Utf8Str logFolder;
7197 getLogFolder(logFolder);
7198 Assert(logFolder.length());
7199 Utf8Str log;
7200 if (idx == 0)
7201 log = Utf8StrFmt("%s%cVBox.log",
7202 logFolder.c_str(), RTPATH_DELIMITER);
7203 else
7204 log = Utf8StrFmt("%s%cVBox.log.%d",
7205 logFolder.c_str(), RTPATH_DELIMITER, idx);
7206 return log;
7207}
7208
7209/**
7210 * Returns the full path to the machine's (hardened) startup log file.
7211 */
7212Utf8Str Machine::i_getStartupLogFilename(void)
7213{
7214 Utf8Str strFilename;
7215 getLogFolder(strFilename);
7216 Assert(strFilename.length());
7217 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7218 return strFilename;
7219}
7220
7221
7222/**
7223 * Composes a unique saved state filename based on the current system time. The filename is
7224 * granular to the second so this will work so long as no more than one snapshot is taken on
7225 * a machine per second.
7226 *
7227 * Before version 4.1, we used this formula for saved state files:
7228 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7229 * which no longer works because saved state files can now be shared between the saved state of the
7230 * "saved" machine and an online snapshot, and the following would cause problems:
7231 * 1) save machine
7232 * 2) create online snapshot from that machine state --> reusing saved state file
7233 * 3) save machine again --> filename would be reused, breaking the online snapshot
7234 *
7235 * So instead we now use a timestamp.
7236 *
7237 * @param str
7238 */
7239
7240void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7241{
7242 AutoCaller autoCaller(this);
7243 AssertComRCReturnVoid(autoCaller.rc());
7244
7245 {
7246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7247 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7248 }
7249
7250 RTTIMESPEC ts;
7251 RTTimeNow(&ts);
7252 RTTIME time;
7253 RTTimeExplode(&time, &ts);
7254
7255 strStateFilePath += RTPATH_DELIMITER;
7256 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7257 time.i32Year, time.u8Month, time.u8MonthDay,
7258 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7259}
7260
7261/**
7262 * Returns the full path to the default video capture file.
7263 */
7264void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7265{
7266 AutoCaller autoCaller(this);
7267 AssertComRCReturnVoid(autoCaller.rc());
7268
7269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7270
7271 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7272 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7273 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7274}
7275
7276/**
7277 * Returns whether at least one USB controller is present for the VM.
7278 */
7279bool Machine::i_isUSBControllerPresent()
7280{
7281 AutoCaller autoCaller(this);
7282 AssertComRCReturn(autoCaller.rc(), false);
7283
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 return (mUSBControllers->size() > 0);
7287}
7288
7289/**
7290 * @note Locks this object for writing, calls the client process
7291 * (inside the lock).
7292 */
7293HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7294 const Utf8Str &strFrontend,
7295 const Utf8Str &strEnvironment,
7296 ProgressProxy *aProgress)
7297{
7298 LogFlowThisFuncEnter();
7299
7300 AssertReturn(aControl, E_FAIL);
7301 AssertReturn(aProgress, E_FAIL);
7302 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7303
7304 AutoCaller autoCaller(this);
7305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7306
7307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7308
7309 if (!mData->mRegistered)
7310 return setError(E_UNEXPECTED,
7311 tr("The machine '%s' is not registered"),
7312 mUserData->s.strName.c_str());
7313
7314 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7315
7316 if ( mData->mSession.mState == SessionState_Locked
7317 || mData->mSession.mState == SessionState_Spawning
7318 || mData->mSession.mState == SessionState_Unlocking)
7319 return setError(VBOX_E_INVALID_OBJECT_STATE,
7320 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7321 mUserData->s.strName.c_str());
7322
7323 /* may not be busy */
7324 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7325
7326 /* get the path to the executable */
7327 char szPath[RTPATH_MAX];
7328 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7329 size_t cchBufLeft = strlen(szPath);
7330 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7331 szPath[cchBufLeft] = 0;
7332 char *pszNamePart = szPath + cchBufLeft;
7333 cchBufLeft = sizeof(szPath) - cchBufLeft;
7334
7335 int vrc = VINF_SUCCESS;
7336 RTPROCESS pid = NIL_RTPROCESS;
7337
7338 RTENV env = RTENV_DEFAULT;
7339
7340 if (!strEnvironment.isEmpty())
7341 {
7342 char *newEnvStr = NULL;
7343
7344 do
7345 {
7346 /* clone the current environment */
7347 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7348 AssertRCBreakStmt(vrc2, vrc = vrc2);
7349
7350 newEnvStr = RTStrDup(strEnvironment.c_str());
7351 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7352
7353 /* put new variables to the environment
7354 * (ignore empty variable names here since RTEnv API
7355 * intentionally doesn't do that) */
7356 char *var = newEnvStr;
7357 for (char *p = newEnvStr; *p; ++p)
7358 {
7359 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7360 {
7361 *p = '\0';
7362 if (*var)
7363 {
7364 char *val = strchr(var, '=');
7365 if (val)
7366 {
7367 *val++ = '\0';
7368 vrc2 = RTEnvSetEx(env, var, val);
7369 }
7370 else
7371 vrc2 = RTEnvUnsetEx(env, var);
7372 if (RT_FAILURE(vrc2))
7373 break;
7374 }
7375 var = p + 1;
7376 }
7377 }
7378 if (RT_SUCCESS(vrc2) && *var)
7379 vrc2 = RTEnvPutEx(env, var);
7380
7381 AssertRCBreakStmt(vrc2, vrc = vrc2);
7382 }
7383 while (0);
7384
7385 if (newEnvStr != NULL)
7386 RTStrFree(newEnvStr);
7387 }
7388
7389 /* Hardened startup logging */
7390#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7391 Utf8Str strSupStartLogArg("--sup-startup-log=");
7392 {
7393 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7394 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7395 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7396 {
7397 Utf8Str strStartupLogDir = strStartupLogFile;
7398 strStartupLogDir.stripFilename();
7399 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7400 file without stripping the file. */
7401 }
7402 strSupStartLogArg.append(strStartupLogFile);
7403 }
7404 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7405#else
7406 const char *pszSupStartupLogArg = NULL;
7407#endif
7408
7409
7410#ifdef VBOX_WITH_QTGUI
7411 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7412 {
7413# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7414 /* Modify the base path so that we don't need to use ".." below. */
7415 RTPathStripTrailingSlash(szPath);
7416 RTPathStripFilename(szPath);
7417 cchBufLeft = strlen(szPath);
7418 pszNamePart = szPath + cchBufLeft;
7419 cchBufLeft = sizeof(szPath) - cchBufLeft;
7420
7421# define OSX_APP_NAME "VirtualBoxVM"
7422# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7423
7424 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7425 if ( strAppOverride.contains(".")
7426 || strAppOverride.contains("/")
7427 || strAppOverride.contains("\\")
7428 || strAppOverride.contains(":"))
7429 strAppOverride.setNull();
7430 Utf8Str strAppPath;
7431 if (!strAppOverride.isEmpty())
7432 {
7433 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7434 Utf8Str strFullPath(szPath);
7435 strFullPath.append(strAppPath);
7436 /* there is a race, but people using this deserve the failure */
7437 if (!RTFileExists(strFullPath.c_str()))
7438 strAppOverride.setNull();
7439 }
7440 if (strAppOverride.isEmpty())
7441 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7442 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7443 strcpy(pszNamePart, strAppPath.c_str());
7444# else
7445 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7446 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7447 strcpy(pszNamePart, s_szVirtualBox_exe);
7448# endif
7449
7450 Utf8Str idStr = mData->mUuid.toString();
7451 const char *apszArgs[] =
7452 {
7453 szPath,
7454 "--comment", mUserData->s.strName.c_str(),
7455 "--startvm", idStr.c_str(),
7456 "--no-startvm-errormsgbox",
7457 pszSupStartupLogArg,
7458 NULL
7459 };
7460 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7461 }
7462#else /* !VBOX_WITH_QTGUI */
7463 if (0)
7464 ;
7465#endif /* VBOX_WITH_QTGUI */
7466
7467 else
7468
7469#ifdef VBOX_WITH_VBOXSDL
7470 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7471 {
7472 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7473 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7474 strcpy(pszNamePart, s_szVBoxSDL_exe);
7475
7476 Utf8Str idStr = mData->mUuid.toString();
7477 const char *apszArgs[] =
7478 {
7479 szPath,
7480 "--comment", mUserData->s.strName.c_str(),
7481 "--startvm", idStr.c_str(),
7482 pszSupStartupLogArg,
7483 NULL
7484 };
7485 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7486 }
7487#else /* !VBOX_WITH_VBOXSDL */
7488 if (0)
7489 ;
7490#endif /* !VBOX_WITH_VBOXSDL */
7491
7492 else
7493
7494#ifdef VBOX_WITH_HEADLESS
7495 if ( strFrontend == "headless"
7496 || strFrontend == "capture"
7497 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7498 )
7499 {
7500 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7501 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7502 * and a VM works even if the server has not been installed.
7503 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7504 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7505 * differently in 4.0 and 3.x.
7506 */
7507 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7508 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7509 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7510
7511 Utf8Str idStr = mData->mUuid.toString();
7512 const char *apszArgs[] =
7513 {
7514 szPath,
7515 "--comment", mUserData->s.strName.c_str(),
7516 "--startvm", idStr.c_str(),
7517 "--vrde", "config",
7518 0, /* For "--capture". */
7519 0, /* For "--sup-startup-log". */
7520 0
7521 };
7522 unsigned iArg = 7;
7523 if (strFrontend == "capture")
7524 apszArgs[iArg++] = "--capture";
7525 apszArgs[iArg++] = pszSupStartupLogArg;
7526
7527# ifdef RT_OS_WINDOWS
7528 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7529# else
7530 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7531# endif
7532 }
7533#else /* !VBOX_WITH_HEADLESS */
7534 if (0)
7535 ;
7536#endif /* !VBOX_WITH_HEADLESS */
7537 else
7538 {
7539 RTEnvDestroy(env);
7540 return setError(E_INVALIDARG,
7541 tr("Invalid frontend name: '%s'"),
7542 strFrontend.c_str());
7543 }
7544
7545 RTEnvDestroy(env);
7546
7547 if (RT_FAILURE(vrc))
7548 return setError(VBOX_E_IPRT_ERROR,
7549 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7550 mUserData->s.strName.c_str(), vrc);
7551
7552 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7553
7554 /*
7555 * Note that we don't release the lock here before calling the client,
7556 * because it doesn't need to call us back if called with a NULL argument.
7557 * Releasing the lock here is dangerous because we didn't prepare the
7558 * launch data yet, but the client we've just started may happen to be
7559 * too fast and call LockMachine() that will fail (because of PID, etc.),
7560 * so that the Machine will never get out of the Spawning session state.
7561 */
7562
7563 /* inform the session that it will be a remote one */
7564 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7565#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7566 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7567#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7568 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7569#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7570 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7571
7572 if (FAILED(rc))
7573 {
7574 /* restore the session state */
7575 mData->mSession.mState = SessionState_Unlocked;
7576 alock.release();
7577 mParent->i_addProcessToReap(pid);
7578 /* The failure may occur w/o any error info (from RPC), so provide one */
7579 return setError(VBOX_E_VM_ERROR,
7580 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7581 }
7582
7583 /* attach launch data to the machine */
7584 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7585 mData->mSession.mRemoteControls.push_back(aControl);
7586 mData->mSession.mProgress = aProgress;
7587 mData->mSession.mPID = pid;
7588 mData->mSession.mState = SessionState_Spawning;
7589 mData->mSession.mType = strFrontend;
7590
7591 alock.release();
7592 mParent->i_addProcessToReap(pid);
7593
7594 LogFlowThisFuncLeave();
7595 return S_OK;
7596}
7597
7598/**
7599 * Returns @c true if the given session machine instance has an open direct
7600 * session (and optionally also for direct sessions which are closing) and
7601 * returns the session control machine instance if so.
7602 *
7603 * Note that when the method returns @c false, the arguments remain unchanged.
7604 *
7605 * @param aMachine Session machine object.
7606 * @param aControl Direct session control object (optional).
7607 * @param aAllowClosing If true then additionally a session which is currently
7608 * being closed will also be allowed.
7609 *
7610 * @note locks this object for reading.
7611 */
7612bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7613 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7614 bool aAllowClosing /*= false*/)
7615{
7616 AutoLimitedCaller autoCaller(this);
7617 AssertComRCReturn(autoCaller.rc(), false);
7618
7619 /* just return false for inaccessible machines */
7620 if (getObjectState().getState() != ObjectState::Ready)
7621 return false;
7622
7623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7624
7625 if ( mData->mSession.mState == SessionState_Locked
7626 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7627 )
7628 {
7629 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7630
7631 aMachine = mData->mSession.mMachine;
7632
7633 if (aControl != NULL)
7634 *aControl = mData->mSession.mDirectControl;
7635
7636 return true;
7637 }
7638
7639 return false;
7640}
7641
7642/**
7643 * Returns @c true if the given machine has an spawning direct session.
7644 *
7645 * @note locks this object for reading.
7646 */
7647bool Machine::i_isSessionSpawning()
7648{
7649 AutoLimitedCaller autoCaller(this);
7650 AssertComRCReturn(autoCaller.rc(), false);
7651
7652 /* just return false for inaccessible machines */
7653 if (getObjectState().getState() != ObjectState::Ready)
7654 return false;
7655
7656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7657
7658 if (mData->mSession.mState == SessionState_Spawning)
7659 return true;
7660
7661 return false;
7662}
7663
7664/**
7665 * Called from the client watcher thread to check for unexpected client process
7666 * death during Session_Spawning state (e.g. before it successfully opened a
7667 * direct session).
7668 *
7669 * On Win32 and on OS/2, this method is called only when we've got the
7670 * direct client's process termination notification, so it always returns @c
7671 * true.
7672 *
7673 * On other platforms, this method returns @c true if the client process is
7674 * terminated and @c false if it's still alive.
7675 *
7676 * @note Locks this object for writing.
7677 */
7678bool Machine::i_checkForSpawnFailure()
7679{
7680 AutoCaller autoCaller(this);
7681 if (!autoCaller.isOk())
7682 {
7683 /* nothing to do */
7684 LogFlowThisFunc(("Already uninitialized!\n"));
7685 return true;
7686 }
7687
7688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7689
7690 if (mData->mSession.mState != SessionState_Spawning)
7691 {
7692 /* nothing to do */
7693 LogFlowThisFunc(("Not spawning any more!\n"));
7694 return true;
7695 }
7696
7697 HRESULT rc = S_OK;
7698
7699 /* PID not yet initialized, skip check. */
7700 if (mData->mSession.mPID == NIL_RTPROCESS)
7701 return false;
7702
7703 RTPROCSTATUS status;
7704 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7705
7706 if (vrc != VERR_PROCESS_RUNNING)
7707 {
7708 Utf8Str strExtraInfo;
7709
7710#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7711 /* If the startup logfile exists and is of non-zero length, tell the
7712 user to look there for more details to encourage them to attach it
7713 when reporting startup issues. */
7714 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7715 uint64_t cbStartupLogFile = 0;
7716 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7717 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7718 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7719#endif
7720
7721 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7722 rc = setError(E_FAIL,
7723 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7724 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7725 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7726 rc = setError(E_FAIL,
7727 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7728 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7729 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7730 rc = setError(E_FAIL,
7731 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7732 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7733 else
7734 rc = setError(E_FAIL,
7735 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7736 i_getName().c_str(), vrc, strExtraInfo.c_str());
7737 }
7738
7739 if (FAILED(rc))
7740 {
7741 /* Close the remote session, remove the remote control from the list
7742 * and reset session state to Closed (@note keep the code in sync with
7743 * the relevant part in LockMachine()). */
7744
7745 Assert(mData->mSession.mRemoteControls.size() == 1);
7746 if (mData->mSession.mRemoteControls.size() == 1)
7747 {
7748 ErrorInfoKeeper eik;
7749 mData->mSession.mRemoteControls.front()->Uninitialize();
7750 }
7751
7752 mData->mSession.mRemoteControls.clear();
7753 mData->mSession.mState = SessionState_Unlocked;
7754
7755 /* finalize the progress after setting the state */
7756 if (!mData->mSession.mProgress.isNull())
7757 {
7758 mData->mSession.mProgress->notifyComplete(rc);
7759 mData->mSession.mProgress.setNull();
7760 }
7761
7762 mData->mSession.mPID = NIL_RTPROCESS;
7763
7764 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7765 return true;
7766 }
7767
7768 return false;
7769}
7770
7771/**
7772 * Checks whether the machine can be registered. If so, commits and saves
7773 * all settings.
7774 *
7775 * @note Must be called from mParent's write lock. Locks this object and
7776 * children for writing.
7777 */
7778HRESULT Machine::i_prepareRegister()
7779{
7780 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7781
7782 AutoLimitedCaller autoCaller(this);
7783 AssertComRCReturnRC(autoCaller.rc());
7784
7785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7786
7787 /* wait for state dependents to drop to zero */
7788 i_ensureNoStateDependencies();
7789
7790 if (!mData->mAccessible)
7791 return setError(VBOX_E_INVALID_OBJECT_STATE,
7792 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7793 mUserData->s.strName.c_str(),
7794 mData->mUuid.toString().c_str());
7795
7796 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7797
7798 if (mData->mRegistered)
7799 return setError(VBOX_E_INVALID_OBJECT_STATE,
7800 tr("The machine '%s' with UUID {%s} is already registered"),
7801 mUserData->s.strName.c_str(),
7802 mData->mUuid.toString().c_str());
7803
7804 HRESULT rc = S_OK;
7805
7806 // Ensure the settings are saved. If we are going to be registered and
7807 // no config file exists yet, create it by calling i_saveSettings() too.
7808 if ( (mData->flModifications)
7809 || (!mData->pMachineConfigFile->fileExists())
7810 )
7811 {
7812 rc = i_saveSettings(NULL);
7813 // no need to check whether VirtualBox.xml needs saving too since
7814 // we can't have a machine XML file rename pending
7815 if (FAILED(rc)) return rc;
7816 }
7817
7818 /* more config checking goes here */
7819
7820 if (SUCCEEDED(rc))
7821 {
7822 /* we may have had implicit modifications we want to fix on success */
7823 i_commit();
7824
7825 mData->mRegistered = true;
7826 }
7827 else
7828 {
7829 /* we may have had implicit modifications we want to cancel on failure*/
7830 i_rollback(false /* aNotify */);
7831 }
7832
7833 return rc;
7834}
7835
7836/**
7837 * Increases the number of objects dependent on the machine state or on the
7838 * registered state. Guarantees that these two states will not change at least
7839 * until #releaseStateDependency() is called.
7840 *
7841 * Depending on the @a aDepType value, additional state checks may be made.
7842 * These checks will set extended error info on failure. See
7843 * #checkStateDependency() for more info.
7844 *
7845 * If this method returns a failure, the dependency is not added and the caller
7846 * is not allowed to rely on any particular machine state or registration state
7847 * value and may return the failed result code to the upper level.
7848 *
7849 * @param aDepType Dependency type to add.
7850 * @param aState Current machine state (NULL if not interested).
7851 * @param aRegistered Current registered state (NULL if not interested).
7852 *
7853 * @note Locks this object for writing.
7854 */
7855HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7856 MachineState_T *aState /* = NULL */,
7857 BOOL *aRegistered /* = NULL */)
7858{
7859 AutoCaller autoCaller(this);
7860 AssertComRCReturnRC(autoCaller.rc());
7861
7862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7863
7864 HRESULT rc = i_checkStateDependency(aDepType);
7865 if (FAILED(rc)) return rc;
7866
7867 {
7868 if (mData->mMachineStateChangePending != 0)
7869 {
7870 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7871 * drop to zero so don't add more. It may make sense to wait a bit
7872 * and retry before reporting an error (since the pending state
7873 * transition should be really quick) but let's just assert for
7874 * now to see if it ever happens on practice. */
7875
7876 AssertFailed();
7877
7878 return setError(E_ACCESSDENIED,
7879 tr("Machine state change is in progress. Please retry the operation later."));
7880 }
7881
7882 ++mData->mMachineStateDeps;
7883 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7884 }
7885
7886 if (aState)
7887 *aState = mData->mMachineState;
7888 if (aRegistered)
7889 *aRegistered = mData->mRegistered;
7890
7891 return S_OK;
7892}
7893
7894/**
7895 * Decreases the number of objects dependent on the machine state.
7896 * Must always complete the #addStateDependency() call after the state
7897 * dependency is no more necessary.
7898 */
7899void Machine::i_releaseStateDependency()
7900{
7901 AutoCaller autoCaller(this);
7902 AssertComRCReturnVoid(autoCaller.rc());
7903
7904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7905
7906 /* releaseStateDependency() w/o addStateDependency()? */
7907 AssertReturnVoid(mData->mMachineStateDeps != 0);
7908 -- mData->mMachineStateDeps;
7909
7910 if (mData->mMachineStateDeps == 0)
7911 {
7912 /* inform i_ensureNoStateDependencies() that there are no more deps */
7913 if (mData->mMachineStateChangePending != 0)
7914 {
7915 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7916 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7917 }
7918 }
7919}
7920
7921Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7922{
7923 /* start with nothing found */
7924 Utf8Str strResult("");
7925
7926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7927
7928 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7929 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7930 // found:
7931 strResult = it->second; // source is a Utf8Str
7932
7933 return strResult;
7934}
7935
7936// protected methods
7937/////////////////////////////////////////////////////////////////////////////
7938
7939/**
7940 * Performs machine state checks based on the @a aDepType value. If a check
7941 * fails, this method will set extended error info, otherwise it will return
7942 * S_OK. It is supposed, that on failure, the caller will immediately return
7943 * the return value of this method to the upper level.
7944 *
7945 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7946 *
7947 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7948 * current state of this machine object allows to change settings of the
7949 * machine (i.e. the machine is not registered, or registered but not running
7950 * and not saved). It is useful to call this method from Machine setters
7951 * before performing any change.
7952 *
7953 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7954 * as for MutableStateDep except that if the machine is saved, S_OK is also
7955 * returned. This is useful in setters which allow changing machine
7956 * properties when it is in the saved state.
7957 *
7958 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7959 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7960 * Aborted).
7961 *
7962 * @param aDepType Dependency type to check.
7963 *
7964 * @note Non Machine based classes should use #addStateDependency() and
7965 * #releaseStateDependency() methods or the smart AutoStateDependency
7966 * template.
7967 *
7968 * @note This method must be called from under this object's read or write
7969 * lock.
7970 */
7971HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7972{
7973 switch (aDepType)
7974 {
7975 case AnyStateDep:
7976 {
7977 break;
7978 }
7979 case MutableStateDep:
7980 {
7981 if ( mData->mRegistered
7982 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7983 Paused should actually be included here... (Live Migration) */
7984 || ( mData->mMachineState != MachineState_Paused
7985 && mData->mMachineState != MachineState_Running
7986 && mData->mMachineState != MachineState_Aborted
7987 && mData->mMachineState != MachineState_Teleported
7988 && mData->mMachineState != MachineState_PoweredOff
7989 )
7990 )
7991 )
7992 return setError(VBOX_E_INVALID_VM_STATE,
7993 tr("The machine is not mutable (state is %s)"),
7994 Global::stringifyMachineState(mData->mMachineState));
7995 break;
7996 }
7997 case MutableOrSavedStateDep:
7998 {
7999 if ( mData->mRegistered
8000 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8001 Paused should actually be included here... (Live Migration) */
8002 || ( mData->mMachineState != MachineState_Paused
8003 && mData->mMachineState != MachineState_Running
8004 && mData->mMachineState != MachineState_Aborted
8005 && mData->mMachineState != MachineState_Teleported
8006 && mData->mMachineState != MachineState_Saved
8007 && mData->mMachineState != MachineState_PoweredOff
8008 )
8009 )
8010 )
8011 return setError(VBOX_E_INVALID_VM_STATE,
8012 tr("The machine is not mutable (state is %s)"),
8013 Global::stringifyMachineState(mData->mMachineState));
8014 break;
8015 }
8016 case OfflineStateDep:
8017 {
8018 if ( mData->mRegistered
8019 && ( !i_isSessionMachine()
8020 || ( mData->mMachineState != MachineState_PoweredOff
8021 && mData->mMachineState != MachineState_Saved
8022 && mData->mMachineState != MachineState_Aborted
8023 && mData->mMachineState != MachineState_Teleported
8024 )
8025 )
8026 )
8027 return setError(VBOX_E_INVALID_VM_STATE,
8028 tr("The machine is not offline (state is %s)"),
8029 Global::stringifyMachineState(mData->mMachineState));
8030 break;
8031 }
8032 }
8033
8034 return S_OK;
8035}
8036
8037/**
8038 * Helper to initialize all associated child objects and allocate data
8039 * structures.
8040 *
8041 * This method must be called as a part of the object's initialization procedure
8042 * (usually done in the #init() method).
8043 *
8044 * @note Must be called only from #init() or from #registeredInit().
8045 */
8046HRESULT Machine::initDataAndChildObjects()
8047{
8048 AutoCaller autoCaller(this);
8049 AssertComRCReturnRC(autoCaller.rc());
8050 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8051 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8052
8053 AssertReturn(!mData->mAccessible, E_FAIL);
8054
8055 /* allocate data structures */
8056 mSSData.allocate();
8057 mUserData.allocate();
8058 mHWData.allocate();
8059 mMediaData.allocate();
8060 mStorageControllers.allocate();
8061 mUSBControllers.allocate();
8062
8063 /* initialize mOSTypeId */
8064 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8065
8066 /* create associated BIOS settings object */
8067 unconst(mBIOSSettings).createObject();
8068 mBIOSSettings->init(this);
8069
8070 /* create an associated VRDE object (default is disabled) */
8071 unconst(mVRDEServer).createObject();
8072 mVRDEServer->init(this);
8073
8074 /* create associated serial port objects */
8075 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8076 {
8077 unconst(mSerialPorts[slot]).createObject();
8078 mSerialPorts[slot]->init(this, slot);
8079 }
8080
8081 /* create associated parallel port objects */
8082 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8083 {
8084 unconst(mParallelPorts[slot]).createObject();
8085 mParallelPorts[slot]->init(this, slot);
8086 }
8087
8088 /* create the audio adapter object (always present, default is disabled) */
8089 unconst(mAudioAdapter).createObject();
8090 mAudioAdapter->init(this);
8091
8092 /* create the USB device filters object (always present) */
8093 unconst(mUSBDeviceFilters).createObject();
8094 mUSBDeviceFilters->init(this);
8095
8096 /* create associated network adapter objects */
8097 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8098 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8099 {
8100 unconst(mNetworkAdapters[slot]).createObject();
8101 mNetworkAdapters[slot]->init(this, slot);
8102 }
8103
8104 /* create the bandwidth control */
8105 unconst(mBandwidthControl).createObject();
8106 mBandwidthControl->init(this);
8107
8108 return S_OK;
8109}
8110
8111/**
8112 * Helper to uninitialize all associated child objects and to free all data
8113 * structures.
8114 *
8115 * This method must be called as a part of the object's uninitialization
8116 * procedure (usually done in the #uninit() method).
8117 *
8118 * @note Must be called only from #uninit() or from #registeredInit().
8119 */
8120void Machine::uninitDataAndChildObjects()
8121{
8122 AutoCaller autoCaller(this);
8123 AssertComRCReturnVoid(autoCaller.rc());
8124 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8125 || getObjectState().getState() == ObjectState::Limited);
8126
8127 /* tell all our other child objects we've been uninitialized */
8128 if (mBandwidthControl)
8129 {
8130 mBandwidthControl->uninit();
8131 unconst(mBandwidthControl).setNull();
8132 }
8133
8134 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8135 {
8136 if (mNetworkAdapters[slot])
8137 {
8138 mNetworkAdapters[slot]->uninit();
8139 unconst(mNetworkAdapters[slot]).setNull();
8140 }
8141 }
8142
8143 if (mUSBDeviceFilters)
8144 {
8145 mUSBDeviceFilters->uninit();
8146 unconst(mUSBDeviceFilters).setNull();
8147 }
8148
8149 if (mAudioAdapter)
8150 {
8151 mAudioAdapter->uninit();
8152 unconst(mAudioAdapter).setNull();
8153 }
8154
8155 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8156 {
8157 if (mParallelPorts[slot])
8158 {
8159 mParallelPorts[slot]->uninit();
8160 unconst(mParallelPorts[slot]).setNull();
8161 }
8162 }
8163
8164 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8165 {
8166 if (mSerialPorts[slot])
8167 {
8168 mSerialPorts[slot]->uninit();
8169 unconst(mSerialPorts[slot]).setNull();
8170 }
8171 }
8172
8173 if (mVRDEServer)
8174 {
8175 mVRDEServer->uninit();
8176 unconst(mVRDEServer).setNull();
8177 }
8178
8179 if (mBIOSSettings)
8180 {
8181 mBIOSSettings->uninit();
8182 unconst(mBIOSSettings).setNull();
8183 }
8184
8185 /* Deassociate media (only when a real Machine or a SnapshotMachine
8186 * instance is uninitialized; SessionMachine instances refer to real
8187 * Machine media). This is necessary for a clean re-initialization of
8188 * the VM after successfully re-checking the accessibility state. Note
8189 * that in case of normal Machine or SnapshotMachine uninitialization (as
8190 * a result of unregistering or deleting the snapshot), outdated media
8191 * attachments will already be uninitialized and deleted, so this
8192 * code will not affect them. */
8193 if ( !!mMediaData
8194 && (!i_isSessionMachine())
8195 )
8196 {
8197 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8198 it != mMediaData->mAttachments.end();
8199 ++it)
8200 {
8201 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8202 if (pMedium.isNull())
8203 continue;
8204 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8205 AssertComRC(rc);
8206 }
8207 }
8208
8209 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8210 {
8211 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8212 if (mData->mFirstSnapshot)
8213 {
8214 // snapshots tree is protected by machine write lock; strictly
8215 // this isn't necessary here since we're deleting the entire
8216 // machine, but otherwise we assert in Snapshot::uninit()
8217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8218 mData->mFirstSnapshot->uninit();
8219 mData->mFirstSnapshot.setNull();
8220 }
8221
8222 mData->mCurrentSnapshot.setNull();
8223 }
8224
8225 /* free data structures (the essential mData structure is not freed here
8226 * since it may be still in use) */
8227 mMediaData.free();
8228 mStorageControllers.free();
8229 mUSBControllers.free();
8230 mHWData.free();
8231 mUserData.free();
8232 mSSData.free();
8233}
8234
8235/**
8236 * Returns a pointer to the Machine object for this machine that acts like a
8237 * parent for complex machine data objects such as shared folders, etc.
8238 *
8239 * For primary Machine objects and for SnapshotMachine objects, returns this
8240 * object's pointer itself. For SessionMachine objects, returns the peer
8241 * (primary) machine pointer.
8242 */
8243Machine* Machine::i_getMachine()
8244{
8245 if (i_isSessionMachine())
8246 return (Machine*)mPeer;
8247 return this;
8248}
8249
8250/**
8251 * Makes sure that there are no machine state dependents. If necessary, waits
8252 * for the number of dependents to drop to zero.
8253 *
8254 * Make sure this method is called from under this object's write lock to
8255 * guarantee that no new dependents may be added when this method returns
8256 * control to the caller.
8257 *
8258 * @note Locks this object for writing. The lock will be released while waiting
8259 * (if necessary).
8260 *
8261 * @warning To be used only in methods that change the machine state!
8262 */
8263void Machine::i_ensureNoStateDependencies()
8264{
8265 AssertReturnVoid(isWriteLockOnCurrentThread());
8266
8267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8268
8269 /* Wait for all state dependents if necessary */
8270 if (mData->mMachineStateDeps != 0)
8271 {
8272 /* lazy semaphore creation */
8273 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8274 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8275
8276 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8277 mData->mMachineStateDeps));
8278
8279 ++mData->mMachineStateChangePending;
8280
8281 /* reset the semaphore before waiting, the last dependent will signal
8282 * it */
8283 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8284
8285 alock.release();
8286
8287 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8288
8289 alock.acquire();
8290
8291 -- mData->mMachineStateChangePending;
8292 }
8293}
8294
8295/**
8296 * Changes the machine state and informs callbacks.
8297 *
8298 * This method is not intended to fail so it either returns S_OK or asserts (and
8299 * returns a failure).
8300 *
8301 * @note Locks this object for writing.
8302 */
8303HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8304{
8305 LogFlowThisFuncEnter();
8306 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8307
8308 AutoCaller autoCaller(this);
8309 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8310
8311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8312
8313 /* wait for state dependents to drop to zero */
8314 i_ensureNoStateDependencies();
8315
8316 if (mData->mMachineState != aMachineState)
8317 {
8318 mData->mMachineState = aMachineState;
8319
8320 RTTimeNow(&mData->mLastStateChange);
8321
8322 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8323 }
8324
8325 LogFlowThisFuncLeave();
8326 return S_OK;
8327}
8328
8329/**
8330 * Searches for a shared folder with the given logical name
8331 * in the collection of shared folders.
8332 *
8333 * @param aName logical name of the shared folder
8334 * @param aSharedFolder where to return the found object
8335 * @param aSetError whether to set the error info if the folder is
8336 * not found
8337 * @return
8338 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8339 *
8340 * @note
8341 * must be called from under the object's lock!
8342 */
8343HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8344 ComObjPtr<SharedFolder> &aSharedFolder,
8345 bool aSetError /* = false */)
8346{
8347 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8348 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8349 it != mHWData->mSharedFolders.end();
8350 ++it)
8351 {
8352 SharedFolder *pSF = *it;
8353 AutoCaller autoCaller(pSF);
8354 if (pSF->i_getName() == aName)
8355 {
8356 aSharedFolder = pSF;
8357 rc = S_OK;
8358 break;
8359 }
8360 }
8361
8362 if (aSetError && FAILED(rc))
8363 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8364
8365 return rc;
8366}
8367
8368/**
8369 * Initializes all machine instance data from the given settings structures
8370 * from XML. The exception is the machine UUID which needs special handling
8371 * depending on the caller's use case, so the caller needs to set that herself.
8372 *
8373 * This gets called in several contexts during machine initialization:
8374 *
8375 * -- When machine XML exists on disk already and needs to be loaded into memory,
8376 * for example, from registeredInit() to load all registered machines on
8377 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8378 * attached to the machine should be part of some media registry already.
8379 *
8380 * -- During OVF import, when a machine config has been constructed from an
8381 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8382 * ensure that the media listed as attachments in the config (which have
8383 * been imported from the OVF) receive the correct registry ID.
8384 *
8385 * -- During VM cloning.
8386 *
8387 * @param config Machine settings from XML.
8388 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8389 * for each attached medium in the config.
8390 * @return
8391 */
8392HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8393 const Guid *puuidRegistry)
8394{
8395 // copy name, description, OS type, teleporter, UTC etc.
8396 mUserData->s = config.machineUserData;
8397
8398 // Decode the Icon overide data from config userdata and set onto Machine.
8399 #define DECODE_STR_MAX _1M
8400 const char* pszStr = config.machineUserData.ovIcon.c_str();
8401 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8402 if (cbOut > DECODE_STR_MAX)
8403 return setError(E_FAIL,
8404 tr("Icon Data too long.'%d' > '%d'"),
8405 cbOut,
8406 DECODE_STR_MAX);
8407 mUserData->mIcon.resize(cbOut);
8408 int vrc = VINF_SUCCESS;
8409 if (cbOut)
8410 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8411 if (RT_FAILURE(vrc))
8412 {
8413 mUserData->mIcon.resize(0);
8414 return setError(E_FAIL,
8415 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8416 pszStr,
8417 vrc);
8418 }
8419
8420 // look up the object by Id to check it is valid
8421 ComPtr<IGuestOSType> guestOSType;
8422 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8423 guestOSType.asOutParam());
8424 if (FAILED(rc)) return rc;
8425
8426 // stateFile (optional)
8427 if (config.strStateFile.isEmpty())
8428 mSSData->strStateFilePath.setNull();
8429 else
8430 {
8431 Utf8Str stateFilePathFull(config.strStateFile);
8432 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8433 if (RT_FAILURE(vrc))
8434 return setError(E_FAIL,
8435 tr("Invalid saved state file path '%s' (%Rrc)"),
8436 config.strStateFile.c_str(),
8437 vrc);
8438 mSSData->strStateFilePath = stateFilePathFull;
8439 }
8440
8441 // snapshot folder needs special processing so set it again
8442 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8443 if (FAILED(rc)) return rc;
8444
8445 /* Copy the extra data items (Not in any case config is already the same as
8446 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8447 * make sure the extra data map is copied). */
8448 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8449
8450 /* currentStateModified (optional, default is true) */
8451 mData->mCurrentStateModified = config.fCurrentStateModified;
8452
8453 mData->mLastStateChange = config.timeLastStateChange;
8454
8455 /*
8456 * note: all mUserData members must be assigned prior this point because
8457 * we need to commit changes in order to let mUserData be shared by all
8458 * snapshot machine instances.
8459 */
8460 mUserData.commitCopy();
8461
8462 // machine registry, if present (must be loaded before snapshots)
8463 if (config.canHaveOwnMediaRegistry())
8464 {
8465 // determine machine folder
8466 Utf8Str strMachineFolder = i_getSettingsFileFull();
8467 strMachineFolder.stripFilename();
8468 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8469 config.mediaRegistry,
8470 strMachineFolder);
8471 if (FAILED(rc)) return rc;
8472 }
8473
8474 /* Snapshot node (optional) */
8475 size_t cRootSnapshots;
8476 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8477 {
8478 // there must be only one root snapshot
8479 Assert(cRootSnapshots == 1);
8480
8481 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8482
8483 rc = i_loadSnapshot(snap,
8484 config.uuidCurrentSnapshot,
8485 NULL); // no parent == first snapshot
8486 if (FAILED(rc)) return rc;
8487 }
8488
8489 // hardware data
8490 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8491 if (FAILED(rc)) return rc;
8492
8493 // load storage controllers
8494 rc = i_loadStorageControllers(config.storageMachine,
8495 puuidRegistry,
8496 NULL /* puuidSnapshot */);
8497 if (FAILED(rc)) return rc;
8498
8499 /*
8500 * NOTE: the assignment below must be the last thing to do,
8501 * otherwise it will be not possible to change the settings
8502 * somewhere in the code above because all setters will be
8503 * blocked by i_checkStateDependency(MutableStateDep).
8504 */
8505
8506 /* set the machine state to Aborted or Saved when appropriate */
8507 if (config.fAborted)
8508 {
8509 mSSData->strStateFilePath.setNull();
8510
8511 /* no need to use i_setMachineState() during init() */
8512 mData->mMachineState = MachineState_Aborted;
8513 }
8514 else if (!mSSData->strStateFilePath.isEmpty())
8515 {
8516 /* no need to use i_setMachineState() during init() */
8517 mData->mMachineState = MachineState_Saved;
8518 }
8519
8520 // after loading settings, we are no longer different from the XML on disk
8521 mData->flModifications = 0;
8522
8523 return S_OK;
8524}
8525
8526/**
8527 * Recursively loads all snapshots starting from the given.
8528 *
8529 * @param aNode <Snapshot> node.
8530 * @param aCurSnapshotId Current snapshot ID from the settings file.
8531 * @param aParentSnapshot Parent snapshot.
8532 */
8533HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8534 const Guid &aCurSnapshotId,
8535 Snapshot *aParentSnapshot)
8536{
8537 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8538 AssertReturn(!i_isSessionMachine(), E_FAIL);
8539
8540 HRESULT rc = S_OK;
8541
8542 Utf8Str strStateFile;
8543 if (!data.strStateFile.isEmpty())
8544 {
8545 /* optional */
8546 strStateFile = data.strStateFile;
8547 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8548 if (RT_FAILURE(vrc))
8549 return setError(E_FAIL,
8550 tr("Invalid saved state file path '%s' (%Rrc)"),
8551 strStateFile.c_str(),
8552 vrc);
8553 }
8554
8555 /* create a snapshot machine object */
8556 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8557 pSnapshotMachine.createObject();
8558 rc = pSnapshotMachine->initFromSettings(this,
8559 data.hardware,
8560 &data.debugging,
8561 &data.autostart,
8562 data.storage,
8563 data.uuid.ref(),
8564 strStateFile);
8565 if (FAILED(rc)) return rc;
8566
8567 /* create a snapshot object */
8568 ComObjPtr<Snapshot> pSnapshot;
8569 pSnapshot.createObject();
8570 /* initialize the snapshot */
8571 rc = pSnapshot->init(mParent, // VirtualBox object
8572 data.uuid,
8573 data.strName,
8574 data.strDescription,
8575 data.timestamp,
8576 pSnapshotMachine,
8577 aParentSnapshot);
8578 if (FAILED(rc)) return rc;
8579
8580 /* memorize the first snapshot if necessary */
8581 if (!mData->mFirstSnapshot)
8582 mData->mFirstSnapshot = pSnapshot;
8583
8584 /* memorize the current snapshot when appropriate */
8585 if ( !mData->mCurrentSnapshot
8586 && pSnapshot->i_getId() == aCurSnapshotId
8587 )
8588 mData->mCurrentSnapshot = pSnapshot;
8589
8590 // now create the children
8591 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8592 it != data.llChildSnapshots.end();
8593 ++it)
8594 {
8595 const settings::Snapshot &childData = *it;
8596 // recurse
8597 rc = i_loadSnapshot(childData,
8598 aCurSnapshotId,
8599 pSnapshot); // parent = the one we created above
8600 if (FAILED(rc)) return rc;
8601 }
8602
8603 return rc;
8604}
8605
8606/**
8607 * Loads settings into mHWData.
8608 *
8609 * @param data Reference to the hardware settings.
8610 * @param pDbg Pointer to the debugging settings.
8611 * @param pAutostart Pointer to the autostart settings.
8612 */
8613HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8614 const settings::Autostart *pAutostart)
8615{
8616 AssertReturn(!i_isSessionMachine(), E_FAIL);
8617
8618 HRESULT rc = S_OK;
8619
8620 try
8621 {
8622 /* The hardware version attribute (optional). */
8623 mHWData->mHWVersion = data.strVersion;
8624 mHWData->mHardwareUUID = data.uuid;
8625
8626 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8627 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8628 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8629 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8630 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8631 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8632 mHWData->mPAEEnabled = data.fPAE;
8633 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8634 mHWData->mLongMode = data.enmLongMode;
8635 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8636 mHWData->mCPUCount = data.cCPUs;
8637 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8638 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8639
8640 // cpu
8641 if (mHWData->mCPUHotPlugEnabled)
8642 {
8643 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8644 it != data.llCpus.end();
8645 ++it)
8646 {
8647 const settings::Cpu &cpu = *it;
8648
8649 mHWData->mCPUAttached[cpu.ulId] = true;
8650 }
8651 }
8652
8653 // cpuid leafs
8654 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8655 it != data.llCpuIdLeafs.end();
8656 ++it)
8657 {
8658 const settings::CpuIdLeaf &leaf = *it;
8659
8660 switch (leaf.ulId)
8661 {
8662 case 0x0:
8663 case 0x1:
8664 case 0x2:
8665 case 0x3:
8666 case 0x4:
8667 case 0x5:
8668 case 0x6:
8669 case 0x7:
8670 case 0x8:
8671 case 0x9:
8672 case 0xA:
8673 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8674 break;
8675
8676 case 0x80000000:
8677 case 0x80000001:
8678 case 0x80000002:
8679 case 0x80000003:
8680 case 0x80000004:
8681 case 0x80000005:
8682 case 0x80000006:
8683 case 0x80000007:
8684 case 0x80000008:
8685 case 0x80000009:
8686 case 0x8000000A:
8687 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8688 break;
8689
8690 default:
8691 /* just ignore */
8692 break;
8693 }
8694 }
8695
8696 mHWData->mMemorySize = data.ulMemorySizeMB;
8697 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8698
8699 // boot order
8700 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8701 {
8702 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8703 if (it == data.mapBootOrder.end())
8704 mHWData->mBootOrder[i] = DeviceType_Null;
8705 else
8706 mHWData->mBootOrder[i] = it->second;
8707 }
8708
8709 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8710 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8711 mHWData->mMonitorCount = data.cMonitors;
8712 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8713 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8714 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8715 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8716 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8717 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8718 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8719 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8720 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8721 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8722 if (!data.strVideoCaptureFile.isEmpty())
8723 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8724 else
8725 mHWData->mVideoCaptureFile.setNull();
8726 mHWData->mFirmwareType = data.firmwareType;
8727 mHWData->mPointingHIDType = data.pointingHIDType;
8728 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8729 mHWData->mChipsetType = data.chipsetType;
8730 mHWData->mParavirtProvider = data.paravirtProvider;
8731 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8732 mHWData->mHPETEnabled = data.fHPETEnabled;
8733
8734 /* VRDEServer */
8735 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8736 if (FAILED(rc)) return rc;
8737
8738 /* BIOS */
8739 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8740 if (FAILED(rc)) return rc;
8741
8742 // Bandwidth control (must come before network adapters)
8743 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8744 if (FAILED(rc)) return rc;
8745
8746 /* Shared folders */
8747 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8748 it != data.usbSettings.llUSBControllers.end();
8749 ++it)
8750 {
8751 const settings::USBController &settingsCtrl = *it;
8752 ComObjPtr<USBController> newCtrl;
8753
8754 newCtrl.createObject();
8755 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8756 mUSBControllers->push_back(newCtrl);
8757 }
8758
8759 /* USB device filters */
8760 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8761 if (FAILED(rc)) return rc;
8762
8763 // network adapters
8764 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8765 size_t oldCount = mNetworkAdapters.size();
8766 if (newCount > oldCount)
8767 {
8768 mNetworkAdapters.resize(newCount);
8769 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8770 {
8771 unconst(mNetworkAdapters[slot]).createObject();
8772 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8773 }
8774 }
8775 else if (newCount < oldCount)
8776 mNetworkAdapters.resize(newCount);
8777 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8778 it != data.llNetworkAdapters.end();
8779 ++it)
8780 {
8781 const settings::NetworkAdapter &nic = *it;
8782
8783 /* slot unicity is guaranteed by XML Schema */
8784 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8785 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8786 if (FAILED(rc)) return rc;
8787 }
8788
8789 // serial ports
8790 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8791 it != data.llSerialPorts.end();
8792 ++it)
8793 {
8794 const settings::SerialPort &s = *it;
8795
8796 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8797 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8798 if (FAILED(rc)) return rc;
8799 }
8800
8801 // parallel ports (optional)
8802 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8803 it != data.llParallelPorts.end();
8804 ++it)
8805 {
8806 const settings::ParallelPort &p = *it;
8807
8808 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8809 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8810 if (FAILED(rc)) return rc;
8811 }
8812
8813 /* AudioAdapter */
8814 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8815 if (FAILED(rc)) return rc;
8816
8817 /* Shared folders */
8818 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8819 it != data.llSharedFolders.end();
8820 ++it)
8821 {
8822 const settings::SharedFolder &sf = *it;
8823
8824 ComObjPtr<SharedFolder> sharedFolder;
8825 /* Check for double entries. Not allowed! */
8826 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8827 if (SUCCEEDED(rc))
8828 return setError(VBOX_E_OBJECT_IN_USE,
8829 tr("Shared folder named '%s' already exists"),
8830 sf.strName.c_str());
8831
8832 /* Create the new shared folder. Don't break on error. This will be
8833 * reported when the machine starts. */
8834 sharedFolder.createObject();
8835 rc = sharedFolder->init(i_getMachine(),
8836 sf.strName,
8837 sf.strHostPath,
8838 RT_BOOL(sf.fWritable),
8839 RT_BOOL(sf.fAutoMount),
8840 false /* fFailOnError */);
8841 if (FAILED(rc)) return rc;
8842 mHWData->mSharedFolders.push_back(sharedFolder);
8843 }
8844
8845 // Clipboard
8846 mHWData->mClipboardMode = data.clipboardMode;
8847
8848 // drag'n'drop
8849 mHWData->mDnDMode = data.dndMode;
8850
8851 // guest settings
8852 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8853
8854 // IO settings
8855 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8856 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8857
8858 // Host PCI devices
8859 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8860 it != data.pciAttachments.end();
8861 ++it)
8862 {
8863 const settings::HostPCIDeviceAttachment &hpda = *it;
8864 ComObjPtr<PCIDeviceAttachment> pda;
8865
8866 pda.createObject();
8867 pda->i_loadSettings(this, hpda);
8868 mHWData->mPCIDeviceAssignments.push_back(pda);
8869 }
8870
8871 /*
8872 * (The following isn't really real hardware, but it lives in HWData
8873 * for reasons of convenience.)
8874 */
8875
8876#ifdef VBOX_WITH_GUEST_PROPS
8877 /* Guest properties (optional) */
8878 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8879 it != data.llGuestProperties.end();
8880 ++it)
8881 {
8882 const settings::GuestProperty &prop = *it;
8883 uint32_t fFlags = guestProp::NILFLAG;
8884 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8885 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8886 mHWData->mGuestProperties[prop.strName] = property;
8887 }
8888
8889 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8890#endif /* VBOX_WITH_GUEST_PROPS defined */
8891
8892 rc = i_loadDebugging(pDbg);
8893 if (FAILED(rc))
8894 return rc;
8895
8896 mHWData->mAutostart = *pAutostart;
8897
8898 /* default frontend */
8899 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8900 }
8901 catch(std::bad_alloc &)
8902 {
8903 return E_OUTOFMEMORY;
8904 }
8905
8906 AssertComRC(rc);
8907 return rc;
8908}
8909
8910/**
8911 * Called from Machine::loadHardware() to load the debugging settings of the
8912 * machine.
8913 *
8914 * @param pDbg Pointer to the settings.
8915 */
8916HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8917{
8918 mHWData->mDebugging = *pDbg;
8919 /* no more processing currently required, this will probably change. */
8920 return S_OK;
8921}
8922
8923/**
8924 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8925 *
8926 * @param data
8927 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8928 * @param puuidSnapshot
8929 * @return
8930 */
8931HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8932 const Guid *puuidRegistry,
8933 const Guid *puuidSnapshot)
8934{
8935 AssertReturn(!i_isSessionMachine(), E_FAIL);
8936
8937 HRESULT rc = S_OK;
8938
8939 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8940 it != data.llStorageControllers.end();
8941 ++it)
8942 {
8943 const settings::StorageController &ctlData = *it;
8944
8945 ComObjPtr<StorageController> pCtl;
8946 /* Try to find one with the name first. */
8947 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8948 if (SUCCEEDED(rc))
8949 return setError(VBOX_E_OBJECT_IN_USE,
8950 tr("Storage controller named '%s' already exists"),
8951 ctlData.strName.c_str());
8952
8953 pCtl.createObject();
8954 rc = pCtl->init(this,
8955 ctlData.strName,
8956 ctlData.storageBus,
8957 ctlData.ulInstance,
8958 ctlData.fBootable);
8959 if (FAILED(rc)) return rc;
8960
8961 mStorageControllers->push_back(pCtl);
8962
8963 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8964 if (FAILED(rc)) return rc;
8965
8966 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8967 if (FAILED(rc)) return rc;
8968
8969 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8970 if (FAILED(rc)) return rc;
8971
8972 /* Set IDE emulation settings (only for AHCI controller). */
8973 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8974 {
8975 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8976 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8977 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8978 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8979 )
8980 return rc;
8981 }
8982
8983 /* Load the attached devices now. */
8984 rc = i_loadStorageDevices(pCtl,
8985 ctlData,
8986 puuidRegistry,
8987 puuidSnapshot);
8988 if (FAILED(rc)) return rc;
8989 }
8990
8991 return S_OK;
8992}
8993
8994/**
8995 * Called from i_loadStorageControllers for a controller's devices.
8996 *
8997 * @param aStorageController
8998 * @param data
8999 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9000 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9001 * @return
9002 */
9003HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9004 const settings::StorageController &data,
9005 const Guid *puuidRegistry,
9006 const Guid *puuidSnapshot)
9007{
9008 HRESULT rc = S_OK;
9009
9010 /* paranoia: detect duplicate attachments */
9011 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9012 it != data.llAttachedDevices.end();
9013 ++it)
9014 {
9015 const settings::AttachedDevice &ad = *it;
9016
9017 for (settings::AttachedDevicesList::const_iterator it2 = it;
9018 it2 != data.llAttachedDevices.end();
9019 ++it2)
9020 {
9021 if (it == it2)
9022 continue;
9023
9024 const settings::AttachedDevice &ad2 = *it2;
9025
9026 if ( ad.lPort == ad2.lPort
9027 && ad.lDevice == ad2.lDevice)
9028 {
9029 return setError(E_FAIL,
9030 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9031 aStorageController->i_getName().c_str(),
9032 ad.lPort,
9033 ad.lDevice,
9034 mUserData->s.strName.c_str());
9035 }
9036 }
9037 }
9038
9039 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9040 it != data.llAttachedDevices.end();
9041 ++it)
9042 {
9043 const settings::AttachedDevice &dev = *it;
9044 ComObjPtr<Medium> medium;
9045
9046 switch (dev.deviceType)
9047 {
9048 case DeviceType_Floppy:
9049 case DeviceType_DVD:
9050 if (dev.strHostDriveSrc.isNotEmpty())
9051 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9052 false /* fRefresh */, medium);
9053 else
9054 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9055 dev.uuid,
9056 false /* fRefresh */,
9057 false /* aSetError */,
9058 medium);
9059 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9060 // This is not an error. The host drive or UUID might have vanished, so just go
9061 // ahead without this removeable medium attachment
9062 rc = S_OK;
9063 break;
9064
9065 case DeviceType_HardDisk:
9066 {
9067 /* find a hard disk by UUID */
9068 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9069 if (FAILED(rc))
9070 {
9071 if (i_isSnapshotMachine())
9072 {
9073 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9074 // so the user knows that the bad disk is in a snapshot somewhere
9075 com::ErrorInfo info;
9076 return setError(E_FAIL,
9077 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9078 puuidSnapshot->raw(),
9079 info.getText().raw());
9080 }
9081 else
9082 return rc;
9083 }
9084
9085 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9086
9087 if (medium->i_getType() == MediumType_Immutable)
9088 {
9089 if (i_isSnapshotMachine())
9090 return setError(E_FAIL,
9091 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9092 "of the virtual machine '%s' ('%s')"),
9093 medium->i_getLocationFull().c_str(),
9094 dev.uuid.raw(),
9095 puuidSnapshot->raw(),
9096 mUserData->s.strName.c_str(),
9097 mData->m_strConfigFileFull.c_str());
9098
9099 return setError(E_FAIL,
9100 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9101 medium->i_getLocationFull().c_str(),
9102 dev.uuid.raw(),
9103 mUserData->s.strName.c_str(),
9104 mData->m_strConfigFileFull.c_str());
9105 }
9106
9107 if (medium->i_getType() == MediumType_MultiAttach)
9108 {
9109 if (i_isSnapshotMachine())
9110 return setError(E_FAIL,
9111 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9112 "of the virtual machine '%s' ('%s')"),
9113 medium->i_getLocationFull().c_str(),
9114 dev.uuid.raw(),
9115 puuidSnapshot->raw(),
9116 mUserData->s.strName.c_str(),
9117 mData->m_strConfigFileFull.c_str());
9118
9119 return setError(E_FAIL,
9120 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9121 medium->i_getLocationFull().c_str(),
9122 dev.uuid.raw(),
9123 mUserData->s.strName.c_str(),
9124 mData->m_strConfigFileFull.c_str());
9125 }
9126
9127 if ( !i_isSnapshotMachine()
9128 && medium->i_getChildren().size() != 0
9129 )
9130 return setError(E_FAIL,
9131 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9132 "because it has %d differencing child hard disks"),
9133 medium->i_getLocationFull().c_str(),
9134 dev.uuid.raw(),
9135 mUserData->s.strName.c_str(),
9136 mData->m_strConfigFileFull.c_str(),
9137 medium->i_getChildren().size());
9138
9139 if (i_findAttachment(mMediaData->mAttachments,
9140 medium))
9141 return setError(E_FAIL,
9142 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9143 medium->i_getLocationFull().c_str(),
9144 dev.uuid.raw(),
9145 mUserData->s.strName.c_str(),
9146 mData->m_strConfigFileFull.c_str());
9147
9148 break;
9149 }
9150
9151 default:
9152 return setError(E_FAIL,
9153 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9154 medium->i_getLocationFull().c_str(),
9155 mUserData->s.strName.c_str(),
9156 mData->m_strConfigFileFull.c_str());
9157 }
9158
9159 if (FAILED(rc))
9160 break;
9161
9162 /* Bandwidth groups are loaded at this point. */
9163 ComObjPtr<BandwidthGroup> pBwGroup;
9164
9165 if (!dev.strBwGroup.isEmpty())
9166 {
9167 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9168 if (FAILED(rc))
9169 return setError(E_FAIL,
9170 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9171 medium->i_getLocationFull().c_str(),
9172 dev.strBwGroup.c_str(),
9173 mUserData->s.strName.c_str(),
9174 mData->m_strConfigFileFull.c_str());
9175 pBwGroup->i_reference();
9176 }
9177
9178 const Bstr controllerName = aStorageController->i_getName();
9179 ComObjPtr<MediumAttachment> pAttachment;
9180 pAttachment.createObject();
9181 rc = pAttachment->init(this,
9182 medium,
9183 controllerName,
9184 dev.lPort,
9185 dev.lDevice,
9186 dev.deviceType,
9187 false,
9188 dev.fPassThrough,
9189 dev.fTempEject,
9190 dev.fNonRotational,
9191 dev.fDiscard,
9192 dev.fHotPluggable,
9193 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9194 if (FAILED(rc)) break;
9195
9196 /* associate the medium with this machine and snapshot */
9197 if (!medium.isNull())
9198 {
9199 AutoCaller medCaller(medium);
9200 if (FAILED(medCaller.rc())) return medCaller.rc();
9201 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9202
9203 if (i_isSnapshotMachine())
9204 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9205 else
9206 rc = medium->i_addBackReference(mData->mUuid);
9207 /* If the medium->addBackReference fails it sets an appropriate
9208 * error message, so no need to do any guesswork here. */
9209
9210 if (puuidRegistry)
9211 // caller wants registry ID to be set on all attached media (OVF import case)
9212 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9213 }
9214
9215 if (FAILED(rc))
9216 break;
9217
9218 /* back up mMediaData to let registeredInit() properly rollback on failure
9219 * (= limited accessibility) */
9220 i_setModified(IsModified_Storage);
9221 mMediaData.backup();
9222 mMediaData->mAttachments.push_back(pAttachment);
9223 }
9224
9225 return rc;
9226}
9227
9228/**
9229 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9230 *
9231 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9232 * @param aSnapshot where to return the found snapshot
9233 * @param aSetError true to set extended error info on failure
9234 */
9235HRESULT Machine::i_findSnapshotById(const Guid &aId,
9236 ComObjPtr<Snapshot> &aSnapshot,
9237 bool aSetError /* = false */)
9238{
9239 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9240
9241 if (!mData->mFirstSnapshot)
9242 {
9243 if (aSetError)
9244 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9245 return E_FAIL;
9246 }
9247
9248 if (aId.isZero())
9249 aSnapshot = mData->mFirstSnapshot;
9250 else
9251 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9252
9253 if (!aSnapshot)
9254 {
9255 if (aSetError)
9256 return setError(E_FAIL,
9257 tr("Could not find a snapshot with UUID {%s}"),
9258 aId.toString().c_str());
9259 return E_FAIL;
9260 }
9261
9262 return S_OK;
9263}
9264
9265/**
9266 * Returns the snapshot with the given name or fails of no such snapshot.
9267 *
9268 * @param aName snapshot name to find
9269 * @param aSnapshot where to return the found snapshot
9270 * @param aSetError true to set extended error info on failure
9271 */
9272HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9273 ComObjPtr<Snapshot> &aSnapshot,
9274 bool aSetError /* = false */)
9275{
9276 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9277
9278 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9279
9280 if (!mData->mFirstSnapshot)
9281 {
9282 if (aSetError)
9283 return setError(VBOX_E_OBJECT_NOT_FOUND,
9284 tr("This machine does not have any snapshots"));
9285 return VBOX_E_OBJECT_NOT_FOUND;
9286 }
9287
9288 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9289
9290 if (!aSnapshot)
9291 {
9292 if (aSetError)
9293 return setError(VBOX_E_OBJECT_NOT_FOUND,
9294 tr("Could not find a snapshot named '%s'"), strName.c_str());
9295 return VBOX_E_OBJECT_NOT_FOUND;
9296 }
9297
9298 return S_OK;
9299}
9300
9301/**
9302 * Returns a storage controller object with the given name.
9303 *
9304 * @param aName storage controller name to find
9305 * @param aStorageController where to return the found storage controller
9306 * @param aSetError true to set extended error info on failure
9307 */
9308HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9309 ComObjPtr<StorageController> &aStorageController,
9310 bool aSetError /* = false */)
9311{
9312 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9313
9314 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9315 it != mStorageControllers->end();
9316 ++it)
9317 {
9318 if ((*it)->i_getName() == aName)
9319 {
9320 aStorageController = (*it);
9321 return S_OK;
9322 }
9323 }
9324
9325 if (aSetError)
9326 return setError(VBOX_E_OBJECT_NOT_FOUND,
9327 tr("Could not find a storage controller named '%s'"),
9328 aName.c_str());
9329 return VBOX_E_OBJECT_NOT_FOUND;
9330}
9331
9332/**
9333 * Returns a USB controller object with the given name.
9334 *
9335 * @param aName USB controller name to find
9336 * @param aUSBController where to return the found USB controller
9337 * @param aSetError true to set extended error info on failure
9338 */
9339HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9340 ComObjPtr<USBController> &aUSBController,
9341 bool aSetError /* = false */)
9342{
9343 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9344
9345 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9346 it != mUSBControllers->end();
9347 ++it)
9348 {
9349 if ((*it)->i_getName() == aName)
9350 {
9351 aUSBController = (*it);
9352 return S_OK;
9353 }
9354 }
9355
9356 if (aSetError)
9357 return setError(VBOX_E_OBJECT_NOT_FOUND,
9358 tr("Could not find a storage controller named '%s'"),
9359 aName.c_str());
9360 return VBOX_E_OBJECT_NOT_FOUND;
9361}
9362
9363/**
9364 * Returns the number of USB controller instance of the given type.
9365 *
9366 * @param enmType USB controller type.
9367 */
9368ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9369{
9370 ULONG cCtrls = 0;
9371
9372 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9373 it != mUSBControllers->end();
9374 ++it)
9375 {
9376 if ((*it)->i_getControllerType() == enmType)
9377 cCtrls++;
9378 }
9379
9380 return cCtrls;
9381}
9382
9383HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9384 MediaData::AttachmentList &atts)
9385{
9386 AutoCaller autoCaller(this);
9387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9388
9389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9390
9391 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9392 it != mMediaData->mAttachments.end();
9393 ++it)
9394 {
9395 const ComObjPtr<MediumAttachment> &pAtt = *it;
9396 // should never happen, but deal with NULL pointers in the list.
9397 AssertStmt(!pAtt.isNull(), continue);
9398
9399 // getControllerName() needs caller+read lock
9400 AutoCaller autoAttCaller(pAtt);
9401 if (FAILED(autoAttCaller.rc()))
9402 {
9403 atts.clear();
9404 return autoAttCaller.rc();
9405 }
9406 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9407
9408 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9409 atts.push_back(pAtt);
9410 }
9411
9412 return S_OK;
9413}
9414
9415
9416/**
9417 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9418 * file if the machine name was changed and about creating a new settings file
9419 * if this is a new machine.
9420 *
9421 * @note Must be never called directly but only from #saveSettings().
9422 */
9423HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9424{
9425 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9426
9427 HRESULT rc = S_OK;
9428
9429 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9430
9431 /// @todo need to handle primary group change, too
9432
9433 /* attempt to rename the settings file if machine name is changed */
9434 if ( mUserData->s.fNameSync
9435 && mUserData.isBackedUp()
9436 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9437 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9438 )
9439 {
9440 bool dirRenamed = false;
9441 bool fileRenamed = false;
9442
9443 Utf8Str configFile, newConfigFile;
9444 Utf8Str configFilePrev, newConfigFilePrev;
9445 Utf8Str configDir, newConfigDir;
9446
9447 do
9448 {
9449 int vrc = VINF_SUCCESS;
9450
9451 Utf8Str name = mUserData.backedUpData()->s.strName;
9452 Utf8Str newName = mUserData->s.strName;
9453 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9454 if (group == "/")
9455 group.setNull();
9456 Utf8Str newGroup = mUserData->s.llGroups.front();
9457 if (newGroup == "/")
9458 newGroup.setNull();
9459
9460 configFile = mData->m_strConfigFileFull;
9461
9462 /* first, rename the directory if it matches the group and machine name */
9463 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9464 group.c_str(), RTPATH_DELIMITER, name.c_str());
9465 /** @todo hack, make somehow use of ComposeMachineFilename */
9466 if (mUserData->s.fDirectoryIncludesUUID)
9467 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9468 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9469 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9470 /** @todo hack, make somehow use of ComposeMachineFilename */
9471 if (mUserData->s.fDirectoryIncludesUUID)
9472 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9473 configDir = configFile;
9474 configDir.stripFilename();
9475 newConfigDir = configDir;
9476 if ( configDir.length() >= groupPlusName.length()
9477 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9478 groupPlusName.c_str()))
9479 {
9480 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9481 Utf8Str newConfigBaseDir(newConfigDir);
9482 newConfigDir.append(newGroupPlusName);
9483 /* consistency: use \ if appropriate on the platform */
9484 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9485 /* new dir and old dir cannot be equal here because of 'if'
9486 * above and because name != newName */
9487 Assert(configDir != newConfigDir);
9488 if (!fSettingsFileIsNew)
9489 {
9490 /* perform real rename only if the machine is not new */
9491 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9492 if ( vrc == VERR_FILE_NOT_FOUND
9493 || vrc == VERR_PATH_NOT_FOUND)
9494 {
9495 /* create the parent directory, then retry renaming */
9496 Utf8Str parent(newConfigDir);
9497 parent.stripFilename();
9498 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9499 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9500 }
9501 if (RT_FAILURE(vrc))
9502 {
9503 rc = setError(E_FAIL,
9504 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9505 configDir.c_str(),
9506 newConfigDir.c_str(),
9507 vrc);
9508 break;
9509 }
9510 /* delete subdirectories which are no longer needed */
9511 Utf8Str dir(configDir);
9512 dir.stripFilename();
9513 while (dir != newConfigBaseDir && dir != ".")
9514 {
9515 vrc = RTDirRemove(dir.c_str());
9516 if (RT_FAILURE(vrc))
9517 break;
9518 dir.stripFilename();
9519 }
9520 dirRenamed = true;
9521 }
9522 }
9523
9524 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9525 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9526
9527 /* then try to rename the settings file itself */
9528 if (newConfigFile != configFile)
9529 {
9530 /* get the path to old settings file in renamed directory */
9531 configFile = Utf8StrFmt("%s%c%s",
9532 newConfigDir.c_str(),
9533 RTPATH_DELIMITER,
9534 RTPathFilename(configFile.c_str()));
9535 if (!fSettingsFileIsNew)
9536 {
9537 /* perform real rename only if the machine is not new */
9538 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9539 if (RT_FAILURE(vrc))
9540 {
9541 rc = setError(E_FAIL,
9542 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9543 configFile.c_str(),
9544 newConfigFile.c_str(),
9545 vrc);
9546 break;
9547 }
9548 fileRenamed = true;
9549 configFilePrev = configFile;
9550 configFilePrev += "-prev";
9551 newConfigFilePrev = newConfigFile;
9552 newConfigFilePrev += "-prev";
9553 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9554 }
9555 }
9556
9557 // update m_strConfigFileFull amd mConfigFile
9558 mData->m_strConfigFileFull = newConfigFile;
9559 // compute the relative path too
9560 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9561
9562 // store the old and new so that VirtualBox::i_saveSettings() can update
9563 // the media registry
9564 if ( mData->mRegistered
9565 && (configDir != newConfigDir || configFile != newConfigFile))
9566 {
9567 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9568
9569 if (pfNeedsGlobalSaveSettings)
9570 *pfNeedsGlobalSaveSettings = true;
9571 }
9572
9573 // in the saved state file path, replace the old directory with the new directory
9574 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9575 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9576
9577 // and do the same thing for the saved state file paths of all the online snapshots
9578 if (mData->mFirstSnapshot)
9579 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9580 newConfigDir.c_str());
9581 }
9582 while (0);
9583
9584 if (FAILED(rc))
9585 {
9586 /* silently try to rename everything back */
9587 if (fileRenamed)
9588 {
9589 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9590 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9591 }
9592 if (dirRenamed)
9593 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9594 }
9595
9596 if (FAILED(rc)) return rc;
9597 }
9598
9599 if (fSettingsFileIsNew)
9600 {
9601 /* create a virgin config file */
9602 int vrc = VINF_SUCCESS;
9603
9604 /* ensure the settings directory exists */
9605 Utf8Str path(mData->m_strConfigFileFull);
9606 path.stripFilename();
9607 if (!RTDirExists(path.c_str()))
9608 {
9609 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9610 if (RT_FAILURE(vrc))
9611 {
9612 return setError(E_FAIL,
9613 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9614 path.c_str(),
9615 vrc);
9616 }
9617 }
9618
9619 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9620 path = Utf8Str(mData->m_strConfigFileFull);
9621 RTFILE f = NIL_RTFILE;
9622 vrc = RTFileOpen(&f, path.c_str(),
9623 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9624 if (RT_FAILURE(vrc))
9625 return setError(E_FAIL,
9626 tr("Could not create the settings file '%s' (%Rrc)"),
9627 path.c_str(),
9628 vrc);
9629 RTFileClose(f);
9630 }
9631
9632 return rc;
9633}
9634
9635/**
9636 * Saves and commits machine data, user data and hardware data.
9637 *
9638 * Note that on failure, the data remains uncommitted.
9639 *
9640 * @a aFlags may combine the following flags:
9641 *
9642 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9643 * Used when saving settings after an operation that makes them 100%
9644 * correspond to the settings from the current snapshot.
9645 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9646 * #isReallyModified() returns false. This is necessary for cases when we
9647 * change machine data directly, not through the backup()/commit() mechanism.
9648 * - SaveS_Force: settings will be saved without doing a deep compare of the
9649 * settings structures. This is used when this is called because snapshots
9650 * have changed to avoid the overhead of the deep compare.
9651 *
9652 * @note Must be called from under this object's write lock. Locks children for
9653 * writing.
9654 *
9655 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9656 * initialized to false and that will be set to true by this function if
9657 * the caller must invoke VirtualBox::i_saveSettings() because the global
9658 * settings have changed. This will happen if a machine rename has been
9659 * saved and the global machine and media registries will therefore need
9660 * updating.
9661 */
9662HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9663 int aFlags /*= 0*/)
9664{
9665 LogFlowThisFuncEnter();
9666
9667 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9668
9669 /* make sure child objects are unable to modify the settings while we are
9670 * saving them */
9671 i_ensureNoStateDependencies();
9672
9673 AssertReturn(!i_isSnapshotMachine(),
9674 E_FAIL);
9675
9676 HRESULT rc = S_OK;
9677 bool fNeedsWrite = false;
9678
9679 /* First, prepare to save settings. It will care about renaming the
9680 * settings directory and file if the machine name was changed and about
9681 * creating a new settings file if this is a new machine. */
9682 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9683 if (FAILED(rc)) return rc;
9684
9685 // keep a pointer to the current settings structures
9686 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9687 settings::MachineConfigFile *pNewConfig = NULL;
9688
9689 try
9690 {
9691 // make a fresh one to have everyone write stuff into
9692 pNewConfig = new settings::MachineConfigFile(NULL);
9693 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9694
9695 // now go and copy all the settings data from COM to the settings structures
9696 // (this calles i_saveSettings() on all the COM objects in the machine)
9697 i_copyMachineDataToSettings(*pNewConfig);
9698
9699 if (aFlags & SaveS_ResetCurStateModified)
9700 {
9701 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9702 mData->mCurrentStateModified = FALSE;
9703 fNeedsWrite = true; // always, no need to compare
9704 }
9705 else if (aFlags & SaveS_Force)
9706 {
9707 fNeedsWrite = true; // always, no need to compare
9708 }
9709 else
9710 {
9711 if (!mData->mCurrentStateModified)
9712 {
9713 // do a deep compare of the settings that we just saved with the settings
9714 // previously stored in the config file; this invokes MachineConfigFile::operator==
9715 // which does a deep compare of all the settings, which is expensive but less expensive
9716 // than writing out XML in vain
9717 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9718
9719 // could still be modified if any settings changed
9720 mData->mCurrentStateModified = fAnySettingsChanged;
9721
9722 fNeedsWrite = fAnySettingsChanged;
9723 }
9724 else
9725 fNeedsWrite = true;
9726 }
9727
9728 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9729
9730 if (fNeedsWrite)
9731 // now spit it all out!
9732 pNewConfig->write(mData->m_strConfigFileFull);
9733
9734 mData->pMachineConfigFile = pNewConfig;
9735 delete pOldConfig;
9736 i_commit();
9737
9738 // after saving settings, we are no longer different from the XML on disk
9739 mData->flModifications = 0;
9740 }
9741 catch (HRESULT err)
9742 {
9743 // we assume that error info is set by the thrower
9744 rc = err;
9745
9746 // restore old config
9747 delete pNewConfig;
9748 mData->pMachineConfigFile = pOldConfig;
9749 }
9750 catch (...)
9751 {
9752 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9753 }
9754
9755 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9756 {
9757 /* Fire the data change event, even on failure (since we've already
9758 * committed all data). This is done only for SessionMachines because
9759 * mutable Machine instances are always not registered (i.e. private
9760 * to the client process that creates them) and thus don't need to
9761 * inform callbacks. */
9762 if (i_isSessionMachine())
9763 mParent->i_onMachineDataChange(mData->mUuid);
9764 }
9765
9766 LogFlowThisFunc(("rc=%08X\n", rc));
9767 LogFlowThisFuncLeave();
9768 return rc;
9769}
9770
9771/**
9772 * Implementation for saving the machine settings into the given
9773 * settings::MachineConfigFile instance. This copies machine extradata
9774 * from the previous machine config file in the instance data, if any.
9775 *
9776 * This gets called from two locations:
9777 *
9778 * -- Machine::i_saveSettings(), during the regular XML writing;
9779 *
9780 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9781 * exported to OVF and we write the VirtualBox proprietary XML
9782 * into a <vbox:Machine> tag.
9783 *
9784 * This routine fills all the fields in there, including snapshots, *except*
9785 * for the following:
9786 *
9787 * -- fCurrentStateModified. There is some special logic associated with that.
9788 *
9789 * The caller can then call MachineConfigFile::write() or do something else
9790 * with it.
9791 *
9792 * Caller must hold the machine lock!
9793 *
9794 * This throws XML errors and HRESULT, so the caller must have a catch block!
9795 */
9796void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9797{
9798 // deep copy extradata
9799 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9800
9801 config.uuid = mData->mUuid;
9802
9803 // copy name, description, OS type, teleport, UTC etc.
9804 config.machineUserData = mUserData->s;
9805
9806 // Encode the Icon Override data from Machine and store on config userdata.
9807 std::vector<BYTE> iconByte;
9808 getIcon(iconByte);
9809 ssize_t cbData = iconByte.size();
9810 if (cbData > 0)
9811 {
9812 ssize_t cchOut = RTBase64EncodedLength(cbData);
9813 Utf8Str strIconData;
9814 strIconData.reserve(cchOut+1);
9815 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9816 strIconData.mutableRaw(), strIconData.capacity(),
9817 NULL);
9818 if (RT_FAILURE(vrc))
9819 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9820 strIconData.jolt();
9821 config.machineUserData.ovIcon = strIconData;
9822 }
9823 else
9824 config.machineUserData.ovIcon.setNull();
9825
9826 if ( mData->mMachineState == MachineState_Saved
9827 || mData->mMachineState == MachineState_Restoring
9828 // when deleting a snapshot we may or may not have a saved state in the current state,
9829 // so let's not assert here please
9830 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9831 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9832 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9833 && (!mSSData->strStateFilePath.isEmpty())
9834 )
9835 )
9836 {
9837 Assert(!mSSData->strStateFilePath.isEmpty());
9838 /* try to make the file name relative to the settings file dir */
9839 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9840 }
9841 else
9842 {
9843 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9844 config.strStateFile.setNull();
9845 }
9846
9847 if (mData->mCurrentSnapshot)
9848 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9849 else
9850 config.uuidCurrentSnapshot.clear();
9851
9852 config.timeLastStateChange = mData->mLastStateChange;
9853 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9854 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9855
9856 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9857 if (FAILED(rc)) throw rc;
9858
9859 rc = i_saveStorageControllers(config.storageMachine);
9860 if (FAILED(rc)) throw rc;
9861
9862 // save machine's media registry if this is VirtualBox 4.0 or later
9863 if (config.canHaveOwnMediaRegistry())
9864 {
9865 // determine machine folder
9866 Utf8Str strMachineFolder = i_getSettingsFileFull();
9867 strMachineFolder.stripFilename();
9868 mParent->i_saveMediaRegistry(config.mediaRegistry,
9869 i_getId(), // only media with registry ID == machine UUID
9870 strMachineFolder);
9871 // this throws HRESULT
9872 }
9873
9874 // save snapshots
9875 rc = i_saveAllSnapshots(config);
9876 if (FAILED(rc)) throw rc;
9877}
9878
9879/**
9880 * Saves all snapshots of the machine into the given machine config file. Called
9881 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9882 * @param config
9883 * @return
9884 */
9885HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9886{
9887 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9888
9889 HRESULT rc = S_OK;
9890
9891 try
9892 {
9893 config.llFirstSnapshot.clear();
9894
9895 if (mData->mFirstSnapshot)
9896 {
9897 settings::Snapshot snapNew;
9898 config.llFirstSnapshot.push_back(snapNew);
9899
9900 // get reference to the fresh copy of the snapshot on the list and
9901 // work on that copy directly to avoid excessive copying later
9902 settings::Snapshot &snap = config.llFirstSnapshot.front();
9903
9904 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9905 if (FAILED(rc)) throw rc;
9906 }
9907
9908// if (mType == IsSessionMachine)
9909// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9910
9911 }
9912 catch (HRESULT err)
9913 {
9914 /* we assume that error info is set by the thrower */
9915 rc = err;
9916 }
9917 catch (...)
9918 {
9919 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9920 }
9921
9922 return rc;
9923}
9924
9925/**
9926 * Saves the VM hardware configuration. It is assumed that the
9927 * given node is empty.
9928 *
9929 * @param data Reference to the settings object for the hardware config.
9930 * @param pDbg Pointer to the settings object for the debugging config
9931 * which happens to live in mHWData.
9932 * @param pAutostart Pointer to the settings object for the autostart config
9933 * which happens to live in mHWData.
9934 */
9935HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9936 settings::Autostart *pAutostart)
9937{
9938 HRESULT rc = S_OK;
9939
9940 try
9941 {
9942 /* The hardware version attribute (optional).
9943 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9944 if ( mHWData->mHWVersion == "1"
9945 && mSSData->strStateFilePath.isEmpty()
9946 )
9947 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9948 other point needs to be found where this can be done. */
9949
9950 data.strVersion = mHWData->mHWVersion;
9951 data.uuid = mHWData->mHardwareUUID;
9952
9953 // CPU
9954 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9955 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9956 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9957 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9958 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9959 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9960 data.fPAE = !!mHWData->mPAEEnabled;
9961 data.enmLongMode = mHWData->mLongMode;
9962 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9963 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9964
9965 /* Standard and Extended CPUID leafs. */
9966 data.llCpuIdLeafs.clear();
9967 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9968 {
9969 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9970 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9971 }
9972 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9973 {
9974 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9975 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9976 }
9977
9978 data.cCPUs = mHWData->mCPUCount;
9979 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9980 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9981
9982 data.llCpus.clear();
9983 if (data.fCpuHotPlug)
9984 {
9985 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9986 {
9987 if (mHWData->mCPUAttached[idx])
9988 {
9989 settings::Cpu cpu;
9990 cpu.ulId = idx;
9991 data.llCpus.push_back(cpu);
9992 }
9993 }
9994 }
9995
9996 // memory
9997 data.ulMemorySizeMB = mHWData->mMemorySize;
9998 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9999
10000 // firmware
10001 data.firmwareType = mHWData->mFirmwareType;
10002
10003 // HID
10004 data.pointingHIDType = mHWData->mPointingHIDType;
10005 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10006
10007 // chipset
10008 data.chipsetType = mHWData->mChipsetType;
10009
10010 // paravirt
10011 data.paravirtProvider = mHWData->mParavirtProvider;
10012
10013
10014 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10015
10016 // HPET
10017 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10018
10019 // boot order
10020 data.mapBootOrder.clear();
10021 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10022 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10023
10024 // display
10025 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10026 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10027 data.cMonitors = mHWData->mMonitorCount;
10028 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10029 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10030 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10031 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10032 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10033 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10034 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10035 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10036 {
10037 if (mHWData->maVideoCaptureScreens[i])
10038 ASMBitSet(&data.u64VideoCaptureScreens, i);
10039 else
10040 ASMBitClear(&data.u64VideoCaptureScreens, i);
10041 }
10042 /* store relative video capture file if possible */
10043 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10044
10045 /* VRDEServer settings (optional) */
10046 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10047 if (FAILED(rc)) throw rc;
10048
10049 /* BIOS (required) */
10050 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10051 if (FAILED(rc)) throw rc;
10052
10053 /* USB Controller (required) */
10054 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10055 {
10056 ComObjPtr<USBController> ctrl = *it;
10057 settings::USBController settingsCtrl;
10058
10059 settingsCtrl.strName = ctrl->i_getName();
10060 settingsCtrl.enmType = ctrl->i_getControllerType();
10061
10062 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10063 }
10064
10065 /* USB device filters (required) */
10066 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10067 if (FAILED(rc)) throw rc;
10068
10069 /* Network adapters (required) */
10070 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10071 data.llNetworkAdapters.clear();
10072 /* Write out only the nominal number of network adapters for this
10073 * chipset type. Since Machine::commit() hasn't been called there
10074 * may be extra NIC settings in the vector. */
10075 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10076 {
10077 settings::NetworkAdapter nic;
10078 nic.ulSlot = (uint32_t)slot;
10079 /* paranoia check... must not be NULL, but must not crash either. */
10080 if (mNetworkAdapters[slot])
10081 {
10082 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10083 if (FAILED(rc)) throw rc;
10084
10085 data.llNetworkAdapters.push_back(nic);
10086 }
10087 }
10088
10089 /* Serial ports */
10090 data.llSerialPorts.clear();
10091 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10092 {
10093 settings::SerialPort s;
10094 s.ulSlot = slot;
10095 rc = mSerialPorts[slot]->i_saveSettings(s);
10096 if (FAILED(rc)) return rc;
10097
10098 data.llSerialPorts.push_back(s);
10099 }
10100
10101 /* Parallel ports */
10102 data.llParallelPorts.clear();
10103 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10104 {
10105 settings::ParallelPort p;
10106 p.ulSlot = slot;
10107 rc = mParallelPorts[slot]->i_saveSettings(p);
10108 if (FAILED(rc)) return rc;
10109
10110 data.llParallelPorts.push_back(p);
10111 }
10112
10113 /* Audio adapter */
10114 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10115 if (FAILED(rc)) return rc;
10116
10117 /* Shared folders */
10118 data.llSharedFolders.clear();
10119 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10120 it != mHWData->mSharedFolders.end();
10121 ++it)
10122 {
10123 SharedFolder *pSF = *it;
10124 AutoCaller sfCaller(pSF);
10125 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10126 settings::SharedFolder sf;
10127 sf.strName = pSF->i_getName();
10128 sf.strHostPath = pSF->i_getHostPath();
10129 sf.fWritable = !!pSF->i_isWritable();
10130 sf.fAutoMount = !!pSF->i_isAutoMounted();
10131
10132 data.llSharedFolders.push_back(sf);
10133 }
10134
10135 // clipboard
10136 data.clipboardMode = mHWData->mClipboardMode;
10137
10138 // drag'n'drop
10139 data.dndMode = mHWData->mDnDMode;
10140
10141 /* Guest */
10142 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10143
10144 // IO settings
10145 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10146 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10147
10148 /* BandwidthControl (required) */
10149 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10150 if (FAILED(rc)) throw rc;
10151
10152 /* Host PCI devices */
10153 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10154 it != mHWData->mPCIDeviceAssignments.end();
10155 ++it)
10156 {
10157 ComObjPtr<PCIDeviceAttachment> pda = *it;
10158 settings::HostPCIDeviceAttachment hpda;
10159
10160 rc = pda->i_saveSettings(hpda);
10161 if (FAILED(rc)) throw rc;
10162
10163 data.pciAttachments.push_back(hpda);
10164 }
10165
10166
10167 // guest properties
10168 data.llGuestProperties.clear();
10169#ifdef VBOX_WITH_GUEST_PROPS
10170 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10171 it != mHWData->mGuestProperties.end();
10172 ++it)
10173 {
10174 HWData::GuestProperty property = it->second;
10175
10176 /* Remove transient guest properties at shutdown unless we
10177 * are saving state */
10178 if ( ( mData->mMachineState == MachineState_PoweredOff
10179 || mData->mMachineState == MachineState_Aborted
10180 || mData->mMachineState == MachineState_Teleported)
10181 && ( property.mFlags & guestProp::TRANSIENT
10182 || property.mFlags & guestProp::TRANSRESET))
10183 continue;
10184 settings::GuestProperty prop;
10185 prop.strName = it->first;
10186 prop.strValue = property.strValue;
10187 prop.timestamp = property.mTimestamp;
10188 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10189 guestProp::writeFlags(property.mFlags, szFlags);
10190 prop.strFlags = szFlags;
10191
10192 data.llGuestProperties.push_back(prop);
10193 }
10194
10195 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10196 /* I presume this doesn't require a backup(). */
10197 mData->mGuestPropertiesModified = FALSE;
10198#endif /* VBOX_WITH_GUEST_PROPS defined */
10199
10200 *pDbg = mHWData->mDebugging;
10201 *pAutostart = mHWData->mAutostart;
10202
10203 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10204 }
10205 catch(std::bad_alloc &)
10206 {
10207 return E_OUTOFMEMORY;
10208 }
10209
10210 AssertComRC(rc);
10211 return rc;
10212}
10213
10214/**
10215 * Saves the storage controller configuration.
10216 *
10217 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10218 */
10219HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10220{
10221 data.llStorageControllers.clear();
10222
10223 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10224 it != mStorageControllers->end();
10225 ++it)
10226 {
10227 HRESULT rc;
10228 ComObjPtr<StorageController> pCtl = *it;
10229
10230 settings::StorageController ctl;
10231 ctl.strName = pCtl->i_getName();
10232 ctl.controllerType = pCtl->i_getControllerType();
10233 ctl.storageBus = pCtl->i_getStorageBus();
10234 ctl.ulInstance = pCtl->i_getInstance();
10235 ctl.fBootable = pCtl->i_getBootable();
10236
10237 /* Save the port count. */
10238 ULONG portCount;
10239 rc = pCtl->COMGETTER(PortCount)(&portCount);
10240 ComAssertComRCRet(rc, rc);
10241 ctl.ulPortCount = portCount;
10242
10243 /* Save fUseHostIOCache */
10244 BOOL fUseHostIOCache;
10245 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10246 ComAssertComRCRet(rc, rc);
10247 ctl.fUseHostIOCache = !!fUseHostIOCache;
10248
10249 /* Save IDE emulation settings. */
10250 if (ctl.controllerType == StorageControllerType_IntelAhci)
10251 {
10252 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10253 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10254 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10255 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10256 )
10257 ComAssertComRCRet(rc, rc);
10258 }
10259
10260 /* save the devices now. */
10261 rc = i_saveStorageDevices(pCtl, ctl);
10262 ComAssertComRCRet(rc, rc);
10263
10264 data.llStorageControllers.push_back(ctl);
10265 }
10266
10267 return S_OK;
10268}
10269
10270/**
10271 * Saves the hard disk configuration.
10272 */
10273HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10274 settings::StorageController &data)
10275{
10276 MediaData::AttachmentList atts;
10277
10278 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10279 if (FAILED(rc)) return rc;
10280
10281 data.llAttachedDevices.clear();
10282 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10283 it != atts.end();
10284 ++it)
10285 {
10286 settings::AttachedDevice dev;
10287 IMediumAttachment *iA = *it;
10288 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10289 Medium *pMedium = pAttach->i_getMedium();
10290
10291 dev.deviceType = pAttach->i_getType();
10292 dev.lPort = pAttach->i_getPort();
10293 dev.lDevice = pAttach->i_getDevice();
10294 dev.fPassThrough = pAttach->i_getPassthrough();
10295 dev.fHotPluggable = pAttach->i_getHotPluggable();
10296 if (pMedium)
10297 {
10298 if (pMedium->i_isHostDrive())
10299 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10300 else
10301 dev.uuid = pMedium->i_getId();
10302 dev.fTempEject = pAttach->i_getTempEject();
10303 dev.fNonRotational = pAttach->i_getNonRotational();
10304 dev.fDiscard = pAttach->i_getDiscard();
10305 }
10306
10307 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10308
10309 data.llAttachedDevices.push_back(dev);
10310 }
10311
10312 return S_OK;
10313}
10314
10315/**
10316 * Saves machine state settings as defined by aFlags
10317 * (SaveSTS_* values).
10318 *
10319 * @param aFlags Combination of SaveSTS_* flags.
10320 *
10321 * @note Locks objects for writing.
10322 */
10323HRESULT Machine::i_saveStateSettings(int aFlags)
10324{
10325 if (aFlags == 0)
10326 return S_OK;
10327
10328 AutoCaller autoCaller(this);
10329 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10330
10331 /* This object's write lock is also necessary to serialize file access
10332 * (prevent concurrent reads and writes) */
10333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10334
10335 HRESULT rc = S_OK;
10336
10337 Assert(mData->pMachineConfigFile);
10338
10339 try
10340 {
10341 if (aFlags & SaveSTS_CurStateModified)
10342 mData->pMachineConfigFile->fCurrentStateModified = true;
10343
10344 if (aFlags & SaveSTS_StateFilePath)
10345 {
10346 if (!mSSData->strStateFilePath.isEmpty())
10347 /* try to make the file name relative to the settings file dir */
10348 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10349 else
10350 mData->pMachineConfigFile->strStateFile.setNull();
10351 }
10352
10353 if (aFlags & SaveSTS_StateTimeStamp)
10354 {
10355 Assert( mData->mMachineState != MachineState_Aborted
10356 || mSSData->strStateFilePath.isEmpty());
10357
10358 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10359
10360 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10361//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10362 }
10363
10364 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10365 }
10366 catch (...)
10367 {
10368 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10369 }
10370
10371 return rc;
10372}
10373
10374/**
10375 * Ensures that the given medium is added to a media registry. If this machine
10376 * was created with 4.0 or later, then the machine registry is used. Otherwise
10377 * the global VirtualBox media registry is used.
10378 *
10379 * Caller must NOT hold machine lock, media tree or any medium locks!
10380 *
10381 * @param pMedium
10382 */
10383void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10384{
10385 /* Paranoia checks: do not hold machine or media tree locks. */
10386 AssertReturnVoid(!isWriteLockOnCurrentThread());
10387 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10388
10389 ComObjPtr<Medium> pBase;
10390 {
10391 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10392 pBase = pMedium->i_getBase();
10393 }
10394
10395 /* Paranoia checks: do not hold medium locks. */
10396 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10397 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10398
10399 // decide which medium registry to use now that the medium is attached:
10400 Guid uuid;
10401 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10402 // machine XML is VirtualBox 4.0 or higher:
10403 uuid = i_getId(); // machine UUID
10404 else
10405 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10406
10407 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10408 mParent->i_markRegistryModified(uuid);
10409
10410 /* For more complex hard disk structures it can happen that the base
10411 * medium isn't yet associated with any medium registry. Do that now. */
10412 if (pMedium != pBase)
10413 {
10414 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10415 mParent->i_markRegistryModified(uuid);
10416 }
10417}
10418
10419/**
10420 * Creates differencing hard disks for all normal hard disks attached to this
10421 * machine and a new set of attachments to refer to created disks.
10422 *
10423 * Used when taking a snapshot or when deleting the current state. Gets called
10424 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10425 *
10426 * This method assumes that mMediaData contains the original hard disk attachments
10427 * it needs to create diffs for. On success, these attachments will be replaced
10428 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10429 * called to delete created diffs which will also rollback mMediaData and restore
10430 * whatever was backed up before calling this method.
10431 *
10432 * Attachments with non-normal hard disks are left as is.
10433 *
10434 * If @a aOnline is @c false then the original hard disks that require implicit
10435 * diffs will be locked for reading. Otherwise it is assumed that they are
10436 * already locked for writing (when the VM was started). Note that in the latter
10437 * case it is responsibility of the caller to lock the newly created diffs for
10438 * writing if this method succeeds.
10439 *
10440 * @param aProgress Progress object to run (must contain at least as
10441 * many operations left as the number of hard disks
10442 * attached).
10443 * @param aOnline Whether the VM was online prior to this operation.
10444 *
10445 * @note The progress object is not marked as completed, neither on success nor
10446 * on failure. This is a responsibility of the caller.
10447 *
10448 * @note Locks this object and the media tree for writing.
10449 */
10450HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10451 ULONG aWeight,
10452 bool aOnline)
10453{
10454 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10455
10456 AutoCaller autoCaller(this);
10457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10458
10459 AutoMultiWriteLock2 alock(this->lockHandle(),
10460 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10461
10462 /* must be in a protective state because we release the lock below */
10463 AssertReturn( mData->mMachineState == MachineState_Saving
10464 || mData->mMachineState == MachineState_LiveSnapshotting
10465 || mData->mMachineState == MachineState_RestoringSnapshot
10466 || mData->mMachineState == MachineState_DeletingSnapshot
10467 , E_FAIL);
10468
10469 HRESULT rc = S_OK;
10470
10471 // use appropriate locked media map (online or offline)
10472 MediumLockListMap lockedMediaOffline;
10473 MediumLockListMap *lockedMediaMap;
10474 if (aOnline)
10475 lockedMediaMap = &mData->mSession.mLockedMedia;
10476 else
10477 lockedMediaMap = &lockedMediaOffline;
10478
10479 try
10480 {
10481 if (!aOnline)
10482 {
10483 /* lock all attached hard disks early to detect "in use"
10484 * situations before creating actual diffs */
10485 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10486 it != mMediaData->mAttachments.end();
10487 ++it)
10488 {
10489 MediumAttachment* pAtt = *it;
10490 if (pAtt->i_getType() == DeviceType_HardDisk)
10491 {
10492 Medium* pMedium = pAtt->i_getMedium();
10493 Assert(pMedium);
10494
10495 MediumLockList *pMediumLockList(new MediumLockList());
10496 alock.release();
10497 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10498 false /* fMediumLockWrite */,
10499 NULL,
10500 *pMediumLockList);
10501 alock.acquire();
10502 if (FAILED(rc))
10503 {
10504 delete pMediumLockList;
10505 throw rc;
10506 }
10507 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10508 if (FAILED(rc))
10509 {
10510 throw setError(rc,
10511 tr("Collecting locking information for all attached media failed"));
10512 }
10513 }
10514 }
10515
10516 /* Now lock all media. If this fails, nothing is locked. */
10517 alock.release();
10518 rc = lockedMediaMap->Lock();
10519 alock.acquire();
10520 if (FAILED(rc))
10521 {
10522 throw setError(rc,
10523 tr("Locking of attached media failed"));
10524 }
10525 }
10526
10527 /* remember the current list (note that we don't use backup() since
10528 * mMediaData may be already backed up) */
10529 MediaData::AttachmentList atts = mMediaData->mAttachments;
10530
10531 /* start from scratch */
10532 mMediaData->mAttachments.clear();
10533
10534 /* go through remembered attachments and create diffs for normal hard
10535 * disks and attach them */
10536 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10537 it != atts.end();
10538 ++it)
10539 {
10540 MediumAttachment* pAtt = *it;
10541
10542 DeviceType_T devType = pAtt->i_getType();
10543 Medium* pMedium = pAtt->i_getMedium();
10544
10545 if ( devType != DeviceType_HardDisk
10546 || pMedium == NULL
10547 || pMedium->i_getType() != MediumType_Normal)
10548 {
10549 /* copy the attachment as is */
10550
10551 /** @todo the progress object created in Console::TakeSnaphot
10552 * only expects operations for hard disks. Later other
10553 * device types need to show up in the progress as well. */
10554 if (devType == DeviceType_HardDisk)
10555 {
10556 if (pMedium == NULL)
10557 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10558 aWeight); // weight
10559 else
10560 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10561 pMedium->i_getBase()->i_getName().c_str()).raw(),
10562 aWeight); // weight
10563 }
10564
10565 mMediaData->mAttachments.push_back(pAtt);
10566 continue;
10567 }
10568
10569 /* need a diff */
10570 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10571 pMedium->i_getBase()->i_getName().c_str()).raw(),
10572 aWeight); // weight
10573
10574 Utf8Str strFullSnapshotFolder;
10575 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10576
10577 ComObjPtr<Medium> diff;
10578 diff.createObject();
10579 // store the diff in the same registry as the parent
10580 // (this cannot fail here because we can't create implicit diffs for
10581 // unregistered images)
10582 Guid uuidRegistryParent;
10583 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10584 Assert(fInRegistry); NOREF(fInRegistry);
10585 rc = diff->init(mParent,
10586 pMedium->i_getPreferredDiffFormat(),
10587 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10588 uuidRegistryParent);
10589 if (FAILED(rc)) throw rc;
10590
10591 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10592 * the push_back? Looks like we're going to release medium with the
10593 * wrong kind of lock (general issue with if we fail anywhere at all)
10594 * and an orphaned VDI in the snapshots folder. */
10595
10596 /* update the appropriate lock list */
10597 MediumLockList *pMediumLockList;
10598 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10599 AssertComRCThrowRC(rc);
10600 if (aOnline)
10601 {
10602 alock.release();
10603 /* The currently attached medium will be read-only, change
10604 * the lock type to read. */
10605 rc = pMediumLockList->Update(pMedium, false);
10606 alock.acquire();
10607 AssertComRCThrowRC(rc);
10608 }
10609
10610 /* release the locks before the potentially lengthy operation */
10611 alock.release();
10612 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10613 pMediumLockList,
10614 NULL /* aProgress */,
10615 true /* aWait */);
10616 alock.acquire();
10617 if (FAILED(rc)) throw rc;
10618
10619 /* actual lock list update is done in Medium::commitMedia */
10620
10621 rc = diff->i_addBackReference(mData->mUuid);
10622 AssertComRCThrowRC(rc);
10623
10624 /* add a new attachment */
10625 ComObjPtr<MediumAttachment> attachment;
10626 attachment.createObject();
10627 rc = attachment->init(this,
10628 diff,
10629 pAtt->i_getControllerName(),
10630 pAtt->i_getPort(),
10631 pAtt->i_getDevice(),
10632 DeviceType_HardDisk,
10633 true /* aImplicit */,
10634 false /* aPassthrough */,
10635 false /* aTempEject */,
10636 pAtt->i_getNonRotational(),
10637 pAtt->i_getDiscard(),
10638 pAtt->i_getHotPluggable(),
10639 pAtt->i_getBandwidthGroup());
10640 if (FAILED(rc)) throw rc;
10641
10642 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10643 AssertComRCThrowRC(rc);
10644 mMediaData->mAttachments.push_back(attachment);
10645 }
10646 }
10647 catch (HRESULT aRC) { rc = aRC; }
10648
10649 /* unlock all hard disks we locked when there is no VM */
10650 if (!aOnline)
10651 {
10652 ErrorInfoKeeper eik;
10653
10654 HRESULT rc1 = lockedMediaMap->Clear();
10655 AssertComRC(rc1);
10656 }
10657
10658 return rc;
10659}
10660
10661/**
10662 * Deletes implicit differencing hard disks created either by
10663 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10664 *
10665 * Note that to delete hard disks created by #AttachDevice() this method is
10666 * called from #fixupMedia() when the changes are rolled back.
10667 *
10668 * @note Locks this object and the media tree for writing.
10669 */
10670HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10671{
10672 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10673
10674 AutoCaller autoCaller(this);
10675 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10676
10677 AutoMultiWriteLock2 alock(this->lockHandle(),
10678 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10679
10680 /* We absolutely must have backed up state. */
10681 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10682
10683 /* Check if there are any implicitly created diff images. */
10684 bool fImplicitDiffs = false;
10685 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10686 it != mMediaData->mAttachments.end();
10687 ++it)
10688 {
10689 const ComObjPtr<MediumAttachment> &pAtt = *it;
10690 if (pAtt->i_isImplicit())
10691 {
10692 fImplicitDiffs = true;
10693 break;
10694 }
10695 }
10696 /* If there is nothing to do, leave early. This saves lots of image locking
10697 * effort. It also avoids a MachineStateChanged event without real reason.
10698 * This is important e.g. when loading a VM config, because there should be
10699 * no events. Otherwise API clients can become thoroughly confused for
10700 * inaccessible VMs (the code for loading VM configs uses this method for
10701 * cleanup if the config makes no sense), as they take such events as an
10702 * indication that the VM is alive, and they would force the VM config to
10703 * be reread, leading to an endless loop. */
10704 if (!fImplicitDiffs)
10705 return S_OK;
10706
10707 HRESULT rc = S_OK;
10708 MachineState_T oldState = mData->mMachineState;
10709
10710 /* will release the lock before the potentially lengthy operation,
10711 * so protect with the special state (unless already protected) */
10712 if ( oldState != MachineState_Saving
10713 && oldState != MachineState_LiveSnapshotting
10714 && oldState != MachineState_RestoringSnapshot
10715 && oldState != MachineState_DeletingSnapshot
10716 && oldState != MachineState_DeletingSnapshotOnline
10717 && oldState != MachineState_DeletingSnapshotPaused
10718 )
10719 i_setMachineState(MachineState_SettingUp);
10720
10721 // use appropriate locked media map (online or offline)
10722 MediumLockListMap lockedMediaOffline;
10723 MediumLockListMap *lockedMediaMap;
10724 if (aOnline)
10725 lockedMediaMap = &mData->mSession.mLockedMedia;
10726 else
10727 lockedMediaMap = &lockedMediaOffline;
10728
10729 try
10730 {
10731 if (!aOnline)
10732 {
10733 /* lock all attached hard disks early to detect "in use"
10734 * situations before deleting actual diffs */
10735 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10736 it != mMediaData->mAttachments.end();
10737 ++it)
10738 {
10739 MediumAttachment* pAtt = *it;
10740 if (pAtt->i_getType() == DeviceType_HardDisk)
10741 {
10742 Medium* pMedium = pAtt->i_getMedium();
10743 Assert(pMedium);
10744
10745 MediumLockList *pMediumLockList(new MediumLockList());
10746 alock.release();
10747 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10748 false /* fMediumLockWrite */,
10749 NULL,
10750 *pMediumLockList);
10751 alock.acquire();
10752
10753 if (FAILED(rc))
10754 {
10755 delete pMediumLockList;
10756 throw rc;
10757 }
10758
10759 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10760 if (FAILED(rc))
10761 throw rc;
10762 }
10763 }
10764
10765 if (FAILED(rc))
10766 throw rc;
10767 } // end of offline
10768
10769 /* Lock lists are now up to date and include implicitly created media */
10770
10771 /* Go through remembered attachments and delete all implicitly created
10772 * diffs and fix up the attachment information */
10773 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10774 MediaData::AttachmentList implicitAtts;
10775 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10776 it != mMediaData->mAttachments.end();
10777 ++it)
10778 {
10779 ComObjPtr<MediumAttachment> pAtt = *it;
10780 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10781 if (pMedium.isNull())
10782 continue;
10783
10784 // Implicit attachments go on the list for deletion and back references are removed.
10785 if (pAtt->i_isImplicit())
10786 {
10787 /* Deassociate and mark for deletion */
10788 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10789 rc = pMedium->i_removeBackReference(mData->mUuid);
10790 if (FAILED(rc))
10791 throw rc;
10792 implicitAtts.push_back(pAtt);
10793 continue;
10794 }
10795
10796 /* Was this medium attached before? */
10797 if (!i_findAttachment(oldAtts, pMedium))
10798 {
10799 /* no: de-associate */
10800 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10801 rc = pMedium->i_removeBackReference(mData->mUuid);
10802 if (FAILED(rc))
10803 throw rc;
10804 continue;
10805 }
10806 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10807 }
10808
10809 /* If there are implicit attachments to delete, throw away the lock
10810 * map contents (which will unlock all media) since the medium
10811 * attachments will be rolled back. Below we need to completely
10812 * recreate the lock map anyway since it is infinitely complex to
10813 * do this incrementally (would need reconstructing each attachment
10814 * change, which would be extremely hairy). */
10815 if (implicitAtts.size() != 0)
10816 {
10817 ErrorInfoKeeper eik;
10818
10819 HRESULT rc1 = lockedMediaMap->Clear();
10820 AssertComRC(rc1);
10821 }
10822
10823 /* rollback hard disk changes */
10824 mMediaData.rollback();
10825
10826 MultiResult mrc(S_OK);
10827
10828 // Delete unused implicit diffs.
10829 if (implicitAtts.size() != 0)
10830 {
10831 alock.release();
10832
10833 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10834 {
10835 // Remove medium associated with this attachment.
10836 ComObjPtr<MediumAttachment> pAtt = *it;
10837 Assert(pAtt);
10838 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10839 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10840 Assert(pMedium);
10841
10842 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10843 // continue on delete failure, just collect error messages
10844 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10845 pMedium->i_getLocationFull().c_str() ));
10846 mrc = rc;
10847 }
10848
10849 alock.acquire();
10850
10851 /* if there is a VM recreate media lock map as mentioned above,
10852 * otherwise it is a waste of time and we leave things unlocked */
10853 if (aOnline)
10854 {
10855 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10856 /* must never be NULL, but better safe than sorry */
10857 if (!pMachine.isNull())
10858 {
10859 alock.release();
10860 rc = mData->mSession.mMachine->i_lockMedia();
10861 alock.acquire();
10862 if (FAILED(rc))
10863 throw rc;
10864 }
10865 }
10866 }
10867 }
10868 catch (HRESULT aRC) {rc = aRC;}
10869
10870 if (mData->mMachineState == MachineState_SettingUp)
10871 i_setMachineState(oldState);
10872
10873 /* unlock all hard disks we locked when there is no VM */
10874 if (!aOnline)
10875 {
10876 ErrorInfoKeeper eik;
10877
10878 HRESULT rc1 = lockedMediaMap->Clear();
10879 AssertComRC(rc1);
10880 }
10881
10882 return rc;
10883}
10884
10885
10886/**
10887 * Looks through the given list of media attachments for one with the given parameters
10888 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10889 * can be searched as well if needed.
10890 *
10891 * @param list
10892 * @param aControllerName
10893 * @param aControllerPort
10894 * @param aDevice
10895 * @return
10896 */
10897MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10898 IN_BSTR aControllerName,
10899 LONG aControllerPort,
10900 LONG aDevice)
10901{
10902 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10903 {
10904 MediumAttachment *pAttach = *it;
10905 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10906 return pAttach;
10907 }
10908
10909 return NULL;
10910}
10911
10912/**
10913 * Looks through the given list of media attachments for one with the given parameters
10914 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10915 * can be searched as well if needed.
10916 *
10917 * @param list
10918 * @param aControllerName
10919 * @param aControllerPort
10920 * @param aDevice
10921 * @return
10922 */
10923MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10924 ComObjPtr<Medium> pMedium)
10925{
10926 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10927 {
10928 MediumAttachment *pAttach = *it;
10929 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10930 if (pMediumThis == pMedium)
10931 return pAttach;
10932 }
10933
10934 return NULL;
10935}
10936
10937/**
10938 * Looks through the given list of media attachments for one with the given parameters
10939 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10940 * can be searched as well if needed.
10941 *
10942 * @param list
10943 * @param aControllerName
10944 * @param aControllerPort
10945 * @param aDevice
10946 * @return
10947 */
10948MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10949 Guid &id)
10950{
10951 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10952 {
10953 MediumAttachment *pAttach = *it;
10954 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10955 if (pMediumThis->i_getId() == id)
10956 return pAttach;
10957 }
10958
10959 return NULL;
10960}
10961
10962/**
10963 * Main implementation for Machine::DetachDevice. This also gets called
10964 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10965 *
10966 * @param pAttach Medium attachment to detach.
10967 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10968 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10969 * SnapshotMachine, and this must be its snapshot.
10970 * @return
10971 */
10972HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10973 AutoWriteLock &writeLock,
10974 Snapshot *pSnapshot)
10975{
10976 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10977 DeviceType_T mediumType = pAttach->i_getType();
10978
10979 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10980
10981 if (pAttach->i_isImplicit())
10982 {
10983 /* attempt to implicitly delete the implicitly created diff */
10984
10985 /// @todo move the implicit flag from MediumAttachment to Medium
10986 /// and forbid any hard disk operation when it is implicit. Or maybe
10987 /// a special media state for it to make it even more simple.
10988
10989 Assert(mMediaData.isBackedUp());
10990
10991 /* will release the lock before the potentially lengthy operation, so
10992 * protect with the special state */
10993 MachineState_T oldState = mData->mMachineState;
10994 i_setMachineState(MachineState_SettingUp);
10995
10996 writeLock.release();
10997
10998 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10999 true /*aWait*/);
11000
11001 writeLock.acquire();
11002
11003 i_setMachineState(oldState);
11004
11005 if (FAILED(rc)) return rc;
11006 }
11007
11008 i_setModified(IsModified_Storage);
11009 mMediaData.backup();
11010 mMediaData->mAttachments.remove(pAttach);
11011
11012 if (!oldmedium.isNull())
11013 {
11014 // if this is from a snapshot, do not defer detachment to commitMedia()
11015 if (pSnapshot)
11016 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11017 // else if non-hard disk media, do not defer detachment to commitMedia() either
11018 else if (mediumType != DeviceType_HardDisk)
11019 oldmedium->i_removeBackReference(mData->mUuid);
11020 }
11021
11022 return S_OK;
11023}
11024
11025/**
11026 * Goes thru all media of the given list and
11027 *
11028 * 1) calls i_detachDevice() on each of them for this machine and
11029 * 2) adds all Medium objects found in the process to the given list,
11030 * depending on cleanupMode.
11031 *
11032 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11033 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11034 * media to the list.
11035 *
11036 * This gets called from Machine::Unregister, both for the actual Machine and
11037 * the SnapshotMachine objects that might be found in the snapshots.
11038 *
11039 * Requires caller and locking. The machine lock must be passed in because it
11040 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11041 *
11042 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11043 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11044 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11045 * Full, then all media get added;
11046 * otherwise no media get added.
11047 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11048 * @return
11049 */
11050HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11051 Snapshot *pSnapshot,
11052 CleanupMode_T cleanupMode,
11053 MediaList &llMedia)
11054{
11055 Assert(isWriteLockOnCurrentThread());
11056
11057 HRESULT rc;
11058
11059 // make a temporary list because i_detachDevice invalidates iterators into
11060 // mMediaData->mAttachments
11061 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11062
11063 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11064 {
11065 ComObjPtr<MediumAttachment> &pAttach = *it;
11066 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11067
11068 if (!pMedium.isNull())
11069 {
11070 AutoCaller mac(pMedium);
11071 if (FAILED(mac.rc())) return mac.rc();
11072 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11073 DeviceType_T devType = pMedium->i_getDeviceType();
11074 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11075 && devType == DeviceType_HardDisk)
11076 || (cleanupMode == CleanupMode_Full)
11077 )
11078 {
11079 llMedia.push_back(pMedium);
11080 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11081 /*
11082 * Search for medias which are not attached to any machine, but
11083 * in the chain to an attached disk. Mediums are only consided
11084 * if they are:
11085 * - have only one child
11086 * - no references to any machines
11087 * - are of normal medium type
11088 */
11089 while (!pParent.isNull())
11090 {
11091 AutoCaller mac1(pParent);
11092 if (FAILED(mac1.rc())) return mac1.rc();
11093 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11094 if (pParent->i_getChildren().size() == 1)
11095 {
11096 if ( pParent->i_getMachineBackRefCount() == 0
11097 && pParent->i_getType() == MediumType_Normal
11098 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11099 llMedia.push_back(pParent);
11100 }
11101 else
11102 break;
11103 pParent = pParent->i_getParent();
11104 }
11105 }
11106 }
11107
11108 // real machine: then we need to use the proper method
11109 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11110
11111 if (FAILED(rc))
11112 return rc;
11113 }
11114
11115 return S_OK;
11116}
11117
11118/**
11119 * Perform deferred hard disk detachments.
11120 *
11121 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11122 * backed up).
11123 *
11124 * If @a aOnline is @c true then this method will also unlock the old hard disks
11125 * for which the new implicit diffs were created and will lock these new diffs for
11126 * writing.
11127 *
11128 * @param aOnline Whether the VM was online prior to this operation.
11129 *
11130 * @note Locks this object for writing!
11131 */
11132void Machine::i_commitMedia(bool aOnline /*= false*/)
11133{
11134 AutoCaller autoCaller(this);
11135 AssertComRCReturnVoid(autoCaller.rc());
11136
11137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11138
11139 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11140
11141 HRESULT rc = S_OK;
11142
11143 /* no attach/detach operations -- nothing to do */
11144 if (!mMediaData.isBackedUp())
11145 return;
11146
11147 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11148 bool fMediaNeedsLocking = false;
11149
11150 /* enumerate new attachments */
11151 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11152 it != mMediaData->mAttachments.end();
11153 ++it)
11154 {
11155 MediumAttachment *pAttach = *it;
11156
11157 pAttach->i_commit();
11158
11159 Medium* pMedium = pAttach->i_getMedium();
11160 bool fImplicit = pAttach->i_isImplicit();
11161
11162 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11163 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11164 fImplicit));
11165
11166 /** @todo convert all this Machine-based voodoo to MediumAttachment
11167 * based commit logic. */
11168 if (fImplicit)
11169 {
11170 /* convert implicit attachment to normal */
11171 pAttach->i_setImplicit(false);
11172
11173 if ( aOnline
11174 && pMedium
11175 && pAttach->i_getType() == DeviceType_HardDisk
11176 )
11177 {
11178 ComObjPtr<Medium> parent = pMedium->i_getParent();
11179 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11180
11181 /* update the appropriate lock list */
11182 MediumLockList *pMediumLockList;
11183 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11184 AssertComRC(rc);
11185 if (pMediumLockList)
11186 {
11187 /* unlock if there's a need to change the locking */
11188 if (!fMediaNeedsLocking)
11189 {
11190 rc = mData->mSession.mLockedMedia.Unlock();
11191 AssertComRC(rc);
11192 fMediaNeedsLocking = true;
11193 }
11194 rc = pMediumLockList->Update(parent, false);
11195 AssertComRC(rc);
11196 rc = pMediumLockList->Append(pMedium, true);
11197 AssertComRC(rc);
11198 }
11199 }
11200
11201 continue;
11202 }
11203
11204 if (pMedium)
11205 {
11206 /* was this medium attached before? */
11207 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11208 {
11209 MediumAttachment *pOldAttach = *oldIt;
11210 if (pOldAttach->i_getMedium() == pMedium)
11211 {
11212 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11213
11214 /* yes: remove from old to avoid de-association */
11215 oldAtts.erase(oldIt);
11216 break;
11217 }
11218 }
11219 }
11220 }
11221
11222 /* enumerate remaining old attachments and de-associate from the
11223 * current machine state */
11224 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11225 {
11226 MediumAttachment *pAttach = *it;
11227 Medium* pMedium = pAttach->i_getMedium();
11228
11229 /* Detach only hard disks, since DVD/floppy media is detached
11230 * instantly in MountMedium. */
11231 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11232 {
11233 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11234
11235 /* now de-associate from the current machine state */
11236 rc = pMedium->i_removeBackReference(mData->mUuid);
11237 AssertComRC(rc);
11238
11239 if (aOnline)
11240 {
11241 /* unlock since medium is not used anymore */
11242 MediumLockList *pMediumLockList;
11243 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11244 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11245 {
11246 /* this happens for online snapshots, there the attachment
11247 * is changing, but only to a diff image created under
11248 * the old one, so there is no separate lock list */
11249 Assert(!pMediumLockList);
11250 }
11251 else
11252 {
11253 AssertComRC(rc);
11254 if (pMediumLockList)
11255 {
11256 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11257 AssertComRC(rc);
11258 }
11259 }
11260 }
11261 }
11262 }
11263
11264 /* take media locks again so that the locking state is consistent */
11265 if (fMediaNeedsLocking)
11266 {
11267 Assert(aOnline);
11268 rc = mData->mSession.mLockedMedia.Lock();
11269 AssertComRC(rc);
11270 }
11271
11272 /* commit the hard disk changes */
11273 mMediaData.commit();
11274
11275 if (i_isSessionMachine())
11276 {
11277 /*
11278 * Update the parent machine to point to the new owner.
11279 * This is necessary because the stored parent will point to the
11280 * session machine otherwise and cause crashes or errors later
11281 * when the session machine gets invalid.
11282 */
11283 /** @todo Change the MediumAttachment class to behave like any other
11284 * class in this regard by creating peer MediumAttachment
11285 * objects for session machines and share the data with the peer
11286 * machine.
11287 */
11288 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11289 it != mMediaData->mAttachments.end();
11290 ++it)
11291 (*it)->i_updateParentMachine(mPeer);
11292
11293 /* attach new data to the primary machine and reshare it */
11294 mPeer->mMediaData.attach(mMediaData);
11295 }
11296
11297 return;
11298}
11299
11300/**
11301 * Perform deferred deletion of implicitly created diffs.
11302 *
11303 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11304 * backed up).
11305 *
11306 * @note Locks this object for writing!
11307 */
11308void Machine::i_rollbackMedia()
11309{
11310 AutoCaller autoCaller(this);
11311 AssertComRCReturnVoid(autoCaller.rc());
11312
11313 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11314 LogFlowThisFunc(("Entering rollbackMedia\n"));
11315
11316 HRESULT rc = S_OK;
11317
11318 /* no attach/detach operations -- nothing to do */
11319 if (!mMediaData.isBackedUp())
11320 return;
11321
11322 /* enumerate new attachments */
11323 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11324 it != mMediaData->mAttachments.end();
11325 ++it)
11326 {
11327 MediumAttachment *pAttach = *it;
11328 /* Fix up the backrefs for DVD/floppy media. */
11329 if (pAttach->i_getType() != DeviceType_HardDisk)
11330 {
11331 Medium* pMedium = pAttach->i_getMedium();
11332 if (pMedium)
11333 {
11334 rc = pMedium->i_removeBackReference(mData->mUuid);
11335 AssertComRC(rc);
11336 }
11337 }
11338
11339 (*it)->i_rollback();
11340
11341 pAttach = *it;
11342 /* Fix up the backrefs for DVD/floppy media. */
11343 if (pAttach->i_getType() != DeviceType_HardDisk)
11344 {
11345 Medium* pMedium = pAttach->i_getMedium();
11346 if (pMedium)
11347 {
11348 rc = pMedium->i_addBackReference(mData->mUuid);
11349 AssertComRC(rc);
11350 }
11351 }
11352 }
11353
11354 /** @todo convert all this Machine-based voodoo to MediumAttachment
11355 * based rollback logic. */
11356 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11357
11358 return;
11359}
11360
11361/**
11362 * Returns true if the settings file is located in the directory named exactly
11363 * as the machine; this means, among other things, that the machine directory
11364 * should be auto-renamed.
11365 *
11366 * @param aSettingsDir if not NULL, the full machine settings file directory
11367 * name will be assigned there.
11368 *
11369 * @note Doesn't lock anything.
11370 * @note Not thread safe (must be called from this object's lock).
11371 */
11372bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11373{
11374 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11375 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11376 if (aSettingsDir)
11377 *aSettingsDir = strMachineDirName;
11378 strMachineDirName.stripPath(); // vmname
11379 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11380 strConfigFileOnly.stripPath() // vmname.vbox
11381 .stripSuffix(); // vmname
11382 /** @todo hack, make somehow use of ComposeMachineFilename */
11383 if (mUserData->s.fDirectoryIncludesUUID)
11384 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11385
11386 AssertReturn(!strMachineDirName.isEmpty(), false);
11387 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11388
11389 return strMachineDirName == strConfigFileOnly;
11390}
11391
11392/**
11393 * Discards all changes to machine settings.
11394 *
11395 * @param aNotify Whether to notify the direct session about changes or not.
11396 *
11397 * @note Locks objects for writing!
11398 */
11399void Machine::i_rollback(bool aNotify)
11400{
11401 AutoCaller autoCaller(this);
11402 AssertComRCReturn(autoCaller.rc(), (void)0);
11403
11404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11405
11406 if (!mStorageControllers.isNull())
11407 {
11408 if (mStorageControllers.isBackedUp())
11409 {
11410 /* unitialize all new devices (absent in the backed up list). */
11411 StorageControllerList::const_iterator it = mStorageControllers->begin();
11412 StorageControllerList *backedList = mStorageControllers.backedUpData();
11413 while (it != mStorageControllers->end())
11414 {
11415 if ( std::find(backedList->begin(), backedList->end(), *it)
11416 == backedList->end()
11417 )
11418 {
11419 (*it)->uninit();
11420 }
11421 ++it;
11422 }
11423
11424 /* restore the list */
11425 mStorageControllers.rollback();
11426 }
11427
11428 /* rollback any changes to devices after restoring the list */
11429 if (mData->flModifications & IsModified_Storage)
11430 {
11431 StorageControllerList::const_iterator it = mStorageControllers->begin();
11432 while (it != mStorageControllers->end())
11433 {
11434 (*it)->i_rollback();
11435 ++it;
11436 }
11437 }
11438 }
11439
11440 if (!mUSBControllers.isNull())
11441 {
11442 if (mUSBControllers.isBackedUp())
11443 {
11444 /* unitialize all new devices (absent in the backed up list). */
11445 USBControllerList::const_iterator it = mUSBControllers->begin();
11446 USBControllerList *backedList = mUSBControllers.backedUpData();
11447 while (it != mUSBControllers->end())
11448 {
11449 if ( std::find(backedList->begin(), backedList->end(), *it)
11450 == backedList->end()
11451 )
11452 {
11453 (*it)->uninit();
11454 }
11455 ++it;
11456 }
11457
11458 /* restore the list */
11459 mUSBControllers.rollback();
11460 }
11461
11462 /* rollback any changes to devices after restoring the list */
11463 if (mData->flModifications & IsModified_USB)
11464 {
11465 USBControllerList::const_iterator it = mUSBControllers->begin();
11466 while (it != mUSBControllers->end())
11467 {
11468 (*it)->i_rollback();
11469 ++it;
11470 }
11471 }
11472 }
11473
11474 mUserData.rollback();
11475
11476 mHWData.rollback();
11477
11478 if (mData->flModifications & IsModified_Storage)
11479 i_rollbackMedia();
11480
11481 if (mBIOSSettings)
11482 mBIOSSettings->i_rollback();
11483
11484 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11485 mVRDEServer->i_rollback();
11486
11487 if (mAudioAdapter)
11488 mAudioAdapter->i_rollback();
11489
11490 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11491 mUSBDeviceFilters->i_rollback();
11492
11493 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11494 mBandwidthControl->i_rollback();
11495
11496 if (!mHWData.isNull())
11497 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11498 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11499 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11500 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11501
11502 if (mData->flModifications & IsModified_NetworkAdapters)
11503 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11504 if ( mNetworkAdapters[slot]
11505 && mNetworkAdapters[slot]->i_isModified())
11506 {
11507 mNetworkAdapters[slot]->i_rollback();
11508 networkAdapters[slot] = mNetworkAdapters[slot];
11509 }
11510
11511 if (mData->flModifications & IsModified_SerialPorts)
11512 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11513 if ( mSerialPorts[slot]
11514 && mSerialPorts[slot]->i_isModified())
11515 {
11516 mSerialPorts[slot]->i_rollback();
11517 serialPorts[slot] = mSerialPorts[slot];
11518 }
11519
11520 if (mData->flModifications & IsModified_ParallelPorts)
11521 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11522 if ( mParallelPorts[slot]
11523 && mParallelPorts[slot]->i_isModified())
11524 {
11525 mParallelPorts[slot]->i_rollback();
11526 parallelPorts[slot] = mParallelPorts[slot];
11527 }
11528
11529 if (aNotify)
11530 {
11531 /* inform the direct session about changes */
11532
11533 ComObjPtr<Machine> that = this;
11534 uint32_t flModifications = mData->flModifications;
11535 alock.release();
11536
11537 if (flModifications & IsModified_SharedFolders)
11538 that->i_onSharedFolderChange();
11539
11540 if (flModifications & IsModified_VRDEServer)
11541 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11542 if (flModifications & IsModified_USB)
11543 that->i_onUSBControllerChange();
11544
11545 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11546 if (networkAdapters[slot])
11547 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11548 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11549 if (serialPorts[slot])
11550 that->i_onSerialPortChange(serialPorts[slot]);
11551 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11552 if (parallelPorts[slot])
11553 that->i_onParallelPortChange(parallelPorts[slot]);
11554
11555 if (flModifications & IsModified_Storage)
11556 that->i_onStorageControllerChange();
11557
11558#if 0
11559 if (flModifications & IsModified_BandwidthControl)
11560 that->onBandwidthControlChange();
11561#endif
11562 }
11563}
11564
11565/**
11566 * Commits all the changes to machine settings.
11567 *
11568 * Note that this operation is supposed to never fail.
11569 *
11570 * @note Locks this object and children for writing.
11571 */
11572void Machine::i_commit()
11573{
11574 AutoCaller autoCaller(this);
11575 AssertComRCReturnVoid(autoCaller.rc());
11576
11577 AutoCaller peerCaller(mPeer);
11578 AssertComRCReturnVoid(peerCaller.rc());
11579
11580 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11581
11582 /*
11583 * use safe commit to ensure Snapshot machines (that share mUserData)
11584 * will still refer to a valid memory location
11585 */
11586 mUserData.commitCopy();
11587
11588 mHWData.commit();
11589
11590 if (mMediaData.isBackedUp())
11591 i_commitMedia(Global::IsOnline(mData->mMachineState));
11592
11593 mBIOSSettings->i_commit();
11594 mVRDEServer->i_commit();
11595 mAudioAdapter->i_commit();
11596 mUSBDeviceFilters->i_commit();
11597 mBandwidthControl->i_commit();
11598
11599 /* Since mNetworkAdapters is a list which might have been changed (resized)
11600 * without using the Backupable<> template we need to handle the copying
11601 * of the list entries manually, including the creation of peers for the
11602 * new objects. */
11603 bool commitNetworkAdapters = false;
11604 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11605 if (mPeer)
11606 {
11607 /* commit everything, even the ones which will go away */
11608 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11609 mNetworkAdapters[slot]->i_commit();
11610 /* copy over the new entries, creating a peer and uninit the original */
11611 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11612 for (size_t slot = 0; slot < newSize; slot++)
11613 {
11614 /* look if this adapter has a peer device */
11615 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11616 if (!peer)
11617 {
11618 /* no peer means the adapter is a newly created one;
11619 * create a peer owning data this data share it with */
11620 peer.createObject();
11621 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11622 }
11623 mPeer->mNetworkAdapters[slot] = peer;
11624 }
11625 /* uninit any no longer needed network adapters */
11626 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11627 mNetworkAdapters[slot]->uninit();
11628 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11629 {
11630 if (mPeer->mNetworkAdapters[slot])
11631 mPeer->mNetworkAdapters[slot]->uninit();
11632 }
11633 /* Keep the original network adapter count until this point, so that
11634 * discarding a chipset type change will not lose settings. */
11635 mNetworkAdapters.resize(newSize);
11636 mPeer->mNetworkAdapters.resize(newSize);
11637 }
11638 else
11639 {
11640 /* we have no peer (our parent is the newly created machine);
11641 * just commit changes to the network adapters */
11642 commitNetworkAdapters = true;
11643 }
11644 if (commitNetworkAdapters)
11645 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11646 mNetworkAdapters[slot]->i_commit();
11647
11648 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11649 mSerialPorts[slot]->i_commit();
11650 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11651 mParallelPorts[slot]->i_commit();
11652
11653 bool commitStorageControllers = false;
11654
11655 if (mStorageControllers.isBackedUp())
11656 {
11657 mStorageControllers.commit();
11658
11659 if (mPeer)
11660 {
11661 /* Commit all changes to new controllers (this will reshare data with
11662 * peers for those who have peers) */
11663 StorageControllerList *newList = new StorageControllerList();
11664 StorageControllerList::const_iterator it = mStorageControllers->begin();
11665 while (it != mStorageControllers->end())
11666 {
11667 (*it)->i_commit();
11668
11669 /* look if this controller has a peer device */
11670 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11671 if (!peer)
11672 {
11673 /* no peer means the device is a newly created one;
11674 * create a peer owning data this device share it with */
11675 peer.createObject();
11676 peer->init(mPeer, *it, true /* aReshare */);
11677 }
11678 else
11679 {
11680 /* remove peer from the old list */
11681 mPeer->mStorageControllers->remove(peer);
11682 }
11683 /* and add it to the new list */
11684 newList->push_back(peer);
11685
11686 ++it;
11687 }
11688
11689 /* uninit old peer's controllers that are left */
11690 it = mPeer->mStorageControllers->begin();
11691 while (it != mPeer->mStorageControllers->end())
11692 {
11693 (*it)->uninit();
11694 ++it;
11695 }
11696
11697 /* attach new list of controllers to our peer */
11698 mPeer->mStorageControllers.attach(newList);
11699 }
11700 else
11701 {
11702 /* we have no peer (our parent is the newly created machine);
11703 * just commit changes to devices */
11704 commitStorageControllers = true;
11705 }
11706 }
11707 else
11708 {
11709 /* the list of controllers itself is not changed,
11710 * just commit changes to controllers themselves */
11711 commitStorageControllers = true;
11712 }
11713
11714 if (commitStorageControllers)
11715 {
11716 StorageControllerList::const_iterator it = mStorageControllers->begin();
11717 while (it != mStorageControllers->end())
11718 {
11719 (*it)->i_commit();
11720 ++it;
11721 }
11722 }
11723
11724 bool commitUSBControllers = false;
11725
11726 if (mUSBControllers.isBackedUp())
11727 {
11728 mUSBControllers.commit();
11729
11730 if (mPeer)
11731 {
11732 /* Commit all changes to new controllers (this will reshare data with
11733 * peers for those who have peers) */
11734 USBControllerList *newList = new USBControllerList();
11735 USBControllerList::const_iterator it = mUSBControllers->begin();
11736 while (it != mUSBControllers->end())
11737 {
11738 (*it)->i_commit();
11739
11740 /* look if this controller has a peer device */
11741 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11742 if (!peer)
11743 {
11744 /* no peer means the device is a newly created one;
11745 * create a peer owning data this device share it with */
11746 peer.createObject();
11747 peer->init(mPeer, *it, true /* aReshare */);
11748 }
11749 else
11750 {
11751 /* remove peer from the old list */
11752 mPeer->mUSBControllers->remove(peer);
11753 }
11754 /* and add it to the new list */
11755 newList->push_back(peer);
11756
11757 ++it;
11758 }
11759
11760 /* uninit old peer's controllers that are left */
11761 it = mPeer->mUSBControllers->begin();
11762 while (it != mPeer->mUSBControllers->end())
11763 {
11764 (*it)->uninit();
11765 ++it;
11766 }
11767
11768 /* attach new list of controllers to our peer */
11769 mPeer->mUSBControllers.attach(newList);
11770 }
11771 else
11772 {
11773 /* we have no peer (our parent is the newly created machine);
11774 * just commit changes to devices */
11775 commitUSBControllers = true;
11776 }
11777 }
11778 else
11779 {
11780 /* the list of controllers itself is not changed,
11781 * just commit changes to controllers themselves */
11782 commitUSBControllers = true;
11783 }
11784
11785 if (commitUSBControllers)
11786 {
11787 USBControllerList::const_iterator it = mUSBControllers->begin();
11788 while (it != mUSBControllers->end())
11789 {
11790 (*it)->i_commit();
11791 ++it;
11792 }
11793 }
11794
11795 if (i_isSessionMachine())
11796 {
11797 /* attach new data to the primary machine and reshare it */
11798 mPeer->mUserData.attach(mUserData);
11799 mPeer->mHWData.attach(mHWData);
11800 /* mMediaData is reshared by fixupMedia */
11801 // mPeer->mMediaData.attach(mMediaData);
11802 Assert(mPeer->mMediaData.data() == mMediaData.data());
11803 }
11804}
11805
11806/**
11807 * Copies all the hardware data from the given machine.
11808 *
11809 * Currently, only called when the VM is being restored from a snapshot. In
11810 * particular, this implies that the VM is not running during this method's
11811 * call.
11812 *
11813 * @note This method must be called from under this object's lock.
11814 *
11815 * @note This method doesn't call #commit(), so all data remains backed up and
11816 * unsaved.
11817 */
11818void Machine::i_copyFrom(Machine *aThat)
11819{
11820 AssertReturnVoid(!i_isSnapshotMachine());
11821 AssertReturnVoid(aThat->i_isSnapshotMachine());
11822
11823 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11824
11825 mHWData.assignCopy(aThat->mHWData);
11826
11827 // create copies of all shared folders (mHWData after attaching a copy
11828 // contains just references to original objects)
11829 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11830 it != mHWData->mSharedFolders.end();
11831 ++it)
11832 {
11833 ComObjPtr<SharedFolder> folder;
11834 folder.createObject();
11835 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11836 AssertComRC(rc);
11837 *it = folder;
11838 }
11839
11840 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11841 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11842 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11843 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11844 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11845
11846 /* create private copies of all controllers */
11847 mStorageControllers.backup();
11848 mStorageControllers->clear();
11849 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11850 it != aThat->mStorageControllers->end();
11851 ++it)
11852 {
11853 ComObjPtr<StorageController> ctrl;
11854 ctrl.createObject();
11855 ctrl->initCopy(this, *it);
11856 mStorageControllers->push_back(ctrl);
11857 }
11858
11859 /* create private copies of all USB controllers */
11860 mUSBControllers.backup();
11861 mUSBControllers->clear();
11862 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11863 it != aThat->mUSBControllers->end();
11864 ++it)
11865 {
11866 ComObjPtr<USBController> ctrl;
11867 ctrl.createObject();
11868 ctrl->initCopy(this, *it);
11869 mUSBControllers->push_back(ctrl);
11870 }
11871
11872 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11873 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11874 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11875 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11876 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11877 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11878 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11879}
11880
11881/**
11882 * Returns whether the given storage controller is hotplug capable.
11883 *
11884 * @returns true if the controller supports hotplugging
11885 * false otherwise.
11886 * @param enmCtrlType The controller type to check for.
11887 */
11888bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11889{
11890 ComPtr<ISystemProperties> systemProperties;
11891 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11892 if (FAILED(rc))
11893 return false;
11894
11895 BOOL aHotplugCapable = FALSE;
11896 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11897
11898 return RT_BOOL(aHotplugCapable);
11899}
11900
11901#ifdef VBOX_WITH_RESOURCE_USAGE_API
11902
11903void Machine::i_getDiskList(MediaList &list)
11904{
11905 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11906 it != mMediaData->mAttachments.end();
11907 ++it)
11908 {
11909 MediumAttachment* pAttach = *it;
11910 /* just in case */
11911 AssertStmt(pAttach, continue);
11912
11913 AutoCaller localAutoCallerA(pAttach);
11914 if (FAILED(localAutoCallerA.rc())) continue;
11915
11916 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11917
11918 if (pAttach->i_getType() == DeviceType_HardDisk)
11919 list.push_back(pAttach->i_getMedium());
11920 }
11921}
11922
11923void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11924{
11925 AssertReturnVoid(isWriteLockOnCurrentThread());
11926 AssertPtrReturnVoid(aCollector);
11927
11928 pm::CollectorHAL *hal = aCollector->getHAL();
11929 /* Create sub metrics */
11930 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11931 "Percentage of processor time spent in user mode by the VM process.");
11932 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11933 "Percentage of processor time spent in kernel mode by the VM process.");
11934 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11935 "Size of resident portion of VM process in memory.");
11936 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11937 "Actual size of all VM disks combined.");
11938 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11939 "Network receive rate.");
11940 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11941 "Network transmit rate.");
11942 /* Create and register base metrics */
11943 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11944 cpuLoadUser, cpuLoadKernel);
11945 aCollector->registerBaseMetric(cpuLoad);
11946 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11947 ramUsageUsed);
11948 aCollector->registerBaseMetric(ramUsage);
11949 MediaList disks;
11950 i_getDiskList(disks);
11951 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11952 diskUsageUsed);
11953 aCollector->registerBaseMetric(diskUsage);
11954
11955 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11956 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11957 new pm::AggregateAvg()));
11958 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11959 new pm::AggregateMin()));
11960 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11961 new pm::AggregateMax()));
11962 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11963 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11964 new pm::AggregateAvg()));
11965 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11966 new pm::AggregateMin()));
11967 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11968 new pm::AggregateMax()));
11969
11970 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11971 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11972 new pm::AggregateAvg()));
11973 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11974 new pm::AggregateMin()));
11975 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11976 new pm::AggregateMax()));
11977
11978 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11979 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11980 new pm::AggregateAvg()));
11981 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11982 new pm::AggregateMin()));
11983 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11984 new pm::AggregateMax()));
11985
11986
11987 /* Guest metrics collector */
11988 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11989 aCollector->registerGuest(mCollectorGuest);
11990 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11991 this, __PRETTY_FUNCTION__, mCollectorGuest));
11992
11993 /* Create sub metrics */
11994 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11995 "Percentage of processor time spent in user mode as seen by the guest.");
11996 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11997 "Percentage of processor time spent in kernel mode as seen by the guest.");
11998 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11999 "Percentage of processor time spent idling as seen by the guest.");
12000
12001 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12002 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12003 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12004 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12005 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12006 pm::SubMetric *guestMemCache = new pm::SubMetric(
12007 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12008
12009 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12010 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12011
12012 /* Create and register base metrics */
12013 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12014 machineNetRx, machineNetTx);
12015 aCollector->registerBaseMetric(machineNetRate);
12016
12017 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12018 guestLoadUser, guestLoadKernel, guestLoadIdle);
12019 aCollector->registerBaseMetric(guestCpuLoad);
12020
12021 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12022 guestMemTotal, guestMemFree,
12023 guestMemBalloon, guestMemShared,
12024 guestMemCache, guestPagedTotal);
12025 aCollector->registerBaseMetric(guestCpuMem);
12026
12027 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12028 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12029 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12030 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12031
12032 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12033 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12034 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12035 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12036
12037 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12038 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12039 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12040 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12041
12042 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12043 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12044 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12045 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12046
12047 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12048 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12049 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12050 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12051
12052 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12053 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12054 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12055 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12056
12057 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12058 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12059 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12060 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12061
12062 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12063 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12064 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12066
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12071
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12076
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12078 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12079 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12081}
12082
12083void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12084{
12085 AssertReturnVoid(isWriteLockOnCurrentThread());
12086
12087 if (aCollector)
12088 {
12089 aCollector->unregisterMetricsFor(aMachine);
12090 aCollector->unregisterBaseMetricsFor(aMachine);
12091 }
12092}
12093
12094#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12095
12096
12097////////////////////////////////////////////////////////////////////////////////
12098
12099DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12100
12101HRESULT SessionMachine::FinalConstruct()
12102{
12103 LogFlowThisFunc(("\n"));
12104
12105 mClientToken = NULL;
12106
12107 return BaseFinalConstruct();
12108}
12109
12110void SessionMachine::FinalRelease()
12111{
12112 LogFlowThisFunc(("\n"));
12113
12114 Assert(!mClientToken);
12115 /* paranoia, should not hang around any more */
12116 if (mClientToken)
12117 {
12118 delete mClientToken;
12119 mClientToken = NULL;
12120 }
12121
12122 uninit(Uninit::Unexpected);
12123
12124 BaseFinalRelease();
12125}
12126
12127/**
12128 * @note Must be called only by Machine::LockMachine() from its own write lock.
12129 */
12130HRESULT SessionMachine::init(Machine *aMachine)
12131{
12132 LogFlowThisFuncEnter();
12133 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12134
12135 AssertReturn(aMachine, E_INVALIDARG);
12136
12137 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12138
12139 /* Enclose the state transition NotReady->InInit->Ready */
12140 AutoInitSpan autoInitSpan(this);
12141 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12142
12143 HRESULT rc = S_OK;
12144
12145 /* create the machine client token */
12146 try
12147 {
12148 mClientToken = new ClientToken(aMachine, this);
12149 if (!mClientToken->isReady())
12150 {
12151 delete mClientToken;
12152 mClientToken = NULL;
12153 rc = E_FAIL;
12154 }
12155 }
12156 catch (std::bad_alloc &)
12157 {
12158 rc = E_OUTOFMEMORY;
12159 }
12160 if (FAILED(rc))
12161 return rc;
12162
12163 /* memorize the peer Machine */
12164 unconst(mPeer) = aMachine;
12165 /* share the parent pointer */
12166 unconst(mParent) = aMachine->mParent;
12167
12168 /* take the pointers to data to share */
12169 mData.share(aMachine->mData);
12170 mSSData.share(aMachine->mSSData);
12171
12172 mUserData.share(aMachine->mUserData);
12173 mHWData.share(aMachine->mHWData);
12174 mMediaData.share(aMachine->mMediaData);
12175
12176 mStorageControllers.allocate();
12177 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12178 it != aMachine->mStorageControllers->end();
12179 ++it)
12180 {
12181 ComObjPtr<StorageController> ctl;
12182 ctl.createObject();
12183 ctl->init(this, *it);
12184 mStorageControllers->push_back(ctl);
12185 }
12186
12187 mUSBControllers.allocate();
12188 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12189 it != aMachine->mUSBControllers->end();
12190 ++it)
12191 {
12192 ComObjPtr<USBController> ctl;
12193 ctl.createObject();
12194 ctl->init(this, *it);
12195 mUSBControllers->push_back(ctl);
12196 }
12197
12198 unconst(mBIOSSettings).createObject();
12199 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12200 /* create another VRDEServer object that will be mutable */
12201 unconst(mVRDEServer).createObject();
12202 mVRDEServer->init(this, aMachine->mVRDEServer);
12203 /* create another audio adapter object that will be mutable */
12204 unconst(mAudioAdapter).createObject();
12205 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12206 /* create a list of serial ports that will be mutable */
12207 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12208 {
12209 unconst(mSerialPorts[slot]).createObject();
12210 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12211 }
12212 /* create a list of parallel ports that will be mutable */
12213 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12214 {
12215 unconst(mParallelPorts[slot]).createObject();
12216 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12217 }
12218
12219 /* create another USB device filters object that will be mutable */
12220 unconst(mUSBDeviceFilters).createObject();
12221 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12222
12223 /* create a list of network adapters that will be mutable */
12224 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12225 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12226 {
12227 unconst(mNetworkAdapters[slot]).createObject();
12228 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12229 }
12230
12231 /* create another bandwidth control object that will be mutable */
12232 unconst(mBandwidthControl).createObject();
12233 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12234
12235 /* default is to delete saved state on Saved -> PoweredOff transition */
12236 mRemoveSavedState = true;
12237
12238 /* Confirm a successful initialization when it's the case */
12239 autoInitSpan.setSucceeded();
12240
12241 miNATNetworksStarted = 0;
12242
12243 LogFlowThisFuncLeave();
12244 return rc;
12245}
12246
12247/**
12248 * Uninitializes this session object. If the reason is other than
12249 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12250 * or the client watcher code.
12251 *
12252 * @param aReason uninitialization reason
12253 *
12254 * @note Locks mParent + this object for writing.
12255 */
12256void SessionMachine::uninit(Uninit::Reason aReason)
12257{
12258 LogFlowThisFuncEnter();
12259 LogFlowThisFunc(("reason=%d\n", aReason));
12260
12261 /*
12262 * Strongly reference ourselves to prevent this object deletion after
12263 * mData->mSession.mMachine.setNull() below (which can release the last
12264 * reference and call the destructor). Important: this must be done before
12265 * accessing any members (and before AutoUninitSpan that does it as well).
12266 * This self reference will be released as the very last step on return.
12267 */
12268 ComObjPtr<SessionMachine> selfRef = this;
12269
12270 /* Enclose the state transition Ready->InUninit->NotReady */
12271 AutoUninitSpan autoUninitSpan(this);
12272 if (autoUninitSpan.uninitDone())
12273 {
12274 LogFlowThisFunc(("Already uninitialized\n"));
12275 LogFlowThisFuncLeave();
12276 return;
12277 }
12278
12279 if (autoUninitSpan.initFailed())
12280 {
12281 /* We've been called by init() because it's failed. It's not really
12282 * necessary (nor it's safe) to perform the regular uninit sequence
12283 * below, the following is enough.
12284 */
12285 LogFlowThisFunc(("Initialization failed.\n"));
12286 /* destroy the machine client token */
12287 if (mClientToken)
12288 {
12289 delete mClientToken;
12290 mClientToken = NULL;
12291 }
12292 uninitDataAndChildObjects();
12293 mData.free();
12294 unconst(mParent) = NULL;
12295 unconst(mPeer) = NULL;
12296 LogFlowThisFuncLeave();
12297 return;
12298 }
12299
12300 MachineState_T lastState;
12301 {
12302 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12303 lastState = mData->mMachineState;
12304 }
12305 NOREF(lastState);
12306
12307#ifdef VBOX_WITH_USB
12308 // release all captured USB devices, but do this before requesting the locks below
12309 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12310 {
12311 /* Console::captureUSBDevices() is called in the VM process only after
12312 * setting the machine state to Starting or Restoring.
12313 * Console::detachAllUSBDevices() will be called upon successful
12314 * termination. So, we need to release USB devices only if there was
12315 * an abnormal termination of a running VM.
12316 *
12317 * This is identical to SessionMachine::DetachAllUSBDevices except
12318 * for the aAbnormal argument. */
12319 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12320 AssertComRC(rc);
12321 NOREF(rc);
12322
12323 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12324 if (service)
12325 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12326 }
12327#endif /* VBOX_WITH_USB */
12328
12329 // we need to lock this object in uninit() because the lock is shared
12330 // with mPeer (as well as data we modify below). mParent lock is needed
12331 // by several calls to it, and USB needs host lock.
12332 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12333
12334#ifdef VBOX_WITH_RESOURCE_USAGE_API
12335 /*
12336 * It is safe to call Machine::i_unregisterMetrics() here because
12337 * PerformanceCollector::samplerCallback no longer accesses guest methods
12338 * holding the lock.
12339 */
12340 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12341 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12342 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12343 this, __PRETTY_FUNCTION__, mCollectorGuest));
12344 if (mCollectorGuest)
12345 {
12346 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12347 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12348 mCollectorGuest = NULL;
12349 }
12350#endif
12351
12352 if (aReason == Uninit::Abnormal)
12353 {
12354 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12355 Global::IsOnlineOrTransient(lastState)));
12356
12357 /* reset the state to Aborted */
12358 if (mData->mMachineState != MachineState_Aborted)
12359 i_setMachineState(MachineState_Aborted);
12360 }
12361
12362 // any machine settings modified?
12363 if (mData->flModifications)
12364 {
12365 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12366 i_rollback(false /* aNotify */);
12367 }
12368
12369 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12370 || !mConsoleTaskData.mSnapshot);
12371 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12372 {
12373 LogWarningThisFunc(("canceling failed save state request!\n"));
12374 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12375 }
12376 else if (!mConsoleTaskData.mSnapshot.isNull())
12377 {
12378 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12379
12380 /* delete all differencing hard disks created (this will also attach
12381 * their parents back by rolling back mMediaData) */
12382 i_rollbackMedia();
12383
12384 // delete the saved state file (it might have been already created)
12385 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12386 // think it's still in use
12387 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12388 mConsoleTaskData.mSnapshot->uninit();
12389 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12390 }
12391
12392 mData->mSession.mPID = NIL_RTPROCESS;
12393
12394 if (aReason == Uninit::Unexpected)
12395 {
12396 /* Uninitialization didn't come from #checkForDeath(), so tell the
12397 * client watcher thread to update the set of machines that have open
12398 * sessions. */
12399 mParent->i_updateClientWatcher();
12400 }
12401
12402 /* uninitialize all remote controls */
12403 if (mData->mSession.mRemoteControls.size())
12404 {
12405 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12406 mData->mSession.mRemoteControls.size()));
12407
12408 Data::Session::RemoteControlList::iterator it =
12409 mData->mSession.mRemoteControls.begin();
12410 while (it != mData->mSession.mRemoteControls.end())
12411 {
12412 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12413 HRESULT rc = (*it)->Uninitialize();
12414 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12415 if (FAILED(rc))
12416 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12417 ++it;
12418 }
12419 mData->mSession.mRemoteControls.clear();
12420 }
12421
12422 /* Remove all references to the NAT network service. The service will stop
12423 * if all references (also from other VMs) are removed. */
12424 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12425 {
12426 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12427 {
12428 NetworkAttachmentType_T type;
12429 HRESULT hrc;
12430
12431 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12432 if ( SUCCEEDED(hrc)
12433 && type == NetworkAttachmentType_NATNetwork)
12434 {
12435 Bstr name;
12436 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12437 if (SUCCEEDED(hrc))
12438 {
12439 multilock.release();
12440 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12441 mUserData->s.strName.c_str(), name.raw()));
12442 mParent->i_natNetworkRefDec(name.raw());
12443 multilock.acquire();
12444 }
12445 }
12446 }
12447 }
12448
12449 /*
12450 * An expected uninitialization can come only from #checkForDeath().
12451 * Otherwise it means that something's gone really wrong (for example,
12452 * the Session implementation has released the VirtualBox reference
12453 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12454 * etc). However, it's also possible, that the client releases the IPC
12455 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12456 * but the VirtualBox release event comes first to the server process.
12457 * This case is practically possible, so we should not assert on an
12458 * unexpected uninit, just log a warning.
12459 */
12460
12461 if ((aReason == Uninit::Unexpected))
12462 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12463
12464 if (aReason != Uninit::Normal)
12465 {
12466 mData->mSession.mDirectControl.setNull();
12467 }
12468 else
12469 {
12470 /* this must be null here (see #OnSessionEnd()) */
12471 Assert(mData->mSession.mDirectControl.isNull());
12472 Assert(mData->mSession.mState == SessionState_Unlocking);
12473 Assert(!mData->mSession.mProgress.isNull());
12474 }
12475 if (mData->mSession.mProgress)
12476 {
12477 if (aReason == Uninit::Normal)
12478 mData->mSession.mProgress->i_notifyComplete(S_OK);
12479 else
12480 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12481 COM_IIDOF(ISession),
12482 getComponentName(),
12483 tr("The VM session was aborted"));
12484 mData->mSession.mProgress.setNull();
12485 }
12486
12487 /* remove the association between the peer machine and this session machine */
12488 Assert( (SessionMachine*)mData->mSession.mMachine == this
12489 || aReason == Uninit::Unexpected);
12490
12491 /* reset the rest of session data */
12492 mData->mSession.mMachine.setNull();
12493 mData->mSession.mState = SessionState_Unlocked;
12494 mData->mSession.mType.setNull();
12495
12496 /* destroy the machine client token before leaving the exclusive lock */
12497 if (mClientToken)
12498 {
12499 delete mClientToken;
12500 mClientToken = NULL;
12501 }
12502
12503 /* fire an event */
12504 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12505
12506 uninitDataAndChildObjects();
12507
12508 /* free the essential data structure last */
12509 mData.free();
12510
12511 /* release the exclusive lock before setting the below two to NULL */
12512 multilock.release();
12513
12514 unconst(mParent) = NULL;
12515 unconst(mPeer) = NULL;
12516
12517 LogFlowThisFuncLeave();
12518}
12519
12520// util::Lockable interface
12521////////////////////////////////////////////////////////////////////////////////
12522
12523/**
12524 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12525 * with the primary Machine instance (mPeer).
12526 */
12527RWLockHandle *SessionMachine::lockHandle() const
12528{
12529 AssertReturn(mPeer != NULL, NULL);
12530 return mPeer->lockHandle();
12531}
12532
12533// IInternalMachineControl methods
12534////////////////////////////////////////////////////////////////////////////////
12535
12536/**
12537 * Passes collected guest statistics to performance collector object
12538 */
12539HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12540 ULONG aCpuKernel, ULONG aCpuIdle,
12541 ULONG aMemTotal, ULONG aMemFree,
12542 ULONG aMemBalloon, ULONG aMemShared,
12543 ULONG aMemCache, ULONG aPageTotal,
12544 ULONG aAllocVMM, ULONG aFreeVMM,
12545 ULONG aBalloonedVMM, ULONG aSharedVMM,
12546 ULONG aVmNetRx, ULONG aVmNetTx)
12547{
12548#ifdef VBOX_WITH_RESOURCE_USAGE_API
12549 if (mCollectorGuest)
12550 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12551 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12552 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12553 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12554
12555 return S_OK;
12556#else
12557 NOREF(aValidStats);
12558 NOREF(aCpuUser);
12559 NOREF(aCpuKernel);
12560 NOREF(aCpuIdle);
12561 NOREF(aMemTotal);
12562 NOREF(aMemFree);
12563 NOREF(aMemBalloon);
12564 NOREF(aMemShared);
12565 NOREF(aMemCache);
12566 NOREF(aPageTotal);
12567 NOREF(aAllocVMM);
12568 NOREF(aFreeVMM);
12569 NOREF(aBalloonedVMM);
12570 NOREF(aSharedVMM);
12571 NOREF(aVmNetRx);
12572 NOREF(aVmNetTx);
12573 return E_NOTIMPL;
12574#endif
12575}
12576
12577/**
12578 * @note Locks this object for writing.
12579 */
12580HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12581{
12582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12583
12584 mRemoveSavedState = RT_BOOL(aRemove);
12585
12586 return S_OK;
12587}
12588
12589/**
12590 * @note Locks the same as #i_setMachineState() does.
12591 */
12592HRESULT SessionMachine::updateState(MachineState_T aState)
12593{
12594 return i_setMachineState(aState);
12595}
12596
12597/**
12598 * @note Locks this object for writing.
12599 */
12600HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12601{
12602 IProgress* pProgress(aProgress);
12603
12604 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12605
12606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12607
12608 if (mData->mSession.mState != SessionState_Locked)
12609 return VBOX_E_INVALID_OBJECT_STATE;
12610
12611 if (!mData->mSession.mProgress.isNull())
12612 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12613
12614 /* If we didn't reference the NAT network service yet, add a reference to
12615 * force a start */
12616 if (miNATNetworksStarted < 1)
12617 {
12618 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12619 {
12620 NetworkAttachmentType_T type;
12621 HRESULT hrc;
12622 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12623 if ( SUCCEEDED(hrc)
12624 && type == NetworkAttachmentType_NATNetwork)
12625 {
12626 Bstr name;
12627 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12628 if (SUCCEEDED(hrc))
12629 {
12630 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12631 mUserData->s.strName.c_str(), name.raw()));
12632 mPeer->lockHandle()->unlockWrite();
12633 mParent->i_natNetworkRefInc(name.raw());
12634#ifdef RT_LOCK_STRICT
12635 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12636#else
12637 mPeer->lockHandle()->lockWrite();
12638#endif
12639 }
12640 }
12641 }
12642 miNATNetworksStarted++;
12643 }
12644
12645 LogFlowThisFunc(("returns S_OK.\n"));
12646 return S_OK;
12647}
12648
12649/**
12650 * @note Locks this object for writing.
12651 */
12652HRESULT SessionMachine::endPowerUp(LONG aResult)
12653{
12654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12655
12656 if (mData->mSession.mState != SessionState_Locked)
12657 return VBOX_E_INVALID_OBJECT_STATE;
12658
12659 /* Finalize the LaunchVMProcess progress object. */
12660 if (mData->mSession.mProgress)
12661 {
12662 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12663 mData->mSession.mProgress.setNull();
12664 }
12665
12666 if (SUCCEEDED((HRESULT)aResult))
12667 {
12668#ifdef VBOX_WITH_RESOURCE_USAGE_API
12669 /* The VM has been powered up successfully, so it makes sense
12670 * now to offer the performance metrics for a running machine
12671 * object. Doing it earlier wouldn't be safe. */
12672 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12673 mData->mSession.mPID);
12674#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12675 }
12676
12677 return S_OK;
12678}
12679
12680/**
12681 * @note Locks this object for writing.
12682 */
12683HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12684{
12685 LogFlowThisFuncEnter();
12686
12687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12688
12689 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12690 E_FAIL);
12691
12692 /* create a progress object to track operation completion */
12693 ComObjPtr<Progress> pProgress;
12694 pProgress.createObject();
12695 pProgress->init(i_getVirtualBox(),
12696 static_cast<IMachine *>(this) /* aInitiator */,
12697 Bstr(tr("Stopping the virtual machine")).raw(),
12698 FALSE /* aCancelable */);
12699
12700 /* fill in the console task data */
12701 mConsoleTaskData.mLastState = mData->mMachineState;
12702 mConsoleTaskData.mProgress = pProgress;
12703
12704 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12705 i_setMachineState(MachineState_Stopping);
12706
12707 pProgress.queryInterfaceTo(aProgress.asOutParam());
12708
12709 return S_OK;
12710}
12711
12712/**
12713 * @note Locks this object for writing.
12714 */
12715HRESULT SessionMachine::endPoweringDown(LONG aResult,
12716 const com::Utf8Str &aErrMsg)
12717{
12718 LogFlowThisFuncEnter();
12719
12720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12721
12722 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12723 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12724 && mConsoleTaskData.mLastState != MachineState_Null,
12725 E_FAIL);
12726
12727 /*
12728 * On failure, set the state to the state we had when BeginPoweringDown()
12729 * was called (this is expected by Console::PowerDown() and the associated
12730 * task). On success the VM process already changed the state to
12731 * MachineState_PoweredOff, so no need to do anything.
12732 */
12733 if (FAILED(aResult))
12734 i_setMachineState(mConsoleTaskData.mLastState);
12735
12736 /* notify the progress object about operation completion */
12737 Assert(mConsoleTaskData.mProgress);
12738 if (SUCCEEDED(aResult))
12739 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12740 else
12741 {
12742 if (aErrMsg.length())
12743 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12744 COM_IIDOF(ISession),
12745 getComponentName(),
12746 aErrMsg.c_str());
12747 else
12748 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12749 }
12750
12751 /* clear out the temporary saved state data */
12752 mConsoleTaskData.mLastState = MachineState_Null;
12753 mConsoleTaskData.mProgress.setNull();
12754
12755 LogFlowThisFuncLeave();
12756 return S_OK;
12757}
12758
12759
12760/**
12761 * Goes through the USB filters of the given machine to see if the given
12762 * device matches any filter or not.
12763 *
12764 * @note Locks the same as USBController::hasMatchingFilter() does.
12765 */
12766HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12767 BOOL *aMatched,
12768 ULONG *aMaskedInterfaces)
12769{
12770 LogFlowThisFunc(("\n"));
12771
12772#ifdef VBOX_WITH_USB
12773 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12774#else
12775 NOREF(aDevice);
12776 NOREF(aMaskedInterfaces);
12777 *aMatched = FALSE;
12778#endif
12779
12780 return S_OK;
12781}
12782
12783/**
12784 * @note Locks the same as Host::captureUSBDevice() does.
12785 */
12786HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId)
12787{
12788 LogFlowThisFunc(("\n"));
12789
12790#ifdef VBOX_WITH_USB
12791 /* if captureDeviceForVM() fails, it must have set extended error info */
12792 clearError();
12793 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12794 if (FAILED(rc)) return rc;
12795
12796 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12797 AssertReturn(service, E_FAIL);
12798 return service->captureDeviceForVM(this, aId.ref());
12799#else
12800 NOREF(aId);
12801 return E_NOTIMPL;
12802#endif
12803}
12804
12805/**
12806 * @note Locks the same as Host::detachUSBDevice() does.
12807 */
12808HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12809 BOOL aDone)
12810{
12811 LogFlowThisFunc(("\n"));
12812
12813#ifdef VBOX_WITH_USB
12814 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12815 AssertReturn(service, E_FAIL);
12816 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12817#else
12818 NOREF(aId);
12819 NOREF(aDone);
12820 return E_NOTIMPL;
12821#endif
12822}
12823
12824/**
12825 * Inserts all machine filters to the USB proxy service and then calls
12826 * Host::autoCaptureUSBDevices().
12827 *
12828 * Called by Console from the VM process upon VM startup.
12829 *
12830 * @note Locks what called methods lock.
12831 */
12832HRESULT SessionMachine::autoCaptureUSBDevices()
12833{
12834 LogFlowThisFunc(("\n"));
12835
12836#ifdef VBOX_WITH_USB
12837 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12838 AssertComRC(rc);
12839 NOREF(rc);
12840
12841 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12842 AssertReturn(service, E_FAIL);
12843 return service->autoCaptureDevicesForVM(this);
12844#else
12845 return S_OK;
12846#endif
12847}
12848
12849/**
12850 * Removes all machine filters from the USB proxy service and then calls
12851 * Host::detachAllUSBDevices().
12852 *
12853 * Called by Console from the VM process upon normal VM termination or by
12854 * SessionMachine::uninit() upon abnormal VM termination (from under the
12855 * Machine/SessionMachine lock).
12856 *
12857 * @note Locks what called methods lock.
12858 */
12859HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12860{
12861 LogFlowThisFunc(("\n"));
12862
12863#ifdef VBOX_WITH_USB
12864 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12865 AssertComRC(rc);
12866 NOREF(rc);
12867
12868 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12869 AssertReturn(service, E_FAIL);
12870 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12871#else
12872 NOREF(aDone);
12873 return S_OK;
12874#endif
12875}
12876
12877/**
12878 * @note Locks this object for writing.
12879 */
12880HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12881 ComPtr<IProgress> &aProgress)
12882{
12883 LogFlowThisFuncEnter();
12884
12885 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12886 /*
12887 * We don't assert below because it might happen that a non-direct session
12888 * informs us it is closed right after we've been uninitialized -- it's ok.
12889 */
12890
12891 /* get IInternalSessionControl interface */
12892 ComPtr<IInternalSessionControl> control(aSession);
12893
12894 ComAssertRet(!control.isNull(), E_INVALIDARG);
12895
12896 /* Creating a Progress object requires the VirtualBox lock, and
12897 * thus locking it here is required by the lock order rules. */
12898 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12899
12900 if (control == mData->mSession.mDirectControl)
12901 {
12902 /* The direct session is being normally closed by the client process
12903 * ----------------------------------------------------------------- */
12904
12905 /* go to the closing state (essential for all open*Session() calls and
12906 * for #checkForDeath()) */
12907 Assert(mData->mSession.mState == SessionState_Locked);
12908 mData->mSession.mState = SessionState_Unlocking;
12909
12910 /* set direct control to NULL to release the remote instance */
12911 mData->mSession.mDirectControl.setNull();
12912 LogFlowThisFunc(("Direct control is set to NULL\n"));
12913
12914 if (mData->mSession.mProgress)
12915 {
12916 /* finalize the progress, someone might wait if a frontend
12917 * closes the session before powering on the VM. */
12918 mData->mSession.mProgress->notifyComplete(E_FAIL,
12919 COM_IIDOF(ISession),
12920 getComponentName(),
12921 tr("The VM session was closed before any attempt to power it on"));
12922 mData->mSession.mProgress.setNull();
12923 }
12924
12925 /* Create the progress object the client will use to wait until
12926 * #checkForDeath() is called to uninitialize this session object after
12927 * it releases the IPC semaphore.
12928 * Note! Because we're "reusing" mProgress here, this must be a proxy
12929 * object just like for LaunchVMProcess. */
12930 Assert(mData->mSession.mProgress.isNull());
12931 ComObjPtr<ProgressProxy> progress;
12932 progress.createObject();
12933 ComPtr<IUnknown> pPeer(mPeer);
12934 progress->init(mParent, pPeer,
12935 Bstr(tr("Closing session")).raw(),
12936 FALSE /* aCancelable */);
12937 progress.queryInterfaceTo(aProgress.asOutParam());
12938 mData->mSession.mProgress = progress;
12939 }
12940 else
12941 {
12942 /* the remote session is being normally closed */
12943 Data::Session::RemoteControlList::iterator it =
12944 mData->mSession.mRemoteControls.begin();
12945 while (it != mData->mSession.mRemoteControls.end())
12946 {
12947 if (control == *it)
12948 break;
12949 ++it;
12950 }
12951 BOOL found = it != mData->mSession.mRemoteControls.end();
12952 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12953 E_INVALIDARG);
12954 // This MUST be erase(it), not remove(*it) as the latter triggers a
12955 // very nasty use after free due to the place where the value "lives".
12956 mData->mSession.mRemoteControls.erase(it);
12957 }
12958
12959 /* signal the client watcher thread, because the client is going away */
12960 mParent->i_updateClientWatcher();
12961
12962 LogFlowThisFuncLeave();
12963 return S_OK;
12964}
12965
12966/**
12967 * @note Locks this object for writing.
12968 */
12969HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12970 com::Utf8Str &aStateFilePath)
12971{
12972 LogFlowThisFuncEnter();
12973
12974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12975
12976 AssertReturn( mData->mMachineState == MachineState_Paused
12977 && mConsoleTaskData.mLastState == MachineState_Null
12978 && mConsoleTaskData.strStateFilePath.isEmpty(),
12979 E_FAIL);
12980
12981 /* create a progress object to track operation completion */
12982 ComObjPtr<Progress> pProgress;
12983 pProgress.createObject();
12984 pProgress->init(i_getVirtualBox(),
12985 static_cast<IMachine *>(this) /* aInitiator */,
12986 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12987 FALSE /* aCancelable */);
12988
12989 /* stateFilePath is null when the machine is not running */
12990 if (mData->mMachineState == MachineState_Paused)
12991 i_composeSavedStateFilename(aStateFilePath);
12992
12993 /* fill in the console task data */
12994 mConsoleTaskData.mLastState = mData->mMachineState;
12995 mConsoleTaskData.strStateFilePath = aStateFilePath;
12996 mConsoleTaskData.mProgress = pProgress;
12997
12998 /* set the state to Saving (this is expected by Console::SaveState()) */
12999 i_setMachineState(MachineState_Saving);
13000
13001 pProgress.queryInterfaceTo(aProgress.asOutParam());
13002
13003 return S_OK;
13004}
13005
13006/**
13007 * @note Locks mParent + this object for writing.
13008 */
13009HRESULT SessionMachine::endSavingState(LONG aResult,
13010 const com::Utf8Str &aErrMsg)
13011{
13012 LogFlowThisFunc(("\n"));
13013
13014 /* endSavingState() need mParent lock */
13015 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13016
13017 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13018 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13019 && mConsoleTaskData.mLastState != MachineState_Null
13020 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13021 E_FAIL);
13022
13023 /*
13024 * On failure, set the state to the state we had when BeginSavingState()
13025 * was called (this is expected by Console::SaveState() and the associated
13026 * task). On success the VM process already changed the state to
13027 * MachineState_Saved, so no need to do anything.
13028 */
13029 if (FAILED(aResult))
13030 i_setMachineState(mConsoleTaskData.mLastState);
13031
13032 return i_endSavingState(aResult, aErrMsg);
13033}
13034
13035/**
13036 * @note Locks this object for writing.
13037 */
13038HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13039{
13040 LogFlowThisFunc(("\n"));
13041
13042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13043
13044 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13045 || mData->mMachineState == MachineState_Teleported
13046 || mData->mMachineState == MachineState_Aborted
13047 , E_FAIL); /** @todo setError. */
13048
13049 com::Utf8Str stateFilePathFull;
13050 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13051 if (RT_FAILURE(vrc))
13052 return setError(VBOX_E_FILE_ERROR,
13053 tr("Invalid saved state file path '%s' (%Rrc)"),
13054 aSavedStateFile.c_str(),
13055 vrc);
13056
13057 mSSData->strStateFilePath = stateFilePathFull;
13058
13059 /* The below i_setMachineState() will detect the state transition and will
13060 * update the settings file */
13061
13062 return i_setMachineState(MachineState_Saved);
13063}
13064
13065HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13066 std::vector<com::Utf8Str> &aValues,
13067 std::vector<LONG64> &aTimestamps,
13068 std::vector<com::Utf8Str> &aFlags)
13069{
13070 LogFlowThisFunc(("\n"));
13071
13072#ifdef VBOX_WITH_GUEST_PROPS
13073 using namespace guestProp;
13074
13075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13076
13077 size_t cEntries = mHWData->mGuestProperties.size();
13078 aNames.resize(cEntries);
13079 aValues.resize(cEntries);
13080 aTimestamps.resize(cEntries);
13081 aFlags.resize(cEntries);
13082
13083 size_t i = 0;
13084 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13085 it != mHWData->mGuestProperties.end();
13086 ++it, ++i)
13087 {
13088 char szFlags[MAX_FLAGS_LEN + 1];
13089 aNames[i] = it->first;
13090 aValues[i] = it->second.strValue;
13091 aTimestamps[i] = it->second.mTimestamp;
13092
13093 /* If it is NULL, keep it NULL. */
13094 if (it->second.mFlags)
13095 {
13096 writeFlags(it->second.mFlags, szFlags);
13097 aFlags[i] = szFlags;
13098 }
13099 else
13100 aFlags[i] = "";
13101 }
13102 return S_OK;
13103#else
13104 ReturnComNotImplemented();
13105#endif
13106}
13107
13108HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13109 const com::Utf8Str &aValue,
13110 LONG64 aTimestamp,
13111 const com::Utf8Str &aFlags)
13112{
13113 LogFlowThisFunc(("\n"));
13114
13115#ifdef VBOX_WITH_GUEST_PROPS
13116 using namespace guestProp;
13117
13118 try
13119 {
13120 /*
13121 * Convert input up front.
13122 */
13123 uint32_t fFlags = NILFLAG;
13124 if (aFlags.length())
13125 {
13126 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13127 AssertRCReturn(vrc, E_INVALIDARG);
13128 }
13129
13130 /*
13131 * Now grab the object lock, validate the state and do the update.
13132 */
13133
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 switch (mData->mMachineState)
13137 {
13138 case MachineState_Paused:
13139 case MachineState_Running:
13140 case MachineState_Teleporting:
13141 case MachineState_TeleportingPausedVM:
13142 case MachineState_LiveSnapshotting:
13143 case MachineState_DeletingSnapshotOnline:
13144 case MachineState_DeletingSnapshotPaused:
13145 case MachineState_Saving:
13146 case MachineState_Stopping:
13147 break;
13148
13149 default:
13150 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13151 VBOX_E_INVALID_VM_STATE);
13152 }
13153
13154 i_setModified(IsModified_MachineData);
13155 mHWData.backup();
13156
13157 bool fDelete = !aValue.length();
13158 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13159 if (it != mHWData->mGuestProperties.end())
13160 {
13161 if (!fDelete)
13162 {
13163 it->second.strValue = aValue;
13164 it->second.mTimestamp = aTimestamp;
13165 it->second.mFlags = fFlags;
13166 }
13167 else
13168 mHWData->mGuestProperties.erase(it);
13169
13170 mData->mGuestPropertiesModified = TRUE;
13171 }
13172 else if (!fDelete)
13173 {
13174 HWData::GuestProperty prop;
13175 prop.strValue = aValue;
13176 prop.mTimestamp = aTimestamp;
13177 prop.mFlags = fFlags;
13178
13179 mHWData->mGuestProperties[aName] = prop;
13180 mData->mGuestPropertiesModified = TRUE;
13181 }
13182
13183 /*
13184 * Send a callback notification if appropriate
13185 */
13186 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13187 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13188 RTSTR_MAX,
13189 aName.c_str(),
13190 RTSTR_MAX, NULL)
13191 )
13192 {
13193 alock.release();
13194
13195 mParent->i_onGuestPropertyChange(mData->mUuid,
13196 Bstr(aName).raw(),
13197 Bstr(aValue).raw(),
13198 Bstr(aFlags).raw());
13199 }
13200 }
13201 catch (...)
13202 {
13203 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13204 }
13205 return S_OK;
13206#else
13207 ReturnComNotImplemented();
13208#endif
13209}
13210
13211
13212HRESULT SessionMachine::lockMedia()
13213{
13214 AutoMultiWriteLock2 alock(this->lockHandle(),
13215 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13216
13217 AssertReturn( mData->mMachineState == MachineState_Starting
13218 || mData->mMachineState == MachineState_Restoring
13219 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13220
13221 clearError();
13222 alock.release();
13223 return i_lockMedia();
13224}
13225
13226HRESULT SessionMachine::unlockMedia()
13227{
13228 HRESULT hrc = i_unlockMedia();
13229 return hrc;
13230}
13231
13232HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13233 ComPtr<IMediumAttachment> &aNewAttachment)
13234{
13235 // request the host lock first, since might be calling Host methods for getting host drives;
13236 // next, protect the media tree all the while we're in here, as well as our member variables
13237 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13238 this->lockHandle(),
13239 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13240
13241 IMediumAttachment *iAttach = aAttachment;
13242 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13243
13244 Bstr ctrlName;
13245 LONG lPort;
13246 LONG lDevice;
13247 bool fTempEject;
13248 {
13249 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13250
13251 /* Need to query the details first, as the IMediumAttachment reference
13252 * might be to the original settings, which we are going to change. */
13253 ctrlName = pAttach->i_getControllerName();
13254 lPort = pAttach->i_getPort();
13255 lDevice = pAttach->i_getDevice();
13256 fTempEject = pAttach->i_getTempEject();
13257 }
13258
13259 if (!fTempEject)
13260 {
13261 /* Remember previously mounted medium. The medium before taking the
13262 * backup is not necessarily the same thing. */
13263 ComObjPtr<Medium> oldmedium;
13264 oldmedium = pAttach->i_getMedium();
13265
13266 i_setModified(IsModified_Storage);
13267 mMediaData.backup();
13268
13269 // The backup operation makes the pAttach reference point to the
13270 // old settings. Re-get the correct reference.
13271 pAttach = i_findAttachment(mMediaData->mAttachments,
13272 ctrlName.raw(),
13273 lPort,
13274 lDevice);
13275
13276 {
13277 AutoCaller autoAttachCaller(this);
13278 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13279
13280 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13281 if (!oldmedium.isNull())
13282 oldmedium->i_removeBackReference(mData->mUuid);
13283
13284 pAttach->i_updateMedium(NULL);
13285 pAttach->i_updateEjected();
13286 }
13287
13288 i_setModified(IsModified_Storage);
13289 }
13290 else
13291 {
13292 {
13293 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13294 pAttach->i_updateEjected();
13295 }
13296 }
13297
13298 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13299
13300 return S_OK;
13301}
13302
13303// public methods only for internal purposes
13304/////////////////////////////////////////////////////////////////////////////
13305
13306#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13307/**
13308 * Called from the client watcher thread to check for expected or unexpected
13309 * death of the client process that has a direct session to this machine.
13310 *
13311 * On Win32 and on OS/2, this method is called only when we've got the
13312 * mutex (i.e. the client has either died or terminated normally) so it always
13313 * returns @c true (the client is terminated, the session machine is
13314 * uninitialized).
13315 *
13316 * On other platforms, the method returns @c true if the client process has
13317 * terminated normally or abnormally and the session machine was uninitialized,
13318 * and @c false if the client process is still alive.
13319 *
13320 * @note Locks this object for writing.
13321 */
13322bool SessionMachine::i_checkForDeath()
13323{
13324 Uninit::Reason reason;
13325 bool terminated = false;
13326
13327 /* Enclose autoCaller with a block because calling uninit() from under it
13328 * will deadlock. */
13329 {
13330 AutoCaller autoCaller(this);
13331 if (!autoCaller.isOk())
13332 {
13333 /* return true if not ready, to cause the client watcher to exclude
13334 * the corresponding session from watching */
13335 LogFlowThisFunc(("Already uninitialized!\n"));
13336 return true;
13337 }
13338
13339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13340
13341 /* Determine the reason of death: if the session state is Closing here,
13342 * everything is fine. Otherwise it means that the client did not call
13343 * OnSessionEnd() before it released the IPC semaphore. This may happen
13344 * either because the client process has abnormally terminated, or
13345 * because it simply forgot to call ISession::Close() before exiting. We
13346 * threat the latter also as an abnormal termination (see
13347 * Session::uninit() for details). */
13348 reason = mData->mSession.mState == SessionState_Unlocking ?
13349 Uninit::Normal :
13350 Uninit::Abnormal;
13351
13352 if (mClientToken)
13353 terminated = mClientToken->release();
13354 } /* AutoCaller block */
13355
13356 if (terminated)
13357 uninit(reason);
13358
13359 return terminated;
13360}
13361
13362void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13363{
13364 LogFlowThisFunc(("\n"));
13365
13366 strTokenId.setNull();
13367
13368 AutoCaller autoCaller(this);
13369 AssertComRCReturnVoid(autoCaller.rc());
13370
13371 Assert(mClientToken);
13372 if (mClientToken)
13373 mClientToken->getId(strTokenId);
13374}
13375#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13376IToken *SessionMachine::i_getToken()
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380 AutoCaller autoCaller(this);
13381 AssertComRCReturn(autoCaller.rc(), NULL);
13382
13383 Assert(mClientToken);
13384 if (mClientToken)
13385 return mClientToken->getToken();
13386 else
13387 return NULL;
13388}
13389#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13390
13391Machine::ClientToken *SessionMachine::i_getClientToken()
13392{
13393 LogFlowThisFunc(("\n"));
13394
13395 AutoCaller autoCaller(this);
13396 AssertComRCReturn(autoCaller.rc(), NULL);
13397
13398 return mClientToken;
13399}
13400
13401
13402/**
13403 * @note Locks this object for reading.
13404 */
13405HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13406{
13407 LogFlowThisFunc(("\n"));
13408
13409 AutoCaller autoCaller(this);
13410 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13411
13412 ComPtr<IInternalSessionControl> directControl;
13413 {
13414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13415 directControl = mData->mSession.mDirectControl;
13416 }
13417
13418 /* ignore notifications sent after #OnSessionEnd() is called */
13419 if (!directControl)
13420 return S_OK;
13421
13422 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13423}
13424
13425/**
13426 * @note Locks this object for reading.
13427 */
13428HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13429 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13430 IN_BSTR aGuestIp, LONG aGuestPort)
13431{
13432 LogFlowThisFunc(("\n"));
13433
13434 AutoCaller autoCaller(this);
13435 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13436
13437 ComPtr<IInternalSessionControl> directControl;
13438 {
13439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13440 directControl = mData->mSession.mDirectControl;
13441 }
13442
13443 /* ignore notifications sent after #OnSessionEnd() is called */
13444 if (!directControl)
13445 return S_OK;
13446 /*
13447 * instead acting like callback we ask IVirtualBox deliver corresponding event
13448 */
13449
13450 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13451 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13452 return S_OK;
13453}
13454
13455/**
13456 * @note Locks this object for reading.
13457 */
13458HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13459{
13460 LogFlowThisFunc(("\n"));
13461
13462 AutoCaller autoCaller(this);
13463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13464
13465 ComPtr<IInternalSessionControl> directControl;
13466 {
13467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13468 directControl = mData->mSession.mDirectControl;
13469 }
13470
13471 /* ignore notifications sent after #OnSessionEnd() is called */
13472 if (!directControl)
13473 return S_OK;
13474
13475 return directControl->OnSerialPortChange(serialPort);
13476}
13477
13478/**
13479 * @note Locks this object for reading.
13480 */
13481HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485 AutoCaller autoCaller(this);
13486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13487
13488 ComPtr<IInternalSessionControl> directControl;
13489 {
13490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13491 directControl = mData->mSession.mDirectControl;
13492 }
13493
13494 /* ignore notifications sent after #OnSessionEnd() is called */
13495 if (!directControl)
13496 return S_OK;
13497
13498 return directControl->OnParallelPortChange(parallelPort);
13499}
13500
13501/**
13502 * @note Locks this object for reading.
13503 */
13504HRESULT SessionMachine::i_onStorageControllerChange()
13505{
13506 LogFlowThisFunc(("\n"));
13507
13508 AutoCaller autoCaller(this);
13509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13510
13511 ComPtr<IInternalSessionControl> directControl;
13512 {
13513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13514 directControl = mData->mSession.mDirectControl;
13515 }
13516
13517 /* ignore notifications sent after #OnSessionEnd() is called */
13518 if (!directControl)
13519 return S_OK;
13520
13521 return directControl->OnStorageControllerChange();
13522}
13523
13524/**
13525 * @note Locks this object for reading.
13526 */
13527HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13528{
13529 LogFlowThisFunc(("\n"));
13530
13531 AutoCaller autoCaller(this);
13532 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13533
13534 ComPtr<IInternalSessionControl> directControl;
13535 {
13536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13537 directControl = mData->mSession.mDirectControl;
13538 }
13539
13540 /* ignore notifications sent after #OnSessionEnd() is called */
13541 if (!directControl)
13542 return S_OK;
13543
13544 return directControl->OnMediumChange(aAttachment, aForce);
13545}
13546
13547/**
13548 * @note Locks this object for reading.
13549 */
13550HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13551{
13552 LogFlowThisFunc(("\n"));
13553
13554 AutoCaller autoCaller(this);
13555 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13556
13557 ComPtr<IInternalSessionControl> directControl;
13558 {
13559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13560 directControl = mData->mSession.mDirectControl;
13561 }
13562
13563 /* ignore notifications sent after #OnSessionEnd() is called */
13564 if (!directControl)
13565 return S_OK;
13566
13567 return directControl->OnCPUChange(aCPU, aRemove);
13568}
13569
13570HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13571{
13572 LogFlowThisFunc(("\n"));
13573
13574 AutoCaller autoCaller(this);
13575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13576
13577 ComPtr<IInternalSessionControl> directControl;
13578 {
13579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13580 directControl = mData->mSession.mDirectControl;
13581 }
13582
13583 /* ignore notifications sent after #OnSessionEnd() is called */
13584 if (!directControl)
13585 return S_OK;
13586
13587 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13588}
13589
13590/**
13591 * @note Locks this object for reading.
13592 */
13593HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13594{
13595 LogFlowThisFunc(("\n"));
13596
13597 AutoCaller autoCaller(this);
13598 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13599
13600 ComPtr<IInternalSessionControl> directControl;
13601 {
13602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13603 directControl = mData->mSession.mDirectControl;
13604 }
13605
13606 /* ignore notifications sent after #OnSessionEnd() is called */
13607 if (!directControl)
13608 return S_OK;
13609
13610 return directControl->OnVRDEServerChange(aRestart);
13611}
13612
13613/**
13614 * @note Locks this object for reading.
13615 */
13616HRESULT SessionMachine::i_onVideoCaptureChange()
13617{
13618 LogFlowThisFunc(("\n"));
13619
13620 AutoCaller autoCaller(this);
13621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13622
13623 ComPtr<IInternalSessionControl> directControl;
13624 {
13625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13626 directControl = mData->mSession.mDirectControl;
13627 }
13628
13629 /* ignore notifications sent after #OnSessionEnd() is called */
13630 if (!directControl)
13631 return S_OK;
13632
13633 return directControl->OnVideoCaptureChange();
13634}
13635
13636/**
13637 * @note Locks this object for reading.
13638 */
13639HRESULT SessionMachine::i_onUSBControllerChange()
13640{
13641 LogFlowThisFunc(("\n"));
13642
13643 AutoCaller autoCaller(this);
13644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13645
13646 ComPtr<IInternalSessionControl> directControl;
13647 {
13648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13649 directControl = mData->mSession.mDirectControl;
13650 }
13651
13652 /* ignore notifications sent after #OnSessionEnd() is called */
13653 if (!directControl)
13654 return S_OK;
13655
13656 return directControl->OnUSBControllerChange();
13657}
13658
13659/**
13660 * @note Locks this object for reading.
13661 */
13662HRESULT SessionMachine::i_onSharedFolderChange()
13663{
13664 LogFlowThisFunc(("\n"));
13665
13666 AutoCaller autoCaller(this);
13667 AssertComRCReturnRC(autoCaller.rc());
13668
13669 ComPtr<IInternalSessionControl> directControl;
13670 {
13671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13672 directControl = mData->mSession.mDirectControl;
13673 }
13674
13675 /* ignore notifications sent after #OnSessionEnd() is called */
13676 if (!directControl)
13677 return S_OK;
13678
13679 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13680}
13681
13682/**
13683 * @note Locks this object for reading.
13684 */
13685HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13686{
13687 LogFlowThisFunc(("\n"));
13688
13689 AutoCaller autoCaller(this);
13690 AssertComRCReturnRC(autoCaller.rc());
13691
13692 ComPtr<IInternalSessionControl> directControl;
13693 {
13694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13695 directControl = mData->mSession.mDirectControl;
13696 }
13697
13698 /* ignore notifications sent after #OnSessionEnd() is called */
13699 if (!directControl)
13700 return S_OK;
13701
13702 return directControl->OnClipboardModeChange(aClipboardMode);
13703}
13704
13705/**
13706 * @note Locks this object for reading.
13707 */
13708HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13709{
13710 LogFlowThisFunc(("\n"));
13711
13712 AutoCaller autoCaller(this);
13713 AssertComRCReturnRC(autoCaller.rc());
13714
13715 ComPtr<IInternalSessionControl> directControl;
13716 {
13717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13718 directControl = mData->mSession.mDirectControl;
13719 }
13720
13721 /* ignore notifications sent after #OnSessionEnd() is called */
13722 if (!directControl)
13723 return S_OK;
13724
13725 return directControl->OnDnDModeChange(aDnDMode);
13726}
13727
13728/**
13729 * @note Locks this object for reading.
13730 */
13731HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13732{
13733 LogFlowThisFunc(("\n"));
13734
13735 AutoCaller autoCaller(this);
13736 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13737
13738 ComPtr<IInternalSessionControl> directControl;
13739 {
13740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13741 directControl = mData->mSession.mDirectControl;
13742 }
13743
13744 /* ignore notifications sent after #OnSessionEnd() is called */
13745 if (!directControl)
13746 return S_OK;
13747
13748 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13749}
13750
13751/**
13752 * @note Locks this object for reading.
13753 */
13754HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13755{
13756 LogFlowThisFunc(("\n"));
13757
13758 AutoCaller autoCaller(this);
13759 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13760
13761 ComPtr<IInternalSessionControl> directControl;
13762 {
13763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13764 directControl = mData->mSession.mDirectControl;
13765 }
13766
13767 /* ignore notifications sent after #OnSessionEnd() is called */
13768 if (!directControl)
13769 return S_OK;
13770
13771 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13772}
13773
13774/**
13775 * Returns @c true if this machine's USB controller reports it has a matching
13776 * filter for the given USB device and @c false otherwise.
13777 *
13778 * @note locks this object for reading.
13779 */
13780bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13781{
13782 AutoCaller autoCaller(this);
13783 /* silently return if not ready -- this method may be called after the
13784 * direct machine session has been called */
13785 if (!autoCaller.isOk())
13786 return false;
13787
13788#ifdef VBOX_WITH_USB
13789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13790
13791 switch (mData->mMachineState)
13792 {
13793 case MachineState_Starting:
13794 case MachineState_Restoring:
13795 case MachineState_TeleportingIn:
13796 case MachineState_Paused:
13797 case MachineState_Running:
13798 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13799 * elsewhere... */
13800 alock.release();
13801 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13802 default: break;
13803 }
13804#else
13805 NOREF(aDevice);
13806 NOREF(aMaskedIfs);
13807#endif
13808 return false;
13809}
13810
13811/**
13812 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13813 */
13814HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13815 IVirtualBoxErrorInfo *aError,
13816 ULONG aMaskedIfs)
13817{
13818 LogFlowThisFunc(("\n"));
13819
13820 AutoCaller autoCaller(this);
13821
13822 /* This notification may happen after the machine object has been
13823 * uninitialized (the session was closed), so don't assert. */
13824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13825
13826 ComPtr<IInternalSessionControl> directControl;
13827 {
13828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13829 directControl = mData->mSession.mDirectControl;
13830 }
13831
13832 /* fail on notifications sent after #OnSessionEnd() is called, it is
13833 * expected by the caller */
13834 if (!directControl)
13835 return E_FAIL;
13836
13837 /* No locks should be held at this point. */
13838 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13839 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13840
13841 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13842}
13843
13844/**
13845 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13846 */
13847HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13848 IVirtualBoxErrorInfo *aError)
13849{
13850 LogFlowThisFunc(("\n"));
13851
13852 AutoCaller autoCaller(this);
13853
13854 /* This notification may happen after the machine object has been
13855 * uninitialized (the session was closed), so don't assert. */
13856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13857
13858 ComPtr<IInternalSessionControl> directControl;
13859 {
13860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13861 directControl = mData->mSession.mDirectControl;
13862 }
13863
13864 /* fail on notifications sent after #OnSessionEnd() is called, it is
13865 * expected by the caller */
13866 if (!directControl)
13867 return E_FAIL;
13868
13869 /* No locks should be held at this point. */
13870 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13871 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13872
13873 return directControl->OnUSBDeviceDetach(aId, aError);
13874}
13875
13876// protected methods
13877/////////////////////////////////////////////////////////////////////////////
13878
13879/**
13880 * Helper method to finalize saving the state.
13881 *
13882 * @note Must be called from under this object's lock.
13883 *
13884 * @param aRc S_OK if the snapshot has been taken successfully
13885 * @param aErrMsg human readable error message for failure
13886 *
13887 * @note Locks mParent + this objects for writing.
13888 */
13889HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13890{
13891 LogFlowThisFuncEnter();
13892
13893 AutoCaller autoCaller(this);
13894 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13895
13896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13897
13898 HRESULT rc = S_OK;
13899
13900 if (SUCCEEDED(aRc))
13901 {
13902 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13903
13904 /* save all VM settings */
13905 rc = i_saveSettings(NULL);
13906 // no need to check whether VirtualBox.xml needs saving also since
13907 // we can't have a name change pending at this point
13908 }
13909 else
13910 {
13911 // delete the saved state file (it might have been already created);
13912 // we need not check whether this is shared with a snapshot here because
13913 // we certainly created this saved state file here anew
13914 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13915 }
13916
13917 /* notify the progress object about operation completion */
13918 Assert(mConsoleTaskData.mProgress);
13919 if (SUCCEEDED(aRc))
13920 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13921 else
13922 {
13923 if (aErrMsg.length())
13924 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13925 COM_IIDOF(ISession),
13926 getComponentName(),
13927 aErrMsg.c_str());
13928 else
13929 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13930 }
13931
13932 /* clear out the temporary saved state data */
13933 mConsoleTaskData.mLastState = MachineState_Null;
13934 mConsoleTaskData.strStateFilePath.setNull();
13935 mConsoleTaskData.mProgress.setNull();
13936
13937 LogFlowThisFuncLeave();
13938 return rc;
13939}
13940
13941/**
13942 * Deletes the given file if it is no longer in use by either the current machine state
13943 * (if the machine is "saved") or any of the machine's snapshots.
13944 *
13945 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13946 * but is different for each SnapshotMachine. When calling this, the order of calling this
13947 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13948 * is therefore critical. I know, it's all rather messy.
13949 *
13950 * @param strStateFile
13951 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13952 * the test for whether the saved state file is in use.
13953 */
13954void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13955 Snapshot *pSnapshotToIgnore)
13956{
13957 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13958 if ( (strStateFile.isNotEmpty())
13959 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13960 )
13961 // ... and it must also not be shared with other snapshots
13962 if ( !mData->mFirstSnapshot
13963 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13964 // this checks the SnapshotMachine's state file paths
13965 )
13966 RTFileDelete(strStateFile.c_str());
13967}
13968
13969/**
13970 * Locks the attached media.
13971 *
13972 * All attached hard disks are locked for writing and DVD/floppy are locked for
13973 * reading. Parents of attached hard disks (if any) are locked for reading.
13974 *
13975 * This method also performs accessibility check of all media it locks: if some
13976 * media is inaccessible, the method will return a failure and a bunch of
13977 * extended error info objects per each inaccessible medium.
13978 *
13979 * Note that this method is atomic: if it returns a success, all media are
13980 * locked as described above; on failure no media is locked at all (all
13981 * succeeded individual locks will be undone).
13982 *
13983 * The caller is responsible for doing the necessary state sanity checks.
13984 *
13985 * The locks made by this method must be undone by calling #unlockMedia() when
13986 * no more needed.
13987 */
13988HRESULT SessionMachine::i_lockMedia()
13989{
13990 AutoCaller autoCaller(this);
13991 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13992
13993 AutoMultiWriteLock2 alock(this->lockHandle(),
13994 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13995
13996 /* bail out if trying to lock things with already set up locking */
13997 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13998
13999 MultiResult mrc(S_OK);
14000
14001 /* Collect locking information for all medium objects attached to the VM. */
14002 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14003 it != mMediaData->mAttachments.end();
14004 ++it)
14005 {
14006 MediumAttachment* pAtt = *it;
14007 DeviceType_T devType = pAtt->i_getType();
14008 Medium *pMedium = pAtt->i_getMedium();
14009
14010 MediumLockList *pMediumLockList(new MediumLockList());
14011 // There can be attachments without a medium (floppy/dvd), and thus
14012 // it's impossible to create a medium lock list. It still makes sense
14013 // to have the empty medium lock list in the map in case a medium is
14014 // attached later.
14015 if (pMedium != NULL)
14016 {
14017 MediumType_T mediumType = pMedium->i_getType();
14018 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14019 || mediumType == MediumType_Shareable;
14020 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14021
14022 alock.release();
14023 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14024 !fIsReadOnlyLock /* fMediumLockWrite */,
14025 NULL,
14026 *pMediumLockList);
14027 alock.acquire();
14028 if (FAILED(mrc))
14029 {
14030 delete pMediumLockList;
14031 mData->mSession.mLockedMedia.Clear();
14032 break;
14033 }
14034 }
14035
14036 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14037 if (FAILED(rc))
14038 {
14039 mData->mSession.mLockedMedia.Clear();
14040 mrc = setError(rc,
14041 tr("Collecting locking information for all attached media failed"));
14042 break;
14043 }
14044 }
14045
14046 if (SUCCEEDED(mrc))
14047 {
14048 /* Now lock all media. If this fails, nothing is locked. */
14049 alock.release();
14050 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14051 alock.acquire();
14052 if (FAILED(rc))
14053 {
14054 mrc = setError(rc,
14055 tr("Locking of attached media failed"));
14056 }
14057 }
14058
14059 return mrc;
14060}
14061
14062/**
14063 * Undoes the locks made by by #lockMedia().
14064 */
14065HRESULT SessionMachine::i_unlockMedia()
14066{
14067 AutoCaller autoCaller(this);
14068 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14069
14070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14071
14072 /* we may be holding important error info on the current thread;
14073 * preserve it */
14074 ErrorInfoKeeper eik;
14075
14076 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14077 AssertComRC(rc);
14078 return rc;
14079}
14080
14081/**
14082 * Helper to change the machine state (reimplementation).
14083 *
14084 * @note Locks this object for writing.
14085 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14086 * it can cause crashes in random places due to unexpectedly committing
14087 * the current settings. The caller is responsible for that. The call
14088 * to saveStateSettings is fine, because this method does not commit.
14089 */
14090HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14091{
14092 LogFlowThisFuncEnter();
14093 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14097
14098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14099
14100 MachineState_T oldMachineState = mData->mMachineState;
14101
14102 AssertMsgReturn(oldMachineState != aMachineState,
14103 ("oldMachineState=%s, aMachineState=%s\n",
14104 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14105 E_FAIL);
14106
14107 HRESULT rc = S_OK;
14108
14109 int stsFlags = 0;
14110 bool deleteSavedState = false;
14111
14112 /* detect some state transitions */
14113
14114 if ( ( oldMachineState == MachineState_Saved
14115 && aMachineState == MachineState_Restoring)
14116 || ( ( oldMachineState == MachineState_PoweredOff
14117 || oldMachineState == MachineState_Teleported
14118 || oldMachineState == MachineState_Aborted
14119 )
14120 && ( aMachineState == MachineState_TeleportingIn
14121 || aMachineState == MachineState_Starting
14122 )
14123 )
14124 )
14125 {
14126 /* The EMT thread is about to start */
14127
14128 /* Nothing to do here for now... */
14129
14130 /// @todo NEWMEDIA don't let mDVDDrive and other children
14131 /// change anything when in the Starting/Restoring state
14132 }
14133 else if ( ( oldMachineState == MachineState_Running
14134 || oldMachineState == MachineState_Paused
14135 || oldMachineState == MachineState_Teleporting
14136 || oldMachineState == MachineState_LiveSnapshotting
14137 || oldMachineState == MachineState_Stuck
14138 || oldMachineState == MachineState_Starting
14139 || oldMachineState == MachineState_Stopping
14140 || oldMachineState == MachineState_Saving
14141 || oldMachineState == MachineState_Restoring
14142 || oldMachineState == MachineState_TeleportingPausedVM
14143 || oldMachineState == MachineState_TeleportingIn
14144 )
14145 && ( aMachineState == MachineState_PoweredOff
14146 || aMachineState == MachineState_Saved
14147 || aMachineState == MachineState_Teleported
14148 || aMachineState == MachineState_Aborted
14149 )
14150 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14151 * snapshot */
14152 && ( mConsoleTaskData.mSnapshot.isNull()
14153 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14154 )
14155 )
14156 {
14157 /* The EMT thread has just stopped, unlock attached media. Note that as
14158 * opposed to locking that is done from Console, we do unlocking here
14159 * because the VM process may have aborted before having a chance to
14160 * properly unlock all media it locked. */
14161
14162 unlockMedia();
14163 }
14164
14165 if (oldMachineState == MachineState_Restoring)
14166 {
14167 if (aMachineState != MachineState_Saved)
14168 {
14169 /*
14170 * delete the saved state file once the machine has finished
14171 * restoring from it (note that Console sets the state from
14172 * Restoring to Saved if the VM couldn't restore successfully,
14173 * to give the user an ability to fix an error and retry --
14174 * we keep the saved state file in this case)
14175 */
14176 deleteSavedState = true;
14177 }
14178 }
14179 else if ( oldMachineState == MachineState_Saved
14180 && ( aMachineState == MachineState_PoweredOff
14181 || aMachineState == MachineState_Aborted
14182 || aMachineState == MachineState_Teleported
14183 )
14184 )
14185 {
14186 /*
14187 * delete the saved state after Console::ForgetSavedState() is called
14188 * or if the VM process (owning a direct VM session) crashed while the
14189 * VM was Saved
14190 */
14191
14192 /// @todo (dmik)
14193 // Not sure that deleting the saved state file just because of the
14194 // client death before it attempted to restore the VM is a good
14195 // thing. But when it crashes we need to go to the Aborted state
14196 // which cannot have the saved state file associated... The only
14197 // way to fix this is to make the Aborted condition not a VM state
14198 // but a bool flag: i.e., when a crash occurs, set it to true and
14199 // change the state to PoweredOff or Saved depending on the
14200 // saved state presence.
14201
14202 deleteSavedState = true;
14203 mData->mCurrentStateModified = TRUE;
14204 stsFlags |= SaveSTS_CurStateModified;
14205 }
14206
14207 if ( aMachineState == MachineState_Starting
14208 || aMachineState == MachineState_Restoring
14209 || aMachineState == MachineState_TeleportingIn
14210 )
14211 {
14212 /* set the current state modified flag to indicate that the current
14213 * state is no more identical to the state in the
14214 * current snapshot */
14215 if (!mData->mCurrentSnapshot.isNull())
14216 {
14217 mData->mCurrentStateModified = TRUE;
14218 stsFlags |= SaveSTS_CurStateModified;
14219 }
14220 }
14221
14222 if (deleteSavedState)
14223 {
14224 if (mRemoveSavedState)
14225 {
14226 Assert(!mSSData->strStateFilePath.isEmpty());
14227
14228 // it is safe to delete the saved state file if ...
14229 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14230 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14231 // ... none of the snapshots share the saved state file
14232 )
14233 RTFileDelete(mSSData->strStateFilePath.c_str());
14234 }
14235
14236 mSSData->strStateFilePath.setNull();
14237 stsFlags |= SaveSTS_StateFilePath;
14238 }
14239
14240 /* redirect to the underlying peer machine */
14241 mPeer->i_setMachineState(aMachineState);
14242
14243 if ( aMachineState == MachineState_PoweredOff
14244 || aMachineState == MachineState_Teleported
14245 || aMachineState == MachineState_Aborted
14246 || aMachineState == MachineState_Saved)
14247 {
14248 /* the machine has stopped execution
14249 * (or the saved state file was adopted) */
14250 stsFlags |= SaveSTS_StateTimeStamp;
14251 }
14252
14253 if ( ( oldMachineState == MachineState_PoweredOff
14254 || oldMachineState == MachineState_Aborted
14255 || oldMachineState == MachineState_Teleported
14256 )
14257 && aMachineState == MachineState_Saved)
14258 {
14259 /* the saved state file was adopted */
14260 Assert(!mSSData->strStateFilePath.isEmpty());
14261 stsFlags |= SaveSTS_StateFilePath;
14262 }
14263
14264#ifdef VBOX_WITH_GUEST_PROPS
14265 if ( aMachineState == MachineState_PoweredOff
14266 || aMachineState == MachineState_Aborted
14267 || aMachineState == MachineState_Teleported)
14268 {
14269 /* Make sure any transient guest properties get removed from the
14270 * property store on shutdown. */
14271
14272 HWData::GuestPropertyMap::const_iterator it;
14273 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14274 if (!fNeedsSaving)
14275 for (it = mHWData->mGuestProperties.begin();
14276 it != mHWData->mGuestProperties.end(); ++it)
14277 if ( (it->second.mFlags & guestProp::TRANSIENT)
14278 || (it->second.mFlags & guestProp::TRANSRESET))
14279 {
14280 fNeedsSaving = true;
14281 break;
14282 }
14283 if (fNeedsSaving)
14284 {
14285 mData->mCurrentStateModified = TRUE;
14286 stsFlags |= SaveSTS_CurStateModified;
14287 }
14288 }
14289#endif
14290
14291 rc = i_saveStateSettings(stsFlags);
14292
14293 if ( ( oldMachineState != MachineState_PoweredOff
14294 && oldMachineState != MachineState_Aborted
14295 && oldMachineState != MachineState_Teleported
14296 )
14297 && ( aMachineState == MachineState_PoweredOff
14298 || aMachineState == MachineState_Aborted
14299 || aMachineState == MachineState_Teleported
14300 )
14301 )
14302 {
14303 /* we've been shut down for any reason */
14304 /* no special action so far */
14305 }
14306
14307 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14308 LogFlowThisFuncLeave();
14309 return rc;
14310}
14311
14312/**
14313 * Sends the current machine state value to the VM process.
14314 *
14315 * @note Locks this object for reading, then calls a client process.
14316 */
14317HRESULT SessionMachine::i_updateMachineStateOnClient()
14318{
14319 AutoCaller autoCaller(this);
14320 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14321
14322 ComPtr<IInternalSessionControl> directControl;
14323 {
14324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14325 AssertReturn(!!mData, E_FAIL);
14326 directControl = mData->mSession.mDirectControl;
14327
14328 /* directControl may be already set to NULL here in #OnSessionEnd()
14329 * called too early by the direct session process while there is still
14330 * some operation (like deleting the snapshot) in progress. The client
14331 * process in this case is waiting inside Session::close() for the
14332 * "end session" process object to complete, while #uninit() called by
14333 * #checkForDeath() on the Watcher thread is waiting for the pending
14334 * operation to complete. For now, we accept this inconsistent behavior
14335 * and simply do nothing here. */
14336
14337 if (mData->mSession.mState == SessionState_Unlocking)
14338 return S_OK;
14339
14340 AssertReturn(!directControl.isNull(), E_FAIL);
14341 }
14342
14343 return directControl->UpdateMachineState(mData->mMachineState);
14344}
14345
14346HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14347{
14348 NOREF(aRemove);
14349 ReturnComNotImplemented();
14350}
14351
14352HRESULT Machine::updateState(MachineState_T aState)
14353{
14354 NOREF(aState);
14355 ReturnComNotImplemented();
14356}
14357
14358HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14359{
14360 NOREF(aProgress);
14361 ReturnComNotImplemented();
14362}
14363
14364HRESULT Machine::endPowerUp(LONG aResult)
14365{
14366 NOREF(aResult);
14367 ReturnComNotImplemented();
14368}
14369
14370HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14371{
14372 NOREF(aProgress);
14373 ReturnComNotImplemented();
14374}
14375
14376HRESULT Machine::endPoweringDown(LONG aResult,
14377 const com::Utf8Str &aErrMsg)
14378{
14379 NOREF(aResult);
14380 NOREF(aErrMsg);
14381 ReturnComNotImplemented();
14382}
14383
14384HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14385 BOOL *aMatched,
14386 ULONG *aMaskedInterfaces)
14387{
14388 NOREF(aDevice);
14389 NOREF(aMatched);
14390 NOREF(aMaskedInterfaces);
14391 ReturnComNotImplemented();
14392
14393}
14394
14395HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14396{
14397 NOREF(aId);
14398 ReturnComNotImplemented();
14399}
14400
14401HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14402 BOOL aDone)
14403{
14404 NOREF(aId);
14405 NOREF(aDone);
14406 ReturnComNotImplemented();
14407}
14408
14409HRESULT Machine::autoCaptureUSBDevices()
14410{
14411 ReturnComNotImplemented();
14412}
14413
14414HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14415{
14416 NOREF(aDone);
14417 ReturnComNotImplemented();
14418}
14419
14420HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14421 ComPtr<IProgress> &aProgress)
14422{
14423 NOREF(aSession);
14424 NOREF(aProgress);
14425 ReturnComNotImplemented();
14426}
14427
14428HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14429 com::Utf8Str &aStateFilePath)
14430{
14431 NOREF(aProgress);
14432 NOREF(aStateFilePath);
14433 ReturnComNotImplemented();
14434}
14435
14436HRESULT Machine::endSavingState(LONG aResult,
14437 const com::Utf8Str &aErrMsg)
14438{
14439 NOREF(aResult);
14440 NOREF(aErrMsg);
14441 ReturnComNotImplemented();
14442}
14443
14444HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14445{
14446 NOREF(aSavedStateFile);
14447 ReturnComNotImplemented();
14448}
14449
14450HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14451 const com::Utf8Str &aName,
14452 const com::Utf8Str &aDescription,
14453 const ComPtr<IProgress> &aConsoleProgress,
14454 BOOL aFTakingSnapshotOnline,
14455 com::Utf8Str &aStateFilePath)
14456{
14457 NOREF(aInitiator);
14458 NOREF(aName);
14459 NOREF(aDescription);
14460 NOREF(aConsoleProgress);
14461 NOREF(aFTakingSnapshotOnline);
14462 NOREF(aStateFilePath);
14463 ReturnComNotImplemented();
14464}
14465
14466HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14467{
14468 NOREF(aSuccess);
14469 ReturnComNotImplemented();
14470}
14471
14472HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14473 const com::Guid &aStartId,
14474 const com::Guid &aEndId,
14475 BOOL aDeleteAllChildren,
14476 MachineState_T *aMachineState,
14477 ComPtr<IProgress> &aProgress)
14478{
14479 NOREF(aInitiator);
14480 NOREF(aStartId);
14481 NOREF(aEndId);
14482 NOREF(aDeleteAllChildren);
14483 NOREF(aMachineState);
14484 NOREF(aProgress);
14485 ReturnComNotImplemented();
14486}
14487
14488HRESULT Machine::finishOnlineMergeMedium()
14489{
14490 ReturnComNotImplemented();
14491}
14492
14493HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14494 const ComPtr<ISnapshot> &aSnapshot,
14495 MachineState_T *aMachineState,
14496 ComPtr<IProgress> &aProgress)
14497{
14498 NOREF(aInitiator);
14499 NOREF(aSnapshot);
14500 NOREF(aMachineState);
14501 NOREF(aProgress);
14502 ReturnComNotImplemented();
14503}
14504
14505HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14506 std::vector<com::Utf8Str> &aValues,
14507 std::vector<LONG64> &aTimestamps,
14508 std::vector<com::Utf8Str> &aFlags)
14509{
14510 NOREF(aNames);
14511 NOREF(aValues);
14512 NOREF(aTimestamps);
14513 NOREF(aFlags);
14514 ReturnComNotImplemented();
14515}
14516
14517HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14518 const com::Utf8Str &aValue,
14519 LONG64 aTimestamp,
14520 const com::Utf8Str &aFlags)
14521{
14522 NOREF(aName);
14523 NOREF(aValue);
14524 NOREF(aTimestamp);
14525 NOREF(aFlags);
14526 ReturnComNotImplemented();
14527}
14528
14529HRESULT Machine::lockMedia()
14530{
14531 ReturnComNotImplemented();
14532}
14533
14534HRESULT Machine::unlockMedia()
14535{
14536 ReturnComNotImplemented();
14537}
14538
14539HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14540 ComPtr<IMediumAttachment> &aNewAttachment)
14541{
14542 NOREF(aAttachment);
14543 NOREF(aNewAttachment);
14544 ReturnComNotImplemented();
14545}
14546
14547HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14548 ULONG aCpuUser,
14549 ULONG aCpuKernel,
14550 ULONG aCpuIdle,
14551 ULONG aMemTotal,
14552 ULONG aMemFree,
14553 ULONG aMemBalloon,
14554 ULONG aMemShared,
14555 ULONG aMemCache,
14556 ULONG aPagedTotal,
14557 ULONG aMemAllocTotal,
14558 ULONG aMemFreeTotal,
14559 ULONG aMemBalloonTotal,
14560 ULONG aMemSharedTotal,
14561 ULONG aVmNetRx,
14562 ULONG aVmNetTx)
14563{
14564 NOREF(aValidStats);
14565 NOREF(aCpuUser);
14566 NOREF(aCpuKernel);
14567 NOREF(aCpuIdle);
14568 NOREF(aMemTotal);
14569 NOREF(aMemFree);
14570 NOREF(aMemBalloon);
14571 NOREF(aMemShared);
14572 NOREF(aMemCache);
14573 NOREF(aPagedTotal);
14574 NOREF(aMemAllocTotal);
14575 NOREF(aMemFreeTotal);
14576 NOREF(aMemBalloonTotal);
14577 NOREF(aMemSharedTotal);
14578 NOREF(aVmNetRx);
14579 NOREF(aVmNetTx);
14580 ReturnComNotImplemented();
14581}
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