VirtualBox

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

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

Main: AutoCaller/VirtualBoxBase refactoring, cleanly splitting out the object state handling, and moving all caller synchronization to one file. Also eliminated a not so vital template (AutoCallerBase) by much simpler inheritance. Theoretically has no visible effects, the behavior should be identical. Done as a preparation for reimplementing the caller synchronization.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 493.3 KB
Line 
1/* $Id: MachineImpl.cpp 51903 2014-07-07 13:03:49Z 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 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mSyntheticCpu = false;
190 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDnDMode = DnDMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mParavirtProvider = ParavirtProvider_Default;
209 mEmulatedUSBCardReaderEnabled = FALSE;
210
211 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
212 mCPUAttached[i] = false;
213
214 mIOCacheEnabled = true;
215 mIOCacheSize = 5; /* 5MB */
216
217 /* Maximum CPU execution cap by default. */
218 mCpuExecutionCap = 100;
219}
220
221Machine::HWData::~HWData()
222{
223}
224
225/////////////////////////////////////////////////////////////////////////////
226// Machine::HDData structure
227/////////////////////////////////////////////////////////////////////////////
228
229Machine::MediaData::MediaData()
230{
231}
232
233Machine::MediaData::~MediaData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = i_isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->i_id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->i_applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->i_applyDefaults(aOsType);
352
353 /* Let the OS type select 64-bit ness. */
354 mHWData->mLongMode = aOsType->i_is64Bit()
355 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
356 }
357
358 /* At this point the changing of the current state modification
359 * flag is allowed. */
360 i_allowStateModification();
361
362 /* commit all changes made during the initialization */
363 i_commit();
364 }
365
366 /* Confirm a successful initialization when it's the case */
367 if (SUCCEEDED(rc))
368 {
369 if (mData->mAccessible)
370 autoInitSpan.setSucceeded();
371 else
372 autoInitSpan.setLimited();
373 }
374
375 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
376 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
377 mData->mRegistered,
378 mData->mAccessible,
379 rc));
380
381 LogFlowThisFuncLeave();
382
383 return rc;
384}
385
386/**
387 * Initializes a new instance with data from machine XML (formerly Init_Registered).
388 * Gets called in two modes:
389 *
390 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
391 * UUID is specified and we mark the machine as "registered";
392 *
393 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
394 * and the machine remains unregistered until RegisterMachine() is called.
395 *
396 * @param aParent Associated parent object
397 * @param aConfigFile Local file system path to the VM settings file (can
398 * be relative to the VirtualBox config directory).
399 * @param aId UUID of the machine or NULL (see above).
400 *
401 * @return Success indicator. if not S_OK, the machine object is invalid
402 */
403HRESULT Machine::initFromSettings(VirtualBox *aParent,
404 const Utf8Str &strConfigFile,
405 const Guid *aId)
406{
407 LogFlowThisFuncEnter();
408 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
409
410 /* Enclose the state transition NotReady->InInit->Ready */
411 AutoInitSpan autoInitSpan(this);
412 AssertReturn(autoInitSpan.isOk(), E_FAIL);
413
414 HRESULT rc = initImpl(aParent, strConfigFile);
415 if (FAILED(rc)) return rc;
416
417 if (aId)
418 {
419 // loading a registered VM:
420 unconst(mData->mUuid) = *aId;
421 mData->mRegistered = TRUE;
422 // now load the settings from XML:
423 rc = i_registeredInit();
424 // this calls initDataAndChildObjects() and loadSettings()
425 }
426 else
427 {
428 // opening an unregistered VM (VirtualBox::OpenMachine()):
429 rc = initDataAndChildObjects();
430
431 if (SUCCEEDED(rc))
432 {
433 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
434 mData->mAccessible = TRUE;
435
436 try
437 {
438 // load and parse machine XML; this will throw on XML or logic errors
439 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
440
441 // reject VM UUID duplicates, they can happen if someone
442 // tries to register an already known VM config again
443 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
444 true /* fPermitInaccessible */,
445 false /* aDoSetError */,
446 NULL) != VBOX_E_OBJECT_NOT_FOUND)
447 {
448 throw setError(E_FAIL,
449 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
450 mData->m_strConfigFile.c_str());
451 }
452
453 // use UUID from machine config
454 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
455
456 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
457 NULL /* puuidRegistry */);
458 if (FAILED(rc)) throw rc;
459
460 /* At this point the changing of the current state modification
461 * flag is allowed. */
462 i_allowStateModification();
463
464 i_commit();
465 }
466 catch (HRESULT err)
467 {
468 /* we assume that error info is set by the thrower */
469 rc = err;
470 }
471 catch (...)
472 {
473 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
474 }
475 }
476 }
477
478 /* Confirm a successful initialization when it's the case */
479 if (SUCCEEDED(rc))
480 {
481 if (mData->mAccessible)
482 autoInitSpan.setSucceeded();
483 else
484 {
485 autoInitSpan.setLimited();
486
487 // uninit media from this machine's media registry, or else
488 // reloading the settings will fail
489 mParent->i_unregisterMachineMedia(i_getId());
490 }
491 }
492
493 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
494 "rc=%08X\n",
495 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
496 mData->mRegistered, mData->mAccessible, rc));
497
498 LogFlowThisFuncLeave();
499
500 return rc;
501}
502
503/**
504 * Initializes a new instance from a machine config that is already in memory
505 * (import OVF case). Since we are importing, the UUID in the machine
506 * config is ignored and we always generate a fresh one.
507 *
508 * @param strName Name for the new machine; this overrides what is specified in config and is used
509 * for the settings file as well.
510 * @param config Machine configuration loaded and parsed from XML.
511 *
512 * @return Success indicator. if not S_OK, the machine object is invalid
513 */
514HRESULT Machine::init(VirtualBox *aParent,
515 const Utf8Str &strName,
516 const settings::MachineConfigFile &config)
517{
518 LogFlowThisFuncEnter();
519
520 /* Enclose the state transition NotReady->InInit->Ready */
521 AutoInitSpan autoInitSpan(this);
522 AssertReturn(autoInitSpan.isOk(), E_FAIL);
523
524 Utf8Str strConfigFile;
525 aParent->i_getDefaultMachineFolder(strConfigFile);
526 strConfigFile.append(RTPATH_DELIMITER);
527 strConfigFile.append(strName);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(".vbox");
531
532 HRESULT rc = initImpl(aParent, strConfigFile);
533 if (FAILED(rc)) return rc;
534
535 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
536 if (FAILED(rc)) return rc;
537
538 rc = initDataAndChildObjects();
539
540 if (SUCCEEDED(rc))
541 {
542 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
543 mData->mAccessible = TRUE;
544
545 // create empty machine config for instance data
546 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
547
548 // generate fresh UUID, ignore machine config
549 unconst(mData->mUuid).create();
550
551 rc = i_loadMachineDataFromSettings(config,
552 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
553
554 // override VM name as well, it may be different
555 mUserData->s.strName = strName;
556
557 if (SUCCEEDED(rc))
558 {
559 /* At this point the changing of the current state modification
560 * flag is allowed. */
561 i_allowStateModification();
562
563 /* commit all changes made during the initialization */
564 i_commit();
565 }
566 }
567
568 /* Confirm a successful initialization when it's the case */
569 if (SUCCEEDED(rc))
570 {
571 if (mData->mAccessible)
572 autoInitSpan.setSucceeded();
573 else
574 {
575 /* Ignore all errors from unregistering, they would destroy
576- * the more interesting error information we already have,
577- * pinpointing the issue with the VM config. */
578 ErrorInfoKeeper eik;
579
580 autoInitSpan.setLimited();
581
582 // uninit media from this machine's media registry, or else
583 // reloading the settings will fail
584 mParent->i_unregisterMachineMedia(i_getId());
585 }
586 }
587
588 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
589 "rc=%08X\n",
590 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
591 mData->mRegistered, mData->mAccessible, rc));
592
593 LogFlowThisFuncLeave();
594
595 return rc;
596}
597
598/**
599 * Shared code between the various init() implementations.
600 * @param aParent
601 * @return
602 */
603HRESULT Machine::initImpl(VirtualBox *aParent,
604 const Utf8Str &strConfigFile)
605{
606 LogFlowThisFuncEnter();
607
608 AssertReturn(aParent, E_INVALIDARG);
609 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
610
611 HRESULT rc = S_OK;
612
613 /* share the parent weakly */
614 unconst(mParent) = aParent;
615
616 /* allocate the essential machine data structure (the rest will be
617 * allocated later by initDataAndChildObjects() */
618 mData.allocate();
619
620 /* memorize the config file name (as provided) */
621 mData->m_strConfigFile = strConfigFile;
622
623 /* get the full file name */
624 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
625 if (RT_FAILURE(vrc1))
626 return setError(VBOX_E_FILE_ERROR,
627 tr("Invalid machine settings file name '%s' (%Rrc)"),
628 strConfigFile.c_str(),
629 vrc1);
630
631 LogFlowThisFuncLeave();
632
633 return rc;
634}
635
636/**
637 * Tries to create a machine settings file in the path stored in the machine
638 * instance data. Used when a new machine is created to fail gracefully if
639 * the settings file could not be written (e.g. because machine dir is read-only).
640 * @return
641 */
642HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
643{
644 HRESULT rc = S_OK;
645
646 // when we create a new machine, we must be able to create the settings file
647 RTFILE f = NIL_RTFILE;
648 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
649 if ( RT_SUCCESS(vrc)
650 || vrc == VERR_SHARING_VIOLATION
651 )
652 {
653 if (RT_SUCCESS(vrc))
654 RTFileClose(f);
655 if (!fForceOverwrite)
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Machine settings file '%s' already exists"),
658 mData->m_strConfigFileFull.c_str());
659 else
660 {
661 /* try to delete the config file, as otherwise the creation
662 * of a new settings file will fail. */
663 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
664 if (RT_FAILURE(vrc2))
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Could not delete the existing settings file '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(), vrc2);
668 }
669 }
670 else if ( vrc != VERR_FILE_NOT_FOUND
671 && vrc != VERR_PATH_NOT_FOUND
672 )
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Invalid machine settings file name '%s' (%Rrc)"),
675 mData->m_strConfigFileFull.c_str(),
676 vrc);
677 return rc;
678}
679
680/**
681 * Initializes the registered machine by loading the settings file.
682 * This method is separated from #init() in order to make it possible to
683 * retry the operation after VirtualBox startup instead of refusing to
684 * startup the whole VirtualBox server in case if the settings file of some
685 * registered VM is invalid or inaccessible.
686 *
687 * @note Must be always called from this object's write lock
688 * (unless called from #init() that doesn't need any locking).
689 * @note Locks the mUSBController method for writing.
690 * @note Subclasses must not call this method.
691 */
692HRESULT Machine::i_registeredInit()
693{
694 AssertReturn(!i_isSessionMachine(), E_FAIL);
695 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
696 AssertReturn(mData->mUuid.isValid(), E_FAIL);
697 AssertReturn(!mData->mAccessible, E_FAIL);
698
699 HRESULT rc = initDataAndChildObjects();
700
701 if (SUCCEEDED(rc))
702 {
703 /* Temporarily reset the registered flag in order to let setters
704 * potentially called from loadSettings() succeed (isMutable() used in
705 * all setters will return FALSE for a Machine instance if mRegistered
706 * is TRUE). */
707 mData->mRegistered = FALSE;
708
709 try
710 {
711 // load and parse machine XML; this will throw on XML or logic errors
712 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
713
714 if (mData->mUuid != mData->pMachineConfigFile->uuid)
715 throw setError(E_FAIL,
716 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
717 mData->pMachineConfigFile->uuid.raw(),
718 mData->m_strConfigFileFull.c_str(),
719 mData->mUuid.toString().c_str(),
720 mParent->i_settingsFilePath().c_str());
721
722 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
723 NULL /* const Guid *puuidRegistry */);
724 if (FAILED(rc)) throw rc;
725 }
726 catch (HRESULT err)
727 {
728 /* we assume that error info is set by the thrower */
729 rc = err;
730 }
731 catch (...)
732 {
733 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
734 }
735
736 /* Restore the registered flag (even on failure) */
737 mData->mRegistered = TRUE;
738 }
739
740 if (SUCCEEDED(rc))
741 {
742 /* Set mAccessible to TRUE only if we successfully locked and loaded
743 * the settings file */
744 mData->mAccessible = TRUE;
745
746 /* commit all changes made during loading the settings file */
747 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
748 /// @todo r=klaus for some reason the settings loading logic backs up
749 // the settings, and therefore a commit is needed. Should probably be changed.
750 }
751 else
752 {
753 /* If the machine is registered, then, instead of returning a
754 * failure, we mark it as inaccessible and set the result to
755 * success to give it a try later */
756
757 /* fetch the current error info */
758 mData->mAccessError = com::ErrorInfo();
759 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
760 mData->mUuid.raw(),
761 mData->mAccessError.getText().raw()));
762
763 /* rollback all changes */
764 i_rollback(false /* aNotify */);
765
766 // uninit media from this machine's media registry, or else
767 // reloading the settings will fail
768 mParent->i_unregisterMachineMedia(i_getId());
769
770 /* uninitialize the common part to make sure all data is reset to
771 * default (null) values */
772 uninitDataAndChildObjects();
773
774 rc = S_OK;
775 }
776
777 return rc;
778}
779
780/**
781 * Uninitializes the instance.
782 * Called either from FinalRelease() or by the parent when it gets destroyed.
783 *
784 * @note The caller of this method must make sure that this object
785 * a) doesn't have active callers on the current thread and b) is not locked
786 * by the current thread; otherwise uninit() will hang either a) due to
787 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
788 * a dead-lock caused by this thread waiting for all callers on the other
789 * threads are done but preventing them from doing so by holding a lock.
790 */
791void Machine::uninit()
792{
793 LogFlowThisFuncEnter();
794
795 Assert(!isWriteLockOnCurrentThread());
796
797 Assert(!uRegistryNeedsSaving);
798 if (uRegistryNeedsSaving)
799 {
800 AutoCaller autoCaller(this);
801 if (SUCCEEDED(autoCaller.rc()))
802 {
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804 i_saveSettings(NULL, Machine::SaveS_Force);
805 }
806 }
807
808 /* Enclose the state transition Ready->InUninit->NotReady */
809 AutoUninitSpan autoUninitSpan(this);
810 if (autoUninitSpan.uninitDone())
811 return;
812
813 Assert(!i_isSnapshotMachine());
814 Assert(!i_isSessionMachine());
815 Assert(!!mData);
816
817 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
818 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
819
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821
822 if (!mData->mSession.mMachine.isNull())
823 {
824 /* Theoretically, this can only happen if the VirtualBox server has been
825 * terminated while there were clients running that owned open direct
826 * sessions. Since in this case we are definitely called by
827 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
828 * won't happen on the client watcher thread (because it does
829 * VirtualBox::addCaller() for the duration of the
830 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
831 * cannot happen until the VirtualBox caller is released). This is
832 * important, because SessionMachine::uninit() cannot correctly operate
833 * after we return from this method (it expects the Machine instance is
834 * still valid). We'll call it ourselves below.
835 */
836 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
837 (SessionMachine*)mData->mSession.mMachine));
838
839 if (Global::IsOnlineOrTransient(mData->mMachineState))
840 {
841 LogWarningThisFunc(("Setting state to Aborted!\n"));
842 /* set machine state using SessionMachine reimplementation */
843 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
844 }
845
846 /*
847 * Uninitialize SessionMachine using public uninit() to indicate
848 * an unexpected uninitialization.
849 */
850 mData->mSession.mMachine->uninit();
851 /* SessionMachine::uninit() must set mSession.mMachine to null */
852 Assert(mData->mSession.mMachine.isNull());
853 }
854
855 // uninit media from this machine's media registry, if they're still there
856 Guid uuidMachine(i_getId());
857
858 /* the lock is no more necessary (SessionMachine is uninitialized) */
859 alock.release();
860
861 /* XXX This will fail with
862 * "cannot be closed because it is still attached to 1 virtual machines"
863 * because at this point we did not call uninitDataAndChildObjects() yet
864 * and therefore also removeBackReference() for all these mediums was not called! */
865
866 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
867 mParent->i_unregisterMachineMedia(uuidMachine);
868
869 // has machine been modified?
870 if (mData->flModifications)
871 {
872 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
873 i_rollback(false /* aNotify */);
874 }
875
876 if (mData->mAccessible)
877 uninitDataAndChildObjects();
878
879 /* free the essential data structure last */
880 mData.free();
881
882 LogFlowThisFuncLeave();
883}
884
885// Wrapped IMachine properties
886/////////////////////////////////////////////////////////////////////////////
887HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
888{
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent.asOutParam());
892
893 return S_OK;
894}
895
896
897HRESULT Machine::getAccessible(BOOL *aAccessible)
898{
899 /* In some cases (medium registry related), it is necessary to be able to
900 * go through the list of all machines. Happens when an inaccessible VM
901 * has a sensible medium registry. */
902 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
904
905 HRESULT rc = S_OK;
906
907 if (!mData->mAccessible)
908 {
909 /* try to initialize the VM once more if not accessible */
910
911 AutoReinitSpan autoReinitSpan(this);
912 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
913
914#ifdef DEBUG
915 LogFlowThisFunc(("Dumping media backreferences\n"));
916 mParent->i_dumpAllBackRefs();
917#endif
918
919 if (mData->pMachineConfigFile)
920 {
921 // reset the XML file to force loadSettings() (called from registeredInit())
922 // to parse it again; the file might have changed
923 delete mData->pMachineConfigFile;
924 mData->pMachineConfigFile = NULL;
925 }
926
927 rc = i_registeredInit();
928
929 if (SUCCEEDED(rc) && mData->mAccessible)
930 {
931 autoReinitSpan.setSucceeded();
932
933 /* make sure interesting parties will notice the accessibility
934 * state change */
935 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
936 mParent->i_onMachineDataChange(mData->mUuid);
937 }
938 }
939
940 if (SUCCEEDED(rc))
941 *aAccessible = mData->mAccessible;
942
943 LogFlowThisFuncLeave();
944
945 return rc;
946}
947
948HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
949{
950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
951
952 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
953 {
954 /* return shortly */
955 aAccessError = NULL;
956 return S_OK;
957 }
958
959 HRESULT rc = S_OK;
960
961 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
962 rc = errorInfo.createObject();
963 if (SUCCEEDED(rc))
964 {
965 errorInfo->init(mData->mAccessError.getResultCode(),
966 mData->mAccessError.getInterfaceID().ref(),
967 Utf8Str(mData->mAccessError.getComponent()).c_str(),
968 Utf8Str(mData->mAccessError.getText()));
969 rc = errorInfo.queryInterfaceTo(aAccessError.asOutParam());
970 }
971
972 return rc;
973}
974
975HRESULT Machine::getName(com::Utf8Str &aName)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 aName = mUserData->s.strName;
980
981 return S_OK;
982}
983
984HRESULT Machine::setName(const com::Utf8Str &aName)
985{
986 // prohibit setting a UUID only as the machine name, or else it can
987 // never be found by findMachine()
988 Guid test(aName);
989
990 if (test.isValid())
991 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
992
993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 HRESULT rc = i_checkStateDependency(MutableStateDep);
996 if (FAILED(rc)) return rc;
997
998 i_setModified(IsModified_MachineData);
999 mUserData.backup();
1000 mUserData->s.strName = aName;
1001
1002 return S_OK;
1003}
1004
1005HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1006{
1007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1008
1009 aDescription = mUserData->s.strDescription;
1010
1011 return S_OK;
1012}
1013
1014HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1015{
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 // this can be done in principle in any state as it doesn't affect the VM
1019 // significantly, but play safe by not messing around while complex
1020 // activities are going on
1021 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 i_setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strDescription = aDescription;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::getId(com::Guid &aId)
1032{
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 aId = mData->mUuid;
1036
1037 return S_OK;
1038}
1039
1040HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1041{
1042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1043 aGroups.resize(mUserData->s.llGroups.size());
1044 size_t i = 0;
1045 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1046 it != mUserData->s.llGroups.end(); ++it, ++i)
1047 aGroups[i] = (*it);
1048
1049 return S_OK;
1050}
1051
1052HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1053{
1054 StringsList llGroups;
1055 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1056 if (FAILED(rc))
1057 return rc;
1058
1059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 // changing machine groups is possible while the VM is offline
1062 rc = i_checkStateDependency(OfflineStateDep);
1063 if (FAILED(rc)) return rc;
1064
1065 i_setModified(IsModified_MachineData);
1066 mUserData.backup();
1067 mUserData->s.llGroups = llGroups;
1068
1069 return S_OK;
1070}
1071
1072HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1073{
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 aOSTypeId = mUserData->s.strOsType;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1082{
1083 /* look up the object by Id to check it is valid */
1084 ComPtr<IGuestOSType> guestOSType;
1085 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1086 if (FAILED(rc)) return rc;
1087
1088 /* when setting, always use the "etalon" value for consistency -- lookup
1089 * by ID is case-insensitive and the input value may have different case */
1090 Bstr osTypeId;
1091 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 rc = i_checkStateDependency(MutableStateDep);
1097 if (FAILED(rc)) return rc;
1098
1099 i_setModified(IsModified_MachineData);
1100 mUserData.backup();
1101 mUserData->s.strOsType = osTypeId;
1102
1103 return S_OK;
1104}
1105
1106HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1107{
1108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 *aFirmwareType = mHWData->mFirmwareType;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1116{
1117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 HRESULT rc = i_checkStateDependency(MutableStateDep);
1120 if (FAILED(rc)) return rc;
1121
1122 i_setModified(IsModified_MachineData);
1123 mHWData.backup();
1124 mHWData->mFirmwareType = aFirmwareType;
1125
1126 return S_OK;
1127}
1128
1129HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1130{
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1139{
1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 HRESULT rc = i_checkStateDependency(MutableStateDep);
1143 if (FAILED(rc)) return rc;
1144
1145 i_setModified(IsModified_MachineData);
1146 mHWData.backup();
1147 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aPointingHIDType = mHWData->mPointingHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mPointingHIDType = aPointingHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aChipsetType = mHWData->mChipsetType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 if (aChipsetType != mHWData->mChipsetType)
1192 {
1193 i_setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mChipsetType = aChipsetType;
1196
1197 // Resize network adapter array, to be finalized on commit/rollback.
1198 // We must not throw away entries yet, otherwise settings are lost
1199 // without a way to roll back.
1200 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1201 size_t oldCount = mNetworkAdapters.size();
1202 if (newCount > oldCount)
1203 {
1204 mNetworkAdapters.resize(newCount);
1205 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1206 {
1207 unconst(mNetworkAdapters[slot]).createObject();
1208 mNetworkAdapters[slot]->init(this, slot);
1209 }
1210 }
1211 }
1212
1213 return S_OK;
1214}
1215
1216HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1217{
1218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 *aParavirtProvider = mHWData->mParavirtProvider;
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1226{
1227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 HRESULT rc = i_checkStateDependency(MutableStateDep);
1230 if (FAILED(rc)) return rc;
1231
1232 if (aParavirtProvider != mHWData->mParavirtProvider)
1233 {
1234 i_setModified(IsModified_MachineData);
1235 mHWData.backup();
1236 mHWData->mParavirtProvider = aParavirtProvider;
1237 }
1238
1239 return S_OK;
1240}
1241
1242HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1243{
1244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1245
1246 aHardwareVersion = mHWData->mHWVersion;
1247
1248 return S_OK;
1249}
1250
1251HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1252{
1253 /* check known version */
1254 Utf8Str hwVersion = aHardwareVersion;
1255 if ( hwVersion.compare("1") != 0
1256 && hwVersion.compare("2") != 0)
1257 return setError(E_INVALIDARG,
1258 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1259
1260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 HRESULT rc = i_checkStateDependency(MutableStateDep);
1263 if (FAILED(rc)) return rc;
1264
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mHWVersion = aHardwareVersion;
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1273{
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 if (!mHWData->mHardwareUUID.isZero())
1277 aHardwareUUID = mHWData->mHardwareUUID;
1278 else
1279 aHardwareUUID = mData->mUuid;
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1285{
1286 if (!aHardwareUUID.isValid())
1287 return E_INVALIDARG;
1288
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 HRESULT rc = i_checkStateDependency(MutableStateDep);
1292 if (FAILED(rc)) return rc;
1293
1294 i_setModified(IsModified_MachineData);
1295 mHWData.backup();
1296 if (aHardwareUUID == mData->mUuid)
1297 mHWData->mHardwareUUID.clear();
1298 else
1299 mHWData->mHardwareUUID = aHardwareUUID;
1300
1301 return S_OK;
1302}
1303
1304HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1305{
1306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 *aMemorySize = mHWData->mMemorySize;
1309
1310 return S_OK;
1311}
1312
1313HRESULT Machine::setMemorySize(ULONG aMemorySize)
1314{
1315 /* check RAM limits */
1316 if ( aMemorySize < MM_RAM_MIN_IN_MB
1317 || aMemorySize > MM_RAM_MAX_IN_MB
1318 )
1319 return setError(E_INVALIDARG,
1320 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1321 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1322
1323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1324
1325 HRESULT rc = i_checkStateDependency(MutableStateDep);
1326 if (FAILED(rc)) return rc;
1327
1328 i_setModified(IsModified_MachineData);
1329 mHWData.backup();
1330 mHWData->mMemorySize = aMemorySize;
1331
1332 return S_OK;
1333}
1334
1335HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1336{
1337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1338
1339 *aCPUCount = mHWData->mCPUCount;
1340
1341 return S_OK;
1342}
1343
1344HRESULT Machine::setCPUCount(ULONG aCPUCount)
1345{
1346 /* check CPU limits */
1347 if ( aCPUCount < SchemaDefs::MinCPUCount
1348 || aCPUCount > SchemaDefs::MaxCPUCount
1349 )
1350 return setError(E_INVALIDARG,
1351 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1352 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1353
1354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1355
1356 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1357 if (mHWData->mCPUHotPlugEnabled)
1358 {
1359 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1360 {
1361 if (mHWData->mCPUAttached[idx])
1362 return setError(E_INVALIDARG,
1363 tr("There is still a CPU attached to socket %lu."
1364 "Detach the CPU before removing the socket"),
1365 aCPUCount, idx+1);
1366 }
1367 }
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mCPUCount = aCPUCount;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1384
1385 return S_OK;
1386}
1387
1388HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1389{
1390 HRESULT rc = S_OK;
1391
1392 /* check throttle limits */
1393 if ( aCPUExecutionCap < 1
1394 || aCPUExecutionCap > 100
1395 )
1396 return setError(E_INVALIDARG,
1397 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1398 aCPUExecutionCap, 1, 100);
1399
1400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 alock.release();
1403 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1404 alock.acquire();
1405 if (FAILED(rc)) return rc;
1406
1407 i_setModified(IsModified_MachineData);
1408 mHWData.backup();
1409 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1410
1411 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1412 if (Global::IsOnline(mData->mMachineState))
1413 i_saveSettings(NULL);
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1423
1424 return S_OK;
1425}
1426
1427HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1428{
1429 HRESULT rc = S_OK;
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 rc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(rc)) return rc;
1435
1436 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1437 {
1438 if (aCPUHotPlugEnabled)
1439 {
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442
1443 /* Add the amount of CPUs currently attached */
1444 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1445 mHWData->mCPUAttached[i] = true;
1446 }
1447 else
1448 {
1449 /*
1450 * We can disable hotplug only if the amount of maximum CPUs is equal
1451 * to the amount of attached CPUs
1452 */
1453 unsigned cCpusAttached = 0;
1454 unsigned iHighestId = 0;
1455
1456 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1457 {
1458 if (mHWData->mCPUAttached[i])
1459 {
1460 cCpusAttached++;
1461 iHighestId = i;
1462 }
1463 }
1464
1465 if ( (cCpusAttached != mHWData->mCPUCount)
1466 || (iHighestId >= mHWData->mCPUCount))
1467 return setError(E_INVALIDARG,
1468 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1469
1470 i_setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 }
1473 }
1474
1475 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1476
1477 return rc;
1478}
1479
1480HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1481{
1482#ifdef VBOX_WITH_USB_CARDREADER
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1486
1487 return S_OK;
1488#else
1489 NOREF(aEmulatedUSBCardReaderEnabled);
1490 return E_NOTIMPL;
1491#endif
1492}
1493
1494HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1495{
1496#ifdef VBOX_WITH_USB_CARDREADER
1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 HRESULT rc = i_checkStateDependency(MutableStateDep);
1500 if (FAILED(rc)) return rc;
1501
1502 i_setModified(IsModified_MachineData);
1503 mHWData.backup();
1504 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1505
1506 return S_OK;
1507#else
1508 NOREF(aEmulatedUSBCardReaderEnabled);
1509 return E_NOTIMPL;
1510#endif
1511}
1512
1513HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aHPETEnabled = mHWData->mHPETEnabled;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1523{
1524 HRESULT rc = S_OK;
1525
1526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1527
1528 rc = i_checkStateDependency(MutableStateDep);
1529 if (FAILED(rc)) return rc;
1530
1531 i_setModified(IsModified_MachineData);
1532 mHWData.backup();
1533
1534 mHWData->mHPETEnabled = aHPETEnabled;
1535
1536 return rc;
1537}
1538
1539HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1540{
1541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1544 return S_OK;
1545}
1546
1547HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1548{
1549 HRESULT rc = S_OK;
1550
1551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1556
1557 alock.release();
1558 rc = i_onVideoCaptureChange();
1559 alock.acquire();
1560 if (FAILED(rc))
1561 {
1562 /*
1563 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1564 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1565 * determine if it should start or stop capturing. Therefore we need to manually
1566 * undo change.
1567 */
1568 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1569 return rc;
1570 }
1571
1572 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1573 if (Global::IsOnline(mData->mMachineState))
1574 i_saveSettings(NULL);
1575
1576 return rc;
1577}
1578
1579HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1580{
1581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1582 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1583 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1584 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1585 return S_OK;
1586}
1587
1588HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1589{
1590 SafeArray<BOOL> screens(aVideoCaptureScreens);
1591 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1592 bool fChanged = false;
1593
1594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 for (unsigned i = 0; i < screens.size(); ++i)
1597 {
1598 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1599 {
1600 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1601 fChanged = true;
1602 }
1603 }
1604 if (fChanged)
1605 {
1606 alock.release();
1607 HRESULT rc = i_onVideoCaptureChange();
1608 alock.acquire();
1609 if (FAILED(rc)) return rc;
1610 i_setModified(IsModified_MachineData);
1611
1612 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1613 if (Global::IsOnline(mData->mMachineState))
1614 i_saveSettings(NULL);
1615 }
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623 if (mHWData->mVideoCaptureFile.isEmpty())
1624 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1625 else
1626 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1631{
1632 Utf8Str strFile(aVideoCaptureFile);
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 if ( Global::IsOnline(mData->mMachineState)
1636 && mHWData->mVideoCaptureEnabled)
1637 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1638
1639 if (!RTPathStartsWithRoot(strFile.c_str()))
1640 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1641
1642 if (!strFile.isEmpty())
1643 {
1644 Utf8Str defaultFile;
1645 i_getDefaultVideoCaptureFile(defaultFile);
1646 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1647 strFile.setNull();
1648 }
1649
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 mHWData->mVideoCaptureFile = strFile;
1653
1654 return S_OK;
1655}
1656
1657HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1658{
1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1665{
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667
1668 if ( Global::IsOnline(mData->mMachineState)
1669 && mHWData->mVideoCaptureEnabled)
1670 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1671
1672 i_setModified(IsModified_MachineData);
1673 mHWData.backup();
1674 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1675
1676 return S_OK;
1677}
1678
1679HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1680{
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1683 return S_OK;
1684}
1685
1686HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1687{
1688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1689
1690 if ( Global::IsOnline(mData->mMachineState)
1691 && mHWData->mVideoCaptureEnabled)
1692 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1693
1694 i_setModified(IsModified_MachineData);
1695 mHWData.backup();
1696 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1697
1698 return S_OK;
1699}
1700
1701HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1702{
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1705 return S_OK;
1706}
1707
1708HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1709{
1710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 if ( Global::IsOnline(mData->mMachineState)
1713 && mHWData->mVideoCaptureEnabled)
1714 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1715
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1719
1720 return S_OK;
1721}
1722
1723HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1724{
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1727 return S_OK;
1728}
1729
1730HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1731{
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 if ( Global::IsOnline(mData->mMachineState)
1735 && mHWData->mVideoCaptureEnabled)
1736 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1737
1738 i_setModified(IsModified_MachineData);
1739 mHWData.backup();
1740 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1755{
1756 switch (aGraphicsControllerType)
1757 {
1758 case GraphicsControllerType_Null:
1759 case GraphicsControllerType_VBoxVGA:
1760#ifdef VBOX_WITH_VMSVGA
1761 case GraphicsControllerType_VMSVGA:
1762#endif
1763 break;
1764 default:
1765 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1766 }
1767
1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1769
1770 HRESULT rc = i_checkStateDependency(MutableStateDep);
1771 if (FAILED(rc)) return rc;
1772
1773 i_setModified(IsModified_MachineData);
1774 mHWData.backup();
1775 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1776
1777 return S_OK;
1778}
1779
1780HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aVRAMSize = mHWData->mVRAMSize;
1785
1786 return S_OK;
1787}
1788
1789HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1790{
1791 /* check VRAM limits */
1792 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1793 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1794 return setError(E_INVALIDARG,
1795 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1796 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1797
1798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1799
1800 HRESULT rc = i_checkStateDependency(MutableStateDep);
1801 if (FAILED(rc)) return rc;
1802
1803 i_setModified(IsModified_MachineData);
1804 mHWData.backup();
1805 mHWData->mVRAMSize = aVRAMSize;
1806
1807 return S_OK;
1808}
1809
1810/** @todo this method should not be public */
1811HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1812{
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1816
1817 return S_OK;
1818}
1819
1820/**
1821 * Set the memory balloon size.
1822 *
1823 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1824 * we have to make sure that we never call IGuest from here.
1825 */
1826HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1827{
1828 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1829#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1830 /* check limits */
1831 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1832 return setError(E_INVALIDARG,
1833 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1834 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1835
1836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 i_setModified(IsModified_MachineData);
1839 mHWData.backup();
1840 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1841
1842 return S_OK;
1843#else
1844 NOREF(aMemoryBalloonSize);
1845 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1846#endif
1847}
1848
1849HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1850{
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1858{
1859#ifdef VBOX_WITH_PAGE_SHARING
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1863 i_setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1866 return S_OK;
1867#else
1868 NOREF(aPageFusionEnabled);
1869 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1870#endif
1871}
1872
1873HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1883{
1884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 HRESULT rc = i_checkStateDependency(MutableStateDep);
1887 if (FAILED(rc)) return rc;
1888
1889 /** @todo check validity! */
1890
1891 i_setModified(IsModified_MachineData);
1892 mHWData.backup();
1893 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1894
1895 return S_OK;
1896}
1897
1898
1899HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1900{
1901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1902
1903 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1904
1905 return S_OK;
1906}
1907
1908HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1909{
1910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 HRESULT rc = i_checkStateDependency(MutableStateDep);
1913 if (FAILED(rc)) return rc;
1914
1915 /** @todo check validity! */
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *aMonitorCount = mHWData->mMonitorCount;
1928
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1933{
1934 /* make sure monitor count is a sensible number */
1935 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1936 return setError(E_INVALIDARG,
1937 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1938 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1939
1940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1941
1942 HRESULT rc = i_checkStateDependency(MutableStateDep);
1943 if (FAILED(rc)) return rc;
1944
1945 i_setModified(IsModified_MachineData);
1946 mHWData.backup();
1947 mHWData->mMonitorCount = aMonitorCount;
1948
1949 return S_OK;
1950}
1951
1952HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1953{
1954 /* mBIOSSettings is constant during life time, no need to lock */
1955 mBIOSSettings.queryInterfaceTo(aBIOSSettings.asOutParam());
1956
1957 return S_OK;
1958}
1959
1960HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1961{
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 switch (aProperty)
1965 {
1966 case CPUPropertyType_PAE:
1967 *aValue = mHWData->mPAEEnabled;
1968 break;
1969
1970 case CPUPropertyType_Synthetic:
1971 *aValue = mHWData->mSyntheticCpu;
1972 break;
1973
1974 case CPUPropertyType_LongMode:
1975 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1976 *aValue = TRUE;
1977 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1978 *aValue = FALSE;
1979#if HC_ARCH_BITS == 64
1980 else
1981 *aValue = TRUE;
1982#else
1983 else
1984 {
1985 *aValue = FALSE;
1986
1987 ComPtr<IGuestOSType> ptrGuestOSType;
1988 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1989 if (SUCCEEDED(hrc2))
1990 {
1991 BOOL fIs64Bit = FALSE;
1992 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
1993 if (SUCCEEDED(hrc2) && fIs64Bit)
1994 {
1995 ComObjPtr<Host> ptrHost = mParent->i_host();
1996 alock.release();
1997
1998 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1999 if (FAILED(hrc2))
2000 *aValue = FALSE;
2001 }
2002 }
2003 }
2004#endif
2005 break;
2006
2007 case CPUPropertyType_TripleFaultReset:
2008 *aValue = mHWData->mTripleFaultReset;
2009 break;
2010
2011 default:
2012 return E_INVALIDARG;
2013 }
2014 return S_OK;
2015}
2016
2017HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2018{
2019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 HRESULT rc = i_checkStateDependency(MutableStateDep);
2022 if (FAILED(rc)) return rc;
2023
2024 switch (aProperty)
2025 {
2026 case CPUPropertyType_PAE:
2027 i_setModified(IsModified_MachineData);
2028 mHWData.backup();
2029 mHWData->mPAEEnabled = !!aValue;
2030 break;
2031
2032 case CPUPropertyType_Synthetic:
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mSyntheticCpu = !!aValue;
2036 break;
2037
2038 case CPUPropertyType_LongMode:
2039 i_setModified(IsModified_MachineData);
2040 mHWData.backup();
2041 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2042 break;
2043
2044 case CPUPropertyType_TripleFaultReset:
2045 i_setModified(IsModified_MachineData);
2046 mHWData.backup();
2047 mHWData->mTripleFaultReset = !!aValue;
2048 break;
2049
2050 default:
2051 return E_INVALIDARG;
2052 }
2053 return S_OK;
2054}
2055
2056HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2057{
2058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 switch(aId)
2061 {
2062 case 0x0:
2063 case 0x1:
2064 case 0x2:
2065 case 0x3:
2066 case 0x4:
2067 case 0x5:
2068 case 0x6:
2069 case 0x7:
2070 case 0x8:
2071 case 0x9:
2072 case 0xA:
2073 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2074 return E_INVALIDARG;
2075
2076 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2077 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2078 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2079 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2080 break;
2081
2082 case 0x80000000:
2083 case 0x80000001:
2084 case 0x80000002:
2085 case 0x80000003:
2086 case 0x80000004:
2087 case 0x80000005:
2088 case 0x80000006:
2089 case 0x80000007:
2090 case 0x80000008:
2091 case 0x80000009:
2092 case 0x8000000A:
2093 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2094 return E_INVALIDARG;
2095
2096 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2097 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2098 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2099 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2100 break;
2101
2102 default:
2103 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2104 }
2105 return S_OK;
2106}
2107
2108
2109HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2110{
2111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2112
2113 HRESULT rc = i_checkStateDependency(MutableStateDep);
2114 if (FAILED(rc)) return rc;
2115
2116 switch(aId)
2117 {
2118 case 0x0:
2119 case 0x1:
2120 case 0x2:
2121 case 0x3:
2122 case 0x4:
2123 case 0x5:
2124 case 0x6:
2125 case 0x7:
2126 case 0x8:
2127 case 0x9:
2128 case 0xA:
2129 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2130 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2131 i_setModified(IsModified_MachineData);
2132 mHWData.backup();
2133 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2134 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2135 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2136 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2137 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2138 break;
2139
2140 case 0x80000000:
2141 case 0x80000001:
2142 case 0x80000002:
2143 case 0x80000003:
2144 case 0x80000004:
2145 case 0x80000005:
2146 case 0x80000006:
2147 case 0x80000007:
2148 case 0x80000008:
2149 case 0x80000009:
2150 case 0x8000000A:
2151 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2152 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2153 i_setModified(IsModified_MachineData);
2154 mHWData.backup();
2155 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2157 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2158 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2159 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2160 break;
2161
2162 default:
2163 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2164 }
2165 return S_OK;
2166}
2167
2168HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2169{
2170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 HRESULT rc = i_checkStateDependency(MutableStateDep);
2173 if (FAILED(rc)) return rc;
2174
2175 switch(aId)
2176 {
2177 case 0x0:
2178 case 0x1:
2179 case 0x2:
2180 case 0x3:
2181 case 0x4:
2182 case 0x5:
2183 case 0x6:
2184 case 0x7:
2185 case 0x8:
2186 case 0x9:
2187 case 0xA:
2188 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2189 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2190 i_setModified(IsModified_MachineData);
2191 mHWData.backup();
2192 /* Invalidate leaf. */
2193 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2194 break;
2195
2196 case 0x80000000:
2197 case 0x80000001:
2198 case 0x80000002:
2199 case 0x80000003:
2200 case 0x80000004:
2201 case 0x80000005:
2202 case 0x80000006:
2203 case 0x80000007:
2204 case 0x80000008:
2205 case 0x80000009:
2206 case 0x8000000A:
2207 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2208 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2209 i_setModified(IsModified_MachineData);
2210 mHWData.backup();
2211 /* Invalidate leaf. */
2212 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2213 break;
2214
2215 default:
2216 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2217 }
2218 return S_OK;
2219}
2220
2221HRESULT Machine::removeAllCPUIDLeaves()
2222{
2223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 HRESULT rc = i_checkStateDependency(MutableStateDep);
2226 if (FAILED(rc)) return rc;
2227
2228 i_setModified(IsModified_MachineData);
2229 mHWData.backup();
2230
2231 /* Invalidate all standard leafs. */
2232 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2233 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2234
2235 /* Invalidate all extended leafs. */
2236 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2237 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2238
2239 return S_OK;
2240}
2241HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2242{
2243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 switch(aProperty)
2246 {
2247 case HWVirtExPropertyType_Enabled:
2248 *aValue = mHWData->mHWVirtExEnabled;
2249 break;
2250
2251 case HWVirtExPropertyType_VPID:
2252 *aValue = mHWData->mHWVirtExVPIDEnabled;
2253 break;
2254
2255 case HWVirtExPropertyType_NestedPaging:
2256 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2257 break;
2258
2259 case HWVirtExPropertyType_UnrestrictedExecution:
2260 *aValue = mHWData->mHWVirtExUXEnabled;
2261 break;
2262
2263 case HWVirtExPropertyType_LargePages:
2264 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2265#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2266 *aValue = FALSE;
2267#endif
2268 break;
2269
2270 case HWVirtExPropertyType_Force:
2271 *aValue = mHWData->mHWVirtExForceEnabled;
2272 break;
2273
2274 default:
2275 return E_INVALIDARG;
2276 }
2277 return S_OK;
2278}
2279
2280HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2281{
2282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2283
2284 HRESULT rc = i_checkStateDependency(MutableStateDep);
2285 if (FAILED(rc)) return rc;
2286
2287 switch(aProperty)
2288 {
2289 case HWVirtExPropertyType_Enabled:
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mHWVirtExEnabled = !!aValue;
2293 break;
2294
2295 case HWVirtExPropertyType_VPID:
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2305 break;
2306
2307 case HWVirtExPropertyType_UnrestrictedExecution:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mHWVirtExUXEnabled = !!aValue;
2311 break;
2312
2313 case HWVirtExPropertyType_LargePages:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2317 break;
2318
2319 case HWVirtExPropertyType_Force:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mHWVirtExForceEnabled = !!aValue;
2323 break;
2324
2325 default:
2326 return E_INVALIDARG;
2327 }
2328
2329 return S_OK;
2330}
2331
2332HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2333{
2334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2335
2336 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2337
2338 return S_OK;
2339}
2340
2341HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2342{
2343 /* @todo (r=dmik):
2344 * 1. Allow to change the name of the snapshot folder containing snapshots
2345 * 2. Rename the folder on disk instead of just changing the property
2346 * value (to be smart and not to leave garbage). Note that it cannot be
2347 * done here because the change may be rolled back. Thus, the right
2348 * place is #saveSettings().
2349 */
2350
2351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2352
2353 HRESULT rc = i_checkStateDependency(MutableStateDep);
2354 if (FAILED(rc)) return rc;
2355
2356 if (!mData->mCurrentSnapshot.isNull())
2357 return setError(E_FAIL,
2358 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2359
2360 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2361
2362 if (strSnapshotFolder.isEmpty())
2363 strSnapshotFolder = "Snapshots";
2364 int vrc = i_calculateFullPath(strSnapshotFolder,
2365 strSnapshotFolder);
2366 if (RT_FAILURE(vrc))
2367 return setError(E_FAIL,
2368 tr("Invalid snapshot folder '%s' (%Rrc)"),
2369 strSnapshotFolder.c_str(), vrc);
2370
2371 i_setModified(IsModified_MachineData);
2372 mUserData.backup();
2373
2374 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2375
2376 return S_OK;
2377}
2378
2379HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2380{
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 aMediumAttachments.resize(mMediaData->mAttachments.size());
2384 size_t i = 0;
2385 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2386 it != mMediaData->mAttachments.end(); ++it, ++i)
2387 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
2388
2389 return S_OK;
2390}
2391
2392HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2393{
2394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2395
2396 Assert(!!mVRDEServer);
2397
2398 mVRDEServer.queryInterfaceTo(aVRDEServer.asOutParam());
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 mAudioAdapter.queryInterfaceTo(aAudioAdapter.asOutParam());
2408
2409 return S_OK;
2410}
2411
2412HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2413{
2414#ifdef VBOX_WITH_VUSB
2415 clearError();
2416 MultiResult rc(S_OK);
2417
2418# ifdef VBOX_WITH_USB
2419 rc = mParent->i_host()->i_checkUSBProxyService();
2420 if (FAILED(rc)) return rc;
2421# endif
2422
2423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2424
2425 USBControllerList data = *mUSBControllers.data();
2426 aUSBControllers.resize(data.size());
2427 size_t i = 0;
2428 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2429 (*it).queryInterfaceTo(aUSBControllers[i].asOutParam());
2430
2431 return S_OK;
2432#else
2433 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2434 * extended error info to indicate that USB is simply not available
2435 * (w/o treating it as a failure), for example, as in OSE */
2436 NOREF(aUSBControllers);
2437 ReturnComNotImplemented();
2438#endif /* VBOX_WITH_VUSB */
2439}
2440
2441HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2442{
2443#ifdef VBOX_WITH_VUSB
2444 clearError();
2445 MultiResult rc(S_OK);
2446
2447# ifdef VBOX_WITH_USB
2448 rc = mParent->i_host()->i_checkUSBProxyService();
2449 if (FAILED(rc)) return rc;
2450# endif
2451
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters.asOutParam());
2455#else
2456 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2457 * extended error info to indicate that USB is simply not available
2458 * (w/o treating it as a failure), for example, as in OSE */
2459 NOREF(aUSBDeviceFilters);
2460 ReturnComNotImplemented();
2461#endif /* VBOX_WITH_VUSB */
2462}
2463
2464HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2465{
2466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 aSettingsFilePath = mData->m_strConfigFileFull;
2469
2470 return S_OK;
2471}
2472
2473HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2474{
2475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2476
2477 HRESULT rc = i_checkStateDependency(MutableStateDep);
2478 if (FAILED(rc)) return rc;
2479
2480 if (!mData->pMachineConfigFile->fileExists())
2481 // this is a new machine, and no config file exists yet:
2482 *aSettingsModified = TRUE;
2483 else
2484 *aSettingsModified = (mData->flModifications != 0);
2485
2486 return S_OK;
2487}
2488
2489HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2490{
2491
2492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 *aSessionState = mData->mSession.mState;
2495
2496 return S_OK;
2497}
2498
2499HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2500{
2501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 aSessionType = mData->mSession.mType;
2504
2505 return S_OK;
2506}
2507
2508HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2509{
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 *aSessionPID = mData->mSession.mPID;
2513
2514 return S_OK;
2515}
2516
2517HRESULT Machine::getState(MachineState_T *aState)
2518{
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 *aState = mData->mMachineState;
2522
2523 return S_OK;
2524}
2525
2526HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2527{
2528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2529
2530 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2531
2532 return S_OK;
2533}
2534
2535HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 aStateFilePath = mSSData->strStateFilePath;
2540
2541 return S_OK;
2542}
2543
2544HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2545{
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 i_getLogFolder(aLogFolder);
2549
2550 return S_OK;
2551}
2552
2553HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2554{
2555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2556
2557 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot.asOutParam());
2558
2559 return S_OK;
2560}
2561
2562HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2563{
2564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2565
2566 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2567 ? 0
2568 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2569
2570 return S_OK;
2571}
2572
2573HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2574{
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 /* Note: for machines with no snapshots, we always return FALSE
2578 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2579 * reasons :) */
2580
2581 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2582 ? FALSE
2583 : mData->mCurrentStateModified;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aSharedFolders.resize(mHWData->mSharedFolders.size());
2593 size_t i = 0;
2594 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2595 it != mHWData->mSharedFolders.end(); ++i, ++it)
2596 (*it).queryInterfaceTo(aSharedFolders[i].asOutParam());
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2602{
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 *aClipboardMode = mHWData->mClipboardMode;
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2611{
2612 HRESULT rc = S_OK;
2613
2614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 alock.release();
2617 rc = i_onClipboardModeChange(aClipboardMode);
2618 alock.acquire();
2619 if (FAILED(rc)) return rc;
2620
2621 i_setModified(IsModified_MachineData);
2622 mHWData.backup();
2623 mHWData->mClipboardMode = aClipboardMode;
2624
2625 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2626 if (Global::IsOnline(mData->mMachineState))
2627 i_saveSettings(NULL);
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 *aDnDMode = mHWData->mDnDMode;
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2642{
2643 HRESULT rc = S_OK;
2644
2645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 alock.release();
2648 rc = i_onDnDModeChange(aDnDMode);
2649
2650 alock.acquire();
2651 if (FAILED(rc)) return rc;
2652
2653 i_setModified(IsModified_MachineData);
2654 mHWData.backup();
2655 mHWData->mDnDMode = aDnDMode;
2656
2657 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2658 if (Global::IsOnline(mData->mMachineState))
2659 i_saveSettings(NULL);
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 try
2669 {
2670 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2671 }
2672 catch (...)
2673 {
2674 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2675 }
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2681{
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 HRESULT rc = i_checkStateDependency(MutableStateDep);
2685 if (FAILED(rc)) return rc;
2686
2687 i_setModified(IsModified_MachineData);
2688 mHWData.backup();
2689 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2690 return rc;
2691}
2692
2693HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696 StorageControllerList data = *mStorageControllers.data();
2697 size_t i = 0;
2698 aStorageControllers.resize(data.size());
2699 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2700 (*it).queryInterfaceTo(aStorageControllers[i].asOutParam());
2701 return S_OK;
2702}
2703
2704HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2705{
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 *aEnabled = mUserData->s.fTeleporterEnabled;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2714{
2715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 /* Only allow it to be set to true when PoweredOff or Aborted.
2718 (Clearing it is always permitted.) */
2719 if ( aTeleporterEnabled
2720 && mData->mRegistered
2721 && ( !i_isSessionMachine()
2722 || ( mData->mMachineState != MachineState_PoweredOff
2723 && mData->mMachineState != MachineState_Teleported
2724 && mData->mMachineState != MachineState_Aborted
2725 )
2726 )
2727 )
2728 return setError(VBOX_E_INVALID_VM_STATE,
2729 tr("The machine is not powered off (state is %s)"),
2730 Global::stringifyMachineState(mData->mMachineState));
2731
2732 i_setModified(IsModified_MachineData);
2733 mUserData.backup();
2734 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2749{
2750 if (aTeleporterPort >= _64K)
2751 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2752
2753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 HRESULT rc = i_checkStateDependency(MutableStateDep);
2756 if (FAILED(rc)) return rc;
2757
2758 i_setModified(IsModified_MachineData);
2759 mUserData.backup();
2760 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2775{
2776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 HRESULT rc = i_checkStateDependency(MutableStateDep);
2779 if (FAILED(rc)) return rc;
2780
2781 i_setModified(IsModified_MachineData);
2782 mUserData.backup();
2783 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2797{
2798 /*
2799 * Hash the password first.
2800 */
2801 com::Utf8Str aT = aTeleporterPassword;
2802
2803 if (!aT.isEmpty())
2804 {
2805 if (VBoxIsPasswordHashed(&aT))
2806 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2807 VBoxHashPassword(&aT);
2808 }
2809
2810 /*
2811 * Do the update.
2812 */
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2815 if (SUCCEEDED(hrc))
2816 {
2817 i_setModified(IsModified_MachineData);
2818 mUserData.backup();
2819 mUserData->s.strTeleporterPassword = aT;
2820 }
2821
2822 return hrc;
2823}
2824
2825HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2830 return S_OK;
2831}
2832
2833HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2834{
2835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2836
2837 /* @todo deal with running state change. */
2838 HRESULT rc = i_checkStateDependency(MutableStateDep);
2839 if (FAILED(rc)) return rc;
2840
2841 i_setModified(IsModified_MachineData);
2842 mUserData.backup();
2843 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2844 return S_OK;
2845}
2846
2847HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2852 return S_OK;
2853}
2854
2855HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2856{
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 /* @todo deal with running state change. */
2860 HRESULT rc = i_checkStateDependency(MutableStateDep);
2861 if (FAILED(rc)) return rc;
2862
2863 i_setModified(IsModified_MachineData);
2864 mUserData.backup();
2865 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /* @todo deal with running state change. */
2882 HRESULT rc = i_checkStateDependency(MutableStateDep);
2883 if (FAILED(rc)) return rc;
2884
2885 i_setModified(IsModified_MachineData);
2886 mUserData.backup();
2887 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2896
2897 return S_OK;
2898}
2899
2900HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2901{
2902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2903
2904 /* @todo deal with running state change. */
2905 HRESULT rc = i_checkStateDependency(MutableStateDep);
2906 if (FAILED(rc)) return rc;
2907
2908 i_setModified(IsModified_MachineData);
2909 mUserData.backup();
2910 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2911
2912 return S_OK;
2913}
2914
2915HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2916{
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2920 return S_OK;
2921}
2922
2923HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2924{
2925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 /* @todo deal with running state change. */
2928 HRESULT rc = i_checkStateDependency(MutableStateDep);
2929 if (FAILED(rc)) return rc;
2930
2931 i_setModified(IsModified_MachineData);
2932 mUserData.backup();
2933 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2934 return S_OK;
2935}
2936
2937HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2938{
2939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2942
2943 return S_OK;
2944}
2945
2946HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2947{
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 /* Only allow it to be set to true when PoweredOff or Aborted.
2951 (Clearing it is always permitted.) */
2952 if ( aRTCUseUTC
2953 && mData->mRegistered
2954 && ( !i_isSessionMachine()
2955 || ( mData->mMachineState != MachineState_PoweredOff
2956 && mData->mMachineState != MachineState_Teleported
2957 && mData->mMachineState != MachineState_Aborted
2958 )
2959 )
2960 )
2961 return setError(VBOX_E_INVALID_VM_STATE,
2962 tr("The machine is not powered off (state is %s)"),
2963 Global::stringifyMachineState(mData->mMachineState));
2964
2965 i_setModified(IsModified_MachineData);
2966 mUserData.backup();
2967 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2968
2969 return S_OK;
2970}
2971
2972HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2973{
2974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2982{
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 HRESULT rc = i_checkStateDependency(MutableStateDep);
2986 if (FAILED(rc)) return rc;
2987
2988 i_setModified(IsModified_MachineData);
2989 mHWData.backup();
2990 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2991
2992 return S_OK;
2993}
2994
2995HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2996{
2997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 *aIOCacheSize = mHWData->mIOCacheSize;
3000
3001 return S_OK;
3002}
3003
3004HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3005{
3006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 HRESULT rc = i_checkStateDependency(MutableStateDep);
3009 if (FAILED(rc)) return rc;
3010
3011 i_setModified(IsModified_MachineData);
3012 mHWData.backup();
3013 mHWData->mIOCacheSize = aIOCacheSize;
3014
3015 return S_OK;
3016}
3017
3018
3019/**
3020 * @note Locks objects!
3021 */
3022HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3023 LockType_T aLockType)
3024
3025{
3026 /* check the session state */
3027 SessionState_T state;
3028 HRESULT rc = aSession->COMGETTER(State)(&state);
3029 if (FAILED(rc)) return rc;
3030
3031 if (state != SessionState_Unlocked)
3032 return setError(VBOX_E_INVALID_OBJECT_STATE,
3033 tr("The given session is busy"));
3034
3035 // get the client's IInternalSessionControl interface
3036 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3037 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3038 E_INVALIDARG);
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 if (!mData->mRegistered)
3043 return setError(E_UNEXPECTED,
3044 tr("The machine '%s' is not registered"),
3045 mUserData->s.strName.c_str());
3046
3047 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3048
3049 SessionState_T oldState = mData->mSession.mState;
3050 /* Hack: in case the session is closing and there is a progress object
3051 * which allows waiting for the session to be closed, take the opportunity
3052 * and do a limited wait (max. 1 second). This helps a lot when the system
3053 * is busy and thus session closing can take a little while. */
3054 if ( mData->mSession.mState == SessionState_Unlocking
3055 && mData->mSession.mProgress)
3056 {
3057 alock.release();
3058 mData->mSession.mProgress->WaitForCompletion(1000);
3059 alock.acquire();
3060 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3061 }
3062
3063 // try again now
3064 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3065 // (i.e. session machine exists)
3066 && (aLockType == LockType_Shared) // caller wants a shared link to the
3067 // existing session that holds the write lock:
3068 )
3069 {
3070 // OK, share the session... we are now dealing with three processes:
3071 // 1) VBoxSVC (where this code runs);
3072 // 2) process C: the caller's client process (who wants a shared session);
3073 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3074
3075 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3076 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3077 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3078 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3079 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3080
3081 /*
3082 * Release the lock before calling the client process. It's safe here
3083 * since the only thing to do after we get the lock again is to add
3084 * the remote control to the list (which doesn't directly influence
3085 * anything).
3086 */
3087 alock.release();
3088
3089 // get the console of the session holding the write lock (this is a remote call)
3090 ComPtr<IConsole> pConsoleW;
3091 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3092 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3093 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3094 if (FAILED(rc))
3095 // the failure may occur w/o any error info (from RPC), so provide one
3096 return setError(VBOX_E_VM_ERROR,
3097 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3098
3099 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3100
3101 // share the session machine and W's console with the caller's session
3102 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3103 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3104 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3105
3106 if (FAILED(rc))
3107 // the failure may occur w/o any error info (from RPC), so provide one
3108 return setError(VBOX_E_VM_ERROR,
3109 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3110 alock.acquire();
3111
3112 // need to revalidate the state after acquiring the lock again
3113 if (mData->mSession.mState != SessionState_Locked)
3114 {
3115 pSessionControl->Uninitialize();
3116 return setError(VBOX_E_INVALID_SESSION_STATE,
3117 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3118 mUserData->s.strName.c_str());
3119 }
3120
3121 // add the caller's session to the list
3122 mData->mSession.mRemoteControls.push_back(pSessionControl);
3123 }
3124 else if ( mData->mSession.mState == SessionState_Locked
3125 || mData->mSession.mState == SessionState_Unlocking
3126 )
3127 {
3128 // sharing not permitted, or machine still unlocking:
3129 return setError(VBOX_E_INVALID_OBJECT_STATE,
3130 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3131 mUserData->s.strName.c_str());
3132 }
3133 else
3134 {
3135 // machine is not locked: then write-lock the machine (create the session machine)
3136
3137 // must not be busy
3138 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3139
3140 // get the caller's session PID
3141 RTPROCESS pid = NIL_RTPROCESS;
3142 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3143 pSessionControl->GetPID((ULONG*)&pid);
3144 Assert(pid != NIL_RTPROCESS);
3145
3146 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3147
3148 if (fLaunchingVMProcess)
3149 {
3150 if (mData->mSession.mPID == NIL_RTPROCESS)
3151 {
3152 // two or more clients racing for a lock, the one which set the
3153 // session state to Spawning will win, the others will get an
3154 // error as we can't decide here if waiting a little would help
3155 // (only for shared locks this would avoid an error)
3156 return setError(VBOX_E_INVALID_OBJECT_STATE,
3157 tr("The machine '%s' already has a lock request pending"),
3158 mUserData->s.strName.c_str());
3159 }
3160
3161 // this machine is awaiting for a spawning session to be opened:
3162 // then the calling process must be the one that got started by
3163 // LaunchVMProcess()
3164
3165 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3166 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3167
3168#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3169 /* Hardened windows builds have spawns two processes when a VM is
3170 launched, the 2nd one is the one that will end up here. */
3171 RTPROCESS ppid;
3172 int rc = RTProcQueryParent(pid, &ppid);
3173 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3174 || rc == VERR_ACCESS_DENIED)
3175 {
3176 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3177 mData->mSession.mPID = pid;
3178 }
3179#endif
3180
3181 if (mData->mSession.mPID != pid)
3182 return setError(E_ACCESSDENIED,
3183 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3184 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3185 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3186 }
3187
3188 // create the mutable SessionMachine from the current machine
3189 ComObjPtr<SessionMachine> sessionMachine;
3190 sessionMachine.createObject();
3191 rc = sessionMachine->init(this);
3192 AssertComRC(rc);
3193
3194 /* NOTE: doing return from this function after this point but
3195 * before the end is forbidden since it may call SessionMachine::uninit()
3196 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3197 * lock while still holding the Machine lock in alock so that a deadlock
3198 * is possible due to the wrong lock order. */
3199
3200 if (SUCCEEDED(rc))
3201 {
3202 /*
3203 * Set the session state to Spawning to protect against subsequent
3204 * attempts to open a session and to unregister the machine after
3205 * we release the lock.
3206 */
3207 SessionState_T origState = mData->mSession.mState;
3208 mData->mSession.mState = SessionState_Spawning;
3209
3210#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3211 /* Get the client token ID to be passed to the client process */
3212 Utf8Str strTokenId;
3213 sessionMachine->i_getTokenId(strTokenId);
3214 Assert(!strTokenId.isEmpty());
3215#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3216 /* Get the client token to be passed to the client process */
3217 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3218 /* The token is now "owned" by pToken, fix refcount */
3219 if (!pToken.isNull())
3220 pToken->Release();
3221#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3222
3223 /*
3224 * Release the lock before calling the client process -- it will call
3225 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3226 * because the state is Spawning, so that LaunchVMProcess() and
3227 * LockMachine() calls will fail. This method, called before we
3228 * acquire the lock again, will fail because of the wrong PID.
3229 *
3230 * Note that mData->mSession.mRemoteControls accessed outside
3231 * the lock may not be modified when state is Spawning, so it's safe.
3232 */
3233 alock.release();
3234
3235 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3236#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3237 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3238#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3239 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3240 /* Now the token is owned by the client process. */
3241 pToken.setNull();
3242#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3243 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3244
3245 /* The failure may occur w/o any error info (from RPC), so provide one */
3246 if (FAILED(rc))
3247 setError(VBOX_E_VM_ERROR,
3248 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3249
3250 if ( SUCCEEDED(rc)
3251 && fLaunchingVMProcess
3252 )
3253 {
3254 /* complete the remote session initialization */
3255
3256 /* get the console from the direct session */
3257 ComPtr<IConsole> console;
3258 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3259 ComAssertComRC(rc);
3260
3261 if (SUCCEEDED(rc) && !console)
3262 {
3263 ComAssert(!!console);
3264 rc = E_FAIL;
3265 }
3266
3267 /* assign machine & console to the remote session */
3268 if (SUCCEEDED(rc))
3269 {
3270 /*
3271 * after LaunchVMProcess(), the first and the only
3272 * entry in remoteControls is that remote session
3273 */
3274 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3275 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3276 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3277
3278 /* The failure may occur w/o any error info (from RPC), so provide one */
3279 if (FAILED(rc))
3280 setError(VBOX_E_VM_ERROR,
3281 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3282 }
3283
3284 if (FAILED(rc))
3285 pSessionControl->Uninitialize();
3286 }
3287
3288 /* acquire the lock again */
3289 alock.acquire();
3290
3291 /* Restore the session state */
3292 mData->mSession.mState = origState;
3293 }
3294
3295 // finalize spawning anyway (this is why we don't return on errors above)
3296 if (fLaunchingVMProcess)
3297 {
3298 /* Note that the progress object is finalized later */
3299 /** @todo Consider checking mData->mSession.mProgress for cancellation
3300 * around here. */
3301
3302 /* We don't reset mSession.mPID here because it is necessary for
3303 * SessionMachine::uninit() to reap the child process later. */
3304
3305 if (FAILED(rc))
3306 {
3307 /* Close the remote session, remove the remote control from the list
3308 * and reset session state to Closed (@note keep the code in sync
3309 * with the relevant part in checkForSpawnFailure()). */
3310
3311 Assert(mData->mSession.mRemoteControls.size() == 1);
3312 if (mData->mSession.mRemoteControls.size() == 1)
3313 {
3314 ErrorInfoKeeper eik;
3315 mData->mSession.mRemoteControls.front()->Uninitialize();
3316 }
3317
3318 mData->mSession.mRemoteControls.clear();
3319 mData->mSession.mState = SessionState_Unlocked;
3320 }
3321 }
3322 else
3323 {
3324 /* memorize PID of the directly opened session */
3325 if (SUCCEEDED(rc))
3326 mData->mSession.mPID = pid;
3327 }
3328
3329 if (SUCCEEDED(rc))
3330 {
3331 /* memorize the direct session control and cache IUnknown for it */
3332 mData->mSession.mDirectControl = pSessionControl;
3333 mData->mSession.mState = SessionState_Locked;
3334 /* associate the SessionMachine with this Machine */
3335 mData->mSession.mMachine = sessionMachine;
3336
3337 /* request an IUnknown pointer early from the remote party for later
3338 * identity checks (it will be internally cached within mDirectControl
3339 * at least on XPCOM) */
3340 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3341 NOREF(unk);
3342 }
3343
3344 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3345 * would break the lock order */
3346 alock.release();
3347
3348 /* uninitialize the created session machine on failure */
3349 if (FAILED(rc))
3350 sessionMachine->uninit();
3351
3352 }
3353
3354 if (SUCCEEDED(rc))
3355 {
3356 /*
3357 * tell the client watcher thread to update the set of
3358 * machines that have open sessions
3359 */
3360 mParent->i_updateClientWatcher();
3361
3362 if (oldState != SessionState_Locked)
3363 /* fire an event */
3364 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3365 }
3366
3367 return rc;
3368}
3369
3370/**
3371 * @note Locks objects!
3372 */
3373HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3374 const com::Utf8Str &aType,
3375 const com::Utf8Str &aEnvironment,
3376 ComPtr<IProgress> &aProgress)
3377{
3378 Utf8Str strFrontend(aType);
3379 Utf8Str strEnvironment(aEnvironment);
3380 /* "emergencystop" doesn't need the session, so skip the checks/interface
3381 * retrieval. This code doesn't quite fit in here, but introducing a
3382 * special API method would be even more effort, and would require explicit
3383 * support by every API client. It's better to hide the feature a bit. */
3384 if (strFrontend != "emergencystop")
3385 CheckComArgNotNull(aSession);
3386
3387 HRESULT rc = S_OK;
3388 if (strFrontend.isEmpty())
3389 {
3390 Bstr bstrFrontend;
3391 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3392 if (FAILED(rc))
3393 return rc;
3394 strFrontend = bstrFrontend;
3395 if (strFrontend.isEmpty())
3396 {
3397 ComPtr<ISystemProperties> systemProperties;
3398 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3399 if (FAILED(rc))
3400 return rc;
3401 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3402 if (FAILED(rc))
3403 return rc;
3404 strFrontend = bstrFrontend;
3405 }
3406 /* paranoia - emergencystop is not a valid default */
3407 if (strFrontend == "emergencystop")
3408 strFrontend = Utf8Str::Empty;
3409 }
3410 /* default frontend: Qt GUI */
3411 if (strFrontend.isEmpty())
3412 strFrontend = "GUI/Qt";
3413
3414 if (strFrontend != "emergencystop")
3415 {
3416 /* check the session state */
3417 SessionState_T state;
3418 rc = aSession->COMGETTER(State)(&state);
3419 if (FAILED(rc))
3420 return rc;
3421
3422 if (state != SessionState_Unlocked)
3423 return setError(VBOX_E_INVALID_OBJECT_STATE,
3424 tr("The given session is busy"));
3425
3426 /* get the IInternalSessionControl interface */
3427 ComPtr<IInternalSessionControl> control(aSession);
3428 ComAssertMsgRet(!control.isNull(),
3429 ("No IInternalSessionControl interface"),
3430 E_INVALIDARG);
3431
3432 /* get the teleporter enable state for the progress object init. */
3433 BOOL fTeleporterEnabled;
3434 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3435 if (FAILED(rc))
3436 return rc;
3437
3438 /* create a progress object */
3439 ComObjPtr<ProgressProxy> progress;
3440 progress.createObject();
3441 rc = progress->init(mParent,
3442 static_cast<IMachine*>(this),
3443 Bstr(tr("Starting VM")).raw(),
3444 TRUE /* aCancelable */,
3445 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3446 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3447 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3448 2 /* uFirstOperationWeight */,
3449 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3450
3451 if (SUCCEEDED(rc))
3452 {
3453 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3454 if (SUCCEEDED(rc))
3455 {
3456 progress.queryInterfaceTo(aProgress.asOutParam());
3457
3458 /* signal the client watcher thread */
3459 mParent->i_updateClientWatcher();
3460
3461 /* fire an event */
3462 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3463 }
3464 }
3465 }
3466 else
3467 {
3468 /* no progress object - either instant success or failure */
3469 aProgress = NULL;
3470
3471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 if (mData->mSession.mState != SessionState_Locked)
3474 return setError(VBOX_E_INVALID_OBJECT_STATE,
3475 tr("The machine '%s' is not locked by a session"),
3476 mUserData->s.strName.c_str());
3477
3478 /* must have a VM process associated - do not kill normal API clients
3479 * with an open session */
3480 if (!Global::IsOnline(mData->mMachineState))
3481 return setError(VBOX_E_INVALID_OBJECT_STATE,
3482 tr("The machine '%s' does not have a VM process"),
3483 mUserData->s.strName.c_str());
3484
3485 /* forcibly terminate the VM process */
3486 if (mData->mSession.mPID != NIL_RTPROCESS)
3487 RTProcTerminate(mData->mSession.mPID);
3488
3489 /* signal the client watcher thread, as most likely the client has
3490 * been terminated */
3491 mParent->i_updateClientWatcher();
3492 }
3493
3494 return rc;
3495}
3496
3497HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3498{
3499 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3500 return setError(E_INVALIDARG,
3501 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3502 aPosition, SchemaDefs::MaxBootPosition);
3503
3504 if (aDevice == DeviceType_USB)
3505 return setError(E_NOTIMPL,
3506 tr("Booting from USB device is currently not supported"));
3507
3508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3509
3510 HRESULT rc = i_checkStateDependency(MutableStateDep);
3511 if (FAILED(rc)) return rc;
3512
3513 i_setModified(IsModified_MachineData);
3514 mHWData.backup();
3515 mHWData->mBootOrder[aPosition - 1] = aDevice;
3516
3517 return S_OK;
3518}
3519
3520HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3521{
3522 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3523 return setError(E_INVALIDARG,
3524 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3525 aPosition, SchemaDefs::MaxBootPosition);
3526
3527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3528
3529 *aDevice = mHWData->mBootOrder[aPosition - 1];
3530
3531 return S_OK;
3532}
3533
3534HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3535 LONG aControllerPort,
3536 LONG aDevice,
3537 DeviceType_T aType,
3538 const ComPtr<IMedium> &aMedium)
3539{
3540 IMedium *aM = aMedium;
3541 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3542 aName.c_str(), aControllerPort, aDevice, aType, aM));
3543
3544 // request the host lock first, since might be calling Host methods for getting host drives;
3545 // next, protect the media tree all the while we're in here, as well as our member variables
3546 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3547 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3548
3549 HRESULT rc = i_checkStateDependency(MutableStateDep);
3550 if (FAILED(rc)) return rc;
3551
3552 /// @todo NEWMEDIA implicit machine registration
3553 if (!mData->mRegistered)
3554 return setError(VBOX_E_INVALID_OBJECT_STATE,
3555 tr("Cannot attach storage devices to an unregistered machine"));
3556
3557 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3558
3559 /* Check for an existing controller. */
3560 ComObjPtr<StorageController> ctl;
3561 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3562 if (FAILED(rc)) return rc;
3563
3564 StorageControllerType_T ctrlType;
3565 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3566 if (FAILED(rc))
3567 return setError(E_FAIL,
3568 tr("Could not get type of controller '%s'"),
3569 aName.c_str());
3570
3571 bool fSilent = false;
3572 Utf8Str strReconfig;
3573
3574 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3575 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3576 if ( mData->mMachineState == MachineState_Paused
3577 && strReconfig == "1")
3578 fSilent = true;
3579
3580 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3581 bool fHotplug = false;
3582 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3583 fHotplug = true;
3584
3585 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3586 return setError(VBOX_E_INVALID_VM_STATE,
3587 tr("Controller '%s' does not support hotplugging"),
3588 aName.c_str());
3589
3590 // check that the port and device are not out of range
3591 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3592 if (FAILED(rc)) return rc;
3593
3594 /* check if the device slot is already busy */
3595 MediumAttachment *pAttachTemp;
3596 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3597 Bstr(aName).raw(),
3598 aControllerPort,
3599 aDevice)))
3600 {
3601 Medium *pMedium = pAttachTemp->i_getMedium();
3602 if (pMedium)
3603 {
3604 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3605 return setError(VBOX_E_OBJECT_IN_USE,
3606 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3607 pMedium->i_getLocationFull().c_str(),
3608 aControllerPort,
3609 aDevice,
3610 aName.c_str());
3611 }
3612 else
3613 return setError(VBOX_E_OBJECT_IN_USE,
3614 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3615 aControllerPort, aDevice, aName.c_str());
3616 }
3617
3618 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3619 if (aMedium && medium.isNull())
3620 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3621
3622 AutoCaller mediumCaller(medium);
3623 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3624
3625 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3626
3627 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3628 && !medium.isNull()
3629 )
3630 return setError(VBOX_E_OBJECT_IN_USE,
3631 tr("Medium '%s' is already attached to this virtual machine"),
3632 medium->i_getLocationFull().c_str());
3633
3634 if (!medium.isNull())
3635 {
3636 MediumType_T mtype = medium->i_getType();
3637 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3638 // For DVDs it's not written to the config file, so needs no global config
3639 // version bump. For floppies it's a new attribute "type", which is ignored
3640 // by older VirtualBox version, so needs no global config version bump either.
3641 // For hard disks this type is not accepted.
3642 if (mtype == MediumType_MultiAttach)
3643 {
3644 // This type is new with VirtualBox 4.0 and therefore requires settings
3645 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3646 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3647 // two reasons: The medium type is a property of the media registry tree, which
3648 // can reside in the global config file (for pre-4.0 media); we would therefore
3649 // possibly need to bump the global config version. We don't want to do that though
3650 // because that might make downgrading to pre-4.0 impossible.
3651 // As a result, we can only use these two new types if the medium is NOT in the
3652 // global registry:
3653 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3654 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3655 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3656 )
3657 return setError(VBOX_E_INVALID_OBJECT_STATE,
3658 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3659 "to machines that were created with VirtualBox 4.0 or later"),
3660 medium->i_getLocationFull().c_str());
3661 }
3662 }
3663
3664 bool fIndirect = false;
3665 if (!medium.isNull())
3666 fIndirect = medium->i_isReadOnly();
3667 bool associate = true;
3668
3669 do
3670 {
3671 if ( aType == DeviceType_HardDisk
3672 && mMediaData.isBackedUp())
3673 {
3674 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3675
3676 /* check if the medium was attached to the VM before we started
3677 * changing attachments in which case the attachment just needs to
3678 * be restored */
3679 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3680 {
3681 AssertReturn(!fIndirect, E_FAIL);
3682
3683 /* see if it's the same bus/channel/device */
3684 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3685 {
3686 /* the simplest case: restore the whole attachment
3687 * and return, nothing else to do */
3688 mMediaData->mAttachments.push_back(pAttachTemp);
3689
3690 /* Reattach the medium to the VM. */
3691 if (fHotplug || fSilent)
3692 {
3693 mediumLock.release();
3694 treeLock.release();
3695 alock.release();
3696
3697 MediumLockList *pMediumLockList(new MediumLockList());
3698
3699 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3700 true /* fMediumLockWrite */,
3701 NULL,
3702 *pMediumLockList);
3703 alock.acquire();
3704 if (FAILED(rc))
3705 delete pMediumLockList;
3706 else
3707 {
3708 mData->mSession.mLockedMedia.Unlock();
3709 alock.release();
3710 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3711 mData->mSession.mLockedMedia.Lock();
3712 alock.acquire();
3713 }
3714 alock.release();
3715
3716 if (SUCCEEDED(rc))
3717 {
3718 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3719 /* Remove lock list in case of error. */
3720 if (FAILED(rc))
3721 {
3722 mData->mSession.mLockedMedia.Unlock();
3723 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3724 mData->mSession.mLockedMedia.Lock();
3725 }
3726 }
3727 }
3728
3729 return S_OK;
3730 }
3731
3732 /* bus/channel/device differ; we need a new attachment object,
3733 * but don't try to associate it again */
3734 associate = false;
3735 break;
3736 }
3737 }
3738
3739 /* go further only if the attachment is to be indirect */
3740 if (!fIndirect)
3741 break;
3742
3743 /* perform the so called smart attachment logic for indirect
3744 * attachments. Note that smart attachment is only applicable to base
3745 * hard disks. */
3746
3747 if (medium->i_getParent().isNull())
3748 {
3749 /* first, investigate the backup copy of the current hard disk
3750 * attachments to make it possible to re-attach existing diffs to
3751 * another device slot w/o losing their contents */
3752 if (mMediaData.isBackedUp())
3753 {
3754 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3755
3756 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3757 uint32_t foundLevel = 0;
3758
3759 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3760 {
3761 uint32_t level = 0;
3762 MediumAttachment *pAttach = *it;
3763 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3764 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3765 if (pMedium.isNull())
3766 continue;
3767
3768 if (pMedium->i_getBase(&level) == medium)
3769 {
3770 /* skip the hard disk if its currently attached (we
3771 * cannot attach the same hard disk twice) */
3772 if (i_findAttachment(mMediaData->mAttachments,
3773 pMedium))
3774 continue;
3775
3776 /* matched device, channel and bus (i.e. attached to the
3777 * same place) will win and immediately stop the search;
3778 * otherwise the attachment that has the youngest
3779 * descendant of medium will be used
3780 */
3781 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3782 {
3783 /* the simplest case: restore the whole attachment
3784 * and return, nothing else to do */
3785 mMediaData->mAttachments.push_back(*it);
3786
3787 /* Reattach the medium to the VM. */
3788 if (fHotplug || fSilent)
3789 {
3790 mediumLock.release();
3791 treeLock.release();
3792 alock.release();
3793
3794 MediumLockList *pMediumLockList(new MediumLockList());
3795
3796 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3797 true /* fMediumLockWrite */,
3798 NULL,
3799 *pMediumLockList);
3800 alock.acquire();
3801 if (FAILED(rc))
3802 delete pMediumLockList;
3803 else
3804 {
3805 mData->mSession.mLockedMedia.Unlock();
3806 alock.release();
3807 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3808 mData->mSession.mLockedMedia.Lock();
3809 alock.acquire();
3810 }
3811 alock.release();
3812
3813 if (SUCCEEDED(rc))
3814 {
3815 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3816 /* Remove lock list in case of error. */
3817 if (FAILED(rc))
3818 {
3819 mData->mSession.mLockedMedia.Unlock();
3820 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3821 mData->mSession.mLockedMedia.Lock();
3822 }
3823 }
3824 }
3825
3826 return S_OK;
3827 }
3828 else if ( foundIt == oldAtts.end()
3829 || level > foundLevel /* prefer younger */
3830 )
3831 {
3832 foundIt = it;
3833 foundLevel = level;
3834 }
3835 }
3836 }
3837
3838 if (foundIt != oldAtts.end())
3839 {
3840 /* use the previously attached hard disk */
3841 medium = (*foundIt)->i_getMedium();
3842 mediumCaller.attach(medium);
3843 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3844 mediumLock.attach(medium);
3845 /* not implicit, doesn't require association with this VM */
3846 fIndirect = false;
3847 associate = false;
3848 /* go right to the MediumAttachment creation */
3849 break;
3850 }
3851 }
3852
3853 /* must give up the medium lock and medium tree lock as below we
3854 * go over snapshots, which needs a lock with higher lock order. */
3855 mediumLock.release();
3856 treeLock.release();
3857
3858 /* then, search through snapshots for the best diff in the given
3859 * hard disk's chain to base the new diff on */
3860
3861 ComObjPtr<Medium> base;
3862 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3863 while (snap)
3864 {
3865 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3866
3867 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
3868
3869 MediumAttachment *pAttachFound = NULL;
3870 uint32_t foundLevel = 0;
3871
3872 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
3873 {
3874 MediumAttachment *pAttach = *it;
3875 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3876 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3877 if (pMedium.isNull())
3878 continue;
3879
3880 uint32_t level = 0;
3881 if (pMedium->i_getBase(&level) == medium)
3882 {
3883 /* matched device, channel and bus (i.e. attached to the
3884 * same place) will win and immediately stop the search;
3885 * otherwise the attachment that has the youngest
3886 * descendant of medium will be used
3887 */
3888 if ( pAttach->i_getDevice() == aDevice
3889 && pAttach->i_getPort() == aControllerPort
3890 && pAttach->i_getControllerName() == aName
3891 )
3892 {
3893 pAttachFound = pAttach;
3894 break;
3895 }
3896 else if ( !pAttachFound
3897 || level > foundLevel /* prefer younger */
3898 )
3899 {
3900 pAttachFound = pAttach;
3901 foundLevel = level;
3902 }
3903 }
3904 }
3905
3906 if (pAttachFound)
3907 {
3908 base = pAttachFound->i_getMedium();
3909 break;
3910 }
3911
3912 snap = snap->i_getParent();
3913 }
3914
3915 /* re-lock medium tree and the medium, as we need it below */
3916 treeLock.acquire();
3917 mediumLock.acquire();
3918
3919 /* found a suitable diff, use it as a base */
3920 if (!base.isNull())
3921 {
3922 medium = base;
3923 mediumCaller.attach(medium);
3924 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3925 mediumLock.attach(medium);
3926 }
3927 }
3928
3929 Utf8Str strFullSnapshotFolder;
3930 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3931
3932 ComObjPtr<Medium> diff;
3933 diff.createObject();
3934 // store this diff in the same registry as the parent
3935 Guid uuidRegistryParent;
3936 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3937 {
3938 // parent image has no registry: this can happen if we're attaching a new immutable
3939 // image that has not yet been attached (medium then points to the base and we're
3940 // creating the diff image for the immutable, and the parent is not yet registered);
3941 // put the parent in the machine registry then
3942 mediumLock.release();
3943 treeLock.release();
3944 alock.release();
3945 i_addMediumToRegistry(medium);
3946 alock.acquire();
3947 treeLock.acquire();
3948 mediumLock.acquire();
3949 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3950 }
3951 rc = diff->init(mParent,
3952 medium->i_getPreferredDiffFormat(),
3953 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3954 uuidRegistryParent);
3955 if (FAILED(rc)) return rc;
3956
3957 /* Apply the normal locking logic to the entire chain. */
3958 MediumLockList *pMediumLockList(new MediumLockList());
3959 mediumLock.release();
3960 treeLock.release();
3961 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3962 true /* fMediumLockWrite */,
3963 medium,
3964 *pMediumLockList);
3965 treeLock.acquire();
3966 mediumLock.acquire();
3967 if (SUCCEEDED(rc))
3968 {
3969 mediumLock.release();
3970 treeLock.release();
3971 rc = pMediumLockList->Lock();
3972 treeLock.acquire();
3973 mediumLock.acquire();
3974 if (FAILED(rc))
3975 setError(rc,
3976 tr("Could not lock medium when creating diff '%s'"),
3977 diff->i_getLocationFull().c_str());
3978 else
3979 {
3980 /* will release the lock before the potentially lengthy
3981 * operation, so protect with the special state */
3982 MachineState_T oldState = mData->mMachineState;
3983 i_setMachineState(MachineState_SettingUp);
3984
3985 mediumLock.release();
3986 treeLock.release();
3987 alock.release();
3988
3989 rc = medium->i_createDiffStorage(diff,
3990 MediumVariant_Standard,
3991 pMediumLockList,
3992 NULL /* aProgress */,
3993 true /* aWait */);
3994
3995 alock.acquire();
3996 treeLock.acquire();
3997 mediumLock.acquire();
3998
3999 i_setMachineState(oldState);
4000 }
4001 }
4002
4003 /* Unlock the media and free the associated memory. */
4004 delete pMediumLockList;
4005
4006 if (FAILED(rc)) return rc;
4007
4008 /* use the created diff for the actual attachment */
4009 medium = diff;
4010 mediumCaller.attach(medium);
4011 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4012 mediumLock.attach(medium);
4013 }
4014 while (0);
4015
4016 ComObjPtr<MediumAttachment> attachment;
4017 attachment.createObject();
4018 rc = attachment->init(this,
4019 medium,
4020 aName,
4021 aControllerPort,
4022 aDevice,
4023 aType,
4024 fIndirect,
4025 false /* fPassthrough */,
4026 false /* fTempEject */,
4027 false /* fNonRotational */,
4028 false /* fDiscard */,
4029 fHotplug /* fHotPluggable */,
4030 Utf8Str::Empty);
4031 if (FAILED(rc)) return rc;
4032
4033 if (associate && !medium.isNull())
4034 {
4035 // as the last step, associate the medium to the VM
4036 rc = medium->i_addBackReference(mData->mUuid);
4037 // here we can fail because of Deleting, or being in process of creating a Diff
4038 if (FAILED(rc)) return rc;
4039
4040 mediumLock.release();
4041 treeLock.release();
4042 alock.release();
4043 i_addMediumToRegistry(medium);
4044 alock.acquire();
4045 treeLock.acquire();
4046 mediumLock.acquire();
4047 }
4048
4049 /* success: finally remember the attachment */
4050 i_setModified(IsModified_Storage);
4051 mMediaData.backup();
4052 mMediaData->mAttachments.push_back(attachment);
4053
4054 mediumLock.release();
4055 treeLock.release();
4056 alock.release();
4057
4058 if (fHotplug || fSilent)
4059 {
4060 if (!medium.isNull())
4061 {
4062 MediumLockList *pMediumLockList(new MediumLockList());
4063
4064 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4065 true /* fMediumLockWrite */,
4066 NULL,
4067 *pMediumLockList);
4068 alock.acquire();
4069 if (FAILED(rc))
4070 delete pMediumLockList;
4071 else
4072 {
4073 mData->mSession.mLockedMedia.Unlock();
4074 alock.release();
4075 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4076 mData->mSession.mLockedMedia.Lock();
4077 alock.acquire();
4078 }
4079 alock.release();
4080 }
4081
4082 if (SUCCEEDED(rc))
4083 {
4084 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4085 /* Remove lock list in case of error. */
4086 if (FAILED(rc))
4087 {
4088 mData->mSession.mLockedMedia.Unlock();
4089 mData->mSession.mLockedMedia.Remove(attachment);
4090 mData->mSession.mLockedMedia.Lock();
4091 }
4092 }
4093 }
4094
4095 mParent->i_saveModifiedRegistries();
4096
4097 return rc;
4098}
4099
4100HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4101 LONG aDevice)
4102{
4103 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4104 aName.c_str(), aControllerPort, aDevice));
4105
4106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4107
4108 HRESULT rc = i_checkStateDependency(MutableStateDep);
4109 if (FAILED(rc)) return rc;
4110
4111 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4112
4113 /* Check for an existing controller. */
4114 ComObjPtr<StorageController> ctl;
4115 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4116 if (FAILED(rc)) return rc;
4117
4118 StorageControllerType_T ctrlType;
4119 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4120 if (FAILED(rc))
4121 return setError(E_FAIL,
4122 tr("Could not get type of controller '%s'"),
4123 aName.c_str());
4124
4125 bool fSilent = false;
4126 Utf8Str strReconfig;
4127
4128 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4129 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4130 if ( mData->mMachineState == MachineState_Paused
4131 && strReconfig == "1")
4132 fSilent = true;
4133
4134 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4135 bool fHotplug = false;
4136 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4137 fHotplug = true;
4138
4139 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4140 return setError(VBOX_E_INVALID_VM_STATE,
4141 tr("Controller '%s' does not support hotplugging"),
4142 aName.c_str());
4143
4144 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4145 Bstr(aName).raw(),
4146 aControllerPort,
4147 aDevice);
4148 if (!pAttach)
4149 return setError(VBOX_E_OBJECT_NOT_FOUND,
4150 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4151 aDevice, aControllerPort, aName.c_str());
4152
4153 if (fHotplug && !pAttach->i_getHotPluggable())
4154 return setError(VBOX_E_NOT_SUPPORTED,
4155 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4156 aDevice, aControllerPort, aName.c_str());
4157
4158 /*
4159 * The VM has to detach the device before we delete any implicit diffs.
4160 * If this fails we can roll back without loosing data.
4161 */
4162 if (fHotplug || fSilent)
4163 {
4164 alock.release();
4165 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4166 alock.acquire();
4167 }
4168 if (FAILED(rc)) return rc;
4169
4170 /* If we are here everything went well and we can delete the implicit now. */
4171 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4172
4173 alock.release();
4174
4175 mParent->i_saveModifiedRegistries();
4176
4177 return rc;
4178}
4179
4180HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4181 LONG aDevice, BOOL aPassthrough)
4182{
4183 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4184 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4185
4186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4187
4188 HRESULT rc = i_checkStateDependency(MutableStateDep);
4189 if (FAILED(rc)) return rc;
4190
4191 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4192
4193 if (Global::IsOnlineOrTransient(mData->mMachineState))
4194 return setError(VBOX_E_INVALID_VM_STATE,
4195 tr("Invalid machine state: %s"),
4196 Global::stringifyMachineState(mData->mMachineState));
4197
4198 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4199 Bstr(aName).raw(),
4200 aControllerPort,
4201 aDevice);
4202 if (!pAttach)
4203 return setError(VBOX_E_OBJECT_NOT_FOUND,
4204 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4205 aDevice, aControllerPort, aName.c_str());
4206
4207
4208 i_setModified(IsModified_Storage);
4209 mMediaData.backup();
4210
4211 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4212
4213 if (pAttach->i_getType() != DeviceType_DVD)
4214 return setError(E_INVALIDARG,
4215 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4216 aDevice, aControllerPort, aName.c_str());
4217 pAttach->i_updatePassthrough(!!aPassthrough);
4218
4219 return S_OK;
4220}
4221
4222HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4223 LONG aDevice, BOOL aTemporaryEject)
4224{
4225
4226 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4227 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4228
4229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4230
4231 HRESULT rc = i_checkStateDependency(MutableStateDep);
4232 if (FAILED(rc)) return rc;
4233
4234 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4235 Bstr(aName).raw(),
4236 aControllerPort,
4237 aDevice);
4238 if (!pAttach)
4239 return setError(VBOX_E_OBJECT_NOT_FOUND,
4240 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4241 aDevice, aControllerPort, aName.c_str());
4242
4243
4244 i_setModified(IsModified_Storage);
4245 mMediaData.backup();
4246
4247 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4248
4249 if (pAttach->i_getType() != DeviceType_DVD)
4250 return setError(E_INVALIDARG,
4251 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4252 aDevice, aControllerPort, aName.c_str());
4253 pAttach->i_updateTempEject(!!aTemporaryEject);
4254
4255 return S_OK;
4256}
4257
4258HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4259 LONG aDevice, BOOL aNonRotational)
4260{
4261
4262 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4263 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4264
4265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4266
4267 HRESULT rc = i_checkStateDependency(MutableStateDep);
4268 if (FAILED(rc)) return rc;
4269
4270 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4271
4272 if (Global::IsOnlineOrTransient(mData->mMachineState))
4273 return setError(VBOX_E_INVALID_VM_STATE,
4274 tr("Invalid machine state: %s"),
4275 Global::stringifyMachineState(mData->mMachineState));
4276
4277 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4278 Bstr(aName).raw(),
4279 aControllerPort,
4280 aDevice);
4281 if (!pAttach)
4282 return setError(VBOX_E_OBJECT_NOT_FOUND,
4283 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4284 aDevice, aControllerPort, aName.c_str());
4285
4286
4287 i_setModified(IsModified_Storage);
4288 mMediaData.backup();
4289
4290 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4291
4292 if (pAttach->i_getType() != DeviceType_HardDisk)
4293 return setError(E_INVALIDARG,
4294 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"),
4295 aDevice, aControllerPort, aName.c_str());
4296 pAttach->i_updateNonRotational(!!aNonRotational);
4297
4298 return S_OK;
4299}
4300
4301HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4302 LONG aDevice, BOOL aDiscard)
4303{
4304
4305 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4306 aName.c_str(), aControllerPort, aDevice, aDiscard));
4307
4308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4309
4310 HRESULT rc = i_checkStateDependency(MutableStateDep);
4311 if (FAILED(rc)) return rc;
4312
4313 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4314
4315 if (Global::IsOnlineOrTransient(mData->mMachineState))
4316 return setError(VBOX_E_INVALID_VM_STATE,
4317 tr("Invalid machine state: %s"),
4318 Global::stringifyMachineState(mData->mMachineState));
4319
4320 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4321 Bstr(aName).raw(),
4322 aControllerPort,
4323 aDevice);
4324 if (!pAttach)
4325 return setError(VBOX_E_OBJECT_NOT_FOUND,
4326 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4327 aDevice, aControllerPort, aName.c_str());
4328
4329
4330 i_setModified(IsModified_Storage);
4331 mMediaData.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->i_getType() != DeviceType_HardDisk)
4336 return setError(E_INVALIDARG,
4337 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"),
4338 aDevice, aControllerPort, aName.c_str());
4339 pAttach->i_updateDiscard(!!aDiscard);
4340
4341 return S_OK;
4342}
4343
4344HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4345 LONG aDevice, BOOL aHotPluggable)
4346{
4347 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4348 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4349
4350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4351
4352 HRESULT rc = i_checkStateDependency(MutableStateDep);
4353 if (FAILED(rc)) return rc;
4354
4355 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4356
4357 if (Global::IsOnlineOrTransient(mData->mMachineState))
4358 return setError(VBOX_E_INVALID_VM_STATE,
4359 tr("Invalid machine state: %s"),
4360 Global::stringifyMachineState(mData->mMachineState));
4361
4362 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4363 Bstr(aName).raw(),
4364 aControllerPort,
4365 aDevice);
4366 if (!pAttach)
4367 return setError(VBOX_E_OBJECT_NOT_FOUND,
4368 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4369 aDevice, aControllerPort, aName.c_str());
4370
4371 /* Check for an existing controller. */
4372 ComObjPtr<StorageController> ctl;
4373 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4374 if (FAILED(rc)) return rc;
4375
4376 StorageControllerType_T ctrlType;
4377 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4378 if (FAILED(rc))
4379 return setError(E_FAIL,
4380 tr("Could not get type of controller '%s'"),
4381 aName.c_str());
4382
4383 if (!i_isControllerHotplugCapable(ctrlType))
4384 return setError(VBOX_E_NOT_SUPPORTED,
4385 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4386 aName.c_str());
4387
4388 i_setModified(IsModified_Storage);
4389 mMediaData.backup();
4390
4391 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4392
4393 if (pAttach->i_getType() == DeviceType_Floppy)
4394 return setError(E_INVALIDARG,
4395 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"),
4396 aDevice, aControllerPort, aName.c_str());
4397 pAttach->i_updateHotPluggable(!!aHotPluggable);
4398
4399 return S_OK;
4400}
4401
4402HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4403 LONG aDevice)
4404{
4405 int rc = S_OK;
4406 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4407 aName.c_str(), aControllerPort, aDevice));
4408
4409 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4410
4411 return rc;
4412}
4413
4414HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4415 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4416{
4417 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4418 aName.c_str(), aControllerPort, aDevice));
4419
4420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4421
4422 HRESULT rc = i_checkStateDependency(MutableStateDep);
4423 if (FAILED(rc)) return rc;
4424
4425 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4426
4427 if (Global::IsOnlineOrTransient(mData->mMachineState))
4428 return setError(VBOX_E_INVALID_VM_STATE,
4429 tr("Invalid machine state: %s"),
4430 Global::stringifyMachineState(mData->mMachineState));
4431
4432 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4433 Bstr(aName).raw(),
4434 aControllerPort,
4435 aDevice);
4436 if (!pAttach)
4437 return setError(VBOX_E_OBJECT_NOT_FOUND,
4438 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4439 aDevice, aControllerPort, aName.c_str());
4440
4441
4442 i_setModified(IsModified_Storage);
4443 mMediaData.backup();
4444
4445 IBandwidthGroup *iB = aBandwidthGroup;
4446 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4447 if (aBandwidthGroup && group.isNull())
4448 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4449
4450 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4451
4452 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4453 if (strBandwidthGroupOld.isNotEmpty())
4454 {
4455 /* Get the bandwidth group object and release it - this must not fail. */
4456 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4457 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4458 Assert(SUCCEEDED(rc));
4459
4460 pBandwidthGroupOld->i_release();
4461 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4462 }
4463
4464 if (!group.isNull())
4465 {
4466 group->i_reference();
4467 pAttach->i_updateBandwidthGroup(group->i_getName());
4468 }
4469
4470 return S_OK;
4471}
4472
4473HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4474 LONG aControllerPort,
4475 LONG aDevice,
4476 DeviceType_T aType)
4477{
4478 HRESULT rc = S_OK;
4479
4480 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4481 aName.c_str(), aControllerPort, aDevice, aType));
4482
4483 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4484
4485 return rc;
4486}
4487
4488
4489HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4490 LONG aControllerPort,
4491 LONG aDevice,
4492 BOOL aForce)
4493{
4494 int rc = S_OK;
4495 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4496 aName.c_str(), aControllerPort, aForce));
4497
4498 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4499
4500 return rc;
4501}
4502
4503HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4504 LONG aControllerPort,
4505 LONG aDevice,
4506 const ComPtr<IMedium> &aMedium,
4507 BOOL aForce)
4508{
4509 int rc = S_OK;
4510 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4511 aName.c_str(), aControllerPort, aDevice, aForce));
4512
4513 // request the host lock first, since might be calling Host methods for getting host drives;
4514 // next, protect the media tree all the while we're in here, as well as our member variables
4515 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4516 this->lockHandle(),
4517 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4518
4519 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4520 Bstr(aName).raw(),
4521 aControllerPort,
4522 aDevice);
4523 if (pAttach.isNull())
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528 /* Remember previously mounted medium. The medium before taking the
4529 * backup is not necessarily the same thing. */
4530 ComObjPtr<Medium> oldmedium;
4531 oldmedium = pAttach->i_getMedium();
4532
4533 IMedium *iM = aMedium;
4534 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4535 if (aMedium && pMedium.isNull())
4536 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4537
4538 AutoCaller mediumCaller(pMedium);
4539 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4540
4541 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4542 if (pMedium)
4543 {
4544 DeviceType_T mediumType = pAttach->i_getType();
4545 switch (mediumType)
4546 {
4547 case DeviceType_DVD:
4548 case DeviceType_Floppy:
4549 break;
4550
4551 default:
4552 return setError(VBOX_E_INVALID_OBJECT_STATE,
4553 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4554 aControllerPort,
4555 aDevice,
4556 aName.c_str());
4557 }
4558 }
4559
4560 i_setModified(IsModified_Storage);
4561 mMediaData.backup();
4562
4563 {
4564 // The backup operation makes the pAttach reference point to the
4565 // old settings. Re-get the correct reference.
4566 pAttach = i_findAttachment(mMediaData->mAttachments,
4567 Bstr(aName).raw(),
4568 aControllerPort,
4569 aDevice);
4570 if (!oldmedium.isNull())
4571 oldmedium->i_removeBackReference(mData->mUuid);
4572 if (!pMedium.isNull())
4573 {
4574 pMedium->i_addBackReference(mData->mUuid);
4575
4576 mediumLock.release();
4577 multiLock.release();
4578 i_addMediumToRegistry(pMedium);
4579 multiLock.acquire();
4580 mediumLock.acquire();
4581 }
4582
4583 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4584 pAttach->i_updateMedium(pMedium);
4585 }
4586
4587 i_setModified(IsModified_Storage);
4588
4589 mediumLock.release();
4590 multiLock.release();
4591 rc = i_onMediumChange(pAttach, aForce);
4592 multiLock.acquire();
4593 mediumLock.acquire();
4594
4595 /* On error roll back this change only. */
4596 if (FAILED(rc))
4597 {
4598 if (!pMedium.isNull())
4599 pMedium->i_removeBackReference(mData->mUuid);
4600 pAttach = i_findAttachment(mMediaData->mAttachments,
4601 Bstr(aName).raw(),
4602 aControllerPort,
4603 aDevice);
4604 /* If the attachment is gone in the meantime, bail out. */
4605 if (pAttach.isNull())
4606 return rc;
4607 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4608 if (!oldmedium.isNull())
4609 oldmedium->i_addBackReference(mData->mUuid);
4610 pAttach->i_updateMedium(oldmedium);
4611 }
4612
4613 mediumLock.release();
4614 multiLock.release();
4615
4616 mParent->i_saveModifiedRegistries();
4617
4618 return rc;
4619}
4620HRESULT Machine::getMedium(const com::Utf8Str &aName,
4621 LONG aControllerPort,
4622 LONG aDevice,
4623 ComPtr<IMedium> &aMedium)
4624{
4625 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4626 aName.c_str(), aControllerPort, aDevice));
4627
4628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4629
4630 aMedium = NULL;
4631
4632 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4633 Bstr(aName).raw(),
4634 aControllerPort,
4635 aDevice);
4636 if (pAttach.isNull())
4637 return setError(VBOX_E_OBJECT_NOT_FOUND,
4638 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4639 aDevice, aControllerPort, aName.c_str());
4640
4641 pAttach->i_getMedium().queryInterfaceTo(aMedium.asOutParam());
4642
4643 return S_OK;
4644}
4645
4646HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4647{
4648
4649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4650
4651 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4652
4653 return S_OK;
4654}
4655
4656HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4657{
4658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4659
4660 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4661
4662 return S_OK;
4663}
4664
4665HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4666{
4667 /* Do not assert if slot is out of range, just return the advertised
4668 status. testdriver/vbox.py triggers this in logVmInfo. */
4669 if (aSlot >= mNetworkAdapters.size())
4670 return setError(E_INVALIDARG,
4671 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4672 aSlot, mNetworkAdapters.size());
4673
4674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4675
4676 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4677
4678 return S_OK;
4679}
4680
4681HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4682{
4683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4684
4685 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4686 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4687 size_t i = 0;
4688 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4689 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4690 ++it, ++i)
4691 aKeys[i] = it->first;
4692
4693 return S_OK;
4694}
4695
4696 /**
4697 * @note Locks this object for reading.
4698 */
4699HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4700 com::Utf8Str &aValue)
4701{
4702 /* start with nothing found */
4703 aValue = "";
4704
4705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4706
4707 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4708 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4709 // found:
4710 aValue = it->second; // source is a Utf8Str
4711
4712 /* return the result to caller (may be empty) */
4713 return S_OK;
4714}
4715
4716 /**
4717 * @note Locks mParent for writing + this object for writing.
4718 */
4719HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4720{
4721 Utf8Str strOldValue; // empty
4722
4723 // locking note: we only hold the read lock briefly to look up the old value,
4724 // then release it and call the onExtraCanChange callbacks. There is a small
4725 // chance of a race insofar as the callback might be called twice if two callers
4726 // change the same key at the same time, but that's a much better solution
4727 // than the deadlock we had here before. The actual changing of the extradata
4728 // is then performed under the write lock and race-free.
4729
4730 // look up the old value first; if nothing has changed then we need not do anything
4731 {
4732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4733 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4734 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4735 strOldValue = it->second;
4736 }
4737
4738 bool fChanged;
4739 if ((fChanged = (strOldValue != aValue)))
4740 {
4741 // ask for permission from all listeners outside the locks;
4742 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4743 // lock to copy the list of callbacks to invoke
4744 Bstr error;
4745 Bstr bstrValue(aValue);
4746
4747 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4748 {
4749 const char *sep = error.isEmpty() ? "" : ": ";
4750 CBSTR err = error.raw();
4751 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4752 sep, err));
4753 return setError(E_ACCESSDENIED,
4754 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4755 aKey.c_str(),
4756 aValue.c_str(),
4757 sep,
4758 err);
4759 }
4760
4761 // data is changing and change not vetoed: then write it out under the lock
4762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4763
4764 if (i_isSnapshotMachine())
4765 {
4766 HRESULT rc = i_checkStateDependency(MutableStateDep);
4767 if (FAILED(rc)) return rc;
4768 }
4769
4770 if (aValue.isEmpty())
4771 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4772 else
4773 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4774 // creates a new key if needed
4775
4776 bool fNeedsGlobalSaveSettings = false;
4777 i_saveSettings(&fNeedsGlobalSaveSettings);
4778
4779 if (fNeedsGlobalSaveSettings)
4780 {
4781 // save the global settings; for that we should hold only the VirtualBox lock
4782 alock.release();
4783 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4784 mParent->i_saveSettings();
4785 }
4786 }
4787
4788 // fire notification outside the lock
4789 if (fChanged)
4790 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4791
4792 return S_OK;
4793}
4794
4795HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4796{
4797 aProgress = NULL;
4798 NOREF(aSettingsFilePath);
4799 ReturnComNotImplemented();
4800}
4801
4802HRESULT Machine::saveSettings()
4803{
4804 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4805
4806 /* when there was auto-conversion, we want to save the file even if
4807 * the VM is saved */
4808 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4809 if (FAILED(rc)) return rc;
4810
4811 /* the settings file path may never be null */
4812 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4813
4814 /* save all VM data excluding snapshots */
4815 bool fNeedsGlobalSaveSettings = false;
4816 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4817 mlock.release();
4818
4819 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4820 {
4821 // save the global settings; for that we should hold only the VirtualBox lock
4822 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4823 rc = mParent->i_saveSettings();
4824 }
4825
4826 return rc;
4827}
4828
4829
4830HRESULT Machine::discardSettings()
4831{
4832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4833
4834 HRESULT rc = i_checkStateDependency(MutableStateDep);
4835 if (FAILED(rc)) return rc;
4836
4837 /*
4838 * during this rollback, the session will be notified if data has
4839 * been actually changed
4840 */
4841 i_rollback(true /* aNotify */);
4842
4843 return S_OK;
4844}
4845
4846/** @note Locks objects! */
4847HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4848 std::vector<ComPtr<IMedium> > &aMedia)
4849{
4850 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4851 AutoLimitedCaller autoCaller(this);
4852 AssertComRCReturnRC(autoCaller.rc());
4853
4854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4855
4856 Guid id(i_getId());
4857
4858 if (mData->mSession.mState != SessionState_Unlocked)
4859 return setError(VBOX_E_INVALID_OBJECT_STATE,
4860 tr("Cannot unregister the machine '%s' while it is locked"),
4861 mUserData->s.strName.c_str());
4862
4863 // wait for state dependents to drop to zero
4864 i_ensureNoStateDependencies();
4865
4866 if (!mData->mAccessible)
4867 {
4868 // inaccessible maschines can only be unregistered; uninitialize ourselves
4869 // here because currently there may be no unregistered that are inaccessible
4870 // (this state combination is not supported). Note releasing the caller and
4871 // leaving the lock before calling uninit()
4872 alock.release();
4873 autoCaller.release();
4874
4875 uninit();
4876
4877 mParent->i_unregisterMachine(this, id);
4878 // calls VirtualBox::i_saveSettings()
4879
4880 return S_OK;
4881 }
4882
4883 HRESULT rc = S_OK;
4884
4885 // discard saved state
4886 if (mData->mMachineState == MachineState_Saved)
4887 {
4888 // add the saved state file to the list of files the caller should delete
4889 Assert(!mSSData->strStateFilePath.isEmpty());
4890 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4891
4892 mSSData->strStateFilePath.setNull();
4893
4894 // unconditionally set the machine state to powered off, we now
4895 // know no session has locked the machine
4896 mData->mMachineState = MachineState_PoweredOff;
4897 }
4898
4899 size_t cSnapshots = 0;
4900 if (mData->mFirstSnapshot)
4901 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
4902 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
4903 // fail now before we start detaching media
4904 return setError(VBOX_E_INVALID_OBJECT_STATE,
4905 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4906 mUserData->s.strName.c_str(), cSnapshots);
4907
4908 // This list collects the medium objects from all medium attachments
4909 // which we will detach from the machine and its snapshots, in a specific
4910 // order which allows for closing all media without getting "media in use"
4911 // errors, simply by going through the list from the front to the back:
4912 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4913 // and must be closed before the parent media from the snapshots, or closing the parents
4914 // will fail because they still have children);
4915 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4916 // the root ("first") snapshot of the machine.
4917 MediaList llMedia;
4918
4919 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4920 && mMediaData->mAttachments.size()
4921 )
4922 {
4923 // we have media attachments: detach them all and add the Medium objects to our list
4924 if (aCleanupMode != CleanupMode_UnregisterOnly)
4925 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4926 else
4927 return setError(VBOX_E_INVALID_OBJECT_STATE,
4928 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4929 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4930 }
4931
4932 if (cSnapshots)
4933 {
4934 // autoCleanup must be true here, or we would have failed above
4935
4936 // add the media from the medium attachments of the snapshots to llMedia
4937 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4938 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4939 // into the children first
4940
4941 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4942 MachineState_T oldState = mData->mMachineState;
4943 mData->mMachineState = MachineState_DeletingSnapshot;
4944
4945 // make a copy of the first snapshot so the refcount does not drop to 0
4946 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4947 // because of the AutoCaller voodoo)
4948 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4949
4950 // GO!
4951 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4952
4953 mData->mMachineState = oldState;
4954 }
4955
4956 if (FAILED(rc))
4957 {
4958 i_rollbackMedia();
4959 return rc;
4960 }
4961
4962 // commit all the media changes made above
4963 i_commitMedia();
4964
4965 mData->mRegistered = false;
4966
4967 // machine lock no longer needed
4968 alock.release();
4969
4970 // return media to caller
4971 size_t i = 0;
4972 aMedia.resize(llMedia.size());
4973 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
4974 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4975
4976 mParent->i_unregisterMachine(this, id);
4977 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4978
4979 return S_OK;
4980}
4981
4982struct Machine::DeleteTask
4983{
4984 ComObjPtr<Machine> pMachine;
4985 RTCList<ComPtr<IMedium> > llMediums;
4986 StringsList llFilesToDelete;
4987 ComObjPtr<Progress> pProgress;
4988};
4989
4990HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4991{
4992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4993
4994 HRESULT rc = i_checkStateDependency(MutableStateDep);
4995 if (FAILED(rc)) return rc;
4996
4997 if (mData->mRegistered)
4998 return setError(VBOX_E_INVALID_VM_STATE,
4999 tr("Cannot delete settings of a registered machine"));
5000
5001 DeleteTask *pTask = new DeleteTask;
5002 pTask->pMachine = this;
5003
5004 // collect files to delete
5005 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5006
5007 for (size_t i = 0; i < aMedia.size(); ++i)
5008 {
5009 IMedium *pIMedium(aMedia[i]);
5010 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5011 if (pMedium.isNull())
5012 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5013 SafeArray<BSTR> ids;
5014 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5015 if (FAILED(rc)) return rc;
5016 /* At this point the medium should not have any back references
5017 * anymore. If it has it is attached to another VM and *must* not
5018 * deleted. */
5019 if (ids.size() < 1)
5020 pTask->llMediums.append(pMedium);
5021 }
5022 if (mData->pMachineConfigFile->fileExists())
5023 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5024
5025 pTask->pProgress.createObject();
5026 pTask->pProgress->init(i_getVirtualBox(),
5027 static_cast<IMachine*>(this) /* aInitiator */,
5028 Bstr(tr("Deleting files")).raw(),
5029 true /* fCancellable */,
5030 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5031 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5032
5033 int vrc = RTThreadCreate(NULL,
5034 Machine::deleteThread,
5035 (void*)pTask,
5036 0,
5037 RTTHREADTYPE_MAIN_WORKER,
5038 0,
5039 "MachineDelete");
5040
5041 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5042
5043 if (RT_FAILURE(vrc))
5044 {
5045 delete pTask;
5046 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5047 }
5048
5049 LogFlowFuncLeave();
5050
5051 return S_OK;
5052}
5053
5054/**
5055 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5056 * calls Machine::deleteTaskWorker() on the actual machine object.
5057 * @param Thread
5058 * @param pvUser
5059 * @return
5060 */
5061/*static*/
5062DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5063{
5064 LogFlowFuncEnter();
5065
5066 DeleteTask *pTask = (DeleteTask*)pvUser;
5067 Assert(pTask);
5068 Assert(pTask->pMachine);
5069 Assert(pTask->pProgress);
5070
5071 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5072 pTask->pProgress->i_notifyComplete(rc);
5073
5074 delete pTask;
5075
5076 LogFlowFuncLeave();
5077
5078 NOREF(Thread);
5079
5080 return VINF_SUCCESS;
5081}
5082
5083/**
5084 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5085 * @param task
5086 * @return
5087 */
5088HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5089{
5090 AutoCaller autoCaller(this);
5091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5092
5093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5094
5095 HRESULT rc = S_OK;
5096
5097 try
5098 {
5099 ULONG uLogHistoryCount = 3;
5100 ComPtr<ISystemProperties> systemProperties;
5101 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5102 if (FAILED(rc)) throw rc;
5103
5104 if (!systemProperties.isNull())
5105 {
5106 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5107 if (FAILED(rc)) throw rc;
5108 }
5109
5110 MachineState_T oldState = mData->mMachineState;
5111 i_setMachineState(MachineState_SettingUp);
5112 alock.release();
5113 for (size_t i = 0; i < task.llMediums.size(); ++i)
5114 {
5115 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5116 {
5117 AutoCaller mac(pMedium);
5118 if (FAILED(mac.rc())) throw mac.rc();
5119 Utf8Str strLocation = pMedium->i_getLocationFull();
5120 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5121 if (FAILED(rc)) throw rc;
5122 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5123 }
5124 ComPtr<IProgress> pProgress2;
5125 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5126 if (FAILED(rc)) throw rc;
5127 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5128 if (FAILED(rc)) throw rc;
5129 /* Check the result of the asynchronous process. */
5130 LONG iRc;
5131 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5132 if (FAILED(rc)) throw rc;
5133 /* If the thread of the progress object has an error, then
5134 * retrieve the error info from there, or it'll be lost. */
5135 if (FAILED(iRc))
5136 throw setError(ProgressErrorInfo(pProgress2));
5137
5138 /* Close the medium, deliberately without checking the return
5139- * code, and without leaving any trace in the error info, as
5140- * a failure here is a very minor issue, which shouldn't happen
5141- * as above we even managed to delete the medium. */
5142 {
5143 ErrorInfoKeeper eik;
5144 pMedium->Close();
5145 }
5146 }
5147 i_setMachineState(oldState);
5148 alock.acquire();
5149
5150 // delete the files pushed on the task list by Machine::Delete()
5151 // (this includes saved states of the machine and snapshots and
5152 // medium storage files from the IMedium list passed in, and the
5153 // machine XML file)
5154 StringsList::const_iterator it = task.llFilesToDelete.begin();
5155 while (it != task.llFilesToDelete.end())
5156 {
5157 const Utf8Str &strFile = *it;
5158 LogFunc(("Deleting file %s\n", strFile.c_str()));
5159 int vrc = RTFileDelete(strFile.c_str());
5160 if (RT_FAILURE(vrc))
5161 throw setError(VBOX_E_IPRT_ERROR,
5162 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5163
5164 ++it;
5165 if (it == task.llFilesToDelete.end())
5166 {
5167 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5168 if (FAILED(rc)) throw rc;
5169 break;
5170 }
5171
5172 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5173 if (FAILED(rc)) throw rc;
5174 }
5175
5176 /* delete the settings only when the file actually exists */
5177 if (mData->pMachineConfigFile->fileExists())
5178 {
5179 /* Delete any backup or uncommitted XML files. Ignore failures.
5180 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5181 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5182 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5183 RTFileDelete(otherXml.c_str());
5184 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5185 RTFileDelete(otherXml.c_str());
5186
5187 /* delete the Logs folder, nothing important should be left
5188 * there (we don't check for errors because the user might have
5189 * some private files there that we don't want to delete) */
5190 Utf8Str logFolder;
5191 getLogFolder(logFolder);
5192 Assert(logFolder.length());
5193 if (RTDirExists(logFolder.c_str()))
5194 {
5195 /* Delete all VBox.log[.N] files from the Logs folder
5196 * (this must be in sync with the rotation logic in
5197 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5198 * files that may have been created by the GUI. */
5199 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5200 logFolder.c_str(), RTPATH_DELIMITER);
5201 RTFileDelete(log.c_str());
5202 log = Utf8StrFmt("%s%cVBox.png",
5203 logFolder.c_str(), RTPATH_DELIMITER);
5204 RTFileDelete(log.c_str());
5205 for (int i = uLogHistoryCount; i > 0; i--)
5206 {
5207 log = Utf8StrFmt("%s%cVBox.log.%d",
5208 logFolder.c_str(), RTPATH_DELIMITER, i);
5209 RTFileDelete(log.c_str());
5210 log = Utf8StrFmt("%s%cVBox.png.%d",
5211 logFolder.c_str(), RTPATH_DELIMITER, i);
5212 RTFileDelete(log.c_str());
5213 }
5214
5215 RTDirRemove(logFolder.c_str());
5216 }
5217
5218 /* delete the Snapshots folder, nothing important should be left
5219 * there (we don't check for errors because the user might have
5220 * some private files there that we don't want to delete) */
5221 Utf8Str strFullSnapshotFolder;
5222 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5223 Assert(!strFullSnapshotFolder.isEmpty());
5224 if (RTDirExists(strFullSnapshotFolder.c_str()))
5225 RTDirRemove(strFullSnapshotFolder.c_str());
5226
5227 // delete the directory that contains the settings file, but only
5228 // if it matches the VM name
5229 Utf8Str settingsDir;
5230 if (i_isInOwnDir(&settingsDir))
5231 RTDirRemove(settingsDir.c_str());
5232 }
5233
5234 alock.release();
5235
5236 mParent->i_saveModifiedRegistries();
5237 }
5238 catch (HRESULT aRC) { rc = aRC; }
5239
5240 return rc;
5241}
5242
5243HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5244{
5245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5246
5247 ComObjPtr<Snapshot> pSnapshot;
5248 HRESULT rc;
5249
5250 if (aNameOrId.isEmpty())
5251 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5252 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5253 else
5254 {
5255 Guid uuid(aNameOrId);
5256 if (uuid.isValid())
5257 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5258 else
5259 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5260 }
5261 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5262
5263 return rc;
5264}
5265
5266HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5267{
5268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5269
5270 HRESULT rc = i_checkStateDependency(MutableStateDep);
5271 if (FAILED(rc)) return rc;
5272
5273 ComObjPtr<SharedFolder> sharedFolder;
5274 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5275 if (SUCCEEDED(rc))
5276 return setError(VBOX_E_OBJECT_IN_USE,
5277 tr("Shared folder named '%s' already exists"),
5278 aName.c_str());
5279
5280 sharedFolder.createObject();
5281 rc = sharedFolder->init(i_getMachine(),
5282 aName,
5283 aHostPath,
5284 !!aWritable,
5285 !!aAutomount,
5286 true /* fFailOnError */);
5287 if (FAILED(rc)) return rc;
5288
5289 i_setModified(IsModified_SharedFolders);
5290 mHWData.backup();
5291 mHWData->mSharedFolders.push_back(sharedFolder);
5292
5293 /* inform the direct session if any */
5294 alock.release();
5295 i_onSharedFolderChange();
5296
5297 return S_OK;
5298}
5299
5300HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5301{
5302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5303
5304 HRESULT rc = i_checkStateDependency(MutableStateDep);
5305 if (FAILED(rc)) return rc;
5306
5307 ComObjPtr<SharedFolder> sharedFolder;
5308 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5309 if (FAILED(rc)) return rc;
5310
5311 i_setModified(IsModified_SharedFolders);
5312 mHWData.backup();
5313 mHWData->mSharedFolders.remove(sharedFolder);
5314
5315 /* inform the direct session if any */
5316 alock.release();
5317 i_onSharedFolderChange();
5318
5319 return S_OK;
5320}
5321
5322HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5323{
5324 /* start with No */
5325 *aCanShow = FALSE;
5326
5327 ComPtr<IInternalSessionControl> directControl;
5328 {
5329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5330
5331 if (mData->mSession.mState != SessionState_Locked)
5332 return setError(VBOX_E_INVALID_VM_STATE,
5333 tr("Machine is not locked for session (session state: %s)"),
5334 Global::stringifySessionState(mData->mSession.mState));
5335
5336 directControl = mData->mSession.mDirectControl;
5337 }
5338
5339 /* ignore calls made after #OnSessionEnd() is called */
5340 if (!directControl)
5341 return S_OK;
5342
5343 LONG64 dummy;
5344 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5345}
5346
5347HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5348{
5349 ComPtr<IInternalSessionControl> directControl;
5350 {
5351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5352
5353 if (mData->mSession.mState != SessionState_Locked)
5354 return setError(E_FAIL,
5355 tr("Machine is not locked for session (session state: %s)"),
5356 Global::stringifySessionState(mData->mSession.mState));
5357
5358 directControl = mData->mSession.mDirectControl;
5359 }
5360
5361 /* ignore calls made after #OnSessionEnd() is called */
5362 if (!directControl)
5363 return S_OK;
5364
5365 BOOL dummy;
5366 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5367}
5368
5369#ifdef VBOX_WITH_GUEST_PROPS
5370/**
5371 * Look up a guest property in VBoxSVC's internal structures.
5372 */
5373HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5374 com::Utf8Str &aValue,
5375 LONG64 *aTimestamp,
5376 com::Utf8Str &aFlags) const
5377{
5378 using namespace guestProp;
5379
5380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5381 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5382
5383 if (it != mHWData->mGuestProperties.end())
5384 {
5385 char szFlags[MAX_FLAGS_LEN + 1];
5386 aValue = it->second.strValue;
5387 *aTimestamp = it->second.mTimestamp;
5388 writeFlags(it->second.mFlags, szFlags);
5389 aFlags = Utf8Str(szFlags);
5390 }
5391
5392 return S_OK;
5393}
5394
5395/**
5396 * Query the VM that a guest property belongs to for the property.
5397 * @returns E_ACCESSDENIED if the VM process is not available or not
5398 * currently handling queries and the lookup should then be done in
5399 * VBoxSVC.
5400 */
5401HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5402 com::Utf8Str &aValue,
5403 LONG64 *aTimestamp,
5404 com::Utf8Str &aFlags) const
5405{
5406 HRESULT rc = S_OK;
5407 BSTR bValue = NULL;
5408 BSTR bFlags = NULL;
5409
5410 ComPtr<IInternalSessionControl> directControl;
5411 directControl = mData->mSession.mDirectControl;
5412
5413 /* fail if we were called after #OnSessionEnd() is called. This is a
5414 * silly race condition. */
5415
5416 /** @todo This code is bothering API clients (like python script clients) with
5417 * the AccessGuestProperty call, creating unncessary IPC. Need to
5418 * have a way of figuring out which kind of direct session it is... */
5419 if (!directControl)
5420 rc = E_ACCESSDENIED;
5421 else
5422 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), NULL, NULL,
5423 false /* isSetter */,
5424 &bValue, aTimestamp, &bFlags);
5425
5426 aValue = bValue;
5427 aFlags = bFlags;
5428
5429 return rc;
5430}
5431#endif // VBOX_WITH_GUEST_PROPS
5432
5433HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5434 com::Utf8Str &aValue,
5435 LONG64 *aTimestamp,
5436 com::Utf8Str &aFlags)
5437{
5438#ifndef VBOX_WITH_GUEST_PROPS
5439 ReturnComNotImplemented();
5440#else // VBOX_WITH_GUEST_PROPS
5441
5442 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5443
5444 if (rc == E_ACCESSDENIED)
5445 /* The VM is not running or the service is not (yet) accessible */
5446 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5447 return rc;
5448#endif // VBOX_WITH_GUEST_PROPS
5449}
5450
5451HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5452{
5453 LONG64 dummyTimestamp;
5454 com::Utf8Str dummyFlags;
5455 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5456 return rc;
5457
5458}
5459HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5460{
5461 com::Utf8Str dummyFlags;
5462 com::Utf8Str dummyValue;
5463 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5464 return rc;
5465}
5466
5467#ifdef VBOX_WITH_GUEST_PROPS
5468/**
5469 * Set a guest property in VBoxSVC's internal structures.
5470 */
5471HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5472 const com::Utf8Str &aFlags)
5473{
5474 using namespace guestProp;
5475
5476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5477 HRESULT rc = S_OK;
5478
5479 rc = i_checkStateDependency(MutableStateDep);
5480 if (FAILED(rc)) return rc;
5481
5482 try
5483 {
5484 uint32_t fFlags = NILFLAG;
5485 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5486 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5487
5488 bool fDelete = aValue.isEmpty();
5489 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5490 if (it == mHWData->mGuestProperties.end())
5491 {
5492 if (!fDelete)
5493 {
5494 i_setModified(IsModified_MachineData);
5495 mHWData.backupEx();
5496
5497 RTTIMESPEC time;
5498 HWData::GuestProperty prop;
5499 prop.strValue = Bstr(aValue).raw();
5500 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5501 prop.mFlags = fFlags;
5502 mHWData->mGuestProperties[aName] = prop;
5503 }
5504 }
5505 else
5506 {
5507 if (it->second.mFlags & (RDONLYHOST))
5508 {
5509 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5510 }
5511 else
5512 {
5513 i_setModified(IsModified_MachineData);
5514 mHWData.backupEx();
5515
5516 /* The backupEx() operation invalidates our iterator,
5517 * so get a new one. */
5518 it = mHWData->mGuestProperties.find(aName);
5519 Assert(it != mHWData->mGuestProperties.end());
5520
5521 if (!fDelete)
5522 {
5523 RTTIMESPEC time;
5524 it->second.strValue = aValue;
5525 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5526 it->second.mFlags = fFlags;
5527 }
5528 else
5529 mHWData->mGuestProperties.erase(it);
5530 }
5531 }
5532
5533 if ( SUCCEEDED(rc)
5534 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5535 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5536 RTSTR_MAX,
5537 aName.c_str(),
5538 RTSTR_MAX,
5539 NULL)
5540 )
5541 )
5542 {
5543 alock.release();
5544
5545 mParent->i_onGuestPropertyChange(mData->mUuid,
5546 Bstr(aName).raw(),
5547 Bstr(aValue).raw(),
5548 Bstr(aFlags).raw());
5549 }
5550 }
5551 catch (std::bad_alloc &)
5552 {
5553 rc = E_OUTOFMEMORY;
5554 }
5555
5556 return rc;
5557}
5558
5559/**
5560 * Set a property on the VM that that property belongs to.
5561 * @returns E_ACCESSDENIED if the VM process is not available or not
5562 * currently handling queries and the setting should then be done in
5563 * VBoxSVC.
5564 */
5565HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5566 const com::Utf8Str &aFlags)
5567{
5568 HRESULT rc;
5569
5570 try
5571 {
5572 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5573
5574 BSTR dummy = NULL; /* will not be changed (setter) */
5575 LONG64 dummy64;
5576 if (!directControl)
5577 rc = E_ACCESSDENIED;
5578 else
5579 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5580 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5581 true /* isSetter */,
5582 &dummy, &dummy64, &dummy);
5583 }
5584 catch (std::bad_alloc &)
5585 {
5586 rc = E_OUTOFMEMORY;
5587 }
5588
5589 return rc;
5590}
5591#endif // VBOX_WITH_GUEST_PROPS
5592
5593HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5594 const com::Utf8Str &aFlags)
5595{
5596#ifndef VBOX_WITH_GUEST_PROPS
5597 ReturnComNotImplemented();
5598#else // VBOX_WITH_GUEST_PROPS
5599 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags);
5600 if (rc == E_ACCESSDENIED)
5601 /* The VM is not running or the service is not (yet) accessible */
5602 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags);
5603 return rc;
5604#endif // VBOX_WITH_GUEST_PROPS
5605}
5606
5607HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5608{
5609 return setGuestProperty(aProperty, aValue, "");
5610}
5611
5612HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5613{
5614 return setGuestProperty(aName, "", "");
5615}
5616
5617#ifdef VBOX_WITH_GUEST_PROPS
5618/**
5619 * Enumerate the guest properties in VBoxSVC's internal structures.
5620 */
5621HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5622 std::vector<com::Utf8Str> &aNames,
5623 std::vector<com::Utf8Str> &aValues,
5624 std::vector<LONG64> &aTimestamps,
5625 std::vector<com::Utf8Str> &aFlags)
5626{
5627 using namespace guestProp;
5628
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630 Utf8Str strPatterns(aPatterns);
5631
5632 HWData::GuestPropertyMap propMap;
5633
5634 /*
5635 * Look for matching patterns and build up a list.
5636 */
5637 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5638 while (it != mHWData->mGuestProperties.end())
5639 {
5640 if ( strPatterns.isEmpty()
5641 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5642 RTSTR_MAX,
5643 it->first.c_str(),
5644 RTSTR_MAX,
5645 NULL)
5646 )
5647 propMap.insert(*it);
5648 it++;
5649 }
5650
5651 alock.release();
5652
5653 /*
5654 * And build up the arrays for returning the property information.
5655 */
5656 size_t cEntries = propMap.size();
5657
5658 aNames.resize(cEntries);
5659 aValues.resize(cEntries);
5660 aTimestamps.resize(cEntries);
5661 aFlags.resize(cEntries);
5662
5663 char szFlags[MAX_FLAGS_LEN + 1];
5664 size_t i= 0;
5665 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5666 {
5667 aNames[i] = it->first;
5668 aValues[i] = it->second.strValue;
5669 aTimestamps[i] = it->second.mTimestamp;
5670 writeFlags(it->second.mFlags, szFlags);
5671 aFlags[i] = Utf8Str(szFlags);
5672 }
5673
5674 return S_OK;
5675}
5676
5677/**
5678 * Enumerate the properties managed by a VM.
5679 * @returns E_ACCESSDENIED if the VM process is not available or not
5680 * currently handling queries and the setting should then be done in
5681 * VBoxSVC.
5682 */
5683HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5684 std::vector<com::Utf8Str> &aNames,
5685 std::vector<com::Utf8Str> &aValues,
5686 std::vector<LONG64> &aTimestamps,
5687 std::vector<com::Utf8Str> &aFlags)
5688{
5689 HRESULT rc;
5690 ComPtr<IInternalSessionControl> directControl;
5691 directControl = mData->mSession.mDirectControl;
5692
5693
5694 com::SafeArray<BSTR> bNames;
5695 com::SafeArray<BSTR> bValues;
5696 com::SafeArray<LONG64> bTimestamps;
5697 com::SafeArray<BSTR> bFlags;
5698
5699 if (!directControl)
5700 rc = E_ACCESSDENIED;
5701 else
5702 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5703 ComSafeArrayAsOutParam(bNames),
5704 ComSafeArrayAsOutParam(bValues),
5705 ComSafeArrayAsOutParam(bTimestamps),
5706 ComSafeArrayAsOutParam(bFlags));
5707 size_t i;
5708 aNames.resize(bNames.size());
5709 for (i = 0; i < bNames.size(); ++i)
5710 aNames[i] = Utf8Str(bNames[i]);
5711 aValues.resize(bValues.size());
5712 for (i = 0; i < bValues.size(); ++i)
5713 aValues[i] = Utf8Str(bValues[i]);
5714 aTimestamps.resize(bTimestamps.size());
5715 for (i = 0; i < bTimestamps.size(); ++i)
5716 aTimestamps[i] = bTimestamps[i];
5717 aFlags.resize(bFlags.size());
5718 for (i = 0; i < bFlags.size(); ++i)
5719 aFlags[i] = Utf8Str(bFlags[i]);
5720
5721 return rc;
5722}
5723#endif // VBOX_WITH_GUEST_PROPS
5724HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5725 std::vector<com::Utf8Str> &aNames,
5726 std::vector<com::Utf8Str> &aValues,
5727 std::vector<LONG64> &aTimestamps,
5728 std::vector<com::Utf8Str> &aFlags)
5729{
5730#ifndef VBOX_WITH_GUEST_PROPS
5731 ReturnComNotImplemented();
5732#else // VBOX_WITH_GUEST_PROPS
5733
5734 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5735
5736 if (rc == E_ACCESSDENIED)
5737 /* The VM is not running or the service is not (yet) accessible */
5738 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5739 return rc;
5740#endif // VBOX_WITH_GUEST_PROPS
5741}
5742
5743HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5744 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5745{
5746 MediaData::AttachmentList atts;
5747
5748 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5749 if (FAILED(rc)) return rc;
5750
5751 size_t i = 0;
5752 aMediumAttachments.resize(atts.size());
5753 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5754 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5755
5756 return S_OK;
5757}
5758
5759HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5760 LONG aControllerPort,
5761 LONG aDevice,
5762 ComPtr<IMediumAttachment> &aAttachment)
5763{
5764 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5765 aName.c_str(), aControllerPort, aDevice));
5766
5767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5768
5769 aAttachment = NULL;
5770
5771 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5772 Bstr(aName).raw(),
5773 aControllerPort,
5774 aDevice);
5775 if (pAttach.isNull())
5776 return setError(VBOX_E_OBJECT_NOT_FOUND,
5777 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5778 aDevice, aControllerPort, aName.c_str());
5779
5780 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5781
5782 return S_OK;
5783}
5784
5785
5786HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5787 StorageBus_T aConnectionType,
5788 ComPtr<IStorageController> &aController)
5789{
5790 if ( (aConnectionType <= StorageBus_Null)
5791 || (aConnectionType > StorageBus_USB))
5792 return setError(E_INVALIDARG,
5793 tr("Invalid connection type: %d"),
5794 aConnectionType);
5795
5796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5797
5798 HRESULT rc = i_checkStateDependency(MutableStateDep);
5799 if (FAILED(rc)) return rc;
5800
5801 /* try to find one with the name first. */
5802 ComObjPtr<StorageController> ctrl;
5803
5804 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5805 if (SUCCEEDED(rc))
5806 return setError(VBOX_E_OBJECT_IN_USE,
5807 tr("Storage controller named '%s' already exists"),
5808 aName.c_str());
5809
5810 ctrl.createObject();
5811
5812 /* get a new instance number for the storage controller */
5813 ULONG ulInstance = 0;
5814 bool fBootable = true;
5815 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5816 it != mStorageControllers->end();
5817 ++it)
5818 {
5819 if ((*it)->i_getStorageBus() == aConnectionType)
5820 {
5821 ULONG ulCurInst = (*it)->i_getInstance();
5822
5823 if (ulCurInst >= ulInstance)
5824 ulInstance = ulCurInst + 1;
5825
5826 /* Only one controller of each type can be marked as bootable. */
5827 if ((*it)->i_getBootable())
5828 fBootable = false;
5829 }
5830 }
5831
5832 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5833 if (FAILED(rc)) return rc;
5834
5835 i_setModified(IsModified_Storage);
5836 mStorageControllers.backup();
5837 mStorageControllers->push_back(ctrl);
5838
5839 ctrl.queryInterfaceTo(aController.asOutParam());
5840
5841 /* inform the direct session if any */
5842 alock.release();
5843 i_onStorageControllerChange();
5844
5845 return S_OK;
5846}
5847
5848HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5849 ComPtr<IStorageController> &aStorageController)
5850{
5851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5852
5853 ComObjPtr<StorageController> ctrl;
5854
5855 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5856 if (SUCCEEDED(rc))
5857 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5858
5859 return rc;
5860}
5861
5862HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
5863 ComPtr<IStorageController> &aStorageController)
5864{
5865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5866
5867 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5868 it != mStorageControllers->end();
5869 ++it)
5870 {
5871 if ((*it)->i_getInstance() == aInstance)
5872 {
5873 (*it).queryInterfaceTo(aStorageController.asOutParam());
5874 return S_OK;
5875 }
5876 }
5877
5878 return setError(VBOX_E_OBJECT_NOT_FOUND,
5879 tr("Could not find a storage controller with instance number '%lu'"),
5880 aInstance);
5881}
5882
5883HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5884{
5885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5886
5887 HRESULT rc = i_checkStateDependency(MutableStateDep);
5888 if (FAILED(rc)) return rc;
5889
5890 ComObjPtr<StorageController> ctrl;
5891
5892 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5893 if (SUCCEEDED(rc))
5894 {
5895 /* Ensure that only one controller of each type is marked as bootable. */
5896 if (aBootable == TRUE)
5897 {
5898 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5899 it != mStorageControllers->end();
5900 ++it)
5901 {
5902 ComObjPtr<StorageController> aCtrl = (*it);
5903
5904 if ( (aCtrl->i_getName() != aName)
5905 && aCtrl->i_getBootable() == TRUE
5906 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5907 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5908 {
5909 aCtrl->i_setBootable(FALSE);
5910 break;
5911 }
5912 }
5913 }
5914
5915 if (SUCCEEDED(rc))
5916 {
5917 ctrl->i_setBootable(aBootable);
5918 i_setModified(IsModified_Storage);
5919 }
5920 }
5921
5922 if (SUCCEEDED(rc))
5923 {
5924 /* inform the direct session if any */
5925 alock.release();
5926 i_onStorageControllerChange();
5927 }
5928
5929 return rc;
5930}
5931
5932HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5933{
5934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5935
5936 HRESULT rc = i_checkStateDependency(MutableStateDep);
5937 if (FAILED(rc)) return rc;
5938
5939 ComObjPtr<StorageController> ctrl;
5940 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5941 if (FAILED(rc)) return rc;
5942
5943 {
5944 /* find all attached devices to the appropriate storage controller and detach them all */
5945 // make a temporary list because detachDevice invalidates iterators into
5946 // mMediaData->mAttachments
5947 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
5948
5949 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
5950 it != llAttachments2.end();
5951 ++it)
5952 {
5953 MediumAttachment *pAttachTemp = *it;
5954
5955 AutoCaller localAutoCaller(pAttachTemp);
5956 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5957
5958 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5959
5960 if (pAttachTemp->i_getControllerName() == aName)
5961 {
5962 rc = i_detachDevice(pAttachTemp, alock, NULL);
5963 if (FAILED(rc)) return rc;
5964 }
5965 }
5966 }
5967
5968 /* We can remove it now. */
5969 i_setModified(IsModified_Storage);
5970 mStorageControllers.backup();
5971
5972 ctrl->i_unshare();
5973
5974 mStorageControllers->remove(ctrl);
5975
5976 /* inform the direct session if any */
5977 alock.release();
5978 i_onStorageControllerChange();
5979
5980 return S_OK;
5981}
5982
5983HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5984 ComPtr<IUSBController> &aController)
5985{
5986 if ( (aType <= USBControllerType_Null)
5987 || (aType >= USBControllerType_Last))
5988 return setError(E_INVALIDARG,
5989 tr("Invalid USB controller type: %d"),
5990 aType);
5991
5992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5993
5994 HRESULT rc = i_checkStateDependency(MutableStateDep);
5995 if (FAILED(rc)) return rc;
5996
5997 /* try to find one with the same type first. */
5998 ComObjPtr<USBController> ctrl;
5999
6000 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6001 if (SUCCEEDED(rc))
6002 return setError(VBOX_E_OBJECT_IN_USE,
6003 tr("USB controller named '%s' already exists"),
6004 aName.c_str());
6005
6006 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6007 ULONG maxInstances;
6008 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6009 if (FAILED(rc))
6010 return rc;
6011
6012 ULONG cInstances = i_getUSBControllerCountByType(aType);
6013 if (cInstances >= maxInstances)
6014 return setError(E_INVALIDARG,
6015 tr("Too many USB controllers of this type"));
6016
6017 ctrl.createObject();
6018
6019 rc = ctrl->init(this, aName, aType);
6020 if (FAILED(rc)) return rc;
6021
6022 i_setModified(IsModified_USB);
6023 mUSBControllers.backup();
6024 mUSBControllers->push_back(ctrl);
6025
6026 ctrl.queryInterfaceTo(aController.asOutParam());
6027
6028 /* inform the direct session if any */
6029 alock.release();
6030 i_onUSBControllerChange();
6031
6032 return S_OK;
6033}
6034
6035HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6036{
6037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 ComObjPtr<USBController> ctrl;
6040
6041 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6042 if (SUCCEEDED(rc))
6043 ctrl.queryInterfaceTo(aController.asOutParam());
6044
6045 return rc;
6046}
6047
6048HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6049 ULONG *aControllers)
6050{
6051 if ( (aType <= USBControllerType_Null)
6052 || (aType >= USBControllerType_Last))
6053 return setError(E_INVALIDARG,
6054 tr("Invalid USB controller type: %d"),
6055 aType);
6056
6057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6058
6059 ComObjPtr<USBController> ctrl;
6060
6061 *aControllers = i_getUSBControllerCountByType(aType);
6062
6063 return S_OK;
6064}
6065
6066HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6067{
6068
6069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6070
6071 HRESULT rc = i_checkStateDependency(MutableStateDep);
6072 if (FAILED(rc)) return rc;
6073
6074 ComObjPtr<USBController> ctrl;
6075 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6076 if (FAILED(rc)) return rc;
6077
6078 i_setModified(IsModified_USB);
6079 mUSBControllers.backup();
6080
6081 ctrl->i_unshare();
6082
6083 mUSBControllers->remove(ctrl);
6084
6085 /* inform the direct session if any */
6086 alock.release();
6087 i_onUSBControllerChange();
6088
6089 return S_OK;
6090}
6091
6092HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6093 ULONG *aOriginX,
6094 ULONG *aOriginY,
6095 ULONG *aWidth,
6096 ULONG *aHeight,
6097 BOOL *aEnabled)
6098{
6099 uint32_t u32OriginX= 0;
6100 uint32_t u32OriginY= 0;
6101 uint32_t u32Width = 0;
6102 uint32_t u32Height = 0;
6103 uint16_t u16Flags = 0;
6104
6105 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6106 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6107 if (RT_FAILURE(vrc))
6108 {
6109#ifdef RT_OS_WINDOWS
6110 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6111 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6112 * So just assign fEnable to TRUE again.
6113 * The right fix would be to change GUI API wrappers to make sure that parameters
6114 * are changed only if API succeeds.
6115 */
6116 *aEnabled = TRUE;
6117#endif
6118 return setError(VBOX_E_IPRT_ERROR,
6119 tr("Saved guest size is not available (%Rrc)"),
6120 vrc);
6121 }
6122
6123 *aOriginX = u32OriginX;
6124 *aOriginY = u32OriginY;
6125 *aWidth = u32Width;
6126 *aHeight = u32Height;
6127 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6128
6129 return S_OK;
6130}
6131
6132HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6133{
6134 if (aScreenId != 0)
6135 return E_NOTIMPL;
6136
6137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6138
6139 uint8_t *pu8Data = NULL;
6140 uint32_t cbData = 0;
6141 uint32_t u32Width = 0;
6142 uint32_t u32Height = 0;
6143
6144 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6145
6146 if (RT_FAILURE(vrc))
6147 return setError(VBOX_E_IPRT_ERROR,
6148 tr("Saved screenshot data is not available (%Rrc)"),
6149 vrc);
6150
6151 *aSize = cbData;
6152 *aWidth = u32Width;
6153 *aHeight = u32Height;
6154
6155 freeSavedDisplayScreenshot(pu8Data);
6156
6157 return S_OK;
6158}
6159
6160
6161HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6162{
6163 if (aScreenId != 0)
6164 return E_NOTIMPL;
6165
6166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6167
6168 uint8_t *pu8Data = NULL;
6169 uint32_t cbData = 0;
6170 uint32_t u32Width = 0;
6171 uint32_t u32Height = 0;
6172
6173 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6174
6175 if (RT_FAILURE(vrc))
6176 return setError(VBOX_E_IPRT_ERROR,
6177 tr("Saved screenshot data is not available (%Rrc)"),
6178 vrc);
6179
6180 *aWidth = u32Width;
6181 *aHeight = u32Height;
6182
6183 com::SafeArray<BYTE> bitmap(cbData);
6184 /* Convert pixels to format expected by the API caller. */
6185 if (aBGR)
6186 {
6187 /* [0] B, [1] G, [2] R, [3] A. */
6188 for (unsigned i = 0; i < cbData; i += 4)
6189 {
6190 bitmap[i] = pu8Data[i];
6191 bitmap[i + 1] = pu8Data[i + 1];
6192 bitmap[i + 2] = pu8Data[i + 2];
6193 bitmap[i + 3] = 0xff;
6194 }
6195 }
6196 else
6197 {
6198 /* [0] R, [1] G, [2] B, [3] A. */
6199 for (unsigned i = 0; i < cbData; i += 4)
6200 {
6201 bitmap[i] = pu8Data[i + 2];
6202 bitmap[i + 1] = pu8Data[i + 1];
6203 bitmap[i + 2] = pu8Data[i];
6204 bitmap[i + 3] = 0xff;
6205 }
6206 }
6207 aData.resize(bitmap.size());
6208 for (size_t i = 0; i < bitmap.size(); ++i)
6209 aData[i] = bitmap[i];
6210
6211 freeSavedDisplayScreenshot(pu8Data);
6212
6213 return S_OK;
6214}
6215
6216HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6217{
6218 if (aScreenId != 0)
6219 return E_NOTIMPL;
6220
6221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6222
6223 uint8_t *pu8Data = NULL;
6224 uint32_t cbData = 0;
6225 uint32_t u32Width = 0;
6226 uint32_t u32Height = 0;
6227
6228 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6229
6230 if (RT_FAILURE(vrc))
6231 return setError(VBOX_E_IPRT_ERROR,
6232 tr("Saved screenshot data is not available (%Rrc)"),
6233 vrc);
6234
6235 *aWidth = u32Width;
6236 *aHeight = u32Height;
6237
6238 HRESULT rc = S_OK;
6239 uint8_t *pu8PNG = NULL;
6240 uint32_t cbPNG = 0;
6241 uint32_t cxPNG = 0;
6242 uint32_t cyPNG = 0;
6243
6244 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6245
6246 if (RT_SUCCESS(vrc))
6247 {
6248 com::SafeArray<BYTE> screenData(cbPNG);
6249 screenData.initFrom(pu8PNG, cbPNG);
6250 if (pu8PNG)
6251 RTMemFree(pu8PNG);
6252 aData.resize(screenData.size());
6253 for (size_t i = 0; i < screenData.size(); ++i)
6254 aData[i] = screenData[i];
6255 }
6256 else
6257 {
6258 if (pu8PNG)
6259 RTMemFree(pu8PNG);
6260 return setError(VBOX_E_IPRT_ERROR,
6261 tr("Could not convert screenshot to PNG (%Rrc)"),
6262 vrc);
6263 }
6264
6265 freeSavedDisplayScreenshot(pu8Data);
6266
6267 return rc;
6268}
6269
6270HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6271{
6272 if (aScreenId != 0)
6273 return E_NOTIMPL;
6274
6275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6276
6277 uint8_t *pu8Data = NULL;
6278 uint32_t cbData = 0;
6279 uint32_t u32Width = 0;
6280 uint32_t u32Height = 0;
6281
6282 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6283
6284 if (RT_FAILURE(vrc))
6285 return setError(VBOX_E_IPRT_ERROR,
6286 tr("Saved screenshot data is not available (%Rrc)"),
6287 vrc);
6288
6289 *aSize = cbData;
6290 *aWidth = u32Width;
6291 *aHeight = u32Height;
6292
6293 freeSavedDisplayScreenshot(pu8Data);
6294
6295 return S_OK;
6296}
6297
6298HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6299{
6300 if (aScreenId != 0)
6301 return E_NOTIMPL;
6302
6303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6304
6305 uint8_t *pu8Data = NULL;
6306 uint32_t cbData = 0;
6307 uint32_t u32Width = 0;
6308 uint32_t u32Height = 0;
6309
6310 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6311
6312 if (RT_FAILURE(vrc))
6313 return setError(VBOX_E_IPRT_ERROR,
6314 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6315 vrc);
6316
6317 *aWidth = u32Width;
6318 *aHeight = u32Height;
6319
6320 com::SafeArray<BYTE> png(cbData);
6321 png.initFrom(pu8Data, cbData);
6322 aData.resize(png.size());
6323 for (size_t i = 0; i < png.size(); ++i)
6324 aData[i] = png[i];
6325
6326 freeSavedDisplayScreenshot(pu8Data);
6327
6328 return S_OK;
6329}
6330
6331HRESULT Machine::hotPlugCPU(ULONG aCpu)
6332{
6333 HRESULT rc = S_OK;
6334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6335
6336 if (!mHWData->mCPUHotPlugEnabled)
6337 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6338
6339 if (aCpu >= mHWData->mCPUCount)
6340 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6341
6342 if (mHWData->mCPUAttached[aCpu])
6343 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6344
6345 alock.release();
6346 rc = i_onCPUChange(aCpu, false);
6347 alock.acquire();
6348 if (FAILED(rc)) return rc;
6349
6350 i_setModified(IsModified_MachineData);
6351 mHWData.backup();
6352 mHWData->mCPUAttached[aCpu] = true;
6353
6354 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6355 if (Global::IsOnline(mData->mMachineState))
6356 i_saveSettings(NULL);
6357
6358 return S_OK;
6359}
6360
6361HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6362{
6363 HRESULT rc = S_OK;
6364
6365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6366
6367 if (!mHWData->mCPUHotPlugEnabled)
6368 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6369
6370 if (aCpu >= SchemaDefs::MaxCPUCount)
6371 return setError(E_INVALIDARG,
6372 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6373 SchemaDefs::MaxCPUCount);
6374
6375 if (!mHWData->mCPUAttached[aCpu])
6376 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6377
6378 /* CPU 0 can't be detached */
6379 if (aCpu == 0)
6380 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6381
6382 alock.release();
6383 rc = i_onCPUChange(aCpu, true);
6384 alock.acquire();
6385 if (FAILED(rc)) return rc;
6386
6387 i_setModified(IsModified_MachineData);
6388 mHWData.backup();
6389 mHWData->mCPUAttached[aCpu] = false;
6390
6391 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6392 if (Global::IsOnline(mData->mMachineState))
6393 i_saveSettings(NULL);
6394
6395 return S_OK;
6396}
6397
6398HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6399{
6400 *aAttached = false;
6401
6402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6403
6404 /* If hotplug is enabled the CPU is always enabled. */
6405 if (!mHWData->mCPUHotPlugEnabled)
6406 {
6407 if (aCpu < mHWData->mCPUCount)
6408 *aAttached = true;
6409 }
6410 else
6411 {
6412 if (aCpu < SchemaDefs::MaxCPUCount)
6413 *aAttached = mHWData->mCPUAttached[aCpu];
6414 }
6415
6416 return S_OK;
6417}
6418
6419HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6420{
6421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6422
6423 Utf8Str log = i_queryLogFilename(aIdx);
6424 if (!RTFileExists(log.c_str()))
6425 log.setNull();
6426 aFilename = log;
6427
6428 return S_OK;
6429}
6430
6431HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6432{
6433 if (aSize < 0)
6434 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6435
6436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 HRESULT rc = S_OK;
6439 Utf8Str log = i_queryLogFilename(aIdx);
6440
6441 /* do not unnecessarily hold the lock while doing something which does
6442 * not need the lock and potentially takes a long time. */
6443 alock.release();
6444
6445 /* Limit the chunk size to 32K for now, as that gives better performance
6446 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6447 * One byte expands to approx. 25 bytes of breathtaking XML. */
6448 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6449 com::SafeArray<BYTE> logData(cbData);
6450
6451 RTFILE LogFile;
6452 int vrc = RTFileOpen(&LogFile, log.c_str(),
6453 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6454 if (RT_SUCCESS(vrc))
6455 {
6456 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6457 if (RT_SUCCESS(vrc))
6458 logData.resize(cbData);
6459 else
6460 rc = setError(VBOX_E_IPRT_ERROR,
6461 tr("Could not read log file '%s' (%Rrc)"),
6462 log.c_str(), vrc);
6463 RTFileClose(LogFile);
6464 }
6465 else
6466 rc = setError(VBOX_E_IPRT_ERROR,
6467 tr("Could not open log file '%s' (%Rrc)"),
6468 log.c_str(), vrc);
6469
6470 if (FAILED(rc))
6471 logData.resize(0);
6472
6473 aData.resize(logData.size());
6474 for (size_t i = 0; i < logData.size(); ++i)
6475 aData[i] = logData[i];
6476
6477 return rc;
6478}
6479
6480
6481/**
6482 * Currently this method doesn't attach device to the running VM,
6483 * just makes sure it's plugged on next VM start.
6484 */
6485HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6486{
6487 // lock scope
6488 {
6489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6490
6491 HRESULT rc = i_checkStateDependency(MutableStateDep);
6492 if (FAILED(rc)) return rc;
6493
6494 ChipsetType_T aChipset = ChipsetType_PIIX3;
6495 COMGETTER(ChipsetType)(&aChipset);
6496
6497 if (aChipset != ChipsetType_ICH9)
6498 {
6499 return setError(E_INVALIDARG,
6500 tr("Host PCI attachment only supported with ICH9 chipset"));
6501 }
6502
6503 // check if device with this host PCI address already attached
6504 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6505 it != mHWData->mPCIDeviceAssignments.end();
6506 ++it)
6507 {
6508 LONG iHostAddress = -1;
6509 ComPtr<PCIDeviceAttachment> pAttach;
6510 pAttach = *it;
6511 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6512 if (iHostAddress == aHostAddress)
6513 return setError(E_INVALIDARG,
6514 tr("Device with host PCI address already attached to this VM"));
6515 }
6516
6517 ComObjPtr<PCIDeviceAttachment> pda;
6518 char name[32];
6519
6520 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6521 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6522 Bstr bname(name);
6523 pda.createObject();
6524 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6525 i_setModified(IsModified_MachineData);
6526 mHWData.backup();
6527 mHWData->mPCIDeviceAssignments.push_back(pda);
6528 }
6529
6530 return S_OK;
6531}
6532
6533/**
6534 * Currently this method doesn't detach device from the running VM,
6535 * just makes sure it's not plugged on next VM start.
6536 */
6537HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6538{
6539 ComObjPtr<PCIDeviceAttachment> pAttach;
6540 bool fRemoved = false;
6541 HRESULT rc;
6542
6543 // lock scope
6544 {
6545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6546
6547 rc = i_checkStateDependency(MutableStateDep);
6548 if (FAILED(rc)) return rc;
6549
6550 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6551 it != mHWData->mPCIDeviceAssignments.end();
6552 ++it)
6553 {
6554 LONG iHostAddress = -1;
6555 pAttach = *it;
6556 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6557 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6558 {
6559 i_setModified(IsModified_MachineData);
6560 mHWData.backup();
6561 mHWData->mPCIDeviceAssignments.remove(pAttach);
6562 fRemoved = true;
6563 break;
6564 }
6565 }
6566 }
6567
6568
6569 /* Fire event outside of the lock */
6570 if (fRemoved)
6571 {
6572 Assert(!pAttach.isNull());
6573 ComPtr<IEventSource> es;
6574 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6575 Assert(SUCCEEDED(rc));
6576 Bstr mid;
6577 rc = this->COMGETTER(Id)(mid.asOutParam());
6578 Assert(SUCCEEDED(rc));
6579 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6580 }
6581
6582 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6583 tr("No host PCI device %08x attached"),
6584 aHostAddress
6585 );
6586}
6587
6588HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6589{
6590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6591
6592 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6593
6594 size_t i = 0;
6595 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6596 it != mHWData->mPCIDeviceAssignments.end();
6597 ++i, ++it)
6598 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6599
6600 return S_OK;
6601}
6602
6603HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6604{
6605 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6606
6607 return S_OK;
6608}
6609
6610HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6611{
6612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6613
6614 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6615
6616 return S_OK;
6617}
6618
6619HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6620{
6621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6622 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6623 if (SUCCEEDED(hrc))
6624 {
6625 hrc = mHWData.backupEx();
6626 if (SUCCEEDED(hrc))
6627 {
6628 i_setModified(IsModified_MachineData);
6629 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6630 }
6631 }
6632 return hrc;
6633}
6634
6635HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6636{
6637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6638 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6639 return S_OK;
6640}
6641
6642HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6643{
6644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6645 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6646 if (SUCCEEDED(hrc))
6647 {
6648 hrc = mHWData.backupEx();
6649 if (SUCCEEDED(hrc))
6650 {
6651 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6652 if (SUCCEEDED(hrc))
6653 i_setModified(IsModified_MachineData);
6654 }
6655 }
6656 return hrc;
6657}
6658
6659HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6660{
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662
6663 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6664
6665 return S_OK;
6666}
6667
6668HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6669{
6670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6671 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6672 if (SUCCEEDED(hrc))
6673 {
6674 hrc = mHWData.backupEx();
6675 if (SUCCEEDED(hrc))
6676 {
6677 i_setModified(IsModified_MachineData);
6678 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6679 }
6680 }
6681 return hrc;
6682}
6683
6684HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6685{
6686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6687
6688 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6689
6690 return S_OK;
6691}
6692
6693HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6694{
6695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6698 if ( SUCCEEDED(hrc)
6699 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6700 {
6701 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6702 int vrc;
6703
6704 if (aAutostartEnabled)
6705 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6706 else
6707 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6708
6709 if (RT_SUCCESS(vrc))
6710 {
6711 hrc = mHWData.backupEx();
6712 if (SUCCEEDED(hrc))
6713 {
6714 i_setModified(IsModified_MachineData);
6715 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6716 }
6717 }
6718 else if (vrc == VERR_NOT_SUPPORTED)
6719 hrc = setError(VBOX_E_NOT_SUPPORTED,
6720 tr("The VM autostart feature is not supported on this platform"));
6721 else if (vrc == VERR_PATH_NOT_FOUND)
6722 hrc = setError(E_FAIL,
6723 tr("The path to the autostart database is not set"));
6724 else
6725 hrc = setError(E_UNEXPECTED,
6726 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6727 aAutostartEnabled ? "Adding" : "Removing",
6728 mUserData->s.strName.c_str(), vrc);
6729 }
6730 return hrc;
6731}
6732
6733HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6734{
6735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6736
6737 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6738
6739 return S_OK;
6740}
6741
6742HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6743{
6744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6745 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6746 if (SUCCEEDED(hrc))
6747 {
6748 hrc = mHWData.backupEx();
6749 if (SUCCEEDED(hrc))
6750 {
6751 i_setModified(IsModified_MachineData);
6752 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6753 }
6754 }
6755 return hrc;
6756}
6757
6758HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6759{
6760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6761
6762 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6763
6764 return S_OK;
6765}
6766
6767HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6768{
6769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6770 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6771 if ( SUCCEEDED(hrc)
6772 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6773 {
6774 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6775 int vrc;
6776
6777 if (aAutostopType != AutostopType_Disabled)
6778 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6779 else
6780 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6781
6782 if (RT_SUCCESS(vrc))
6783 {
6784 hrc = mHWData.backupEx();
6785 if (SUCCEEDED(hrc))
6786 {
6787 i_setModified(IsModified_MachineData);
6788 mHWData->mAutostart.enmAutostopType = aAutostopType;
6789 }
6790 }
6791 else if (vrc == VERR_NOT_SUPPORTED)
6792 hrc = setError(VBOX_E_NOT_SUPPORTED,
6793 tr("The VM autostop feature is not supported on this platform"));
6794 else if (vrc == VERR_PATH_NOT_FOUND)
6795 hrc = setError(E_FAIL,
6796 tr("The path to the autostart database is not set"));
6797 else
6798 hrc = setError(E_UNEXPECTED,
6799 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6800 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6801 mUserData->s.strName.c_str(), vrc);
6802 }
6803 return hrc;
6804}
6805
6806HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6807{
6808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 aDefaultFrontend = mHWData->mDefaultFrontend;
6811
6812 return S_OK;
6813}
6814
6815HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6816{
6817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6818 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6819 if (SUCCEEDED(hrc))
6820 {
6821 hrc = mHWData.backupEx();
6822 if (SUCCEEDED(hrc))
6823 {
6824 i_setModified(IsModified_MachineData);
6825 mHWData->mDefaultFrontend = aDefaultFrontend;
6826 }
6827 }
6828 return hrc;
6829}
6830
6831HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6832{
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6835 aIcon.resize(mUserData->mIcon.size());
6836 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6837 aIcon.resize(icon.size());
6838 for (size_t i = 0; i < icon.size(); ++i)
6839 aIcon[i] = icon[i];
6840 return S_OK;
6841}
6842
6843HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6844{
6845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6846 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6847 if (SUCCEEDED(hrc))
6848 {
6849 i_setModified(IsModified_MachineData);
6850 mUserData.backup();
6851 com::SafeArray<BYTE> icon(aIcon);
6852 mUserData->mIcon.resize(aIcon.size());
6853 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
6854 }
6855 return hrc;
6856}
6857
6858HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6859{
6860
6861#ifdef VBOX_WITH_USB
6862 *aUSBProxyAvailable = true;
6863#else
6864 *aUSBProxyAvailable = false;
6865#endif
6866 return S_OK;
6867}
6868
6869HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6870 ComPtr<IProgress> &aProgress)
6871{
6872 ComObjPtr<Progress> pP;
6873 Progress *ppP = pP;
6874 IProgress *iP = static_cast<IProgress *>(ppP);
6875 IProgress **pProgress = &iP;
6876
6877 IMachine *pTarget = aTarget;
6878
6879 /* Convert the options. */
6880 RTCList<CloneOptions_T> optList;
6881 if (aOptions.size())
6882 for (size_t i = 0; i < aOptions.size(); ++i)
6883 optList.append(aOptions[i]);
6884
6885 if (optList.contains(CloneOptions_Link))
6886 {
6887 if (!i_isSnapshotMachine())
6888 return setError(E_INVALIDARG,
6889 tr("Linked clone can only be created from a snapshot"));
6890 if (aMode != CloneMode_MachineState)
6891 return setError(E_INVALIDARG,
6892 tr("Linked clone can only be created for a single machine state"));
6893 }
6894 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6895
6896 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6897
6898 HRESULT rc = pWorker->start(pProgress);
6899
6900 pP = static_cast<Progress *>(*pProgress);
6901 pP.queryInterfaceTo(aProgress.asOutParam());
6902
6903 return rc;
6904
6905}
6906
6907// public methods for internal purposes
6908/////////////////////////////////////////////////////////////////////////////
6909
6910/**
6911 * Adds the given IsModified_* flag to the dirty flags of the machine.
6912 * This must be called either during i_loadSettings or under the machine write lock.
6913 * @param fl
6914 */
6915void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6916{
6917 mData->flModifications |= fl;
6918 if (fAllowStateModification && i_isStateModificationAllowed())
6919 mData->mCurrentStateModified = true;
6920}
6921
6922/**
6923 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6924 * care of the write locking.
6925 *
6926 * @param fModifications The flag to add.
6927 */
6928void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6929{
6930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6931 i_setModified(fModification, fAllowStateModification);
6932}
6933
6934/**
6935 * Saves the registry entry of this machine to the given configuration node.
6936 *
6937 * @param aEntryNode Node to save the registry entry to.
6938 *
6939 * @note locks this object for reading.
6940 */
6941HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6942{
6943 AutoLimitedCaller autoCaller(this);
6944 AssertComRCReturnRC(autoCaller.rc());
6945
6946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6947
6948 data.uuid = mData->mUuid;
6949 data.strSettingsFile = mData->m_strConfigFile;
6950
6951 return S_OK;
6952}
6953
6954/**
6955 * Calculates the absolute path of the given path taking the directory of the
6956 * machine settings file as the current directory.
6957 *
6958 * @param aPath Path to calculate the absolute path for.
6959 * @param aResult Where to put the result (used only on success, can be the
6960 * same Utf8Str instance as passed in @a aPath).
6961 * @return IPRT result.
6962 *
6963 * @note Locks this object for reading.
6964 */
6965int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6966{
6967 AutoCaller autoCaller(this);
6968 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6969
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971
6972 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6973
6974 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6975
6976 strSettingsDir.stripFilename();
6977 char folder[RTPATH_MAX];
6978 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6979 if (RT_SUCCESS(vrc))
6980 aResult = folder;
6981
6982 return vrc;
6983}
6984
6985/**
6986 * Copies strSource to strTarget, making it relative to the machine folder
6987 * if it is a subdirectory thereof, or simply copying it otherwise.
6988 *
6989 * @param strSource Path to evaluate and copy.
6990 * @param strTarget Buffer to receive target path.
6991 *
6992 * @note Locks this object for reading.
6993 */
6994void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6995 Utf8Str &strTarget)
6996{
6997 AutoCaller autoCaller(this);
6998 AssertComRCReturn(autoCaller.rc(), (void)0);
6999
7000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7001
7002 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7003 // use strTarget as a temporary buffer to hold the machine settings dir
7004 strTarget = mData->m_strConfigFileFull;
7005 strTarget.stripFilename();
7006 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7007 {
7008 // is relative: then append what's left
7009 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7010 // for empty paths (only possible for subdirs) use "." to avoid
7011 // triggering default settings for not present config attributes.
7012 if (strTarget.isEmpty())
7013 strTarget = ".";
7014 }
7015 else
7016 // is not relative: then overwrite
7017 strTarget = strSource;
7018}
7019
7020/**
7021 * Returns the full path to the machine's log folder in the
7022 * \a aLogFolder argument.
7023 */
7024void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7025{
7026 AutoCaller autoCaller(this);
7027 AssertComRCReturnVoid(autoCaller.rc());
7028
7029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7030
7031 char szTmp[RTPATH_MAX];
7032 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7033 if (RT_SUCCESS(vrc))
7034 {
7035 if (szTmp[0] && !mUserData.isNull())
7036 {
7037 char szTmp2[RTPATH_MAX];
7038 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7039 if (RT_SUCCESS(vrc))
7040 aLogFolder = BstrFmt("%s%c%s",
7041 szTmp2,
7042 RTPATH_DELIMITER,
7043 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7044 }
7045 else
7046 vrc = VERR_PATH_IS_RELATIVE;
7047 }
7048
7049 if (RT_FAILURE(vrc))
7050 {
7051 // fallback if VBOX_USER_LOGHOME is not set or invalid
7052 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7053 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7054 aLogFolder.append(RTPATH_DELIMITER);
7055 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7056 }
7057}
7058
7059/**
7060 * Returns the full path to the machine's log file for an given index.
7061 */
7062Utf8Str Machine::i_queryLogFilename(ULONG idx)
7063{
7064 Utf8Str logFolder;
7065 getLogFolder(logFolder);
7066 Assert(logFolder.length());
7067 Utf8Str log;
7068 if (idx == 0)
7069 log = Utf8StrFmt("%s%cVBox.log",
7070 logFolder.c_str(), RTPATH_DELIMITER);
7071 else
7072 log = Utf8StrFmt("%s%cVBox.log.%d",
7073 logFolder.c_str(), RTPATH_DELIMITER, idx);
7074 return log;
7075}
7076
7077/**
7078 * Composes a unique saved state filename based on the current system time. The filename is
7079 * granular to the second so this will work so long as no more than one snapshot is taken on
7080 * a machine per second.
7081 *
7082 * Before version 4.1, we used this formula for saved state files:
7083 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7084 * which no longer works because saved state files can now be shared between the saved state of the
7085 * "saved" machine and an online snapshot, and the following would cause problems:
7086 * 1) save machine
7087 * 2) create online snapshot from that machine state --> reusing saved state file
7088 * 3) save machine again --> filename would be reused, breaking the online snapshot
7089 *
7090 * So instead we now use a timestamp.
7091 *
7092 * @param str
7093 */
7094
7095void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7096{
7097 AutoCaller autoCaller(this);
7098 AssertComRCReturnVoid(autoCaller.rc());
7099
7100 {
7101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7102 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7103 }
7104
7105 RTTIMESPEC ts;
7106 RTTimeNow(&ts);
7107 RTTIME time;
7108 RTTimeExplode(&time, &ts);
7109
7110 strStateFilePath += RTPATH_DELIMITER;
7111 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7112 time.i32Year, time.u8Month, time.u8MonthDay,
7113 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7114}
7115
7116/**
7117 * Returns the full path to the default video capture file.
7118 */
7119void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7120{
7121 AutoCaller autoCaller(this);
7122 AssertComRCReturnVoid(autoCaller.rc());
7123
7124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7125
7126 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7127 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7128 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7129}
7130
7131/**
7132 * Returns whether at least one USB controller is present for the VM.
7133 */
7134bool Machine::i_isUSBControllerPresent()
7135{
7136 AutoCaller autoCaller(this);
7137 AssertComRCReturn(autoCaller.rc(), false);
7138
7139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7140
7141 return (mUSBControllers->size() > 0);
7142}
7143
7144/**
7145 * @note Locks this object for writing, calls the client process
7146 * (inside the lock).
7147 */
7148HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7149 const Utf8Str &strFrontend,
7150 const Utf8Str &strEnvironment,
7151 ProgressProxy *aProgress)
7152{
7153 LogFlowThisFuncEnter();
7154
7155 AssertReturn(aControl, E_FAIL);
7156 AssertReturn(aProgress, E_FAIL);
7157 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7158
7159 AutoCaller autoCaller(this);
7160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7161
7162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7163
7164 if (!mData->mRegistered)
7165 return setError(E_UNEXPECTED,
7166 tr("The machine '%s' is not registered"),
7167 mUserData->s.strName.c_str());
7168
7169 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7170
7171 if ( mData->mSession.mState == SessionState_Locked
7172 || mData->mSession.mState == SessionState_Spawning
7173 || mData->mSession.mState == SessionState_Unlocking)
7174 return setError(VBOX_E_INVALID_OBJECT_STATE,
7175 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7176 mUserData->s.strName.c_str());
7177
7178 /* may not be busy */
7179 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7180
7181 /* get the path to the executable */
7182 char szPath[RTPATH_MAX];
7183 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7184 size_t sz = strlen(szPath);
7185 szPath[sz++] = RTPATH_DELIMITER;
7186 szPath[sz] = 0;
7187 char *cmd = szPath + sz;
7188 sz = sizeof(szPath) - sz;
7189
7190 int vrc = VINF_SUCCESS;
7191 RTPROCESS pid = NIL_RTPROCESS;
7192
7193 RTENV env = RTENV_DEFAULT;
7194
7195 if (!strEnvironment.isEmpty())
7196 {
7197 char *newEnvStr = NULL;
7198
7199 do
7200 {
7201 /* clone the current environment */
7202 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7203 AssertRCBreakStmt(vrc2, vrc = vrc2);
7204
7205 newEnvStr = RTStrDup(strEnvironment.c_str());
7206 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7207
7208 /* put new variables to the environment
7209 * (ignore empty variable names here since RTEnv API
7210 * intentionally doesn't do that) */
7211 char *var = newEnvStr;
7212 for (char *p = newEnvStr; *p; ++p)
7213 {
7214 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7215 {
7216 *p = '\0';
7217 if (*var)
7218 {
7219 char *val = strchr(var, '=');
7220 if (val)
7221 {
7222 *val++ = '\0';
7223 vrc2 = RTEnvSetEx(env, var, val);
7224 }
7225 else
7226 vrc2 = RTEnvUnsetEx(env, var);
7227 if (RT_FAILURE(vrc2))
7228 break;
7229 }
7230 var = p + 1;
7231 }
7232 }
7233 if (RT_SUCCESS(vrc2) && *var)
7234 vrc2 = RTEnvPutEx(env, var);
7235
7236 AssertRCBreakStmt(vrc2, vrc = vrc2);
7237 }
7238 while (0);
7239
7240 if (newEnvStr != NULL)
7241 RTStrFree(newEnvStr);
7242 }
7243
7244#ifdef VBOX_WITH_QTGUI
7245 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7246 {
7247# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7248 /* Modify the base path so that we don't need to use ".." below. */
7249 RTPathStripTrailingSlash(szPath);
7250 RTPathStripFilename(szPath);
7251 sz = strlen(szPath);
7252 cmd = szPath + sz;
7253 sz = sizeof(szPath) - sz;
7254
7255#define OSX_APP_NAME "VirtualBoxVM"
7256#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7257
7258 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7259 if ( strAppOverride.contains(".")
7260 || strAppOverride.contains("/")
7261 || strAppOverride.contains("\\")
7262 || strAppOverride.contains(":"))
7263 strAppOverride.setNull();
7264 Utf8Str strAppPath;
7265 if (!strAppOverride.isEmpty())
7266 {
7267 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7268 Utf8Str strFullPath(szPath);
7269 strFullPath.append(strAppPath);
7270 /* there is a race, but people using this deserve the failure */
7271 if (!RTFileExists(strFullPath.c_str()))
7272 strAppOverride.setNull();
7273 }
7274 if (strAppOverride.isEmpty())
7275 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7276 const char *VirtualBox_exe = strAppPath.c_str();
7277 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
7278# else
7279 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7280 Assert(sz >= sizeof(VirtualBox_exe));
7281# endif
7282 strcpy(cmd, VirtualBox_exe);
7283
7284 Utf8Str idStr = mData->mUuid.toString();
7285 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7286 "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7287 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7288 }
7289#else /* !VBOX_WITH_QTGUI */
7290 if (0)
7291 ;
7292#endif /* VBOX_WITH_QTGUI */
7293
7294 else
7295
7296#ifdef VBOX_WITH_VBOXSDL
7297 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7298 {
7299 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7300 Assert(sz >= sizeof(VBoxSDL_exe));
7301 strcpy(cmd, VBoxSDL_exe);
7302
7303 Utf8Str idStr = mData->mUuid.toString();
7304 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7305 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7306 }
7307#else /* !VBOX_WITH_VBOXSDL */
7308 if (0)
7309 ;
7310#endif /* !VBOX_WITH_VBOXSDL */
7311
7312 else
7313
7314#ifdef VBOX_WITH_HEADLESS
7315 if ( strFrontend == "headless"
7316 || strFrontend == "capture"
7317 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7318 )
7319 {
7320 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7321 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7322 * and a VM works even if the server has not been installed.
7323 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7324 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7325 * differently in 4.0 and 3.x.
7326 */
7327 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7328 Assert(sz >= sizeof(VBoxHeadless_exe));
7329 strcpy(cmd, VBoxHeadless_exe);
7330
7331 Utf8Str idStr = mData->mUuid.toString();
7332 /* Leave space for "--capture" arg. */
7333 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7334 "--startvm", idStr.c_str(),
7335 "--vrde", "config",
7336 0, /* For "--capture". */
7337 0 };
7338 if (strFrontend == "capture")
7339 {
7340 unsigned pos = RT_ELEMENTS(args) - 2;
7341 args[pos] = "--capture";
7342 }
7343 vrc = RTProcCreate(szPath, args, env,
7344#ifdef RT_OS_WINDOWS
7345 RTPROC_FLAGS_NO_WINDOW
7346#else
7347 0
7348#endif
7349 , &pid);
7350 }
7351#else /* !VBOX_WITH_HEADLESS */
7352 if (0)
7353 ;
7354#endif /* !VBOX_WITH_HEADLESS */
7355 else
7356 {
7357 RTEnvDestroy(env);
7358 return setError(E_INVALIDARG,
7359 tr("Invalid frontend name: '%s'"),
7360 strFrontend.c_str());
7361 }
7362
7363 RTEnvDestroy(env);
7364
7365 if (RT_FAILURE(vrc))
7366 return setError(VBOX_E_IPRT_ERROR,
7367 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7368 mUserData->s.strName.c_str(), vrc);
7369
7370 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7371
7372 /*
7373 * Note that we don't release the lock here before calling the client,
7374 * because it doesn't need to call us back if called with a NULL argument.
7375 * Releasing the lock here is dangerous because we didn't prepare the
7376 * launch data yet, but the client we've just started may happen to be
7377 * too fast and call LockMachine() that will fail (because of PID, etc.),
7378 * so that the Machine will never get out of the Spawning session state.
7379 */
7380
7381 /* inform the session that it will be a remote one */
7382 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7383#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7384 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7385#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7386 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7387#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7388 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7389
7390 if (FAILED(rc))
7391 {
7392 /* restore the session state */
7393 mData->mSession.mState = SessionState_Unlocked;
7394 alock.release();
7395 mParent->i_addProcessToReap(pid);
7396 /* The failure may occur w/o any error info (from RPC), so provide one */
7397 return setError(VBOX_E_VM_ERROR,
7398 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7399 }
7400
7401 /* attach launch data to the machine */
7402 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7403 mData->mSession.mRemoteControls.push_back(aControl);
7404 mData->mSession.mProgress = aProgress;
7405 mData->mSession.mPID = pid;
7406 mData->mSession.mState = SessionState_Spawning;
7407 mData->mSession.mType = strFrontend;
7408
7409 alock.release();
7410 mParent->i_addProcessToReap(pid);
7411
7412 LogFlowThisFuncLeave();
7413 return S_OK;
7414}
7415
7416/**
7417 * Returns @c true if the given session machine instance has an open direct
7418 * session (and optionally also for direct sessions which are closing) and
7419 * returns the session control machine instance if so.
7420 *
7421 * Note that when the method returns @c false, the arguments remain unchanged.
7422 *
7423 * @param aMachine Session machine object.
7424 * @param aControl Direct session control object (optional).
7425 * @param aAllowClosing If true then additionally a session which is currently
7426 * being closed will also be allowed.
7427 *
7428 * @note locks this object for reading.
7429 */
7430bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7431 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7432 bool aAllowClosing /*= false*/)
7433{
7434 AutoLimitedCaller autoCaller(this);
7435 AssertComRCReturn(autoCaller.rc(), false);
7436
7437 /* just return false for inaccessible machines */
7438 if (getObjectState().getState() != ObjectState::Ready)
7439 return false;
7440
7441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7442
7443 if ( mData->mSession.mState == SessionState_Locked
7444 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7445 )
7446 {
7447 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7448
7449 aMachine = mData->mSession.mMachine;
7450
7451 if (aControl != NULL)
7452 *aControl = mData->mSession.mDirectControl;
7453
7454 return true;
7455 }
7456
7457 return false;
7458}
7459
7460/**
7461 * Returns @c true if the given machine has an spawning direct session.
7462 *
7463 * @note locks this object for reading.
7464 */
7465bool Machine::i_isSessionSpawning()
7466{
7467 AutoLimitedCaller autoCaller(this);
7468 AssertComRCReturn(autoCaller.rc(), false);
7469
7470 /* just return false for inaccessible machines */
7471 if (getObjectState().getState() != ObjectState::Ready)
7472 return false;
7473
7474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7475
7476 if (mData->mSession.mState == SessionState_Spawning)
7477 return true;
7478
7479 return false;
7480}
7481
7482/**
7483 * Called from the client watcher thread to check for unexpected client process
7484 * death during Session_Spawning state (e.g. before it successfully opened a
7485 * direct session).
7486 *
7487 * On Win32 and on OS/2, this method is called only when we've got the
7488 * direct client's process termination notification, so it always returns @c
7489 * true.
7490 *
7491 * On other platforms, this method returns @c true if the client process is
7492 * terminated and @c false if it's still alive.
7493 *
7494 * @note Locks this object for writing.
7495 */
7496bool Machine::i_checkForSpawnFailure()
7497{
7498 AutoCaller autoCaller(this);
7499 if (!autoCaller.isOk())
7500 {
7501 /* nothing to do */
7502 LogFlowThisFunc(("Already uninitialized!\n"));
7503 return true;
7504 }
7505
7506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7507
7508 if (mData->mSession.mState != SessionState_Spawning)
7509 {
7510 /* nothing to do */
7511 LogFlowThisFunc(("Not spawning any more!\n"));
7512 return true;
7513 }
7514
7515 HRESULT rc = S_OK;
7516
7517 /* PID not yet initialized, skip check. */
7518 if (mData->mSession.mPID == NIL_RTPROCESS)
7519 return false;
7520
7521 RTPROCSTATUS status;
7522 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7523
7524 if (vrc != VERR_PROCESS_RUNNING)
7525 {
7526 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7527 rc = setError(E_FAIL,
7528 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7529 i_getName().c_str(), status.iStatus);
7530 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7531 rc = setError(E_FAIL,
7532 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7533 i_getName().c_str(), status.iStatus);
7534 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7535 rc = setError(E_FAIL,
7536 tr("The virtual machine '%s' has terminated abnormally"),
7537 i_getName().c_str(), status.iStatus);
7538 else
7539 rc = setError(E_FAIL,
7540 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7541 i_getName().c_str(), vrc);
7542 }
7543
7544 if (FAILED(rc))
7545 {
7546 /* Close the remote session, remove the remote control from the list
7547 * and reset session state to Closed (@note keep the code in sync with
7548 * the relevant part in LockMachine()). */
7549
7550 Assert(mData->mSession.mRemoteControls.size() == 1);
7551 if (mData->mSession.mRemoteControls.size() == 1)
7552 {
7553 ErrorInfoKeeper eik;
7554 mData->mSession.mRemoteControls.front()->Uninitialize();
7555 }
7556
7557 mData->mSession.mRemoteControls.clear();
7558 mData->mSession.mState = SessionState_Unlocked;
7559
7560 /* finalize the progress after setting the state */
7561 if (!mData->mSession.mProgress.isNull())
7562 {
7563 mData->mSession.mProgress->notifyComplete(rc);
7564 mData->mSession.mProgress.setNull();
7565 }
7566
7567 mData->mSession.mPID = NIL_RTPROCESS;
7568
7569 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7570 return true;
7571 }
7572
7573 return false;
7574}
7575
7576/**
7577 * Checks whether the machine can be registered. If so, commits and saves
7578 * all settings.
7579 *
7580 * @note Must be called from mParent's write lock. Locks this object and
7581 * children for writing.
7582 */
7583HRESULT Machine::i_prepareRegister()
7584{
7585 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7586
7587 AutoLimitedCaller autoCaller(this);
7588 AssertComRCReturnRC(autoCaller.rc());
7589
7590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7591
7592 /* wait for state dependents to drop to zero */
7593 i_ensureNoStateDependencies();
7594
7595 if (!mData->mAccessible)
7596 return setError(VBOX_E_INVALID_OBJECT_STATE,
7597 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7598 mUserData->s.strName.c_str(),
7599 mData->mUuid.toString().c_str());
7600
7601 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7602
7603 if (mData->mRegistered)
7604 return setError(VBOX_E_INVALID_OBJECT_STATE,
7605 tr("The machine '%s' with UUID {%s} is already registered"),
7606 mUserData->s.strName.c_str(),
7607 mData->mUuid.toString().c_str());
7608
7609 HRESULT rc = S_OK;
7610
7611 // Ensure the settings are saved. If we are going to be registered and
7612 // no config file exists yet, create it by calling i_saveSettings() too.
7613 if ( (mData->flModifications)
7614 || (!mData->pMachineConfigFile->fileExists())
7615 )
7616 {
7617 rc = i_saveSettings(NULL);
7618 // no need to check whether VirtualBox.xml needs saving too since
7619 // we can't have a machine XML file rename pending
7620 if (FAILED(rc)) return rc;
7621 }
7622
7623 /* more config checking goes here */
7624
7625 if (SUCCEEDED(rc))
7626 {
7627 /* we may have had implicit modifications we want to fix on success */
7628 i_commit();
7629
7630 mData->mRegistered = true;
7631 }
7632 else
7633 {
7634 /* we may have had implicit modifications we want to cancel on failure*/
7635 i_rollback(false /* aNotify */);
7636 }
7637
7638 return rc;
7639}
7640
7641/**
7642 * Increases the number of objects dependent on the machine state or on the
7643 * registered state. Guarantees that these two states will not change at least
7644 * until #releaseStateDependency() is called.
7645 *
7646 * Depending on the @a aDepType value, additional state checks may be made.
7647 * These checks will set extended error info on failure. See
7648 * #checkStateDependency() for more info.
7649 *
7650 * If this method returns a failure, the dependency is not added and the caller
7651 * is not allowed to rely on any particular machine state or registration state
7652 * value and may return the failed result code to the upper level.
7653 *
7654 * @param aDepType Dependency type to add.
7655 * @param aState Current machine state (NULL if not interested).
7656 * @param aRegistered Current registered state (NULL if not interested).
7657 *
7658 * @note Locks this object for writing.
7659 */
7660HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7661 MachineState_T *aState /* = NULL */,
7662 BOOL *aRegistered /* = NULL */)
7663{
7664 AutoCaller autoCaller(this);
7665 AssertComRCReturnRC(autoCaller.rc());
7666
7667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7668
7669 HRESULT rc = i_checkStateDependency(aDepType);
7670 if (FAILED(rc)) return rc;
7671
7672 {
7673 if (mData->mMachineStateChangePending != 0)
7674 {
7675 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7676 * drop to zero so don't add more. It may make sense to wait a bit
7677 * and retry before reporting an error (since the pending state
7678 * transition should be really quick) but let's just assert for
7679 * now to see if it ever happens on practice. */
7680
7681 AssertFailed();
7682
7683 return setError(E_ACCESSDENIED,
7684 tr("Machine state change is in progress. Please retry the operation later."));
7685 }
7686
7687 ++mData->mMachineStateDeps;
7688 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7689 }
7690
7691 if (aState)
7692 *aState = mData->mMachineState;
7693 if (aRegistered)
7694 *aRegistered = mData->mRegistered;
7695
7696 return S_OK;
7697}
7698
7699/**
7700 * Decreases the number of objects dependent on the machine state.
7701 * Must always complete the #addStateDependency() call after the state
7702 * dependency is no more necessary.
7703 */
7704void Machine::i_releaseStateDependency()
7705{
7706 AutoCaller autoCaller(this);
7707 AssertComRCReturnVoid(autoCaller.rc());
7708
7709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7710
7711 /* releaseStateDependency() w/o addStateDependency()? */
7712 AssertReturnVoid(mData->mMachineStateDeps != 0);
7713 -- mData->mMachineStateDeps;
7714
7715 if (mData->mMachineStateDeps == 0)
7716 {
7717 /* inform i_ensureNoStateDependencies() that there are no more deps */
7718 if (mData->mMachineStateChangePending != 0)
7719 {
7720 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7721 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7722 }
7723 }
7724}
7725
7726Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7727{
7728 /* start with nothing found */
7729 Utf8Str strResult("");
7730
7731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7732
7733 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7734 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7735 // found:
7736 strResult = it->second; // source is a Utf8Str
7737
7738 return strResult;
7739}
7740
7741// protected methods
7742/////////////////////////////////////////////////////////////////////////////
7743
7744/**
7745 * Performs machine state checks based on the @a aDepType value. If a check
7746 * fails, this method will set extended error info, otherwise it will return
7747 * S_OK. It is supposed, that on failure, the caller will immediately return
7748 * the return value of this method to the upper level.
7749 *
7750 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7751 *
7752 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7753 * current state of this machine object allows to change settings of the
7754 * machine (i.e. the machine is not registered, or registered but not running
7755 * and not saved). It is useful to call this method from Machine setters
7756 * before performing any change.
7757 *
7758 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7759 * as for MutableStateDep except that if the machine is saved, S_OK is also
7760 * returned. This is useful in setters which allow changing machine
7761 * properties when it is in the saved state.
7762 *
7763 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7764 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7765 * Aborted).
7766 *
7767 * @param aDepType Dependency type to check.
7768 *
7769 * @note Non Machine based classes should use #addStateDependency() and
7770 * #releaseStateDependency() methods or the smart AutoStateDependency
7771 * template.
7772 *
7773 * @note This method must be called from under this object's read or write
7774 * lock.
7775 */
7776HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7777{
7778 switch (aDepType)
7779 {
7780 case AnyStateDep:
7781 {
7782 break;
7783 }
7784 case MutableStateDep:
7785 {
7786 if ( mData->mRegistered
7787 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7788 Paused should actually be included here... (Live Migration) */
7789 || ( mData->mMachineState != MachineState_Paused
7790 && mData->mMachineState != MachineState_Running
7791 && mData->mMachineState != MachineState_Aborted
7792 && mData->mMachineState != MachineState_Teleported
7793 && mData->mMachineState != MachineState_PoweredOff
7794 )
7795 )
7796 )
7797 return setError(VBOX_E_INVALID_VM_STATE,
7798 tr("The machine is not mutable (state is %s)"),
7799 Global::stringifyMachineState(mData->mMachineState));
7800 break;
7801 }
7802 case MutableOrSavedStateDep:
7803 {
7804 if ( mData->mRegistered
7805 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7806 Paused should actually be included here... (Live Migration) */
7807 || ( mData->mMachineState != MachineState_Paused
7808 && mData->mMachineState != MachineState_Running
7809 && mData->mMachineState != MachineState_Aborted
7810 && mData->mMachineState != MachineState_Teleported
7811 && mData->mMachineState != MachineState_Saved
7812 && mData->mMachineState != MachineState_PoweredOff
7813 )
7814 )
7815 )
7816 return setError(VBOX_E_INVALID_VM_STATE,
7817 tr("The machine is not mutable (state is %s)"),
7818 Global::stringifyMachineState(mData->mMachineState));
7819 break;
7820 }
7821 case OfflineStateDep:
7822 {
7823 if ( mData->mRegistered
7824 && ( !i_isSessionMachine()
7825 || ( mData->mMachineState != MachineState_PoweredOff
7826 && mData->mMachineState != MachineState_Saved
7827 && mData->mMachineState != MachineState_Aborted
7828 && mData->mMachineState != MachineState_Teleported
7829 )
7830 )
7831 )
7832 return setError(VBOX_E_INVALID_VM_STATE,
7833 tr("The machine is not offline (state is %s)"),
7834 Global::stringifyMachineState(mData->mMachineState));
7835 break;
7836 }
7837 }
7838
7839 return S_OK;
7840}
7841
7842/**
7843 * Helper to initialize all associated child objects and allocate data
7844 * structures.
7845 *
7846 * This method must be called as a part of the object's initialization procedure
7847 * (usually done in the #init() method).
7848 *
7849 * @note Must be called only from #init() or from #registeredInit().
7850 */
7851HRESULT Machine::initDataAndChildObjects()
7852{
7853 AutoCaller autoCaller(this);
7854 AssertComRCReturnRC(autoCaller.rc());
7855 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
7856 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7857
7858 AssertReturn(!mData->mAccessible, E_FAIL);
7859
7860 /* allocate data structures */
7861 mSSData.allocate();
7862 mUserData.allocate();
7863 mHWData.allocate();
7864 mMediaData.allocate();
7865 mStorageControllers.allocate();
7866 mUSBControllers.allocate();
7867
7868 /* initialize mOSTypeId */
7869 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7870
7871 /* create associated BIOS settings object */
7872 unconst(mBIOSSettings).createObject();
7873 mBIOSSettings->init(this);
7874
7875 /* create an associated VRDE object (default is disabled) */
7876 unconst(mVRDEServer).createObject();
7877 mVRDEServer->init(this);
7878
7879 /* create associated serial port objects */
7880 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7881 {
7882 unconst(mSerialPorts[slot]).createObject();
7883 mSerialPorts[slot]->init(this, slot);
7884 }
7885
7886 /* create associated parallel port objects */
7887 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
7888 {
7889 unconst(mParallelPorts[slot]).createObject();
7890 mParallelPorts[slot]->init(this, slot);
7891 }
7892
7893 /* create the audio adapter object (always present, default is disabled) */
7894 unconst(mAudioAdapter).createObject();
7895 mAudioAdapter->init(this);
7896
7897 /* create the USB device filters object (always present) */
7898 unconst(mUSBDeviceFilters).createObject();
7899 mUSBDeviceFilters->init(this);
7900
7901 /* create associated network adapter objects */
7902 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7903 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7904 {
7905 unconst(mNetworkAdapters[slot]).createObject();
7906 mNetworkAdapters[slot]->init(this, slot);
7907 }
7908
7909 /* create the bandwidth control */
7910 unconst(mBandwidthControl).createObject();
7911 mBandwidthControl->init(this);
7912
7913 return S_OK;
7914}
7915
7916/**
7917 * Helper to uninitialize all associated child objects and to free all data
7918 * structures.
7919 *
7920 * This method must be called as a part of the object's uninitialization
7921 * procedure (usually done in the #uninit() method).
7922 *
7923 * @note Must be called only from #uninit() or from #registeredInit().
7924 */
7925void Machine::uninitDataAndChildObjects()
7926{
7927 AutoCaller autoCaller(this);
7928 AssertComRCReturnVoid(autoCaller.rc());
7929 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
7930 || getObjectState().getState() == ObjectState::Limited);
7931
7932 /* tell all our other child objects we've been uninitialized */
7933 if (mBandwidthControl)
7934 {
7935 mBandwidthControl->uninit();
7936 unconst(mBandwidthControl).setNull();
7937 }
7938
7939 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7940 {
7941 if (mNetworkAdapters[slot])
7942 {
7943 mNetworkAdapters[slot]->uninit();
7944 unconst(mNetworkAdapters[slot]).setNull();
7945 }
7946 }
7947
7948 if (mUSBDeviceFilters)
7949 {
7950 mUSBDeviceFilters->uninit();
7951 unconst(mUSBDeviceFilters).setNull();
7952 }
7953
7954 if (mAudioAdapter)
7955 {
7956 mAudioAdapter->uninit();
7957 unconst(mAudioAdapter).setNull();
7958 }
7959
7960 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
7961 {
7962 if (mParallelPorts[slot])
7963 {
7964 mParallelPorts[slot]->uninit();
7965 unconst(mParallelPorts[slot]).setNull();
7966 }
7967 }
7968
7969 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7970 {
7971 if (mSerialPorts[slot])
7972 {
7973 mSerialPorts[slot]->uninit();
7974 unconst(mSerialPorts[slot]).setNull();
7975 }
7976 }
7977
7978 if (mVRDEServer)
7979 {
7980 mVRDEServer->uninit();
7981 unconst(mVRDEServer).setNull();
7982 }
7983
7984 if (mBIOSSettings)
7985 {
7986 mBIOSSettings->uninit();
7987 unconst(mBIOSSettings).setNull();
7988 }
7989
7990 /* Deassociate media (only when a real Machine or a SnapshotMachine
7991 * instance is uninitialized; SessionMachine instances refer to real
7992 * Machine media). This is necessary for a clean re-initialization of
7993 * the VM after successfully re-checking the accessibility state. Note
7994 * that in case of normal Machine or SnapshotMachine uninitialization (as
7995 * a result of unregistering or deleting the snapshot), outdated media
7996 * attachments will already be uninitialized and deleted, so this
7997 * code will not affect them. */
7998 if ( !!mMediaData
7999 && (!i_isSessionMachine())
8000 )
8001 {
8002 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8003 it != mMediaData->mAttachments.end();
8004 ++it)
8005 {
8006 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8007 if (pMedium.isNull())
8008 continue;
8009 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8010 AssertComRC(rc);
8011 }
8012 }
8013
8014 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8015 {
8016 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8017 if (mData->mFirstSnapshot)
8018 {
8019 // snapshots tree is protected by machine write lock; strictly
8020 // this isn't necessary here since we're deleting the entire
8021 // machine, but otherwise we assert in Snapshot::uninit()
8022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8023 mData->mFirstSnapshot->uninit();
8024 mData->mFirstSnapshot.setNull();
8025 }
8026
8027 mData->mCurrentSnapshot.setNull();
8028 }
8029
8030 /* free data structures (the essential mData structure is not freed here
8031 * since it may be still in use) */
8032 mMediaData.free();
8033 mStorageControllers.free();
8034 mUSBControllers.free();
8035 mHWData.free();
8036 mUserData.free();
8037 mSSData.free();
8038}
8039
8040/**
8041 * Returns a pointer to the Machine object for this machine that acts like a
8042 * parent for complex machine data objects such as shared folders, etc.
8043 *
8044 * For primary Machine objects and for SnapshotMachine objects, returns this
8045 * object's pointer itself. For SessionMachine objects, returns the peer
8046 * (primary) machine pointer.
8047 */
8048Machine* Machine::i_getMachine()
8049{
8050 if (i_isSessionMachine())
8051 return (Machine*)mPeer;
8052 return this;
8053}
8054
8055/**
8056 * Makes sure that there are no machine state dependents. If necessary, waits
8057 * for the number of dependents to drop to zero.
8058 *
8059 * Make sure this method is called from under this object's write lock to
8060 * guarantee that no new dependents may be added when this method returns
8061 * control to the caller.
8062 *
8063 * @note Locks this object for writing. The lock will be released while waiting
8064 * (if necessary).
8065 *
8066 * @warning To be used only in methods that change the machine state!
8067 */
8068void Machine::i_ensureNoStateDependencies()
8069{
8070 AssertReturnVoid(isWriteLockOnCurrentThread());
8071
8072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8073
8074 /* Wait for all state dependents if necessary */
8075 if (mData->mMachineStateDeps != 0)
8076 {
8077 /* lazy semaphore creation */
8078 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8079 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8080
8081 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8082 mData->mMachineStateDeps));
8083
8084 ++mData->mMachineStateChangePending;
8085
8086 /* reset the semaphore before waiting, the last dependent will signal
8087 * it */
8088 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8089
8090 alock.release();
8091
8092 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8093
8094 alock.acquire();
8095
8096 -- mData->mMachineStateChangePending;
8097 }
8098}
8099
8100/**
8101 * Changes the machine state and informs callbacks.
8102 *
8103 * This method is not intended to fail so it either returns S_OK or asserts (and
8104 * returns a failure).
8105 *
8106 * @note Locks this object for writing.
8107 */
8108HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8109{
8110 LogFlowThisFuncEnter();
8111 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8112
8113 AutoCaller autoCaller(this);
8114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8115
8116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8117
8118 /* wait for state dependents to drop to zero */
8119 i_ensureNoStateDependencies();
8120
8121 if (mData->mMachineState != aMachineState)
8122 {
8123 mData->mMachineState = aMachineState;
8124
8125 RTTimeNow(&mData->mLastStateChange);
8126
8127 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8128 }
8129
8130 LogFlowThisFuncLeave();
8131 return S_OK;
8132}
8133
8134/**
8135 * Searches for a shared folder with the given logical name
8136 * in the collection of shared folders.
8137 *
8138 * @param aName logical name of the shared folder
8139 * @param aSharedFolder where to return the found object
8140 * @param aSetError whether to set the error info if the folder is
8141 * not found
8142 * @return
8143 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8144 *
8145 * @note
8146 * must be called from under the object's lock!
8147 */
8148HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8149 ComObjPtr<SharedFolder> &aSharedFolder,
8150 bool aSetError /* = false */)
8151{
8152 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8153 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8154 it != mHWData->mSharedFolders.end();
8155 ++it)
8156 {
8157 SharedFolder *pSF = *it;
8158 AutoCaller autoCaller(pSF);
8159 if (pSF->i_getName() == aName)
8160 {
8161 aSharedFolder = pSF;
8162 rc = S_OK;
8163 break;
8164 }
8165 }
8166
8167 if (aSetError && FAILED(rc))
8168 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8169
8170 return rc;
8171}
8172
8173/**
8174 * Initializes all machine instance data from the given settings structures
8175 * from XML. The exception is the machine UUID which needs special handling
8176 * depending on the caller's use case, so the caller needs to set that herself.
8177 *
8178 * This gets called in several contexts during machine initialization:
8179 *
8180 * -- When machine XML exists on disk already and needs to be loaded into memory,
8181 * for example, from registeredInit() to load all registered machines on
8182 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8183 * attached to the machine should be part of some media registry already.
8184 *
8185 * -- During OVF import, when a machine config has been constructed from an
8186 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8187 * ensure that the media listed as attachments in the config (which have
8188 * been imported from the OVF) receive the correct registry ID.
8189 *
8190 * -- During VM cloning.
8191 *
8192 * @param config Machine settings from XML.
8193 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8194 * for each attached medium in the config.
8195 * @return
8196 */
8197HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8198 const Guid *puuidRegistry)
8199{
8200 // copy name, description, OS type, teleporter, UTC etc.
8201 mUserData->s = config.machineUserData;
8202
8203 // Decode the Icon overide data from config userdata and set onto Machine.
8204 #define DECODE_STR_MAX _1M
8205 const char* pszStr = config.machineUserData.ovIcon.c_str();
8206 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8207 if (cbOut > DECODE_STR_MAX)
8208 return setError(E_FAIL,
8209 tr("Icon Data too long.'%d' > '%d'"),
8210 cbOut,
8211 DECODE_STR_MAX);
8212 com::SafeArray<BYTE> iconByte(cbOut);
8213 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8214 if (FAILED(rc))
8215 return setError(E_FAIL,
8216 tr("Failure to Decode Icon Data. '%s' (%d)"),
8217 pszStr,
8218 rc);
8219 mUserData->mIcon.resize(iconByte.size());
8220 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8221
8222 // look up the object by Id to check it is valid
8223 ComPtr<IGuestOSType> guestOSType;
8224 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8225 guestOSType.asOutParam());
8226 if (FAILED(rc)) return rc;
8227
8228 // stateFile (optional)
8229 if (config.strStateFile.isEmpty())
8230 mSSData->strStateFilePath.setNull();
8231 else
8232 {
8233 Utf8Str stateFilePathFull(config.strStateFile);
8234 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8235 if (RT_FAILURE(vrc))
8236 return setError(E_FAIL,
8237 tr("Invalid saved state file path '%s' (%Rrc)"),
8238 config.strStateFile.c_str(),
8239 vrc);
8240 mSSData->strStateFilePath = stateFilePathFull;
8241 }
8242
8243 // snapshot folder needs special processing so set it again
8244 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8245 if (FAILED(rc)) return rc;
8246
8247 /* Copy the extra data items (Not in any case config is already the same as
8248 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8249 * make sure the extra data map is copied). */
8250 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8251
8252 /* currentStateModified (optional, default is true) */
8253 mData->mCurrentStateModified = config.fCurrentStateModified;
8254
8255 mData->mLastStateChange = config.timeLastStateChange;
8256
8257 /*
8258 * note: all mUserData members must be assigned prior this point because
8259 * we need to commit changes in order to let mUserData be shared by all
8260 * snapshot machine instances.
8261 */
8262 mUserData.commitCopy();
8263
8264 // machine registry, if present (must be loaded before snapshots)
8265 if (config.canHaveOwnMediaRegistry())
8266 {
8267 // determine machine folder
8268 Utf8Str strMachineFolder = i_getSettingsFileFull();
8269 strMachineFolder.stripFilename();
8270 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8271 config.mediaRegistry,
8272 strMachineFolder);
8273 if (FAILED(rc)) return rc;
8274 }
8275
8276 /* Snapshot node (optional) */
8277 size_t cRootSnapshots;
8278 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8279 {
8280 // there must be only one root snapshot
8281 Assert(cRootSnapshots == 1);
8282
8283 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8284
8285 rc = i_loadSnapshot(snap,
8286 config.uuidCurrentSnapshot,
8287 NULL); // no parent == first snapshot
8288 if (FAILED(rc)) return rc;
8289 }
8290
8291 // hardware data
8292 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8293 if (FAILED(rc)) return rc;
8294
8295 // load storage controllers
8296 rc = i_loadStorageControllers(config.storageMachine,
8297 puuidRegistry,
8298 NULL /* puuidSnapshot */);
8299 if (FAILED(rc)) return rc;
8300
8301 /*
8302 * NOTE: the assignment below must be the last thing to do,
8303 * otherwise it will be not possible to change the settings
8304 * somewhere in the code above because all setters will be
8305 * blocked by i_checkStateDependency(MutableStateDep).
8306 */
8307
8308 /* set the machine state to Aborted or Saved when appropriate */
8309 if (config.fAborted)
8310 {
8311 mSSData->strStateFilePath.setNull();
8312
8313 /* no need to use i_setMachineState() during init() */
8314 mData->mMachineState = MachineState_Aborted;
8315 }
8316 else if (!mSSData->strStateFilePath.isEmpty())
8317 {
8318 /* no need to use i_setMachineState() during init() */
8319 mData->mMachineState = MachineState_Saved;
8320 }
8321
8322 // after loading settings, we are no longer different from the XML on disk
8323 mData->flModifications = 0;
8324
8325 return S_OK;
8326}
8327
8328/**
8329 * Recursively loads all snapshots starting from the given.
8330 *
8331 * @param aNode <Snapshot> node.
8332 * @param aCurSnapshotId Current snapshot ID from the settings file.
8333 * @param aParentSnapshot Parent snapshot.
8334 */
8335HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8336 const Guid &aCurSnapshotId,
8337 Snapshot *aParentSnapshot)
8338{
8339 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8340 AssertReturn(!i_isSessionMachine(), E_FAIL);
8341
8342 HRESULT rc = S_OK;
8343
8344 Utf8Str strStateFile;
8345 if (!data.strStateFile.isEmpty())
8346 {
8347 /* optional */
8348 strStateFile = data.strStateFile;
8349 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8350 if (RT_FAILURE(vrc))
8351 return setError(E_FAIL,
8352 tr("Invalid saved state file path '%s' (%Rrc)"),
8353 strStateFile.c_str(),
8354 vrc);
8355 }
8356
8357 /* create a snapshot machine object */
8358 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8359 pSnapshotMachine.createObject();
8360 rc = pSnapshotMachine->initFromSettings(this,
8361 data.hardware,
8362 &data.debugging,
8363 &data.autostart,
8364 data.storage,
8365 data.uuid.ref(),
8366 strStateFile);
8367 if (FAILED(rc)) return rc;
8368
8369 /* create a snapshot object */
8370 ComObjPtr<Snapshot> pSnapshot;
8371 pSnapshot.createObject();
8372 /* initialize the snapshot */
8373 rc = pSnapshot->init(mParent, // VirtualBox object
8374 data.uuid,
8375 data.strName,
8376 data.strDescription,
8377 data.timestamp,
8378 pSnapshotMachine,
8379 aParentSnapshot);
8380 if (FAILED(rc)) return rc;
8381
8382 /* memorize the first snapshot if necessary */
8383 if (!mData->mFirstSnapshot)
8384 mData->mFirstSnapshot = pSnapshot;
8385
8386 /* memorize the current snapshot when appropriate */
8387 if ( !mData->mCurrentSnapshot
8388 && pSnapshot->i_getId() == aCurSnapshotId
8389 )
8390 mData->mCurrentSnapshot = pSnapshot;
8391
8392 // now create the children
8393 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8394 it != data.llChildSnapshots.end();
8395 ++it)
8396 {
8397 const settings::Snapshot &childData = *it;
8398 // recurse
8399 rc = i_loadSnapshot(childData,
8400 aCurSnapshotId,
8401 pSnapshot); // parent = the one we created above
8402 if (FAILED(rc)) return rc;
8403 }
8404
8405 return rc;
8406}
8407
8408/**
8409 * Loads settings into mHWData.
8410 *
8411 * @param data Reference to the hardware settings.
8412 * @param pDbg Pointer to the debugging settings.
8413 * @param pAutostart Pointer to the autostart settings.
8414 */
8415HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8416 const settings::Autostart *pAutostart)
8417{
8418 AssertReturn(!i_isSessionMachine(), E_FAIL);
8419
8420 HRESULT rc = S_OK;
8421
8422 try
8423 {
8424 /* The hardware version attribute (optional). */
8425 mHWData->mHWVersion = data.strVersion;
8426 mHWData->mHardwareUUID = data.uuid;
8427
8428 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8429 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8430 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8431 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8432 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8433 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8434 mHWData->mPAEEnabled = data.fPAE;
8435 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8436 mHWData->mLongMode = data.enmLongMode;
8437 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8438 mHWData->mCPUCount = data.cCPUs;
8439 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8440 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8441
8442 // cpu
8443 if (mHWData->mCPUHotPlugEnabled)
8444 {
8445 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8446 it != data.llCpus.end();
8447 ++it)
8448 {
8449 const settings::Cpu &cpu = *it;
8450
8451 mHWData->mCPUAttached[cpu.ulId] = true;
8452 }
8453 }
8454
8455 // cpuid leafs
8456 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8457 it != data.llCpuIdLeafs.end();
8458 ++it)
8459 {
8460 const settings::CpuIdLeaf &leaf = *it;
8461
8462 switch (leaf.ulId)
8463 {
8464 case 0x0:
8465 case 0x1:
8466 case 0x2:
8467 case 0x3:
8468 case 0x4:
8469 case 0x5:
8470 case 0x6:
8471 case 0x7:
8472 case 0x8:
8473 case 0x9:
8474 case 0xA:
8475 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8476 break;
8477
8478 case 0x80000000:
8479 case 0x80000001:
8480 case 0x80000002:
8481 case 0x80000003:
8482 case 0x80000004:
8483 case 0x80000005:
8484 case 0x80000006:
8485 case 0x80000007:
8486 case 0x80000008:
8487 case 0x80000009:
8488 case 0x8000000A:
8489 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8490 break;
8491
8492 default:
8493 /* just ignore */
8494 break;
8495 }
8496 }
8497
8498 mHWData->mMemorySize = data.ulMemorySizeMB;
8499 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8500
8501 // boot order
8502 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8503 {
8504 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8505 if (it == data.mapBootOrder.end())
8506 mHWData->mBootOrder[i] = DeviceType_Null;
8507 else
8508 mHWData->mBootOrder[i] = it->second;
8509 }
8510
8511 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8512 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8513 mHWData->mMonitorCount = data.cMonitors;
8514 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8515 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8516 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8517 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8518 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8519 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8520 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8521 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8522 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8523 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8524 if (!data.strVideoCaptureFile.isEmpty())
8525 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8526 else
8527 mHWData->mVideoCaptureFile.setNull();
8528 mHWData->mFirmwareType = data.firmwareType;
8529 mHWData->mPointingHIDType = data.pointingHIDType;
8530 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8531 mHWData->mChipsetType = data.chipsetType;
8532 mHWData->mParavirtProvider = data.paravirtProvider;
8533 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8534 mHWData->mHPETEnabled = data.fHPETEnabled;
8535
8536 /* VRDEServer */
8537 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8538 if (FAILED(rc)) return rc;
8539
8540 /* BIOS */
8541 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8542 if (FAILED(rc)) return rc;
8543
8544 // Bandwidth control (must come before network adapters)
8545 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8546 if (FAILED(rc)) return rc;
8547
8548 /* Shared folders */
8549 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8550 it != data.usbSettings.llUSBControllers.end();
8551 ++it)
8552 {
8553 const settings::USBController &settingsCtrl = *it;
8554 ComObjPtr<USBController> newCtrl;
8555
8556 newCtrl.createObject();
8557 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8558 mUSBControllers->push_back(newCtrl);
8559 }
8560
8561 /* USB device filters */
8562 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8563 if (FAILED(rc)) return rc;
8564
8565 // network adapters
8566 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8567 uint32_t oldCount = mNetworkAdapters.size();
8568 if (newCount > oldCount)
8569 {
8570 mNetworkAdapters.resize(newCount);
8571 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8572 {
8573 unconst(mNetworkAdapters[slot]).createObject();
8574 mNetworkAdapters[slot]->init(this, slot);
8575 }
8576 }
8577 else if (newCount < oldCount)
8578 mNetworkAdapters.resize(newCount);
8579 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8580 it != data.llNetworkAdapters.end();
8581 ++it)
8582 {
8583 const settings::NetworkAdapter &nic = *it;
8584
8585 /* slot unicity is guaranteed by XML Schema */
8586 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8587 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8588 if (FAILED(rc)) return rc;
8589 }
8590
8591 // serial ports
8592 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8593 it != data.llSerialPorts.end();
8594 ++it)
8595 {
8596 const settings::SerialPort &s = *it;
8597
8598 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8599 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8600 if (FAILED(rc)) return rc;
8601 }
8602
8603 // parallel ports (optional)
8604 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8605 it != data.llParallelPorts.end();
8606 ++it)
8607 {
8608 const settings::ParallelPort &p = *it;
8609
8610 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8611 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8612 if (FAILED(rc)) return rc;
8613 }
8614
8615 /* AudioAdapter */
8616 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8617 if (FAILED(rc)) return rc;
8618
8619 /* Shared folders */
8620 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8621 it != data.llSharedFolders.end();
8622 ++it)
8623 {
8624 const settings::SharedFolder &sf = *it;
8625
8626 ComObjPtr<SharedFolder> sharedFolder;
8627 /* Check for double entries. Not allowed! */
8628 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8629 if (SUCCEEDED(rc))
8630 return setError(VBOX_E_OBJECT_IN_USE,
8631 tr("Shared folder named '%s' already exists"),
8632 sf.strName.c_str());
8633
8634 /* Create the new shared folder. Don't break on error. This will be
8635 * reported when the machine starts. */
8636 sharedFolder.createObject();
8637 rc = sharedFolder->init(i_getMachine(),
8638 sf.strName,
8639 sf.strHostPath,
8640 RT_BOOL(sf.fWritable),
8641 RT_BOOL(sf.fAutoMount),
8642 false /* fFailOnError */);
8643 if (FAILED(rc)) return rc;
8644 mHWData->mSharedFolders.push_back(sharedFolder);
8645 }
8646
8647 // Clipboard
8648 mHWData->mClipboardMode = data.clipboardMode;
8649
8650 // drag'n'drop
8651 mHWData->mDnDMode = data.dndMode;
8652
8653 // guest settings
8654 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8655
8656 // IO settings
8657 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8658 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8659
8660 // Host PCI devices
8661 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8662 it != data.pciAttachments.end();
8663 ++it)
8664 {
8665 const settings::HostPCIDeviceAttachment &hpda = *it;
8666 ComObjPtr<PCIDeviceAttachment> pda;
8667
8668 pda.createObject();
8669 pda->i_loadSettings(this, hpda);
8670 mHWData->mPCIDeviceAssignments.push_back(pda);
8671 }
8672
8673 /*
8674 * (The following isn't really real hardware, but it lives in HWData
8675 * for reasons of convenience.)
8676 */
8677
8678#ifdef VBOX_WITH_GUEST_PROPS
8679 /* Guest properties (optional) */
8680 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8681 it != data.llGuestProperties.end();
8682 ++it)
8683 {
8684 const settings::GuestProperty &prop = *it;
8685 uint32_t fFlags = guestProp::NILFLAG;
8686 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8687 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8688 mHWData->mGuestProperties[prop.strName] = property;
8689 }
8690
8691 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8692#endif /* VBOX_WITH_GUEST_PROPS defined */
8693
8694 rc = i_loadDebugging(pDbg);
8695 if (FAILED(rc))
8696 return rc;
8697
8698 mHWData->mAutostart = *pAutostart;
8699
8700 /* default frontend */
8701 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8702 }
8703 catch(std::bad_alloc &)
8704 {
8705 return E_OUTOFMEMORY;
8706 }
8707
8708 AssertComRC(rc);
8709 return rc;
8710}
8711
8712/**
8713 * Called from Machine::loadHardware() to load the debugging settings of the
8714 * machine.
8715 *
8716 * @param pDbg Pointer to the settings.
8717 */
8718HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8719{
8720 mHWData->mDebugging = *pDbg;
8721 /* no more processing currently required, this will probably change. */
8722 return S_OK;
8723}
8724
8725/**
8726 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8727 *
8728 * @param data
8729 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8730 * @param puuidSnapshot
8731 * @return
8732 */
8733HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8734 const Guid *puuidRegistry,
8735 const Guid *puuidSnapshot)
8736{
8737 AssertReturn(!i_isSessionMachine(), E_FAIL);
8738
8739 HRESULT rc = S_OK;
8740
8741 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8742 it != data.llStorageControllers.end();
8743 ++it)
8744 {
8745 const settings::StorageController &ctlData = *it;
8746
8747 ComObjPtr<StorageController> pCtl;
8748 /* Try to find one with the name first. */
8749 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8750 if (SUCCEEDED(rc))
8751 return setError(VBOX_E_OBJECT_IN_USE,
8752 tr("Storage controller named '%s' already exists"),
8753 ctlData.strName.c_str());
8754
8755 pCtl.createObject();
8756 rc = pCtl->init(this,
8757 ctlData.strName,
8758 ctlData.storageBus,
8759 ctlData.ulInstance,
8760 ctlData.fBootable);
8761 if (FAILED(rc)) return rc;
8762
8763 mStorageControllers->push_back(pCtl);
8764
8765 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8766 if (FAILED(rc)) return rc;
8767
8768 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8769 if (FAILED(rc)) return rc;
8770
8771 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8772 if (FAILED(rc)) return rc;
8773
8774 /* Set IDE emulation settings (only for AHCI controller). */
8775 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8776 {
8777 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8778 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8779 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8780 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8781 )
8782 return rc;
8783 }
8784
8785 /* Load the attached devices now. */
8786 rc = i_loadStorageDevices(pCtl,
8787 ctlData,
8788 puuidRegistry,
8789 puuidSnapshot);
8790 if (FAILED(rc)) return rc;
8791 }
8792
8793 return S_OK;
8794}
8795
8796/**
8797 * Called from i_loadStorageControllers for a controller's devices.
8798 *
8799 * @param aStorageController
8800 * @param data
8801 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8802 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8803 * @return
8804 */
8805HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8806 const settings::StorageController &data,
8807 const Guid *puuidRegistry,
8808 const Guid *puuidSnapshot)
8809{
8810 HRESULT rc = S_OK;
8811
8812 /* paranoia: detect duplicate attachments */
8813 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8814 it != data.llAttachedDevices.end();
8815 ++it)
8816 {
8817 const settings::AttachedDevice &ad = *it;
8818
8819 for (settings::AttachedDevicesList::const_iterator it2 = it;
8820 it2 != data.llAttachedDevices.end();
8821 ++it2)
8822 {
8823 if (it == it2)
8824 continue;
8825
8826 const settings::AttachedDevice &ad2 = *it2;
8827
8828 if ( ad.lPort == ad2.lPort
8829 && ad.lDevice == ad2.lDevice)
8830 {
8831 return setError(E_FAIL,
8832 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8833 aStorageController->i_getName().c_str(),
8834 ad.lPort,
8835 ad.lDevice,
8836 mUserData->s.strName.c_str());
8837 }
8838 }
8839 }
8840
8841 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8842 it != data.llAttachedDevices.end();
8843 ++it)
8844 {
8845 const settings::AttachedDevice &dev = *it;
8846 ComObjPtr<Medium> medium;
8847
8848 switch (dev.deviceType)
8849 {
8850 case DeviceType_Floppy:
8851 case DeviceType_DVD:
8852 if (dev.strHostDriveSrc.isNotEmpty())
8853 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
8854 false /* fRefresh */, medium);
8855 else
8856 rc = mParent->i_findRemoveableMedium(dev.deviceType,
8857 dev.uuid,
8858 false /* fRefresh */,
8859 false /* aSetError */,
8860 medium);
8861 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8862 // This is not an error. The host drive or UUID might have vanished, so just go
8863 // ahead without this removeable medium attachment
8864 rc = S_OK;
8865 break;
8866
8867 case DeviceType_HardDisk:
8868 {
8869 /* find a hard disk by UUID */
8870 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8871 if (FAILED(rc))
8872 {
8873 if (i_isSnapshotMachine())
8874 {
8875 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8876 // so the user knows that the bad disk is in a snapshot somewhere
8877 com::ErrorInfo info;
8878 return setError(E_FAIL,
8879 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8880 puuidSnapshot->raw(),
8881 info.getText().raw());
8882 }
8883 else
8884 return rc;
8885 }
8886
8887 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8888
8889 if (medium->i_getType() == MediumType_Immutable)
8890 {
8891 if (i_isSnapshotMachine())
8892 return setError(E_FAIL,
8893 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8894 "of the virtual machine '%s' ('%s')"),
8895 medium->i_getLocationFull().c_str(),
8896 dev.uuid.raw(),
8897 puuidSnapshot->raw(),
8898 mUserData->s.strName.c_str(),
8899 mData->m_strConfigFileFull.c_str());
8900
8901 return setError(E_FAIL,
8902 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8903 medium->i_getLocationFull().c_str(),
8904 dev.uuid.raw(),
8905 mUserData->s.strName.c_str(),
8906 mData->m_strConfigFileFull.c_str());
8907 }
8908
8909 if (medium->i_getType() == MediumType_MultiAttach)
8910 {
8911 if (i_isSnapshotMachine())
8912 return setError(E_FAIL,
8913 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8914 "of the virtual machine '%s' ('%s')"),
8915 medium->i_getLocationFull().c_str(),
8916 dev.uuid.raw(),
8917 puuidSnapshot->raw(),
8918 mUserData->s.strName.c_str(),
8919 mData->m_strConfigFileFull.c_str());
8920
8921 return setError(E_FAIL,
8922 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8923 medium->i_getLocationFull().c_str(),
8924 dev.uuid.raw(),
8925 mUserData->s.strName.c_str(),
8926 mData->m_strConfigFileFull.c_str());
8927 }
8928
8929 if ( !i_isSnapshotMachine()
8930 && medium->i_getChildren().size() != 0
8931 )
8932 return setError(E_FAIL,
8933 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8934 "because it has %d differencing child hard disks"),
8935 medium->i_getLocationFull().c_str(),
8936 dev.uuid.raw(),
8937 mUserData->s.strName.c_str(),
8938 mData->m_strConfigFileFull.c_str(),
8939 medium->i_getChildren().size());
8940
8941 if (i_findAttachment(mMediaData->mAttachments,
8942 medium))
8943 return setError(E_FAIL,
8944 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8945 medium->i_getLocationFull().c_str(),
8946 dev.uuid.raw(),
8947 mUserData->s.strName.c_str(),
8948 mData->m_strConfigFileFull.c_str());
8949
8950 break;
8951 }
8952
8953 default:
8954 return setError(E_FAIL,
8955 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8956 medium->i_getLocationFull().c_str(),
8957 mUserData->s.strName.c_str(),
8958 mData->m_strConfigFileFull.c_str());
8959 }
8960
8961 if (FAILED(rc))
8962 break;
8963
8964 /* Bandwidth groups are loaded at this point. */
8965 ComObjPtr<BandwidthGroup> pBwGroup;
8966
8967 if (!dev.strBwGroup.isEmpty())
8968 {
8969 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8970 if (FAILED(rc))
8971 return setError(E_FAIL,
8972 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8973 medium->i_getLocationFull().c_str(),
8974 dev.strBwGroup.c_str(),
8975 mUserData->s.strName.c_str(),
8976 mData->m_strConfigFileFull.c_str());
8977 pBwGroup->i_reference();
8978 }
8979
8980 const Bstr controllerName = aStorageController->i_getName();
8981 ComObjPtr<MediumAttachment> pAttachment;
8982 pAttachment.createObject();
8983 rc = pAttachment->init(this,
8984 medium,
8985 controllerName,
8986 dev.lPort,
8987 dev.lDevice,
8988 dev.deviceType,
8989 false,
8990 dev.fPassThrough,
8991 dev.fTempEject,
8992 dev.fNonRotational,
8993 dev.fDiscard,
8994 dev.fHotPluggable,
8995 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
8996 if (FAILED(rc)) break;
8997
8998 /* associate the medium with this machine and snapshot */
8999 if (!medium.isNull())
9000 {
9001 AutoCaller medCaller(medium);
9002 if (FAILED(medCaller.rc())) return medCaller.rc();
9003 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9004
9005 if (i_isSnapshotMachine())
9006 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9007 else
9008 rc = medium->i_addBackReference(mData->mUuid);
9009 /* If the medium->addBackReference fails it sets an appropriate
9010 * error message, so no need to do any guesswork here. */
9011
9012 if (puuidRegistry)
9013 // caller wants registry ID to be set on all attached media (OVF import case)
9014 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9015 }
9016
9017 if (FAILED(rc))
9018 break;
9019
9020 /* back up mMediaData to let registeredInit() properly rollback on failure
9021 * (= limited accessibility) */
9022 i_setModified(IsModified_Storage);
9023 mMediaData.backup();
9024 mMediaData->mAttachments.push_back(pAttachment);
9025 }
9026
9027 return rc;
9028}
9029
9030/**
9031 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9032 *
9033 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9034 * @param aSnapshot where to return the found snapshot
9035 * @param aSetError true to set extended error info on failure
9036 */
9037HRESULT Machine::i_findSnapshotById(const Guid &aId,
9038 ComObjPtr<Snapshot> &aSnapshot,
9039 bool aSetError /* = false */)
9040{
9041 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9042
9043 if (!mData->mFirstSnapshot)
9044 {
9045 if (aSetError)
9046 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9047 return E_FAIL;
9048 }
9049
9050 if (aId.isZero())
9051 aSnapshot = mData->mFirstSnapshot;
9052 else
9053 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9054
9055 if (!aSnapshot)
9056 {
9057 if (aSetError)
9058 return setError(E_FAIL,
9059 tr("Could not find a snapshot with UUID {%s}"),
9060 aId.toString().c_str());
9061 return E_FAIL;
9062 }
9063
9064 return S_OK;
9065}
9066
9067/**
9068 * Returns the snapshot with the given name or fails of no such snapshot.
9069 *
9070 * @param aName snapshot name to find
9071 * @param aSnapshot where to return the found snapshot
9072 * @param aSetError true to set extended error info on failure
9073 */
9074HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9075 ComObjPtr<Snapshot> &aSnapshot,
9076 bool aSetError /* = false */)
9077{
9078 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9079
9080 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9081
9082 if (!mData->mFirstSnapshot)
9083 {
9084 if (aSetError)
9085 return setError(VBOX_E_OBJECT_NOT_FOUND,
9086 tr("This machine does not have any snapshots"));
9087 return VBOX_E_OBJECT_NOT_FOUND;
9088 }
9089
9090 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9091
9092 if (!aSnapshot)
9093 {
9094 if (aSetError)
9095 return setError(VBOX_E_OBJECT_NOT_FOUND,
9096 tr("Could not find a snapshot named '%s'"), strName.c_str());
9097 return VBOX_E_OBJECT_NOT_FOUND;
9098 }
9099
9100 return S_OK;
9101}
9102
9103/**
9104 * Returns a storage controller object with the given name.
9105 *
9106 * @param aName storage controller name to find
9107 * @param aStorageController where to return the found storage controller
9108 * @param aSetError true to set extended error info on failure
9109 */
9110HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9111 ComObjPtr<StorageController> &aStorageController,
9112 bool aSetError /* = false */)
9113{
9114 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9115
9116 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9117 it != mStorageControllers->end();
9118 ++it)
9119 {
9120 if ((*it)->i_getName() == aName)
9121 {
9122 aStorageController = (*it);
9123 return S_OK;
9124 }
9125 }
9126
9127 if (aSetError)
9128 return setError(VBOX_E_OBJECT_NOT_FOUND,
9129 tr("Could not find a storage controller named '%s'"),
9130 aName.c_str());
9131 return VBOX_E_OBJECT_NOT_FOUND;
9132}
9133
9134/**
9135 * Returns a USB controller object with the given name.
9136 *
9137 * @param aName USB controller name to find
9138 * @param aUSBController where to return the found USB controller
9139 * @param aSetError true to set extended error info on failure
9140 */
9141HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9142 ComObjPtr<USBController> &aUSBController,
9143 bool aSetError /* = false */)
9144{
9145 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9146
9147 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9148 it != mUSBControllers->end();
9149 ++it)
9150 {
9151 if ((*it)->i_getName() == aName)
9152 {
9153 aUSBController = (*it);
9154 return S_OK;
9155 }
9156 }
9157
9158 if (aSetError)
9159 return setError(VBOX_E_OBJECT_NOT_FOUND,
9160 tr("Could not find a storage controller named '%s'"),
9161 aName.c_str());
9162 return VBOX_E_OBJECT_NOT_FOUND;
9163}
9164
9165/**
9166 * Returns the number of USB controller instance of the given type.
9167 *
9168 * @param enmType USB controller type.
9169 */
9170ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9171{
9172 ULONG cCtrls = 0;
9173
9174 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9175 it != mUSBControllers->end();
9176 ++it)
9177 {
9178 if ((*it)->i_getControllerType() == enmType)
9179 cCtrls++;
9180 }
9181
9182 return cCtrls;
9183}
9184
9185HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9186 MediaData::AttachmentList &atts)
9187{
9188 AutoCaller autoCaller(this);
9189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9190
9191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9192
9193 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9194 it != mMediaData->mAttachments.end();
9195 ++it)
9196 {
9197 const ComObjPtr<MediumAttachment> &pAtt = *it;
9198 // should never happen, but deal with NULL pointers in the list.
9199 AssertStmt(!pAtt.isNull(), continue);
9200
9201 // getControllerName() needs caller+read lock
9202 AutoCaller autoAttCaller(pAtt);
9203 if (FAILED(autoAttCaller.rc()))
9204 {
9205 atts.clear();
9206 return autoAttCaller.rc();
9207 }
9208 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9209
9210 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9211 atts.push_back(pAtt);
9212 }
9213
9214 return S_OK;
9215}
9216
9217
9218/**
9219 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9220 * file if the machine name was changed and about creating a new settings file
9221 * if this is a new machine.
9222 *
9223 * @note Must be never called directly but only from #saveSettings().
9224 */
9225HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9226{
9227 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9228
9229 HRESULT rc = S_OK;
9230
9231 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9232
9233 /// @todo need to handle primary group change, too
9234
9235 /* attempt to rename the settings file if machine name is changed */
9236 if ( mUserData->s.fNameSync
9237 && mUserData.isBackedUp()
9238 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9239 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9240 )
9241 {
9242 bool dirRenamed = false;
9243 bool fileRenamed = false;
9244
9245 Utf8Str configFile, newConfigFile;
9246 Utf8Str configFilePrev, newConfigFilePrev;
9247 Utf8Str configDir, newConfigDir;
9248
9249 do
9250 {
9251 int vrc = VINF_SUCCESS;
9252
9253 Utf8Str name = mUserData.backedUpData()->s.strName;
9254 Utf8Str newName = mUserData->s.strName;
9255 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9256 if (group == "/")
9257 group.setNull();
9258 Utf8Str newGroup = mUserData->s.llGroups.front();
9259 if (newGroup == "/")
9260 newGroup.setNull();
9261
9262 configFile = mData->m_strConfigFileFull;
9263
9264 /* first, rename the directory if it matches the group and machine name */
9265 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9266 group.c_str(), RTPATH_DELIMITER, name.c_str());
9267 /** @todo hack, make somehow use of ComposeMachineFilename */
9268 if (mUserData->s.fDirectoryIncludesUUID)
9269 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9270 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9271 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9272 /** @todo hack, make somehow use of ComposeMachineFilename */
9273 if (mUserData->s.fDirectoryIncludesUUID)
9274 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9275 configDir = configFile;
9276 configDir.stripFilename();
9277 newConfigDir = configDir;
9278 if ( configDir.length() >= groupPlusName.length()
9279 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9280 groupPlusName.c_str()))
9281 {
9282 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9283 Utf8Str newConfigBaseDir(newConfigDir);
9284 newConfigDir.append(newGroupPlusName);
9285 /* consistency: use \ if appropriate on the platform */
9286 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9287 /* new dir and old dir cannot be equal here because of 'if'
9288 * above and because name != newName */
9289 Assert(configDir != newConfigDir);
9290 if (!fSettingsFileIsNew)
9291 {
9292 /* perform real rename only if the machine is not new */
9293 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9294 if ( vrc == VERR_FILE_NOT_FOUND
9295 || vrc == VERR_PATH_NOT_FOUND)
9296 {
9297 /* create the parent directory, then retry renaming */
9298 Utf8Str parent(newConfigDir);
9299 parent.stripFilename();
9300 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9301 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9302 }
9303 if (RT_FAILURE(vrc))
9304 {
9305 rc = setError(E_FAIL,
9306 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9307 configDir.c_str(),
9308 newConfigDir.c_str(),
9309 vrc);
9310 break;
9311 }
9312 /* delete subdirectories which are no longer needed */
9313 Utf8Str dir(configDir);
9314 dir.stripFilename();
9315 while (dir != newConfigBaseDir && dir != ".")
9316 {
9317 vrc = RTDirRemove(dir.c_str());
9318 if (RT_FAILURE(vrc))
9319 break;
9320 dir.stripFilename();
9321 }
9322 dirRenamed = true;
9323 }
9324 }
9325
9326 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9327 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9328
9329 /* then try to rename the settings file itself */
9330 if (newConfigFile != configFile)
9331 {
9332 /* get the path to old settings file in renamed directory */
9333 configFile = Utf8StrFmt("%s%c%s",
9334 newConfigDir.c_str(),
9335 RTPATH_DELIMITER,
9336 RTPathFilename(configFile.c_str()));
9337 if (!fSettingsFileIsNew)
9338 {
9339 /* perform real rename only if the machine is not new */
9340 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9341 if (RT_FAILURE(vrc))
9342 {
9343 rc = setError(E_FAIL,
9344 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9345 configFile.c_str(),
9346 newConfigFile.c_str(),
9347 vrc);
9348 break;
9349 }
9350 fileRenamed = true;
9351 configFilePrev = configFile;
9352 configFilePrev += "-prev";
9353 newConfigFilePrev = newConfigFile;
9354 newConfigFilePrev += "-prev";
9355 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9356 }
9357 }
9358
9359 // update m_strConfigFileFull amd mConfigFile
9360 mData->m_strConfigFileFull = newConfigFile;
9361 // compute the relative path too
9362 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9363
9364 // store the old and new so that VirtualBox::i_saveSettings() can update
9365 // the media registry
9366 if ( mData->mRegistered
9367 && (configDir != newConfigDir || configFile != newConfigFile))
9368 {
9369 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9370
9371 if (pfNeedsGlobalSaveSettings)
9372 *pfNeedsGlobalSaveSettings = true;
9373 }
9374
9375 // in the saved state file path, replace the old directory with the new directory
9376 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9377 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9378
9379 // and do the same thing for the saved state file paths of all the online snapshots
9380 if (mData->mFirstSnapshot)
9381 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9382 newConfigDir.c_str());
9383 }
9384 while (0);
9385
9386 if (FAILED(rc))
9387 {
9388 /* silently try to rename everything back */
9389 if (fileRenamed)
9390 {
9391 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9392 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9393 }
9394 if (dirRenamed)
9395 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9396 }
9397
9398 if (FAILED(rc)) return rc;
9399 }
9400
9401 if (fSettingsFileIsNew)
9402 {
9403 /* create a virgin config file */
9404 int vrc = VINF_SUCCESS;
9405
9406 /* ensure the settings directory exists */
9407 Utf8Str path(mData->m_strConfigFileFull);
9408 path.stripFilename();
9409 if (!RTDirExists(path.c_str()))
9410 {
9411 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9412 if (RT_FAILURE(vrc))
9413 {
9414 return setError(E_FAIL,
9415 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9416 path.c_str(),
9417 vrc);
9418 }
9419 }
9420
9421 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9422 path = Utf8Str(mData->m_strConfigFileFull);
9423 RTFILE f = NIL_RTFILE;
9424 vrc = RTFileOpen(&f, path.c_str(),
9425 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9426 if (RT_FAILURE(vrc))
9427 return setError(E_FAIL,
9428 tr("Could not create the settings file '%s' (%Rrc)"),
9429 path.c_str(),
9430 vrc);
9431 RTFileClose(f);
9432 }
9433
9434 return rc;
9435}
9436
9437/**
9438 * Saves and commits machine data, user data and hardware data.
9439 *
9440 * Note that on failure, the data remains uncommitted.
9441 *
9442 * @a aFlags may combine the following flags:
9443 *
9444 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9445 * Used when saving settings after an operation that makes them 100%
9446 * correspond to the settings from the current snapshot.
9447 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9448 * #isReallyModified() returns false. This is necessary for cases when we
9449 * change machine data directly, not through the backup()/commit() mechanism.
9450 * - SaveS_Force: settings will be saved without doing a deep compare of the
9451 * settings structures. This is used when this is called because snapshots
9452 * have changed to avoid the overhead of the deep compare.
9453 *
9454 * @note Must be called from under this object's write lock. Locks children for
9455 * writing.
9456 *
9457 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9458 * initialized to false and that will be set to true by this function if
9459 * the caller must invoke VirtualBox::i_saveSettings() because the global
9460 * settings have changed. This will happen if a machine rename has been
9461 * saved and the global machine and media registries will therefore need
9462 * updating.
9463 */
9464HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9465 int aFlags /*= 0*/)
9466{
9467 LogFlowThisFuncEnter();
9468
9469 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9470
9471 /* make sure child objects are unable to modify the settings while we are
9472 * saving them */
9473 i_ensureNoStateDependencies();
9474
9475 AssertReturn(!i_isSnapshotMachine(),
9476 E_FAIL);
9477
9478 HRESULT rc = S_OK;
9479 bool fNeedsWrite = false;
9480
9481 /* First, prepare to save settings. It will care about renaming the
9482 * settings directory and file if the machine name was changed and about
9483 * creating a new settings file if this is a new machine. */
9484 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9485 if (FAILED(rc)) return rc;
9486
9487 // keep a pointer to the current settings structures
9488 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9489 settings::MachineConfigFile *pNewConfig = NULL;
9490
9491 try
9492 {
9493 // make a fresh one to have everyone write stuff into
9494 pNewConfig = new settings::MachineConfigFile(NULL);
9495 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9496
9497 // now go and copy all the settings data from COM to the settings structures
9498 // (this calles i_saveSettings() on all the COM objects in the machine)
9499 i_copyMachineDataToSettings(*pNewConfig);
9500
9501 if (aFlags & SaveS_ResetCurStateModified)
9502 {
9503 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9504 mData->mCurrentStateModified = FALSE;
9505 fNeedsWrite = true; // always, no need to compare
9506 }
9507 else if (aFlags & SaveS_Force)
9508 {
9509 fNeedsWrite = true; // always, no need to compare
9510 }
9511 else
9512 {
9513 if (!mData->mCurrentStateModified)
9514 {
9515 // do a deep compare of the settings that we just saved with the settings
9516 // previously stored in the config file; this invokes MachineConfigFile::operator==
9517 // which does a deep compare of all the settings, which is expensive but less expensive
9518 // than writing out XML in vain
9519 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9520
9521 // could still be modified if any settings changed
9522 mData->mCurrentStateModified = fAnySettingsChanged;
9523
9524 fNeedsWrite = fAnySettingsChanged;
9525 }
9526 else
9527 fNeedsWrite = true;
9528 }
9529
9530 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9531
9532 if (fNeedsWrite)
9533 // now spit it all out!
9534 pNewConfig->write(mData->m_strConfigFileFull);
9535
9536 mData->pMachineConfigFile = pNewConfig;
9537 delete pOldConfig;
9538 i_commit();
9539
9540 // after saving settings, we are no longer different from the XML on disk
9541 mData->flModifications = 0;
9542 }
9543 catch (HRESULT err)
9544 {
9545 // we assume that error info is set by the thrower
9546 rc = err;
9547
9548 // restore old config
9549 delete pNewConfig;
9550 mData->pMachineConfigFile = pOldConfig;
9551 }
9552 catch (...)
9553 {
9554 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9555 }
9556
9557 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9558 {
9559 /* Fire the data change event, even on failure (since we've already
9560 * committed all data). This is done only for SessionMachines because
9561 * mutable Machine instances are always not registered (i.e. private
9562 * to the client process that creates them) and thus don't need to
9563 * inform callbacks. */
9564 if (i_isSessionMachine())
9565 mParent->i_onMachineDataChange(mData->mUuid);
9566 }
9567
9568 LogFlowThisFunc(("rc=%08X\n", rc));
9569 LogFlowThisFuncLeave();
9570 return rc;
9571}
9572
9573/**
9574 * Implementation for saving the machine settings into the given
9575 * settings::MachineConfigFile instance. This copies machine extradata
9576 * from the previous machine config file in the instance data, if any.
9577 *
9578 * This gets called from two locations:
9579 *
9580 * -- Machine::i_saveSettings(), during the regular XML writing;
9581 *
9582 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9583 * exported to OVF and we write the VirtualBox proprietary XML
9584 * into a <vbox:Machine> tag.
9585 *
9586 * This routine fills all the fields in there, including snapshots, *except*
9587 * for the following:
9588 *
9589 * -- fCurrentStateModified. There is some special logic associated with that.
9590 *
9591 * The caller can then call MachineConfigFile::write() or do something else
9592 * with it.
9593 *
9594 * Caller must hold the machine lock!
9595 *
9596 * This throws XML errors and HRESULT, so the caller must have a catch block!
9597 */
9598void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9599{
9600 // deep copy extradata
9601 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9602
9603 config.uuid = mData->mUuid;
9604
9605 // copy name, description, OS type, teleport, UTC etc.
9606 config.machineUserData = mUserData->s;
9607
9608 // Encode the Icon Override data from Machine and store on config userdata.
9609 com::SafeArray<BYTE> iconByte;
9610 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9611 ssize_t cbData = iconByte.size();
9612 if (cbData > 0)
9613 {
9614 ssize_t cchOut = RTBase64EncodedLength(cbData);
9615 Utf8Str strIconData;
9616 strIconData.reserve(cchOut+1);
9617 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9618 strIconData.mutableRaw(), strIconData.capacity(),
9619 NULL);
9620 if (RT_FAILURE(vrc))
9621 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9622 strIconData.jolt();
9623 config.machineUserData.ovIcon = strIconData;
9624 }
9625 else
9626 config.machineUserData.ovIcon.setNull();
9627
9628 if ( mData->mMachineState == MachineState_Saved
9629 || mData->mMachineState == MachineState_Restoring
9630 // when deleting a snapshot we may or may not have a saved state in the current state,
9631 // so let's not assert here please
9632 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9633 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9634 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9635 && (!mSSData->strStateFilePath.isEmpty())
9636 )
9637 )
9638 {
9639 Assert(!mSSData->strStateFilePath.isEmpty());
9640 /* try to make the file name relative to the settings file dir */
9641 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9642 }
9643 else
9644 {
9645 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9646 config.strStateFile.setNull();
9647 }
9648
9649 if (mData->mCurrentSnapshot)
9650 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9651 else
9652 config.uuidCurrentSnapshot.clear();
9653
9654 config.timeLastStateChange = mData->mLastStateChange;
9655 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9656 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9657
9658 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9659 if (FAILED(rc)) throw rc;
9660
9661 rc = i_saveStorageControllers(config.storageMachine);
9662 if (FAILED(rc)) throw rc;
9663
9664 // save machine's media registry if this is VirtualBox 4.0 or later
9665 if (config.canHaveOwnMediaRegistry())
9666 {
9667 // determine machine folder
9668 Utf8Str strMachineFolder = i_getSettingsFileFull();
9669 strMachineFolder.stripFilename();
9670 mParent->i_saveMediaRegistry(config.mediaRegistry,
9671 i_getId(), // only media with registry ID == machine UUID
9672 strMachineFolder);
9673 // this throws HRESULT
9674 }
9675
9676 // save snapshots
9677 rc = i_saveAllSnapshots(config);
9678 if (FAILED(rc)) throw rc;
9679}
9680
9681/**
9682 * Saves all snapshots of the machine into the given machine config file. Called
9683 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9684 * @param config
9685 * @return
9686 */
9687HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9688{
9689 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9690
9691 HRESULT rc = S_OK;
9692
9693 try
9694 {
9695 config.llFirstSnapshot.clear();
9696
9697 if (mData->mFirstSnapshot)
9698 {
9699 settings::Snapshot snapNew;
9700 config.llFirstSnapshot.push_back(snapNew);
9701
9702 // get reference to the fresh copy of the snapshot on the list and
9703 // work on that copy directly to avoid excessive copying later
9704 settings::Snapshot &snap = config.llFirstSnapshot.front();
9705
9706 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9707 if (FAILED(rc)) throw rc;
9708 }
9709
9710// if (mType == IsSessionMachine)
9711// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9712
9713 }
9714 catch (HRESULT err)
9715 {
9716 /* we assume that error info is set by the thrower */
9717 rc = err;
9718 }
9719 catch (...)
9720 {
9721 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9722 }
9723
9724 return rc;
9725}
9726
9727/**
9728 * Saves the VM hardware configuration. It is assumed that the
9729 * given node is empty.
9730 *
9731 * @param data Reference to the settings object for the hardware config.
9732 * @param pDbg Pointer to the settings object for the debugging config
9733 * which happens to live in mHWData.
9734 * @param pAutostart Pointer to the settings object for the autostart config
9735 * which happens to live in mHWData.
9736 */
9737HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9738 settings::Autostart *pAutostart)
9739{
9740 HRESULT rc = S_OK;
9741
9742 try
9743 {
9744 /* The hardware version attribute (optional).
9745 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9746 if ( mHWData->mHWVersion == "1"
9747 && mSSData->strStateFilePath.isEmpty()
9748 )
9749 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9750 other point needs to be found where this can be done. */
9751
9752 data.strVersion = mHWData->mHWVersion;
9753 data.uuid = mHWData->mHardwareUUID;
9754
9755 // CPU
9756 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9757 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9758 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9759 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9760 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9761 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9762 data.fPAE = !!mHWData->mPAEEnabled;
9763 data.enmLongMode = mHWData->mLongMode;
9764 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9765 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9766
9767 /* Standard and Extended CPUID leafs. */
9768 data.llCpuIdLeafs.clear();
9769 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9770 {
9771 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9772 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9773 }
9774 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9775 {
9776 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9777 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9778 }
9779
9780 data.cCPUs = mHWData->mCPUCount;
9781 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9782 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9783
9784 data.llCpus.clear();
9785 if (data.fCpuHotPlug)
9786 {
9787 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9788 {
9789 if (mHWData->mCPUAttached[idx])
9790 {
9791 settings::Cpu cpu;
9792 cpu.ulId = idx;
9793 data.llCpus.push_back(cpu);
9794 }
9795 }
9796 }
9797
9798 // memory
9799 data.ulMemorySizeMB = mHWData->mMemorySize;
9800 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9801
9802 // firmware
9803 data.firmwareType = mHWData->mFirmwareType;
9804
9805 // HID
9806 data.pointingHIDType = mHWData->mPointingHIDType;
9807 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9808
9809 // chipset
9810 data.chipsetType = mHWData->mChipsetType;
9811
9812 // paravirt
9813 data.paravirtProvider = mHWData->mParavirtProvider;
9814
9815
9816 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9817
9818 // HPET
9819 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9820
9821 // boot order
9822 data.mapBootOrder.clear();
9823 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9824 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9825
9826 // display
9827 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9828 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9829 data.cMonitors = mHWData->mMonitorCount;
9830 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9831 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9832 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9833 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9834 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
9835 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
9836 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9837 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
9838 {
9839 if (mHWData->maVideoCaptureScreens[i])
9840 ASMBitSet(&data.u64VideoCaptureScreens, i);
9841 else
9842 ASMBitClear(&data.u64VideoCaptureScreens, i);
9843 }
9844 /* store relative video capture file if possible */
9845 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
9846
9847 /* VRDEServer settings (optional) */
9848 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
9849 if (FAILED(rc)) throw rc;
9850
9851 /* BIOS (required) */
9852 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
9853 if (FAILED(rc)) throw rc;
9854
9855 /* USB Controller (required) */
9856 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
9857 {
9858 ComObjPtr<USBController> ctrl = *it;
9859 settings::USBController settingsCtrl;
9860
9861 settingsCtrl.strName = ctrl->i_getName();
9862 settingsCtrl.enmType = ctrl->i_getControllerType();
9863
9864 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
9865 }
9866
9867 /* USB device filters (required) */
9868 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
9869 if (FAILED(rc)) throw rc;
9870
9871 /* Network adapters (required) */
9872 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9873 data.llNetworkAdapters.clear();
9874 /* Write out only the nominal number of network adapters for this
9875 * chipset type. Since Machine::commit() hasn't been called there
9876 * may be extra NIC settings in the vector. */
9877 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9878 {
9879 settings::NetworkAdapter nic;
9880 nic.ulSlot = slot;
9881 /* paranoia check... must not be NULL, but must not crash either. */
9882 if (mNetworkAdapters[slot])
9883 {
9884 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
9885 if (FAILED(rc)) throw rc;
9886
9887 data.llNetworkAdapters.push_back(nic);
9888 }
9889 }
9890
9891 /* Serial ports */
9892 data.llSerialPorts.clear();
9893 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
9894 {
9895 settings::SerialPort s;
9896 s.ulSlot = slot;
9897 rc = mSerialPorts[slot]->i_saveSettings(s);
9898 if (FAILED(rc)) return rc;
9899
9900 data.llSerialPorts.push_back(s);
9901 }
9902
9903 /* Parallel ports */
9904 data.llParallelPorts.clear();
9905 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
9906 {
9907 settings::ParallelPort p;
9908 p.ulSlot = slot;
9909 rc = mParallelPorts[slot]->i_saveSettings(p);
9910 if (FAILED(rc)) return rc;
9911
9912 data.llParallelPorts.push_back(p);
9913 }
9914
9915 /* Audio adapter */
9916 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
9917 if (FAILED(rc)) return rc;
9918
9919 /* Shared folders */
9920 data.llSharedFolders.clear();
9921 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9922 it != mHWData->mSharedFolders.end();
9923 ++it)
9924 {
9925 SharedFolder *pSF = *it;
9926 AutoCaller sfCaller(pSF);
9927 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9928 settings::SharedFolder sf;
9929 sf.strName = pSF->i_getName();
9930 sf.strHostPath = pSF->i_getHostPath();
9931 sf.fWritable = !!pSF->i_isWritable();
9932 sf.fAutoMount = !!pSF->i_isAutoMounted();
9933
9934 data.llSharedFolders.push_back(sf);
9935 }
9936
9937 // clipboard
9938 data.clipboardMode = mHWData->mClipboardMode;
9939
9940 // drag'n'drop
9941 data.dndMode = mHWData->mDnDMode;
9942
9943 /* Guest */
9944 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9945
9946 // IO settings
9947 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9948 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9949
9950 /* BandwidthControl (required) */
9951 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
9952 if (FAILED(rc)) throw rc;
9953
9954 /* Host PCI devices */
9955 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9956 it != mHWData->mPCIDeviceAssignments.end();
9957 ++it)
9958 {
9959 ComObjPtr<PCIDeviceAttachment> pda = *it;
9960 settings::HostPCIDeviceAttachment hpda;
9961
9962 rc = pda->i_saveSettings(hpda);
9963 if (FAILED(rc)) throw rc;
9964
9965 data.pciAttachments.push_back(hpda);
9966 }
9967
9968
9969 // guest properties
9970 data.llGuestProperties.clear();
9971#ifdef VBOX_WITH_GUEST_PROPS
9972 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9973 it != mHWData->mGuestProperties.end();
9974 ++it)
9975 {
9976 HWData::GuestProperty property = it->second;
9977
9978 /* Remove transient guest properties at shutdown unless we
9979 * are saving state */
9980 if ( ( mData->mMachineState == MachineState_PoweredOff
9981 || mData->mMachineState == MachineState_Aborted
9982 || mData->mMachineState == MachineState_Teleported)
9983 && ( property.mFlags & guestProp::TRANSIENT
9984 || property.mFlags & guestProp::TRANSRESET))
9985 continue;
9986 settings::GuestProperty prop;
9987 prop.strName = it->first;
9988 prop.strValue = property.strValue;
9989 prop.timestamp = property.mTimestamp;
9990 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9991 guestProp::writeFlags(property.mFlags, szFlags);
9992 prop.strFlags = szFlags;
9993
9994 data.llGuestProperties.push_back(prop);
9995 }
9996
9997 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9998 /* I presume this doesn't require a backup(). */
9999 mData->mGuestPropertiesModified = FALSE;
10000#endif /* VBOX_WITH_GUEST_PROPS defined */
10001
10002 *pDbg = mHWData->mDebugging;
10003 *pAutostart = mHWData->mAutostart;
10004
10005 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10006 }
10007 catch(std::bad_alloc &)
10008 {
10009 return E_OUTOFMEMORY;
10010 }
10011
10012 AssertComRC(rc);
10013 return rc;
10014}
10015
10016/**
10017 * Saves the storage controller configuration.
10018 *
10019 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10020 */
10021HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10022{
10023 data.llStorageControllers.clear();
10024
10025 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10026 it != mStorageControllers->end();
10027 ++it)
10028 {
10029 HRESULT rc;
10030 ComObjPtr<StorageController> pCtl = *it;
10031
10032 settings::StorageController ctl;
10033 ctl.strName = pCtl->i_getName();
10034 ctl.controllerType = pCtl->i_getControllerType();
10035 ctl.storageBus = pCtl->i_getStorageBus();
10036 ctl.ulInstance = pCtl->i_getInstance();
10037 ctl.fBootable = pCtl->i_getBootable();
10038
10039 /* Save the port count. */
10040 ULONG portCount;
10041 rc = pCtl->COMGETTER(PortCount)(&portCount);
10042 ComAssertComRCRet(rc, rc);
10043 ctl.ulPortCount = portCount;
10044
10045 /* Save fUseHostIOCache */
10046 BOOL fUseHostIOCache;
10047 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10048 ComAssertComRCRet(rc, rc);
10049 ctl.fUseHostIOCache = !!fUseHostIOCache;
10050
10051 /* Save IDE emulation settings. */
10052 if (ctl.controllerType == StorageControllerType_IntelAhci)
10053 {
10054 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10055 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10056 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10057 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10058 )
10059 ComAssertComRCRet(rc, rc);
10060 }
10061
10062 /* save the devices now. */
10063 rc = i_saveStorageDevices(pCtl, ctl);
10064 ComAssertComRCRet(rc, rc);
10065
10066 data.llStorageControllers.push_back(ctl);
10067 }
10068
10069 return S_OK;
10070}
10071
10072/**
10073 * Saves the hard disk configuration.
10074 */
10075HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10076 settings::StorageController &data)
10077{
10078 MediaData::AttachmentList atts;
10079
10080 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10081 if (FAILED(rc)) return rc;
10082
10083 data.llAttachedDevices.clear();
10084 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10085 it != atts.end();
10086 ++it)
10087 {
10088 settings::AttachedDevice dev;
10089 IMediumAttachment *iA = *it;
10090 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10091 Medium *pMedium = pAttach->i_getMedium();
10092
10093 dev.deviceType = pAttach->i_getType();
10094 dev.lPort = pAttach->i_getPort();
10095 dev.lDevice = pAttach->i_getDevice();
10096 dev.fPassThrough = pAttach->i_getPassthrough();
10097 dev.fHotPluggable = pAttach->i_getHotPluggable();
10098 if (pMedium)
10099 {
10100 if (pMedium->i_isHostDrive())
10101 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10102 else
10103 dev.uuid = pMedium->i_getId();
10104 dev.fTempEject = pAttach->i_getTempEject();
10105 dev.fNonRotational = pAttach->i_getNonRotational();
10106 dev.fDiscard = pAttach->i_getDiscard();
10107 }
10108
10109 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10110
10111 data.llAttachedDevices.push_back(dev);
10112 }
10113
10114 return S_OK;
10115}
10116
10117/**
10118 * Saves machine state settings as defined by aFlags
10119 * (SaveSTS_* values).
10120 *
10121 * @param aFlags Combination of SaveSTS_* flags.
10122 *
10123 * @note Locks objects for writing.
10124 */
10125HRESULT Machine::i_saveStateSettings(int aFlags)
10126{
10127 if (aFlags == 0)
10128 return S_OK;
10129
10130 AutoCaller autoCaller(this);
10131 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10132
10133 /* This object's write lock is also necessary to serialize file access
10134 * (prevent concurrent reads and writes) */
10135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10136
10137 HRESULT rc = S_OK;
10138
10139 Assert(mData->pMachineConfigFile);
10140
10141 try
10142 {
10143 if (aFlags & SaveSTS_CurStateModified)
10144 mData->pMachineConfigFile->fCurrentStateModified = true;
10145
10146 if (aFlags & SaveSTS_StateFilePath)
10147 {
10148 if (!mSSData->strStateFilePath.isEmpty())
10149 /* try to make the file name relative to the settings file dir */
10150 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10151 else
10152 mData->pMachineConfigFile->strStateFile.setNull();
10153 }
10154
10155 if (aFlags & SaveSTS_StateTimeStamp)
10156 {
10157 Assert( mData->mMachineState != MachineState_Aborted
10158 || mSSData->strStateFilePath.isEmpty());
10159
10160 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10161
10162 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10163//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10164 }
10165
10166 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10167 }
10168 catch (...)
10169 {
10170 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10171 }
10172
10173 return rc;
10174}
10175
10176/**
10177 * Ensures that the given medium is added to a media registry. If this machine
10178 * was created with 4.0 or later, then the machine registry is used. Otherwise
10179 * the global VirtualBox media registry is used.
10180 *
10181 * Caller must NOT hold machine lock, media tree or any medium locks!
10182 *
10183 * @param pMedium
10184 */
10185void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10186{
10187 /* Paranoia checks: do not hold machine or media tree locks. */
10188 AssertReturnVoid(!isWriteLockOnCurrentThread());
10189 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10190
10191 ComObjPtr<Medium> pBase;
10192 {
10193 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10194 pBase = pMedium->i_getBase();
10195 }
10196
10197 /* Paranoia checks: do not hold medium locks. */
10198 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10199 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10200
10201 // decide which medium registry to use now that the medium is attached:
10202 Guid uuid;
10203 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10204 // machine XML is VirtualBox 4.0 or higher:
10205 uuid = i_getId(); // machine UUID
10206 else
10207 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10208
10209 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10210 mParent->i_markRegistryModified(uuid);
10211
10212 /* For more complex hard disk structures it can happen that the base
10213 * medium isn't yet associated with any medium registry. Do that now. */
10214 if (pMedium != pBase)
10215 {
10216 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10217 mParent->i_markRegistryModified(uuid);
10218 }
10219}
10220
10221/**
10222 * Creates differencing hard disks for all normal hard disks attached to this
10223 * machine and a new set of attachments to refer to created disks.
10224 *
10225 * Used when taking a snapshot or when deleting the current state. Gets called
10226 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10227 *
10228 * This method assumes that mMediaData contains the original hard disk attachments
10229 * it needs to create diffs for. On success, these attachments will be replaced
10230 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10231 * called to delete created diffs which will also rollback mMediaData and restore
10232 * whatever was backed up before calling this method.
10233 *
10234 * Attachments with non-normal hard disks are left as is.
10235 *
10236 * If @a aOnline is @c false then the original hard disks that require implicit
10237 * diffs will be locked for reading. Otherwise it is assumed that they are
10238 * already locked for writing (when the VM was started). Note that in the latter
10239 * case it is responsibility of the caller to lock the newly created diffs for
10240 * writing if this method succeeds.
10241 *
10242 * @param aProgress Progress object to run (must contain at least as
10243 * many operations left as the number of hard disks
10244 * attached).
10245 * @param aOnline Whether the VM was online prior to this operation.
10246 *
10247 * @note The progress object is not marked as completed, neither on success nor
10248 * on failure. This is a responsibility of the caller.
10249 *
10250 * @note Locks this object and the media tree for writing.
10251 */
10252HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10253 ULONG aWeight,
10254 bool aOnline)
10255{
10256 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10257
10258 AutoCaller autoCaller(this);
10259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10260
10261 AutoMultiWriteLock2 alock(this->lockHandle(),
10262 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10263
10264 /* must be in a protective state because we release the lock below */
10265 AssertReturn( mData->mMachineState == MachineState_Saving
10266 || mData->mMachineState == MachineState_LiveSnapshotting
10267 || mData->mMachineState == MachineState_RestoringSnapshot
10268 || mData->mMachineState == MachineState_DeletingSnapshot
10269 , E_FAIL);
10270
10271 HRESULT rc = S_OK;
10272
10273 // use appropriate locked media map (online or offline)
10274 MediumLockListMap lockedMediaOffline;
10275 MediumLockListMap *lockedMediaMap;
10276 if (aOnline)
10277 lockedMediaMap = &mData->mSession.mLockedMedia;
10278 else
10279 lockedMediaMap = &lockedMediaOffline;
10280
10281 try
10282 {
10283 if (!aOnline)
10284 {
10285 /* lock all attached hard disks early to detect "in use"
10286 * situations before creating actual diffs */
10287 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10288 it != mMediaData->mAttachments.end();
10289 ++it)
10290 {
10291 MediumAttachment* pAtt = *it;
10292 if (pAtt->i_getType() == DeviceType_HardDisk)
10293 {
10294 Medium* pMedium = pAtt->i_getMedium();
10295 Assert(pMedium);
10296
10297 MediumLockList *pMediumLockList(new MediumLockList());
10298 alock.release();
10299 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10300 false /* fMediumLockWrite */,
10301 NULL,
10302 *pMediumLockList);
10303 alock.acquire();
10304 if (FAILED(rc))
10305 {
10306 delete pMediumLockList;
10307 throw rc;
10308 }
10309 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10310 if (FAILED(rc))
10311 {
10312 throw setError(rc,
10313 tr("Collecting locking information for all attached media failed"));
10314 }
10315 }
10316 }
10317
10318 /* Now lock all media. If this fails, nothing is locked. */
10319 alock.release();
10320 rc = lockedMediaMap->Lock();
10321 alock.acquire();
10322 if (FAILED(rc))
10323 {
10324 throw setError(rc,
10325 tr("Locking of attached media failed"));
10326 }
10327 }
10328
10329 /* remember the current list (note that we don't use backup() since
10330 * mMediaData may be already backed up) */
10331 MediaData::AttachmentList atts = mMediaData->mAttachments;
10332
10333 /* start from scratch */
10334 mMediaData->mAttachments.clear();
10335
10336 /* go through remembered attachments and create diffs for normal hard
10337 * disks and attach them */
10338 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10339 it != atts.end();
10340 ++it)
10341 {
10342 MediumAttachment* pAtt = *it;
10343
10344 DeviceType_T devType = pAtt->i_getType();
10345 Medium* pMedium = pAtt->i_getMedium();
10346
10347 if ( devType != DeviceType_HardDisk
10348 || pMedium == NULL
10349 || pMedium->i_getType() != MediumType_Normal)
10350 {
10351 /* copy the attachment as is */
10352
10353 /** @todo the progress object created in Console::TakeSnaphot
10354 * only expects operations for hard disks. Later other
10355 * device types need to show up in the progress as well. */
10356 if (devType == DeviceType_HardDisk)
10357 {
10358 if (pMedium == NULL)
10359 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10360 aWeight); // weight
10361 else
10362 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10363 pMedium->i_getBase()->i_getName().c_str()).raw(),
10364 aWeight); // weight
10365 }
10366
10367 mMediaData->mAttachments.push_back(pAtt);
10368 continue;
10369 }
10370
10371 /* need a diff */
10372 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10373 pMedium->i_getBase()->i_getName().c_str()).raw(),
10374 aWeight); // weight
10375
10376 Utf8Str strFullSnapshotFolder;
10377 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10378
10379 ComObjPtr<Medium> diff;
10380 diff.createObject();
10381 // store the diff in the same registry as the parent
10382 // (this cannot fail here because we can't create implicit diffs for
10383 // unregistered images)
10384 Guid uuidRegistryParent;
10385 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10386 Assert(fInRegistry); NOREF(fInRegistry);
10387 rc = diff->init(mParent,
10388 pMedium->i_getPreferredDiffFormat(),
10389 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10390 uuidRegistryParent);
10391 if (FAILED(rc)) throw rc;
10392
10393 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10394 * the push_back? Looks like we're going to release medium with the
10395 * wrong kind of lock (general issue with if we fail anywhere at all)
10396 * and an orphaned VDI in the snapshots folder. */
10397
10398 /* update the appropriate lock list */
10399 MediumLockList *pMediumLockList;
10400 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10401 AssertComRCThrowRC(rc);
10402 if (aOnline)
10403 {
10404 alock.release();
10405 /* The currently attached medium will be read-only, change
10406 * the lock type to read. */
10407 rc = pMediumLockList->Update(pMedium, false);
10408 alock.acquire();
10409 AssertComRCThrowRC(rc);
10410 }
10411
10412 /* release the locks before the potentially lengthy operation */
10413 alock.release();
10414 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10415 pMediumLockList,
10416 NULL /* aProgress */,
10417 true /* aWait */);
10418 alock.acquire();
10419 if (FAILED(rc)) throw rc;
10420
10421 /* actual lock list update is done in Medium::commitMedia */
10422
10423 rc = diff->i_addBackReference(mData->mUuid);
10424 AssertComRCThrowRC(rc);
10425
10426 /* add a new attachment */
10427 ComObjPtr<MediumAttachment> attachment;
10428 attachment.createObject();
10429 rc = attachment->init(this,
10430 diff,
10431 pAtt->i_getControllerName(),
10432 pAtt->i_getPort(),
10433 pAtt->i_getDevice(),
10434 DeviceType_HardDisk,
10435 true /* aImplicit */,
10436 false /* aPassthrough */,
10437 false /* aTempEject */,
10438 pAtt->i_getNonRotational(),
10439 pAtt->i_getDiscard(),
10440 pAtt->i_getHotPluggable(),
10441 pAtt->i_getBandwidthGroup());
10442 if (FAILED(rc)) throw rc;
10443
10444 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10445 AssertComRCThrowRC(rc);
10446 mMediaData->mAttachments.push_back(attachment);
10447 }
10448 }
10449 catch (HRESULT aRC) { rc = aRC; }
10450
10451 /* unlock all hard disks we locked when there is no VM */
10452 if (!aOnline)
10453 {
10454 ErrorInfoKeeper eik;
10455
10456 HRESULT rc1 = lockedMediaMap->Clear();
10457 AssertComRC(rc1);
10458 }
10459
10460 return rc;
10461}
10462
10463/**
10464 * Deletes implicit differencing hard disks created either by
10465 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10466 *
10467 * Note that to delete hard disks created by #AttachDevice() this method is
10468 * called from #fixupMedia() when the changes are rolled back.
10469 *
10470 * @note Locks this object and the media tree for writing.
10471 */
10472HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10473{
10474 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10475
10476 AutoCaller autoCaller(this);
10477 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10478
10479 AutoMultiWriteLock2 alock(this->lockHandle(),
10480 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10481
10482 /* We absolutely must have backed up state. */
10483 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10484
10485 /* Check if there are any implicitly created diff images. */
10486 bool fImplicitDiffs = false;
10487 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10488 it != mMediaData->mAttachments.end();
10489 ++it)
10490 {
10491 const ComObjPtr<MediumAttachment> &pAtt = *it;
10492 if (pAtt->i_isImplicit())
10493 {
10494 fImplicitDiffs = true;
10495 break;
10496 }
10497 }
10498 /* If there is nothing to do, leave early. This saves lots of image locking
10499 * effort. It also avoids a MachineStateChanged event without real reason.
10500 * This is important e.g. when loading a VM config, because there should be
10501 * no events. Otherwise API clients can become thoroughly confused for
10502 * inaccessible VMs (the code for loading VM configs uses this method for
10503 * cleanup if the config makes no sense), as they take such events as an
10504 * indication that the VM is alive, and they would force the VM config to
10505 * be reread, leading to an endless loop. */
10506 if (!fImplicitDiffs)
10507 return S_OK;
10508
10509 HRESULT rc = S_OK;
10510 MachineState_T oldState = mData->mMachineState;
10511
10512 /* will release the lock before the potentially lengthy operation,
10513 * so protect with the special state (unless already protected) */
10514 if ( oldState != MachineState_Saving
10515 && oldState != MachineState_LiveSnapshotting
10516 && oldState != MachineState_RestoringSnapshot
10517 && oldState != MachineState_DeletingSnapshot
10518 && oldState != MachineState_DeletingSnapshotOnline
10519 && oldState != MachineState_DeletingSnapshotPaused
10520 )
10521 i_setMachineState(MachineState_SettingUp);
10522
10523 // use appropriate locked media map (online or offline)
10524 MediumLockListMap lockedMediaOffline;
10525 MediumLockListMap *lockedMediaMap;
10526 if (aOnline)
10527 lockedMediaMap = &mData->mSession.mLockedMedia;
10528 else
10529 lockedMediaMap = &lockedMediaOffline;
10530
10531 try
10532 {
10533 if (!aOnline)
10534 {
10535 /* lock all attached hard disks early to detect "in use"
10536 * situations before deleting actual diffs */
10537 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10538 it != mMediaData->mAttachments.end();
10539 ++it)
10540 {
10541 MediumAttachment* pAtt = *it;
10542 if (pAtt->i_getType() == DeviceType_HardDisk)
10543 {
10544 Medium* pMedium = pAtt->i_getMedium();
10545 Assert(pMedium);
10546
10547 MediumLockList *pMediumLockList(new MediumLockList());
10548 alock.release();
10549 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10550 false /* fMediumLockWrite */,
10551 NULL,
10552 *pMediumLockList);
10553 alock.acquire();
10554
10555 if (FAILED(rc))
10556 {
10557 delete pMediumLockList;
10558 throw rc;
10559 }
10560
10561 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10562 if (FAILED(rc))
10563 throw rc;
10564 }
10565 }
10566
10567 if (FAILED(rc))
10568 throw rc;
10569 } // end of offline
10570
10571 /* Lock lists are now up to date and include implicitly created media */
10572
10573 /* Go through remembered attachments and delete all implicitly created
10574 * diffs and fix up the attachment information */
10575 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10576 MediaData::AttachmentList implicitAtts;
10577 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10578 it != mMediaData->mAttachments.end();
10579 ++it)
10580 {
10581 ComObjPtr<MediumAttachment> pAtt = *it;
10582 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10583 if (pMedium.isNull())
10584 continue;
10585
10586 // Implicit attachments go on the list for deletion and back references are removed.
10587 if (pAtt->i_isImplicit())
10588 {
10589 /* Deassociate and mark for deletion */
10590 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10591 rc = pMedium->i_removeBackReference(mData->mUuid);
10592 if (FAILED(rc))
10593 throw rc;
10594 implicitAtts.push_back(pAtt);
10595 continue;
10596 }
10597
10598 /* Was this medium attached before? */
10599 if (!i_findAttachment(oldAtts, pMedium))
10600 {
10601 /* no: de-associate */
10602 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10603 rc = pMedium->i_removeBackReference(mData->mUuid);
10604 if (FAILED(rc))
10605 throw rc;
10606 continue;
10607 }
10608 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10609 }
10610
10611 /* If there are implicit attachments to delete, throw away the lock
10612 * map contents (which will unlock all media) since the medium
10613 * attachments will be rolled back. Below we need to completely
10614 * recreate the lock map anyway since it is infinitely complex to
10615 * do this incrementally (would need reconstructing each attachment
10616 * change, which would be extremely hairy). */
10617 if (implicitAtts.size() != 0)
10618 {
10619 ErrorInfoKeeper eik;
10620
10621 HRESULT rc1 = lockedMediaMap->Clear();
10622 AssertComRC(rc1);
10623 }
10624
10625 /* rollback hard disk changes */
10626 mMediaData.rollback();
10627
10628 MultiResult mrc(S_OK);
10629
10630 // Delete unused implicit diffs.
10631 if (implicitAtts.size() != 0)
10632 {
10633 alock.release();
10634
10635 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10636 {
10637 // Remove medium associated with this attachment.
10638 ComObjPtr<MediumAttachment> pAtt = *it;
10639 Assert(pAtt);
10640 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10641 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10642 Assert(pMedium);
10643
10644 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10645 // continue on delete failure, just collect error messages
10646 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10647 pMedium->i_getLocationFull().c_str() ));
10648 mrc = rc;
10649 }
10650
10651 alock.acquire();
10652
10653 /* if there is a VM recreate media lock map as mentioned above,
10654 * otherwise it is a waste of time and we leave things unlocked */
10655 if (aOnline)
10656 {
10657 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10658 /* must never be NULL, but better safe than sorry */
10659 if (!pMachine.isNull())
10660 {
10661 alock.release();
10662 rc = mData->mSession.mMachine->lockMedia();
10663 alock.acquire();
10664 if (FAILED(rc))
10665 throw rc;
10666 }
10667 }
10668 }
10669 }
10670 catch (HRESULT aRC) {rc = aRC;}
10671
10672 if (mData->mMachineState == MachineState_SettingUp)
10673 i_setMachineState(oldState);
10674
10675 /* unlock all hard disks we locked when there is no VM */
10676 if (!aOnline)
10677 {
10678 ErrorInfoKeeper eik;
10679
10680 HRESULT rc1 = lockedMediaMap->Clear();
10681 AssertComRC(rc1);
10682 }
10683
10684 return rc;
10685}
10686
10687
10688/**
10689 * Looks through the given list of media attachments for one with the given parameters
10690 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10691 * can be searched as well if needed.
10692 *
10693 * @param list
10694 * @param aControllerName
10695 * @param aControllerPort
10696 * @param aDevice
10697 * @return
10698 */
10699MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10700 IN_BSTR aControllerName,
10701 LONG aControllerPort,
10702 LONG aDevice)
10703{
10704 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10705 {
10706 MediumAttachment *pAttach = *it;
10707 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10708 return pAttach;
10709 }
10710
10711 return NULL;
10712}
10713
10714/**
10715 * Looks through the given list of media attachments for one with the given parameters
10716 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10717 * can be searched as well if needed.
10718 *
10719 * @param list
10720 * @param aControllerName
10721 * @param aControllerPort
10722 * @param aDevice
10723 * @return
10724 */
10725MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10726 ComObjPtr<Medium> pMedium)
10727{
10728 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10729 {
10730 MediumAttachment *pAttach = *it;
10731 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10732 if (pMediumThis == pMedium)
10733 return pAttach;
10734 }
10735
10736 return NULL;
10737}
10738
10739/**
10740 * Looks through the given list of media attachments for one with the given parameters
10741 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10742 * can be searched as well if needed.
10743 *
10744 * @param list
10745 * @param aControllerName
10746 * @param aControllerPort
10747 * @param aDevice
10748 * @return
10749 */
10750MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10751 Guid &id)
10752{
10753 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10754 {
10755 MediumAttachment *pAttach = *it;
10756 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10757 if (pMediumThis->i_getId() == id)
10758 return pAttach;
10759 }
10760
10761 return NULL;
10762}
10763
10764/**
10765 * Main implementation for Machine::DetachDevice. This also gets called
10766 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10767 *
10768 * @param pAttach Medium attachment to detach.
10769 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10770 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10771 * SnapshotMachine, and this must be its snapshot.
10772 * @return
10773 */
10774HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10775 AutoWriteLock &writeLock,
10776 Snapshot *pSnapshot)
10777{
10778 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10779 DeviceType_T mediumType = pAttach->i_getType();
10780
10781 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10782
10783 if (pAttach->i_isImplicit())
10784 {
10785 /* attempt to implicitly delete the implicitly created diff */
10786
10787 /// @todo move the implicit flag from MediumAttachment to Medium
10788 /// and forbid any hard disk operation when it is implicit. Or maybe
10789 /// a special media state for it to make it even more simple.
10790
10791 Assert(mMediaData.isBackedUp());
10792
10793 /* will release the lock before the potentially lengthy operation, so
10794 * protect with the special state */
10795 MachineState_T oldState = mData->mMachineState;
10796 i_setMachineState(MachineState_SettingUp);
10797
10798 writeLock.release();
10799
10800 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10801 true /*aWait*/);
10802
10803 writeLock.acquire();
10804
10805 i_setMachineState(oldState);
10806
10807 if (FAILED(rc)) return rc;
10808 }
10809
10810 i_setModified(IsModified_Storage);
10811 mMediaData.backup();
10812 mMediaData->mAttachments.remove(pAttach);
10813
10814 if (!oldmedium.isNull())
10815 {
10816 // if this is from a snapshot, do not defer detachment to commitMedia()
10817 if (pSnapshot)
10818 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
10819 // else if non-hard disk media, do not defer detachment to commitMedia() either
10820 else if (mediumType != DeviceType_HardDisk)
10821 oldmedium->i_removeBackReference(mData->mUuid);
10822 }
10823
10824 return S_OK;
10825}
10826
10827/**
10828 * Goes thru all media of the given list and
10829 *
10830 * 1) calls i_detachDevice() on each of them for this machine and
10831 * 2) adds all Medium objects found in the process to the given list,
10832 * depending on cleanupMode.
10833 *
10834 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10835 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10836 * media to the list.
10837 *
10838 * This gets called from Machine::Unregister, both for the actual Machine and
10839 * the SnapshotMachine objects that might be found in the snapshots.
10840 *
10841 * Requires caller and locking. The machine lock must be passed in because it
10842 * will be passed on to i_detachDevice which needs it for temporary unlocking.
10843 *
10844 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
10845 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10846 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
10847 * Full, then all media get added;
10848 * otherwise no media get added.
10849 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10850 * @return
10851 */
10852HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
10853 Snapshot *pSnapshot,
10854 CleanupMode_T cleanupMode,
10855 MediaList &llMedia)
10856{
10857 Assert(isWriteLockOnCurrentThread());
10858
10859 HRESULT rc;
10860
10861 // make a temporary list because i_detachDevice invalidates iterators into
10862 // mMediaData->mAttachments
10863 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10864
10865 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
10866 {
10867 ComObjPtr<MediumAttachment> &pAttach = *it;
10868 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
10869
10870 if (!pMedium.isNull())
10871 {
10872 AutoCaller mac(pMedium);
10873 if (FAILED(mac.rc())) return mac.rc();
10874 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10875 DeviceType_T devType = pMedium->i_getDeviceType();
10876 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10877 && devType == DeviceType_HardDisk)
10878 || (cleanupMode == CleanupMode_Full)
10879 )
10880 {
10881 llMedia.push_back(pMedium);
10882 ComObjPtr<Medium> pParent = pMedium->i_getParent();
10883 /*
10884 * Search for medias which are not attached to any machine, but
10885 * in the chain to an attached disk. Mediums are only consided
10886 * if they are:
10887 * - have only one child
10888 * - no references to any machines
10889 * - are of normal medium type
10890 */
10891 while (!pParent.isNull())
10892 {
10893 AutoCaller mac1(pParent);
10894 if (FAILED(mac1.rc())) return mac1.rc();
10895 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10896 if (pParent->i_getChildren().size() == 1)
10897 {
10898 if ( pParent->i_getMachineBackRefCount() == 0
10899 && pParent->i_getType() == MediumType_Normal
10900 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10901 llMedia.push_back(pParent);
10902 }
10903 else
10904 break;
10905 pParent = pParent->i_getParent();
10906 }
10907 }
10908 }
10909
10910 // real machine: then we need to use the proper method
10911 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
10912
10913 if (FAILED(rc))
10914 return rc;
10915 }
10916
10917 return S_OK;
10918}
10919
10920/**
10921 * Perform deferred hard disk detachments.
10922 *
10923 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10924 * backed up).
10925 *
10926 * If @a aOnline is @c true then this method will also unlock the old hard disks
10927 * for which the new implicit diffs were created and will lock these new diffs for
10928 * writing.
10929 *
10930 * @param aOnline Whether the VM was online prior to this operation.
10931 *
10932 * @note Locks this object for writing!
10933 */
10934void Machine::i_commitMedia(bool aOnline /*= false*/)
10935{
10936 AutoCaller autoCaller(this);
10937 AssertComRCReturnVoid(autoCaller.rc());
10938
10939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10940
10941 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10942
10943 HRESULT rc = S_OK;
10944
10945 /* no attach/detach operations -- nothing to do */
10946 if (!mMediaData.isBackedUp())
10947 return;
10948
10949 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10950 bool fMediaNeedsLocking = false;
10951
10952 /* enumerate new attachments */
10953 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10954 it != mMediaData->mAttachments.end();
10955 ++it)
10956 {
10957 MediumAttachment *pAttach = *it;
10958
10959 pAttach->i_commit();
10960
10961 Medium* pMedium = pAttach->i_getMedium();
10962 bool fImplicit = pAttach->i_isImplicit();
10963
10964 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10965 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
10966 fImplicit));
10967
10968 /** @todo convert all this Machine-based voodoo to MediumAttachment
10969 * based commit logic. */
10970 if (fImplicit)
10971 {
10972 /* convert implicit attachment to normal */
10973 pAttach->i_setImplicit(false);
10974
10975 if ( aOnline
10976 && pMedium
10977 && pAttach->i_getType() == DeviceType_HardDisk
10978 )
10979 {
10980 ComObjPtr<Medium> parent = pMedium->i_getParent();
10981 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10982
10983 /* update the appropriate lock list */
10984 MediumLockList *pMediumLockList;
10985 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10986 AssertComRC(rc);
10987 if (pMediumLockList)
10988 {
10989 /* unlock if there's a need to change the locking */
10990 if (!fMediaNeedsLocking)
10991 {
10992 rc = mData->mSession.mLockedMedia.Unlock();
10993 AssertComRC(rc);
10994 fMediaNeedsLocking = true;
10995 }
10996 rc = pMediumLockList->Update(parent, false);
10997 AssertComRC(rc);
10998 rc = pMediumLockList->Append(pMedium, true);
10999 AssertComRC(rc);
11000 }
11001 }
11002
11003 continue;
11004 }
11005
11006 if (pMedium)
11007 {
11008 /* was this medium attached before? */
11009 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11010 {
11011 MediumAttachment *pOldAttach = *oldIt;
11012 if (pOldAttach->i_getMedium() == pMedium)
11013 {
11014 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11015
11016 /* yes: remove from old to avoid de-association */
11017 oldAtts.erase(oldIt);
11018 break;
11019 }
11020 }
11021 }
11022 }
11023
11024 /* enumerate remaining old attachments and de-associate from the
11025 * current machine state */
11026 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11027 {
11028 MediumAttachment *pAttach = *it;
11029 Medium* pMedium = pAttach->i_getMedium();
11030
11031 /* Detach only hard disks, since DVD/floppy media is detached
11032 * instantly in MountMedium. */
11033 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11034 {
11035 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11036
11037 /* now de-associate from the current machine state */
11038 rc = pMedium->i_removeBackReference(mData->mUuid);
11039 AssertComRC(rc);
11040
11041 if (aOnline)
11042 {
11043 /* unlock since medium is not used anymore */
11044 MediumLockList *pMediumLockList;
11045 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11046 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11047 {
11048 /* this happens for online snapshots, there the attachment
11049 * is changing, but only to a diff image created under
11050 * the old one, so there is no separate lock list */
11051 Assert(!pMediumLockList);
11052 }
11053 else
11054 {
11055 AssertComRC(rc);
11056 if (pMediumLockList)
11057 {
11058 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11059 AssertComRC(rc);
11060 }
11061 }
11062 }
11063 }
11064 }
11065
11066 /* take media locks again so that the locking state is consistent */
11067 if (fMediaNeedsLocking)
11068 {
11069 Assert(aOnline);
11070 rc = mData->mSession.mLockedMedia.Lock();
11071 AssertComRC(rc);
11072 }
11073
11074 /* commit the hard disk changes */
11075 mMediaData.commit();
11076
11077 if (i_isSessionMachine())
11078 {
11079 /*
11080 * Update the parent machine to point to the new owner.
11081 * This is necessary because the stored parent will point to the
11082 * session machine otherwise and cause crashes or errors later
11083 * when the session machine gets invalid.
11084 */
11085 /** @todo Change the MediumAttachment class to behave like any other
11086 * class in this regard by creating peer MediumAttachment
11087 * objects for session machines and share the data with the peer
11088 * machine.
11089 */
11090 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11091 it != mMediaData->mAttachments.end();
11092 ++it)
11093 (*it)->i_updateParentMachine(mPeer);
11094
11095 /* attach new data to the primary machine and reshare it */
11096 mPeer->mMediaData.attach(mMediaData);
11097 }
11098
11099 return;
11100}
11101
11102/**
11103 * Perform deferred deletion of implicitly created diffs.
11104 *
11105 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11106 * backed up).
11107 *
11108 * @note Locks this object for writing!
11109 */
11110void Machine::i_rollbackMedia()
11111{
11112 AutoCaller autoCaller(this);
11113 AssertComRCReturnVoid(autoCaller.rc());
11114
11115 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11116 LogFlowThisFunc(("Entering rollbackMedia\n"));
11117
11118 HRESULT rc = S_OK;
11119
11120 /* no attach/detach operations -- nothing to do */
11121 if (!mMediaData.isBackedUp())
11122 return;
11123
11124 /* enumerate new attachments */
11125 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11126 it != mMediaData->mAttachments.end();
11127 ++it)
11128 {
11129 MediumAttachment *pAttach = *it;
11130 /* Fix up the backrefs for DVD/floppy media. */
11131 if (pAttach->i_getType() != DeviceType_HardDisk)
11132 {
11133 Medium* pMedium = pAttach->i_getMedium();
11134 if (pMedium)
11135 {
11136 rc = pMedium->i_removeBackReference(mData->mUuid);
11137 AssertComRC(rc);
11138 }
11139 }
11140
11141 (*it)->i_rollback();
11142
11143 pAttach = *it;
11144 /* Fix up the backrefs for DVD/floppy media. */
11145 if (pAttach->i_getType() != DeviceType_HardDisk)
11146 {
11147 Medium* pMedium = pAttach->i_getMedium();
11148 if (pMedium)
11149 {
11150 rc = pMedium->i_addBackReference(mData->mUuid);
11151 AssertComRC(rc);
11152 }
11153 }
11154 }
11155
11156 /** @todo convert all this Machine-based voodoo to MediumAttachment
11157 * based rollback logic. */
11158 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11159
11160 return;
11161}
11162
11163/**
11164 * Returns true if the settings file is located in the directory named exactly
11165 * as the machine; this means, among other things, that the machine directory
11166 * should be auto-renamed.
11167 *
11168 * @param aSettingsDir if not NULL, the full machine settings file directory
11169 * name will be assigned there.
11170 *
11171 * @note Doesn't lock anything.
11172 * @note Not thread safe (must be called from this object's lock).
11173 */
11174bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11175{
11176 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11177 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11178 if (aSettingsDir)
11179 *aSettingsDir = strMachineDirName;
11180 strMachineDirName.stripPath(); // vmname
11181 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11182 strConfigFileOnly.stripPath() // vmname.vbox
11183 .stripSuffix(); // vmname
11184 /** @todo hack, make somehow use of ComposeMachineFilename */
11185 if (mUserData->s.fDirectoryIncludesUUID)
11186 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11187
11188 AssertReturn(!strMachineDirName.isEmpty(), false);
11189 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11190
11191 return strMachineDirName == strConfigFileOnly;
11192}
11193
11194/**
11195 * Discards all changes to machine settings.
11196 *
11197 * @param aNotify Whether to notify the direct session about changes or not.
11198 *
11199 * @note Locks objects for writing!
11200 */
11201void Machine::i_rollback(bool aNotify)
11202{
11203 AutoCaller autoCaller(this);
11204 AssertComRCReturn(autoCaller.rc(), (void)0);
11205
11206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11207
11208 if (!mStorageControllers.isNull())
11209 {
11210 if (mStorageControllers.isBackedUp())
11211 {
11212 /* unitialize all new devices (absent in the backed up list). */
11213 StorageControllerList::const_iterator it = mStorageControllers->begin();
11214 StorageControllerList *backedList = mStorageControllers.backedUpData();
11215 while (it != mStorageControllers->end())
11216 {
11217 if ( std::find(backedList->begin(), backedList->end(), *it)
11218 == backedList->end()
11219 )
11220 {
11221 (*it)->uninit();
11222 }
11223 ++it;
11224 }
11225
11226 /* restore the list */
11227 mStorageControllers.rollback();
11228 }
11229
11230 /* rollback any changes to devices after restoring the list */
11231 if (mData->flModifications & IsModified_Storage)
11232 {
11233 StorageControllerList::const_iterator it = mStorageControllers->begin();
11234 while (it != mStorageControllers->end())
11235 {
11236 (*it)->i_rollback();
11237 ++it;
11238 }
11239 }
11240 }
11241
11242 if (!mUSBControllers.isNull())
11243 {
11244 if (mUSBControllers.isBackedUp())
11245 {
11246 /* unitialize all new devices (absent in the backed up list). */
11247 USBControllerList::const_iterator it = mUSBControllers->begin();
11248 USBControllerList *backedList = mUSBControllers.backedUpData();
11249 while (it != mUSBControllers->end())
11250 {
11251 if ( std::find(backedList->begin(), backedList->end(), *it)
11252 == backedList->end()
11253 )
11254 {
11255 (*it)->uninit();
11256 }
11257 ++it;
11258 }
11259
11260 /* restore the list */
11261 mUSBControllers.rollback();
11262 }
11263
11264 /* rollback any changes to devices after restoring the list */
11265 if (mData->flModifications & IsModified_USB)
11266 {
11267 USBControllerList::const_iterator it = mUSBControllers->begin();
11268 while (it != mUSBControllers->end())
11269 {
11270 (*it)->i_rollback();
11271 ++it;
11272 }
11273 }
11274 }
11275
11276 mUserData.rollback();
11277
11278 mHWData.rollback();
11279
11280 if (mData->flModifications & IsModified_Storage)
11281 i_rollbackMedia();
11282
11283 if (mBIOSSettings)
11284 mBIOSSettings->i_rollback();
11285
11286 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11287 mVRDEServer->i_rollback();
11288
11289 if (mAudioAdapter)
11290 mAudioAdapter->i_rollback();
11291
11292 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11293 mUSBDeviceFilters->i_rollback();
11294
11295 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11296 mBandwidthControl->i_rollback();
11297
11298 if (!mHWData.isNull())
11299 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11300 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11301 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11302 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11303
11304 if (mData->flModifications & IsModified_NetworkAdapters)
11305 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11306 if ( mNetworkAdapters[slot]
11307 && mNetworkAdapters[slot]->i_isModified())
11308 {
11309 mNetworkAdapters[slot]->i_rollback();
11310 networkAdapters[slot] = mNetworkAdapters[slot];
11311 }
11312
11313 if (mData->flModifications & IsModified_SerialPorts)
11314 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11315 if ( mSerialPorts[slot]
11316 && mSerialPorts[slot]->i_isModified())
11317 {
11318 mSerialPorts[slot]->i_rollback();
11319 serialPorts[slot] = mSerialPorts[slot];
11320 }
11321
11322 if (mData->flModifications & IsModified_ParallelPorts)
11323 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11324 if ( mParallelPorts[slot]
11325 && mParallelPorts[slot]->i_isModified())
11326 {
11327 mParallelPorts[slot]->i_rollback();
11328 parallelPorts[slot] = mParallelPorts[slot];
11329 }
11330
11331 if (aNotify)
11332 {
11333 /* inform the direct session about changes */
11334
11335 ComObjPtr<Machine> that = this;
11336 uint32_t flModifications = mData->flModifications;
11337 alock.release();
11338
11339 if (flModifications & IsModified_SharedFolders)
11340 that->i_onSharedFolderChange();
11341
11342 if (flModifications & IsModified_VRDEServer)
11343 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11344 if (flModifications & IsModified_USB)
11345 that->i_onUSBControllerChange();
11346
11347 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11348 if (networkAdapters[slot])
11349 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11350 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11351 if (serialPorts[slot])
11352 that->i_onSerialPortChange(serialPorts[slot]);
11353 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11354 if (parallelPorts[slot])
11355 that->i_onParallelPortChange(parallelPorts[slot]);
11356
11357 if (flModifications & IsModified_Storage)
11358 that->i_onStorageControllerChange();
11359
11360#if 0
11361 if (flModifications & IsModified_BandwidthControl)
11362 that->onBandwidthControlChange();
11363#endif
11364 }
11365}
11366
11367/**
11368 * Commits all the changes to machine settings.
11369 *
11370 * Note that this operation is supposed to never fail.
11371 *
11372 * @note Locks this object and children for writing.
11373 */
11374void Machine::i_commit()
11375{
11376 AutoCaller autoCaller(this);
11377 AssertComRCReturnVoid(autoCaller.rc());
11378
11379 AutoCaller peerCaller(mPeer);
11380 AssertComRCReturnVoid(peerCaller.rc());
11381
11382 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11383
11384 /*
11385 * use safe commit to ensure Snapshot machines (that share mUserData)
11386 * will still refer to a valid memory location
11387 */
11388 mUserData.commitCopy();
11389
11390 mHWData.commit();
11391
11392 if (mMediaData.isBackedUp())
11393 i_commitMedia(Global::IsOnline(mData->mMachineState));
11394
11395 mBIOSSettings->i_commit();
11396 mVRDEServer->i_commit();
11397 mAudioAdapter->i_commit();
11398 mUSBDeviceFilters->i_commit();
11399 mBandwidthControl->i_commit();
11400
11401 /* Since mNetworkAdapters is a list which might have been changed (resized)
11402 * without using the Backupable<> template we need to handle the copying
11403 * of the list entries manually, including the creation of peers for the
11404 * new objects. */
11405 bool commitNetworkAdapters = false;
11406 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11407 if (mPeer)
11408 {
11409 /* commit everything, even the ones which will go away */
11410 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11411 mNetworkAdapters[slot]->i_commit();
11412 /* copy over the new entries, creating a peer and uninit the original */
11413 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11414 for (size_t slot = 0; slot < newSize; slot++)
11415 {
11416 /* look if this adapter has a peer device */
11417 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11418 if (!peer)
11419 {
11420 /* no peer means the adapter is a newly created one;
11421 * create a peer owning data this data share it with */
11422 peer.createObject();
11423 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11424 }
11425 mPeer->mNetworkAdapters[slot] = peer;
11426 }
11427 /* uninit any no longer needed network adapters */
11428 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11429 mNetworkAdapters[slot]->uninit();
11430 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11431 {
11432 if (mPeer->mNetworkAdapters[slot])
11433 mPeer->mNetworkAdapters[slot]->uninit();
11434 }
11435 /* Keep the original network adapter count until this point, so that
11436 * discarding a chipset type change will not lose settings. */
11437 mNetworkAdapters.resize(newSize);
11438 mPeer->mNetworkAdapters.resize(newSize);
11439 }
11440 else
11441 {
11442 /* we have no peer (our parent is the newly created machine);
11443 * just commit changes to the network adapters */
11444 commitNetworkAdapters = true;
11445 }
11446 if (commitNetworkAdapters)
11447 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11448 mNetworkAdapters[slot]->i_commit();
11449
11450 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11451 mSerialPorts[slot]->i_commit();
11452 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11453 mParallelPorts[slot]->i_commit();
11454
11455 bool commitStorageControllers = false;
11456
11457 if (mStorageControllers.isBackedUp())
11458 {
11459 mStorageControllers.commit();
11460
11461 if (mPeer)
11462 {
11463 /* Commit all changes to new controllers (this will reshare data with
11464 * peers for those who have peers) */
11465 StorageControllerList *newList = new StorageControllerList();
11466 StorageControllerList::const_iterator it = mStorageControllers->begin();
11467 while (it != mStorageControllers->end())
11468 {
11469 (*it)->i_commit();
11470
11471 /* look if this controller has a peer device */
11472 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11473 if (!peer)
11474 {
11475 /* no peer means the device is a newly created one;
11476 * create a peer owning data this device share it with */
11477 peer.createObject();
11478 peer->init(mPeer, *it, true /* aReshare */);
11479 }
11480 else
11481 {
11482 /* remove peer from the old list */
11483 mPeer->mStorageControllers->remove(peer);
11484 }
11485 /* and add it to the new list */
11486 newList->push_back(peer);
11487
11488 ++it;
11489 }
11490
11491 /* uninit old peer's controllers that are left */
11492 it = mPeer->mStorageControllers->begin();
11493 while (it != mPeer->mStorageControllers->end())
11494 {
11495 (*it)->uninit();
11496 ++it;
11497 }
11498
11499 /* attach new list of controllers to our peer */
11500 mPeer->mStorageControllers.attach(newList);
11501 }
11502 else
11503 {
11504 /* we have no peer (our parent is the newly created machine);
11505 * just commit changes to devices */
11506 commitStorageControllers = true;
11507 }
11508 }
11509 else
11510 {
11511 /* the list of controllers itself is not changed,
11512 * just commit changes to controllers themselves */
11513 commitStorageControllers = true;
11514 }
11515
11516 if (commitStorageControllers)
11517 {
11518 StorageControllerList::const_iterator it = mStorageControllers->begin();
11519 while (it != mStorageControllers->end())
11520 {
11521 (*it)->i_commit();
11522 ++it;
11523 }
11524 }
11525
11526 bool commitUSBControllers = false;
11527
11528 if (mUSBControllers.isBackedUp())
11529 {
11530 mUSBControllers.commit();
11531
11532 if (mPeer)
11533 {
11534 /* Commit all changes to new controllers (this will reshare data with
11535 * peers for those who have peers) */
11536 USBControllerList *newList = new USBControllerList();
11537 USBControllerList::const_iterator it = mUSBControllers->begin();
11538 while (it != mUSBControllers->end())
11539 {
11540 (*it)->i_commit();
11541
11542 /* look if this controller has a peer device */
11543 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11544 if (!peer)
11545 {
11546 /* no peer means the device is a newly created one;
11547 * create a peer owning data this device share it with */
11548 peer.createObject();
11549 peer->init(mPeer, *it, true /* aReshare */);
11550 }
11551 else
11552 {
11553 /* remove peer from the old list */
11554 mPeer->mUSBControllers->remove(peer);
11555 }
11556 /* and add it to the new list */
11557 newList->push_back(peer);
11558
11559 ++it;
11560 }
11561
11562 /* uninit old peer's controllers that are left */
11563 it = mPeer->mUSBControllers->begin();
11564 while (it != mPeer->mUSBControllers->end())
11565 {
11566 (*it)->uninit();
11567 ++it;
11568 }
11569
11570 /* attach new list of controllers to our peer */
11571 mPeer->mUSBControllers.attach(newList);
11572 }
11573 else
11574 {
11575 /* we have no peer (our parent is the newly created machine);
11576 * just commit changes to devices */
11577 commitUSBControllers = true;
11578 }
11579 }
11580 else
11581 {
11582 /* the list of controllers itself is not changed,
11583 * just commit changes to controllers themselves */
11584 commitUSBControllers = true;
11585 }
11586
11587 if (commitUSBControllers)
11588 {
11589 USBControllerList::const_iterator it = mUSBControllers->begin();
11590 while (it != mUSBControllers->end())
11591 {
11592 (*it)->i_commit();
11593 ++it;
11594 }
11595 }
11596
11597 if (i_isSessionMachine())
11598 {
11599 /* attach new data to the primary machine and reshare it */
11600 mPeer->mUserData.attach(mUserData);
11601 mPeer->mHWData.attach(mHWData);
11602 /* mMediaData is reshared by fixupMedia */
11603 // mPeer->mMediaData.attach(mMediaData);
11604 Assert(mPeer->mMediaData.data() == mMediaData.data());
11605 }
11606}
11607
11608/**
11609 * Copies all the hardware data from the given machine.
11610 *
11611 * Currently, only called when the VM is being restored from a snapshot. In
11612 * particular, this implies that the VM is not running during this method's
11613 * call.
11614 *
11615 * @note This method must be called from under this object's lock.
11616 *
11617 * @note This method doesn't call #commit(), so all data remains backed up and
11618 * unsaved.
11619 */
11620void Machine::i_copyFrom(Machine *aThat)
11621{
11622 AssertReturnVoid(!i_isSnapshotMachine());
11623 AssertReturnVoid(aThat->i_isSnapshotMachine());
11624
11625 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11626
11627 mHWData.assignCopy(aThat->mHWData);
11628
11629 // create copies of all shared folders (mHWData after attaching a copy
11630 // contains just references to original objects)
11631 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11632 it != mHWData->mSharedFolders.end();
11633 ++it)
11634 {
11635 ComObjPtr<SharedFolder> folder;
11636 folder.createObject();
11637 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11638 AssertComRC(rc);
11639 *it = folder;
11640 }
11641
11642 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11643 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11644 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11645 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11646 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11647
11648 /* create private copies of all controllers */
11649 mStorageControllers.backup();
11650 mStorageControllers->clear();
11651 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11652 it != aThat->mStorageControllers->end();
11653 ++it)
11654 {
11655 ComObjPtr<StorageController> ctrl;
11656 ctrl.createObject();
11657 ctrl->initCopy(this, *it);
11658 mStorageControllers->push_back(ctrl);
11659 }
11660
11661 /* create private copies of all USB controllers */
11662 mUSBControllers.backup();
11663 mUSBControllers->clear();
11664 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11665 it != aThat->mUSBControllers->end();
11666 ++it)
11667 {
11668 ComObjPtr<USBController> ctrl;
11669 ctrl.createObject();
11670 ctrl->initCopy(this, *it);
11671 mUSBControllers->push_back(ctrl);
11672 }
11673
11674 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11675 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11676 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11677 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11678 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11679 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11680 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11681}
11682
11683/**
11684 * Returns whether the given storage controller is hotplug capable.
11685 *
11686 * @returns true if the controller supports hotplugging
11687 * false otherwise.
11688 * @param enmCtrlType The controller type to check for.
11689 */
11690bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11691{
11692 ComPtr<ISystemProperties> systemProperties;
11693 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11694 if (FAILED(rc))
11695 return false;
11696
11697 BOOL aHotplugCapable = FALSE;
11698 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11699
11700 return RT_BOOL(aHotplugCapable);
11701}
11702
11703#ifdef VBOX_WITH_RESOURCE_USAGE_API
11704
11705void Machine::i_getDiskList(MediaList &list)
11706{
11707 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11708 it != mMediaData->mAttachments.end();
11709 ++it)
11710 {
11711 MediumAttachment* pAttach = *it;
11712 /* just in case */
11713 AssertStmt(pAttach, continue);
11714
11715 AutoCaller localAutoCallerA(pAttach);
11716 if (FAILED(localAutoCallerA.rc())) continue;
11717
11718 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11719
11720 if (pAttach->i_getType() == DeviceType_HardDisk)
11721 list.push_back(pAttach->i_getMedium());
11722 }
11723}
11724
11725void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11726{
11727 AssertReturnVoid(isWriteLockOnCurrentThread());
11728 AssertPtrReturnVoid(aCollector);
11729
11730 pm::CollectorHAL *hal = aCollector->getHAL();
11731 /* Create sub metrics */
11732 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11733 "Percentage of processor time spent in user mode by the VM process.");
11734 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11735 "Percentage of processor time spent in kernel mode by the VM process.");
11736 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11737 "Size of resident portion of VM process in memory.");
11738 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11739 "Actual size of all VM disks combined.");
11740 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11741 "Network receive rate.");
11742 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11743 "Network transmit rate.");
11744 /* Create and register base metrics */
11745 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11746 cpuLoadUser, cpuLoadKernel);
11747 aCollector->registerBaseMetric(cpuLoad);
11748 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11749 ramUsageUsed);
11750 aCollector->registerBaseMetric(ramUsage);
11751 MediaList disks;
11752 i_getDiskList(disks);
11753 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11754 diskUsageUsed);
11755 aCollector->registerBaseMetric(diskUsage);
11756
11757 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11758 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11759 new pm::AggregateAvg()));
11760 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11761 new pm::AggregateMin()));
11762 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11763 new pm::AggregateMax()));
11764 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11765 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11766 new pm::AggregateAvg()));
11767 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11768 new pm::AggregateMin()));
11769 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11770 new pm::AggregateMax()));
11771
11772 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11773 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11774 new pm::AggregateAvg()));
11775 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11776 new pm::AggregateMin()));
11777 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11778 new pm::AggregateMax()));
11779
11780 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11781 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11782 new pm::AggregateAvg()));
11783 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11784 new pm::AggregateMin()));
11785 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11786 new pm::AggregateMax()));
11787
11788
11789 /* Guest metrics collector */
11790 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11791 aCollector->registerGuest(mCollectorGuest);
11792 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11793 this, __PRETTY_FUNCTION__, mCollectorGuest));
11794
11795 /* Create sub metrics */
11796 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11797 "Percentage of processor time spent in user mode as seen by the guest.");
11798 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11799 "Percentage of processor time spent in kernel mode as seen by the guest.");
11800 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11801 "Percentage of processor time spent idling as seen by the guest.");
11802
11803 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11804 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11805 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11806 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11807 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11808 pm::SubMetric *guestMemCache = new pm::SubMetric(
11809 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11810
11811 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
11812 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11813
11814 /* Create and register base metrics */
11815 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11816 machineNetRx, machineNetTx);
11817 aCollector->registerBaseMetric(machineNetRate);
11818
11819 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11820 guestLoadUser, guestLoadKernel, guestLoadIdle);
11821 aCollector->registerBaseMetric(guestCpuLoad);
11822
11823 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11824 guestMemTotal, guestMemFree,
11825 guestMemBalloon, guestMemShared,
11826 guestMemCache, guestPagedTotal);
11827 aCollector->registerBaseMetric(guestCpuMem);
11828
11829 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11830 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11831 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11832 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11833
11834 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11835 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11836 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11837 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11838
11839 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11840 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11841 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11842 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11843
11844 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11845 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11846 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11847 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11848
11849 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11850 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11851 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11852 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11853
11854 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11855 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11856 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11857 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11858
11859 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11860 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11861 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11862 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11863
11864 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11865 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11866 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11867 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11868
11869 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11870 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11871 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11872 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11873
11874 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11875 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11876 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11877 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11878
11879 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11880 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11881 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11882 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11883}
11884
11885void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11886{
11887 AssertReturnVoid(isWriteLockOnCurrentThread());
11888
11889 if (aCollector)
11890 {
11891 aCollector->unregisterMetricsFor(aMachine);
11892 aCollector->unregisterBaseMetricsFor(aMachine);
11893 }
11894}
11895
11896#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11897
11898
11899////////////////////////////////////////////////////////////////////////////////
11900
11901DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11902
11903HRESULT SessionMachine::FinalConstruct()
11904{
11905 LogFlowThisFunc(("\n"));
11906
11907 mClientToken = NULL;
11908
11909 return BaseFinalConstruct();
11910}
11911
11912void SessionMachine::FinalRelease()
11913{
11914 LogFlowThisFunc(("\n"));
11915
11916 Assert(!mClientToken);
11917 /* paranoia, should not hang around any more */
11918 if (mClientToken)
11919 {
11920 delete mClientToken;
11921 mClientToken = NULL;
11922 }
11923
11924 uninit(Uninit::Unexpected);
11925
11926 BaseFinalRelease();
11927}
11928
11929/**
11930 * @note Must be called only by Machine::LockMachine() from its own write lock.
11931 */
11932HRESULT SessionMachine::init(Machine *aMachine)
11933{
11934 LogFlowThisFuncEnter();
11935 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11936
11937 AssertReturn(aMachine, E_INVALIDARG);
11938
11939 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11940
11941 /* Enclose the state transition NotReady->InInit->Ready */
11942 AutoInitSpan autoInitSpan(this);
11943 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11944
11945 HRESULT rc = S_OK;
11946
11947 /* create the machine client token */
11948 try
11949 {
11950 mClientToken = new ClientToken(aMachine, this);
11951 if (!mClientToken->isReady())
11952 {
11953 delete mClientToken;
11954 mClientToken = NULL;
11955 rc = E_FAIL;
11956 }
11957 }
11958 catch (std::bad_alloc &)
11959 {
11960 rc = E_OUTOFMEMORY;
11961 }
11962 if (FAILED(rc))
11963 return rc;
11964
11965 /* memorize the peer Machine */
11966 unconst(mPeer) = aMachine;
11967 /* share the parent pointer */
11968 unconst(mParent) = aMachine->mParent;
11969
11970 /* take the pointers to data to share */
11971 mData.share(aMachine->mData);
11972 mSSData.share(aMachine->mSSData);
11973
11974 mUserData.share(aMachine->mUserData);
11975 mHWData.share(aMachine->mHWData);
11976 mMediaData.share(aMachine->mMediaData);
11977
11978 mStorageControllers.allocate();
11979 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11980 it != aMachine->mStorageControllers->end();
11981 ++it)
11982 {
11983 ComObjPtr<StorageController> ctl;
11984 ctl.createObject();
11985 ctl->init(this, *it);
11986 mStorageControllers->push_back(ctl);
11987 }
11988
11989 mUSBControllers.allocate();
11990 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
11991 it != aMachine->mUSBControllers->end();
11992 ++it)
11993 {
11994 ComObjPtr<USBController> ctl;
11995 ctl.createObject();
11996 ctl->init(this, *it);
11997 mUSBControllers->push_back(ctl);
11998 }
11999
12000 unconst(mBIOSSettings).createObject();
12001 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12002 /* create another VRDEServer object that will be mutable */
12003 unconst(mVRDEServer).createObject();
12004 mVRDEServer->init(this, aMachine->mVRDEServer);
12005 /* create another audio adapter object that will be mutable */
12006 unconst(mAudioAdapter).createObject();
12007 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12008 /* create a list of serial ports that will be mutable */
12009 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12010 {
12011 unconst(mSerialPorts[slot]).createObject();
12012 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12013 }
12014 /* create a list of parallel ports that will be mutable */
12015 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12016 {
12017 unconst(mParallelPorts[slot]).createObject();
12018 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12019 }
12020
12021 /* create another USB device filters object that will be mutable */
12022 unconst(mUSBDeviceFilters).createObject();
12023 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12024
12025 /* create a list of network adapters that will be mutable */
12026 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12027 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12028 {
12029 unconst(mNetworkAdapters[slot]).createObject();
12030 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12031 }
12032
12033 /* create another bandwidth control object that will be mutable */
12034 unconst(mBandwidthControl).createObject();
12035 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12036
12037 /* default is to delete saved state on Saved -> PoweredOff transition */
12038 mRemoveSavedState = true;
12039
12040 /* Confirm a successful initialization when it's the case */
12041 autoInitSpan.setSucceeded();
12042
12043 miNATNetworksStarted = 0;
12044
12045 LogFlowThisFuncLeave();
12046 return rc;
12047}
12048
12049/**
12050 * Uninitializes this session object. If the reason is other than
12051 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12052 * or the client watcher code.
12053 *
12054 * @param aReason uninitialization reason
12055 *
12056 * @note Locks mParent + this object for writing.
12057 */
12058void SessionMachine::uninit(Uninit::Reason aReason)
12059{
12060 LogFlowThisFuncEnter();
12061 LogFlowThisFunc(("reason=%d\n", aReason));
12062
12063 /*
12064 * Strongly reference ourselves to prevent this object deletion after
12065 * mData->mSession.mMachine.setNull() below (which can release the last
12066 * reference and call the destructor). Important: this must be done before
12067 * accessing any members (and before AutoUninitSpan that does it as well).
12068 * This self reference will be released as the very last step on return.
12069 */
12070 ComObjPtr<SessionMachine> selfRef = this;
12071
12072 /* Enclose the state transition Ready->InUninit->NotReady */
12073 AutoUninitSpan autoUninitSpan(this);
12074 if (autoUninitSpan.uninitDone())
12075 {
12076 LogFlowThisFunc(("Already uninitialized\n"));
12077 LogFlowThisFuncLeave();
12078 return;
12079 }
12080
12081 if (autoUninitSpan.initFailed())
12082 {
12083 /* We've been called by init() because it's failed. It's not really
12084 * necessary (nor it's safe) to perform the regular uninit sequence
12085 * below, the following is enough.
12086 */
12087 LogFlowThisFunc(("Initialization failed.\n"));
12088 /* destroy the machine client token */
12089 if (mClientToken)
12090 {
12091 delete mClientToken;
12092 mClientToken = NULL;
12093 }
12094 uninitDataAndChildObjects();
12095 mData.free();
12096 unconst(mParent) = NULL;
12097 unconst(mPeer) = NULL;
12098 LogFlowThisFuncLeave();
12099 return;
12100 }
12101
12102 MachineState_T lastState;
12103 {
12104 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12105 lastState = mData->mMachineState;
12106 }
12107 NOREF(lastState);
12108
12109#ifdef VBOX_WITH_USB
12110 // release all captured USB devices, but do this before requesting the locks below
12111 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12112 {
12113 /* Console::captureUSBDevices() is called in the VM process only after
12114 * setting the machine state to Starting or Restoring.
12115 * Console::detachAllUSBDevices() will be called upon successful
12116 * termination. So, we need to release USB devices only if there was
12117 * an abnormal termination of a running VM.
12118 *
12119 * This is identical to SessionMachine::DetachAllUSBDevices except
12120 * for the aAbnormal argument. */
12121 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12122 AssertComRC(rc);
12123 NOREF(rc);
12124
12125 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12126 if (service)
12127 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12128 }
12129#endif /* VBOX_WITH_USB */
12130
12131 // we need to lock this object in uninit() because the lock is shared
12132 // with mPeer (as well as data we modify below). mParent lock is needed
12133 // by several calls to it, and USB needs host lock.
12134 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12135
12136#ifdef VBOX_WITH_RESOURCE_USAGE_API
12137 /*
12138 * It is safe to call Machine::i_unregisterMetrics() here because
12139 * PerformanceCollector::samplerCallback no longer accesses guest methods
12140 * holding the lock.
12141 */
12142 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12143 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12144 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12145 this, __PRETTY_FUNCTION__, mCollectorGuest));
12146 if (mCollectorGuest)
12147 {
12148 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12149 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12150 mCollectorGuest = NULL;
12151 }
12152#endif
12153
12154 if (aReason == Uninit::Abnormal)
12155 {
12156 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12157 Global::IsOnlineOrTransient(lastState)));
12158
12159 /* reset the state to Aborted */
12160 if (mData->mMachineState != MachineState_Aborted)
12161 i_setMachineState(MachineState_Aborted);
12162 }
12163
12164 // any machine settings modified?
12165 if (mData->flModifications)
12166 {
12167 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12168 i_rollback(false /* aNotify */);
12169 }
12170
12171 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12172 || !mConsoleTaskData.mSnapshot);
12173 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12174 {
12175 LogWarningThisFunc(("canceling failed save state request!\n"));
12176 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12177 }
12178 else if (!mConsoleTaskData.mSnapshot.isNull())
12179 {
12180 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12181
12182 /* delete all differencing hard disks created (this will also attach
12183 * their parents back by rolling back mMediaData) */
12184 i_rollbackMedia();
12185
12186 // delete the saved state file (it might have been already created)
12187 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12188 // think it's still in use
12189 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12190 mConsoleTaskData.mSnapshot->uninit();
12191 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12192 }
12193
12194 mData->mSession.mPID = NIL_RTPROCESS;
12195
12196 if (aReason == Uninit::Unexpected)
12197 {
12198 /* Uninitialization didn't come from #checkForDeath(), so tell the
12199 * client watcher thread to update the set of machines that have open
12200 * sessions. */
12201 mParent->i_updateClientWatcher();
12202 }
12203
12204 /* uninitialize all remote controls */
12205 if (mData->mSession.mRemoteControls.size())
12206 {
12207 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12208 mData->mSession.mRemoteControls.size()));
12209
12210 Data::Session::RemoteControlList::iterator it =
12211 mData->mSession.mRemoteControls.begin();
12212 while (it != mData->mSession.mRemoteControls.end())
12213 {
12214 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12215 HRESULT rc = (*it)->Uninitialize();
12216 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12217 if (FAILED(rc))
12218 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12219 ++it;
12220 }
12221 mData->mSession.mRemoteControls.clear();
12222 }
12223
12224 /* Remove all references to the NAT network service. The service will stop
12225 * if all references (also from other VMs) are removed. */
12226 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12227 {
12228 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12229 {
12230 NetworkAttachmentType_T type;
12231 HRESULT hrc;
12232
12233 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12234 if ( SUCCEEDED(hrc)
12235 && type == NetworkAttachmentType_NATNetwork)
12236 {
12237 Bstr name;
12238 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12239 if (SUCCEEDED(hrc))
12240 {
12241 multilock.release();
12242 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12243 mUserData->s.strName.c_str(), name.raw()));
12244 mParent->i_natNetworkRefDec(name.raw());
12245 multilock.acquire();
12246 }
12247 }
12248 }
12249 }
12250
12251 /*
12252 * An expected uninitialization can come only from #checkForDeath().
12253 * Otherwise it means that something's gone really wrong (for example,
12254 * the Session implementation has released the VirtualBox reference
12255 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12256 * etc). However, it's also possible, that the client releases the IPC
12257 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12258 * but the VirtualBox release event comes first to the server process.
12259 * This case is practically possible, so we should not assert on an
12260 * unexpected uninit, just log a warning.
12261 */
12262
12263 if ((aReason == Uninit::Unexpected))
12264 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12265
12266 if (aReason != Uninit::Normal)
12267 {
12268 mData->mSession.mDirectControl.setNull();
12269 }
12270 else
12271 {
12272 /* this must be null here (see #OnSessionEnd()) */
12273 Assert(mData->mSession.mDirectControl.isNull());
12274 Assert(mData->mSession.mState == SessionState_Unlocking);
12275 Assert(!mData->mSession.mProgress.isNull());
12276 }
12277 if (mData->mSession.mProgress)
12278 {
12279 if (aReason == Uninit::Normal)
12280 mData->mSession.mProgress->i_notifyComplete(S_OK);
12281 else
12282 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12283 COM_IIDOF(ISession),
12284 getComponentName(),
12285 tr("The VM session was aborted"));
12286 mData->mSession.mProgress.setNull();
12287 }
12288
12289 /* remove the association between the peer machine and this session machine */
12290 Assert( (SessionMachine*)mData->mSession.mMachine == this
12291 || aReason == Uninit::Unexpected);
12292
12293 /* reset the rest of session data */
12294 mData->mSession.mMachine.setNull();
12295 mData->mSession.mState = SessionState_Unlocked;
12296 mData->mSession.mType.setNull();
12297
12298 /* destroy the machine client token before leaving the exclusive lock */
12299 if (mClientToken)
12300 {
12301 delete mClientToken;
12302 mClientToken = NULL;
12303 }
12304
12305 /* fire an event */
12306 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12307
12308 uninitDataAndChildObjects();
12309
12310 /* free the essential data structure last */
12311 mData.free();
12312
12313 /* release the exclusive lock before setting the below two to NULL */
12314 multilock.release();
12315
12316 unconst(mParent) = NULL;
12317 unconst(mPeer) = NULL;
12318
12319 LogFlowThisFuncLeave();
12320}
12321
12322// util::Lockable interface
12323////////////////////////////////////////////////////////////////////////////////
12324
12325/**
12326 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12327 * with the primary Machine instance (mPeer).
12328 */
12329RWLockHandle *SessionMachine::lockHandle() const
12330{
12331 AssertReturn(mPeer != NULL, NULL);
12332 return mPeer->lockHandle();
12333}
12334
12335// IInternalMachineControl methods
12336////////////////////////////////////////////////////////////////////////////////
12337
12338/**
12339 * Passes collected guest statistics to performance collector object
12340 */
12341STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12342 ULONG aCpuKernel, ULONG aCpuIdle,
12343 ULONG aMemTotal, ULONG aMemFree,
12344 ULONG aMemBalloon, ULONG aMemShared,
12345 ULONG aMemCache, ULONG aPageTotal,
12346 ULONG aAllocVMM, ULONG aFreeVMM,
12347 ULONG aBalloonedVMM, ULONG aSharedVMM,
12348 ULONG aVmNetRx, ULONG aVmNetTx)
12349{
12350#ifdef VBOX_WITH_RESOURCE_USAGE_API
12351 if (mCollectorGuest)
12352 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12353 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12354 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12355 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12356
12357 return S_OK;
12358#else
12359 NOREF(aValidStats);
12360 NOREF(aCpuUser);
12361 NOREF(aCpuKernel);
12362 NOREF(aCpuIdle);
12363 NOREF(aMemTotal);
12364 NOREF(aMemFree);
12365 NOREF(aMemBalloon);
12366 NOREF(aMemShared);
12367 NOREF(aMemCache);
12368 NOREF(aPageTotal);
12369 NOREF(aAllocVMM);
12370 NOREF(aFreeVMM);
12371 NOREF(aBalloonedVMM);
12372 NOREF(aSharedVMM);
12373 NOREF(aVmNetRx);
12374 NOREF(aVmNetTx);
12375 return E_NOTIMPL;
12376#endif
12377}
12378
12379/**
12380 * @note Locks this object for writing.
12381 */
12382STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12383{
12384 AutoCaller autoCaller(this);
12385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12386
12387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12388
12389 mRemoveSavedState = aRemove;
12390
12391 return S_OK;
12392}
12393
12394/**
12395 * @note Locks the same as #i_setMachineState() does.
12396 */
12397STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12398{
12399 return i_setMachineState(aMachineState);
12400}
12401
12402/**
12403 * @note Locks this object for writing.
12404 */
12405STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12406{
12407 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12408 AutoCaller autoCaller(this);
12409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12410
12411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12412
12413 if (mData->mSession.mState != SessionState_Locked)
12414 return VBOX_E_INVALID_OBJECT_STATE;
12415
12416 if (!mData->mSession.mProgress.isNull())
12417 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12418
12419 /* If we didn't reference the NAT network service yet, add a reference to
12420 * force a start */
12421 if (miNATNetworksStarted < 1)
12422 {
12423 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12424 {
12425 NetworkAttachmentType_T type;
12426 HRESULT hrc;
12427 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12428 if ( SUCCEEDED(hrc)
12429 && type == NetworkAttachmentType_NATNetwork)
12430 {
12431 Bstr name;
12432 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12433 if (SUCCEEDED(hrc))
12434 {
12435 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12436 mUserData->s.strName.c_str(), name.raw()));
12437 mPeer->lockHandle()->unlockWrite();
12438 mParent->i_natNetworkRefInc(name.raw());
12439#ifdef RT_LOCK_STRICT
12440 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12441#else
12442 mPeer->lockHandle()->lockWrite();
12443#endif
12444 }
12445 }
12446 }
12447 miNATNetworksStarted++;
12448 }
12449
12450 LogFlowThisFunc(("returns S_OK.\n"));
12451 return S_OK;
12452}
12453
12454/**
12455 * @note Locks this object for writing.
12456 */
12457STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12458{
12459 AutoCaller autoCaller(this);
12460 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12461
12462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12463
12464 if (mData->mSession.mState != SessionState_Locked)
12465 return VBOX_E_INVALID_OBJECT_STATE;
12466
12467 /* Finalize the LaunchVMProcess progress object. */
12468 if (mData->mSession.mProgress)
12469 {
12470 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12471 mData->mSession.mProgress.setNull();
12472 }
12473
12474 if (SUCCEEDED((HRESULT)iResult))
12475 {
12476#ifdef VBOX_WITH_RESOURCE_USAGE_API
12477 /* The VM has been powered up successfully, so it makes sense
12478 * now to offer the performance metrics for a running machine
12479 * object. Doing it earlier wouldn't be safe. */
12480 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12481 mData->mSession.mPID);
12482#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12483 }
12484
12485 return S_OK;
12486}
12487
12488/**
12489 * @note Locks this object for writing.
12490 */
12491STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12492{
12493 LogFlowThisFuncEnter();
12494
12495 AutoCaller autoCaller(this);
12496 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12497
12498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12499
12500 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12501 E_FAIL);
12502
12503 /* create a progress object to track operation completion */
12504 ComObjPtr<Progress> pProgress;
12505 pProgress.createObject();
12506 pProgress->init(i_getVirtualBox(),
12507 static_cast<IMachine *>(this) /* aInitiator */,
12508 Bstr(tr("Stopping the virtual machine")).raw(),
12509 FALSE /* aCancelable */);
12510
12511 /* fill in the console task data */
12512 mConsoleTaskData.mLastState = mData->mMachineState;
12513 mConsoleTaskData.mProgress = pProgress;
12514
12515 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12516 i_setMachineState(MachineState_Stopping);
12517
12518 pProgress.queryInterfaceTo(aProgress);
12519
12520 return S_OK;
12521}
12522
12523/**
12524 * @note Locks this object for writing.
12525 */
12526STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12527{
12528 LogFlowThisFuncEnter();
12529
12530 AutoCaller autoCaller(this);
12531 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12532
12533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12534
12535 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12536 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12537 && mConsoleTaskData.mLastState != MachineState_Null,
12538 E_FAIL);
12539
12540 /*
12541 * On failure, set the state to the state we had when BeginPoweringDown()
12542 * was called (this is expected by Console::PowerDown() and the associated
12543 * task). On success the VM process already changed the state to
12544 * MachineState_PoweredOff, so no need to do anything.
12545 */
12546 if (FAILED(iResult))
12547 i_setMachineState(mConsoleTaskData.mLastState);
12548
12549 /* notify the progress object about operation completion */
12550 Assert(mConsoleTaskData.mProgress);
12551 if (SUCCEEDED(iResult))
12552 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12553 else
12554 {
12555 Utf8Str strErrMsg(aErrMsg);
12556 if (strErrMsg.length())
12557 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12558 COM_IIDOF(ISession),
12559 getComponentName(),
12560 strErrMsg.c_str());
12561 else
12562 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12563 }
12564
12565 /* clear out the temporary saved state data */
12566 mConsoleTaskData.mLastState = MachineState_Null;
12567 mConsoleTaskData.mProgress.setNull();
12568
12569 LogFlowThisFuncLeave();
12570 return S_OK;
12571}
12572
12573
12574/**
12575 * Goes through the USB filters of the given machine to see if the given
12576 * device matches any filter or not.
12577 *
12578 * @note Locks the same as USBController::hasMatchingFilter() does.
12579 */
12580STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12581 BOOL *aMatched,
12582 ULONG *aMaskedIfs)
12583{
12584 LogFlowThisFunc(("\n"));
12585
12586 AutoCaller autoCaller(this);
12587 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12588
12589#ifdef VBOX_WITH_USB
12590 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12591#else
12592 NOREF(aUSBDevice);
12593 NOREF(aMaskedIfs);
12594 *aMatched = FALSE;
12595#endif
12596
12597 return S_OK;
12598}
12599
12600/**
12601 * @note Locks the same as Host::captureUSBDevice() does.
12602 */
12603STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12604{
12605 LogFlowThisFunc(("\n"));
12606
12607 AutoCaller autoCaller(this);
12608 AssertComRCReturnRC(autoCaller.rc());
12609
12610#ifdef VBOX_WITH_USB
12611 /* if captureDeviceForVM() fails, it must have set extended error info */
12612 clearError();
12613 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12614 if (FAILED(rc)) return rc;
12615
12616 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12617 AssertReturn(service, E_FAIL);
12618 return service->captureDeviceForVM(this, Guid(aId).ref());
12619#else
12620 NOREF(aId);
12621 return E_NOTIMPL;
12622#endif
12623}
12624
12625/**
12626 * @note Locks the same as Host::detachUSBDevice() does.
12627 */
12628STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12629{
12630 LogFlowThisFunc(("\n"));
12631
12632 AutoCaller autoCaller(this);
12633 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12634
12635#ifdef VBOX_WITH_USB
12636 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12637 AssertReturn(service, E_FAIL);
12638 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12639#else
12640 NOREF(aId);
12641 NOREF(aDone);
12642 return E_NOTIMPL;
12643#endif
12644}
12645
12646/**
12647 * Inserts all machine filters to the USB proxy service and then calls
12648 * Host::autoCaptureUSBDevices().
12649 *
12650 * Called by Console from the VM process upon VM startup.
12651 *
12652 * @note Locks what called methods lock.
12653 */
12654STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12655{
12656 LogFlowThisFunc(("\n"));
12657
12658 AutoCaller autoCaller(this);
12659 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12660
12661#ifdef VBOX_WITH_USB
12662 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12663 AssertComRC(rc);
12664 NOREF(rc);
12665
12666 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12667 AssertReturn(service, E_FAIL);
12668 return service->autoCaptureDevicesForVM(this);
12669#else
12670 return S_OK;
12671#endif
12672}
12673
12674/**
12675 * Removes all machine filters from the USB proxy service and then calls
12676 * Host::detachAllUSBDevices().
12677 *
12678 * Called by Console from the VM process upon normal VM termination or by
12679 * SessionMachine::uninit() upon abnormal VM termination (from under the
12680 * Machine/SessionMachine lock).
12681 *
12682 * @note Locks what called methods lock.
12683 */
12684STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12685{
12686 LogFlowThisFunc(("\n"));
12687
12688 AutoCaller autoCaller(this);
12689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12690
12691#ifdef VBOX_WITH_USB
12692 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12693 AssertComRC(rc);
12694 NOREF(rc);
12695
12696 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12697 AssertReturn(service, E_FAIL);
12698 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12699#else
12700 NOREF(aDone);
12701 return S_OK;
12702#endif
12703}
12704
12705/**
12706 * @note Locks this object for writing.
12707 */
12708STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12709 IProgress **aProgress)
12710{
12711 LogFlowThisFuncEnter();
12712
12713 AssertReturn(aSession, E_INVALIDARG);
12714 AssertReturn(aProgress, E_INVALIDARG);
12715
12716 AutoCaller autoCaller(this);
12717
12718 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12719 /*
12720 * We don't assert below because it might happen that a non-direct session
12721 * informs us it is closed right after we've been uninitialized -- it's ok.
12722 */
12723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12724
12725 /* get IInternalSessionControl interface */
12726 ComPtr<IInternalSessionControl> control(aSession);
12727
12728 ComAssertRet(!control.isNull(), E_INVALIDARG);
12729
12730 /* Creating a Progress object requires the VirtualBox lock, and
12731 * thus locking it here is required by the lock order rules. */
12732 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12733
12734 if (control == mData->mSession.mDirectControl)
12735 {
12736 ComAssertRet(aProgress, E_POINTER);
12737
12738 /* The direct session is being normally closed by the client process
12739 * ----------------------------------------------------------------- */
12740
12741 /* go to the closing state (essential for all open*Session() calls and
12742 * for #checkForDeath()) */
12743 Assert(mData->mSession.mState == SessionState_Locked);
12744 mData->mSession.mState = SessionState_Unlocking;
12745
12746 /* set direct control to NULL to release the remote instance */
12747 mData->mSession.mDirectControl.setNull();
12748 LogFlowThisFunc(("Direct control is set to NULL\n"));
12749
12750 if (mData->mSession.mProgress)
12751 {
12752 /* finalize the progress, someone might wait if a frontend
12753 * closes the session before powering on the VM. */
12754 mData->mSession.mProgress->notifyComplete(E_FAIL,
12755 COM_IIDOF(ISession),
12756 getComponentName(),
12757 tr("The VM session was closed before any attempt to power it on"));
12758 mData->mSession.mProgress.setNull();
12759 }
12760
12761 /* Create the progress object the client will use to wait until
12762 * #checkForDeath() is called to uninitialize this session object after
12763 * it releases the IPC semaphore.
12764 * Note! Because we're "reusing" mProgress here, this must be a proxy
12765 * object just like for LaunchVMProcess. */
12766 Assert(mData->mSession.mProgress.isNull());
12767 ComObjPtr<ProgressProxy> progress;
12768 progress.createObject();
12769 ComPtr<IUnknown> pPeer(mPeer);
12770 progress->init(mParent, pPeer,
12771 Bstr(tr("Closing session")).raw(),
12772 FALSE /* aCancelable */);
12773 progress.queryInterfaceTo(aProgress);
12774 mData->mSession.mProgress = progress;
12775 }
12776 else
12777 {
12778 /* the remote session is being normally closed */
12779 Data::Session::RemoteControlList::iterator it =
12780 mData->mSession.mRemoteControls.begin();
12781 while (it != mData->mSession.mRemoteControls.end())
12782 {
12783 if (control == *it)
12784 break;
12785 ++it;
12786 }
12787 BOOL found = it != mData->mSession.mRemoteControls.end();
12788 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12789 E_INVALIDARG);
12790 // This MUST be erase(it), not remove(*it) as the latter triggers a
12791 // very nasty use after free due to the place where the value "lives".
12792 mData->mSession.mRemoteControls.erase(it);
12793 }
12794
12795 /* signal the client watcher thread, because the client is going away */
12796 mParent->i_updateClientWatcher();
12797
12798 LogFlowThisFuncLeave();
12799 return S_OK;
12800}
12801
12802/**
12803 * @note Locks this object for writing.
12804 */
12805STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12806{
12807 LogFlowThisFuncEnter();
12808
12809 CheckComArgOutPointerValid(aProgress);
12810 CheckComArgOutPointerValid(aStateFilePath);
12811
12812 AutoCaller autoCaller(this);
12813 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12814
12815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12816
12817 AssertReturn( mData->mMachineState == MachineState_Paused
12818 && mConsoleTaskData.mLastState == MachineState_Null
12819 && mConsoleTaskData.strStateFilePath.isEmpty(),
12820 E_FAIL);
12821
12822 /* create a progress object to track operation completion */
12823 ComObjPtr<Progress> pProgress;
12824 pProgress.createObject();
12825 pProgress->init(i_getVirtualBox(),
12826 static_cast<IMachine *>(this) /* aInitiator */,
12827 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12828 FALSE /* aCancelable */);
12829
12830 Utf8Str strStateFilePath;
12831 /* stateFilePath is null when the machine is not running */
12832 if (mData->mMachineState == MachineState_Paused)
12833 i_composeSavedStateFilename(strStateFilePath);
12834
12835 /* fill in the console task data */
12836 mConsoleTaskData.mLastState = mData->mMachineState;
12837 mConsoleTaskData.strStateFilePath = strStateFilePath;
12838 mConsoleTaskData.mProgress = pProgress;
12839
12840 /* set the state to Saving (this is expected by Console::SaveState()) */
12841 i_setMachineState(MachineState_Saving);
12842
12843 strStateFilePath.cloneTo(aStateFilePath);
12844 pProgress.queryInterfaceTo(aProgress);
12845
12846 return S_OK;
12847}
12848
12849/**
12850 * @note Locks mParent + this object for writing.
12851 */
12852STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12853{
12854 LogFlowThisFunc(("\n"));
12855
12856 AutoCaller autoCaller(this);
12857 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12858
12859 /* endSavingState() need mParent lock */
12860 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12861
12862 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12863 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12864 && mConsoleTaskData.mLastState != MachineState_Null
12865 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12866 E_FAIL);
12867
12868 /*
12869 * On failure, set the state to the state we had when BeginSavingState()
12870 * was called (this is expected by Console::SaveState() and the associated
12871 * task). On success the VM process already changed the state to
12872 * MachineState_Saved, so no need to do anything.
12873 */
12874 if (FAILED(iResult))
12875 i_setMachineState(mConsoleTaskData.mLastState);
12876
12877 return endSavingState(iResult, aErrMsg);
12878}
12879
12880/**
12881 * @note Locks this object for writing.
12882 */
12883STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12884{
12885 LogFlowThisFunc(("\n"));
12886
12887 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12888
12889 AutoCaller autoCaller(this);
12890 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12891
12892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12893
12894 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12895 || mData->mMachineState == MachineState_Teleported
12896 || mData->mMachineState == MachineState_Aborted
12897 , E_FAIL); /** @todo setError. */
12898
12899 Utf8Str stateFilePathFull = aSavedStateFile;
12900 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
12901 if (RT_FAILURE(vrc))
12902 return setError(VBOX_E_FILE_ERROR,
12903 tr("Invalid saved state file path '%ls' (%Rrc)"),
12904 aSavedStateFile,
12905 vrc);
12906
12907 mSSData->strStateFilePath = stateFilePathFull;
12908
12909 /* The below i_setMachineState() will detect the state transition and will
12910 * update the settings file */
12911
12912 return i_setMachineState(MachineState_Saved);
12913}
12914
12915STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12916 ComSafeArrayOut(BSTR, aValues),
12917 ComSafeArrayOut(LONG64, aTimestamps),
12918 ComSafeArrayOut(BSTR, aFlags))
12919{
12920 LogFlowThisFunc(("\n"));
12921
12922#ifdef VBOX_WITH_GUEST_PROPS
12923 using namespace guestProp;
12924
12925 AutoCaller autoCaller(this);
12926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12927
12928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12929
12930 CheckComArgOutSafeArrayPointerValid(aNames);
12931 CheckComArgOutSafeArrayPointerValid(aValues);
12932 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12933 CheckComArgOutSafeArrayPointerValid(aFlags);
12934
12935 size_t cEntries = mHWData->mGuestProperties.size();
12936 com::SafeArray<BSTR> names(cEntries);
12937 com::SafeArray<BSTR> values(cEntries);
12938 com::SafeArray<LONG64> timestamps(cEntries);
12939 com::SafeArray<BSTR> flags(cEntries);
12940 unsigned i = 0;
12941 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12942 it != mHWData->mGuestProperties.end();
12943 ++it)
12944 {
12945 char szFlags[MAX_FLAGS_LEN + 1];
12946 it->first.cloneTo(&names[i]);
12947 it->second.strValue.cloneTo(&values[i]);
12948 timestamps[i] = it->second.mTimestamp;
12949 /* If it is NULL, keep it NULL. */
12950 if (it->second.mFlags)
12951 {
12952 writeFlags(it->second.mFlags, szFlags);
12953 Bstr(szFlags).cloneTo(&flags[i]);
12954 }
12955 else
12956 flags[i] = NULL;
12957 ++i;
12958 }
12959 names.detachTo(ComSafeArrayOutArg(aNames));
12960 values.detachTo(ComSafeArrayOutArg(aValues));
12961 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12962 flags.detachTo(ComSafeArrayOutArg(aFlags));
12963 return S_OK;
12964#else
12965 ReturnComNotImplemented();
12966#endif
12967}
12968
12969STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12970 IN_BSTR aValue,
12971 LONG64 aTimestamp,
12972 IN_BSTR aFlags)
12973{
12974 LogFlowThisFunc(("\n"));
12975
12976#ifdef VBOX_WITH_GUEST_PROPS
12977 using namespace guestProp;
12978
12979 CheckComArgStrNotEmptyOrNull(aName);
12980 CheckComArgNotNull(aValue);
12981 CheckComArgNotNull(aFlags);
12982
12983 try
12984 {
12985 /*
12986 * Convert input up front.
12987 */
12988 Utf8Str utf8Name(aName);
12989 uint32_t fFlags = NILFLAG;
12990 if (aFlags)
12991 {
12992 Utf8Str utf8Flags(aFlags);
12993 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12994 AssertRCReturn(vrc, E_INVALIDARG);
12995 }
12996
12997 /*
12998 * Now grab the object lock, validate the state and do the update.
12999 */
13000 AutoCaller autoCaller(this);
13001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13002
13003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13004
13005 switch (mData->mMachineState)
13006 {
13007 case MachineState_Paused:
13008 case MachineState_Running:
13009 case MachineState_Teleporting:
13010 case MachineState_TeleportingPausedVM:
13011 case MachineState_LiveSnapshotting:
13012 case MachineState_DeletingSnapshotOnline:
13013 case MachineState_DeletingSnapshotPaused:
13014 case MachineState_Saving:
13015 case MachineState_Stopping:
13016 break;
13017
13018 default:
13019 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13020 VBOX_E_INVALID_VM_STATE);
13021 }
13022
13023 i_setModified(IsModified_MachineData);
13024 mHWData.backup();
13025
13026 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13027 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13028 if (it != mHWData->mGuestProperties.end())
13029 {
13030 if (!fDelete)
13031 {
13032 it->second.strValue = aValue;
13033 it->second.mTimestamp = aTimestamp;
13034 it->second.mFlags = fFlags;
13035 }
13036 else
13037 mHWData->mGuestProperties.erase(it);
13038
13039 mData->mGuestPropertiesModified = TRUE;
13040 }
13041 else if (!fDelete)
13042 {
13043 HWData::GuestProperty prop;
13044 prop.strValue = aValue;
13045 prop.mTimestamp = aTimestamp;
13046 prop.mFlags = fFlags;
13047
13048 mHWData->mGuestProperties[utf8Name] = prop;
13049 mData->mGuestPropertiesModified = TRUE;
13050 }
13051
13052 /*
13053 * Send a callback notification if appropriate
13054 */
13055 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13056 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13057 RTSTR_MAX,
13058 utf8Name.c_str(),
13059 RTSTR_MAX, NULL)
13060 )
13061 {
13062 alock.release();
13063
13064 mParent->i_onGuestPropertyChange(mData->mUuid,
13065 aName,
13066 aValue,
13067 aFlags);
13068 }
13069 }
13070 catch (...)
13071 {
13072 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13073 }
13074 return S_OK;
13075#else
13076 ReturnComNotImplemented();
13077#endif
13078}
13079
13080STDMETHODIMP SessionMachine::LockMedia()
13081{
13082 AutoCaller autoCaller(this);
13083 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13084
13085 AutoMultiWriteLock2 alock(this->lockHandle(),
13086 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13087
13088 AssertReturn( mData->mMachineState == MachineState_Starting
13089 || mData->mMachineState == MachineState_Restoring
13090 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13091
13092 clearError();
13093 alock.release();
13094 return lockMedia();
13095}
13096
13097STDMETHODIMP SessionMachine::UnlockMedia()
13098{
13099 HRESULT hrc = unlockMedia();
13100 return hrc;
13101}
13102
13103STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13104 IMediumAttachment **aNewAttachment)
13105{
13106 CheckComArgNotNull(aAttachment);
13107 CheckComArgOutPointerValid(aNewAttachment);
13108
13109 AutoCaller autoCaller(this);
13110 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13111
13112 // request the host lock first, since might be calling Host methods for getting host drives;
13113 // next, protect the media tree all the while we're in here, as well as our member variables
13114 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13115 this->lockHandle(),
13116 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13117
13118 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13119
13120 Bstr ctrlName;
13121 LONG lPort;
13122 LONG lDevice;
13123 bool fTempEject;
13124 {
13125 AutoCaller autoAttachCaller(this);
13126 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13127
13128 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13129
13130 /* Need to query the details first, as the IMediumAttachment reference
13131 * might be to the original settings, which we are going to change. */
13132 ctrlName = pAttach->i_getControllerName();
13133 lPort = pAttach->i_getPort();
13134 lDevice = pAttach->i_getDevice();
13135 fTempEject = pAttach->i_getTempEject();
13136 }
13137
13138 if (!fTempEject)
13139 {
13140 /* Remember previously mounted medium. The medium before taking the
13141 * backup is not necessarily the same thing. */
13142 ComObjPtr<Medium> oldmedium;
13143 oldmedium = pAttach->i_getMedium();
13144
13145 i_setModified(IsModified_Storage);
13146 mMediaData.backup();
13147
13148 // The backup operation makes the pAttach reference point to the
13149 // old settings. Re-get the correct reference.
13150 pAttach = i_findAttachment(mMediaData->mAttachments,
13151 ctrlName.raw(),
13152 lPort,
13153 lDevice);
13154
13155 {
13156 AutoCaller autoAttachCaller(this);
13157 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13158
13159 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13160 if (!oldmedium.isNull())
13161 oldmedium->i_removeBackReference(mData->mUuid);
13162
13163 pAttach->i_updateMedium(NULL);
13164 pAttach->i_updateEjected();
13165 }
13166
13167 i_setModified(IsModified_Storage);
13168 }
13169 else
13170 {
13171 {
13172 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13173 pAttach->i_updateEjected();
13174 }
13175 }
13176
13177 pAttach.queryInterfaceTo(aNewAttachment);
13178
13179 return S_OK;
13180}
13181
13182// public methods only for internal purposes
13183/////////////////////////////////////////////////////////////////////////////
13184
13185#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13186/**
13187 * Called from the client watcher thread to check for expected or unexpected
13188 * death of the client process that has a direct session to this machine.
13189 *
13190 * On Win32 and on OS/2, this method is called only when we've got the
13191 * mutex (i.e. the client has either died or terminated normally) so it always
13192 * returns @c true (the client is terminated, the session machine is
13193 * uninitialized).
13194 *
13195 * On other platforms, the method returns @c true if the client process has
13196 * terminated normally or abnormally and the session machine was uninitialized,
13197 * and @c false if the client process is still alive.
13198 *
13199 * @note Locks this object for writing.
13200 */
13201bool SessionMachine::i_checkForDeath()
13202{
13203 Uninit::Reason reason;
13204 bool terminated = false;
13205
13206 /* Enclose autoCaller with a block because calling uninit() from under it
13207 * will deadlock. */
13208 {
13209 AutoCaller autoCaller(this);
13210 if (!autoCaller.isOk())
13211 {
13212 /* return true if not ready, to cause the client watcher to exclude
13213 * the corresponding session from watching */
13214 LogFlowThisFunc(("Already uninitialized!\n"));
13215 return true;
13216 }
13217
13218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13219
13220 /* Determine the reason of death: if the session state is Closing here,
13221 * everything is fine. Otherwise it means that the client did not call
13222 * OnSessionEnd() before it released the IPC semaphore. This may happen
13223 * either because the client process has abnormally terminated, or
13224 * because it simply forgot to call ISession::Close() before exiting. We
13225 * threat the latter also as an abnormal termination (see
13226 * Session::uninit() for details). */
13227 reason = mData->mSession.mState == SessionState_Unlocking ?
13228 Uninit::Normal :
13229 Uninit::Abnormal;
13230
13231 if (mClientToken)
13232 terminated = mClientToken->release();
13233 } /* AutoCaller block */
13234
13235 if (terminated)
13236 uninit(reason);
13237
13238 return terminated;
13239}
13240
13241void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13242{
13243 LogFlowThisFunc(("\n"));
13244
13245 strTokenId.setNull();
13246
13247 AutoCaller autoCaller(this);
13248 AssertComRCReturnVoid(autoCaller.rc());
13249
13250 Assert(mClientToken);
13251 if (mClientToken)
13252 mClientToken->getId(strTokenId);
13253}
13254#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13255IToken *SessionMachine::i_getToken()
13256{
13257 LogFlowThisFunc(("\n"));
13258
13259 AutoCaller autoCaller(this);
13260 AssertComRCReturn(autoCaller.rc(), NULL);
13261
13262 Assert(mClientToken);
13263 if (mClientToken)
13264 return mClientToken->getToken();
13265 else
13266 return NULL;
13267}
13268#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13269
13270Machine::ClientToken *SessionMachine::i_getClientToken()
13271{
13272 LogFlowThisFunc(("\n"));
13273
13274 AutoCaller autoCaller(this);
13275 AssertComRCReturn(autoCaller.rc(), NULL);
13276
13277 return mClientToken;
13278}
13279
13280
13281/**
13282 * @note Locks this object for reading.
13283 */
13284HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13285{
13286 LogFlowThisFunc(("\n"));
13287
13288 AutoCaller autoCaller(this);
13289 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13290
13291 ComPtr<IInternalSessionControl> directControl;
13292 {
13293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13294 directControl = mData->mSession.mDirectControl;
13295 }
13296
13297 /* ignore notifications sent after #OnSessionEnd() is called */
13298 if (!directControl)
13299 return S_OK;
13300
13301 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13302}
13303
13304/**
13305 * @note Locks this object for reading.
13306 */
13307HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13308 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13309 IN_BSTR aGuestIp, LONG aGuestPort)
13310{
13311 LogFlowThisFunc(("\n"));
13312
13313 AutoCaller autoCaller(this);
13314 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13315
13316 ComPtr<IInternalSessionControl> directControl;
13317 {
13318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13319 directControl = mData->mSession.mDirectControl;
13320 }
13321
13322 /* ignore notifications sent after #OnSessionEnd() is called */
13323 if (!directControl)
13324 return S_OK;
13325 /*
13326 * instead acting like callback we ask IVirtualBox deliver corresponding event
13327 */
13328
13329 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13330 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13331 return S_OK;
13332}
13333
13334/**
13335 * @note Locks this object for reading.
13336 */
13337HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13338{
13339 LogFlowThisFunc(("\n"));
13340
13341 AutoCaller autoCaller(this);
13342 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13343
13344 ComPtr<IInternalSessionControl> directControl;
13345 {
13346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13347 directControl = mData->mSession.mDirectControl;
13348 }
13349
13350 /* ignore notifications sent after #OnSessionEnd() is called */
13351 if (!directControl)
13352 return S_OK;
13353
13354 return directControl->OnSerialPortChange(serialPort);
13355}
13356
13357/**
13358 * @note Locks this object for reading.
13359 */
13360HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13361{
13362 LogFlowThisFunc(("\n"));
13363
13364 AutoCaller autoCaller(this);
13365 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13366
13367 ComPtr<IInternalSessionControl> directControl;
13368 {
13369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13370 directControl = mData->mSession.mDirectControl;
13371 }
13372
13373 /* ignore notifications sent after #OnSessionEnd() is called */
13374 if (!directControl)
13375 return S_OK;
13376
13377 return directControl->OnParallelPortChange(parallelPort);
13378}
13379
13380/**
13381 * @note Locks this object for reading.
13382 */
13383HRESULT SessionMachine::i_onStorageControllerChange()
13384{
13385 LogFlowThisFunc(("\n"));
13386
13387 AutoCaller autoCaller(this);
13388 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13389
13390 ComPtr<IInternalSessionControl> directControl;
13391 {
13392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13393 directControl = mData->mSession.mDirectControl;
13394 }
13395
13396 /* ignore notifications sent after #OnSessionEnd() is called */
13397 if (!directControl)
13398 return S_OK;
13399
13400 return directControl->OnStorageControllerChange();
13401}
13402
13403/**
13404 * @note Locks this object for reading.
13405 */
13406HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13407{
13408 LogFlowThisFunc(("\n"));
13409
13410 AutoCaller autoCaller(this);
13411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13412
13413 ComPtr<IInternalSessionControl> directControl;
13414 {
13415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13416 directControl = mData->mSession.mDirectControl;
13417 }
13418
13419 /* ignore notifications sent after #OnSessionEnd() is called */
13420 if (!directControl)
13421 return S_OK;
13422
13423 return directControl->OnMediumChange(aAttachment, aForce);
13424}
13425
13426/**
13427 * @note Locks this object for reading.
13428 */
13429HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13430{
13431 LogFlowThisFunc(("\n"));
13432
13433 AutoCaller autoCaller(this);
13434 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13435
13436 ComPtr<IInternalSessionControl> directControl;
13437 {
13438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13439 directControl = mData->mSession.mDirectControl;
13440 }
13441
13442 /* ignore notifications sent after #OnSessionEnd() is called */
13443 if (!directControl)
13444 return S_OK;
13445
13446 return directControl->OnCPUChange(aCPU, aRemove);
13447}
13448
13449HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13450{
13451 LogFlowThisFunc(("\n"));
13452
13453 AutoCaller autoCaller(this);
13454 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13455
13456 ComPtr<IInternalSessionControl> directControl;
13457 {
13458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13459 directControl = mData->mSession.mDirectControl;
13460 }
13461
13462 /* ignore notifications sent after #OnSessionEnd() is called */
13463 if (!directControl)
13464 return S_OK;
13465
13466 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13467}
13468
13469/**
13470 * @note Locks this object for reading.
13471 */
13472HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13473{
13474 LogFlowThisFunc(("\n"));
13475
13476 AutoCaller autoCaller(this);
13477 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13478
13479 ComPtr<IInternalSessionControl> directControl;
13480 {
13481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13482 directControl = mData->mSession.mDirectControl;
13483 }
13484
13485 /* ignore notifications sent after #OnSessionEnd() is called */
13486 if (!directControl)
13487 return S_OK;
13488
13489 return directControl->OnVRDEServerChange(aRestart);
13490}
13491
13492/**
13493 * @note Locks this object for reading.
13494 */
13495HRESULT SessionMachine::i_onVideoCaptureChange()
13496{
13497 LogFlowThisFunc(("\n"));
13498
13499 AutoCaller autoCaller(this);
13500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13501
13502 ComPtr<IInternalSessionControl> directControl;
13503 {
13504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13505 directControl = mData->mSession.mDirectControl;
13506 }
13507
13508 /* ignore notifications sent after #OnSessionEnd() is called */
13509 if (!directControl)
13510 return S_OK;
13511
13512 return directControl->OnVideoCaptureChange();
13513}
13514
13515/**
13516 * @note Locks this object for reading.
13517 */
13518HRESULT SessionMachine::i_onUSBControllerChange()
13519{
13520 LogFlowThisFunc(("\n"));
13521
13522 AutoCaller autoCaller(this);
13523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13524
13525 ComPtr<IInternalSessionControl> directControl;
13526 {
13527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13528 directControl = mData->mSession.mDirectControl;
13529 }
13530
13531 /* ignore notifications sent after #OnSessionEnd() is called */
13532 if (!directControl)
13533 return S_OK;
13534
13535 return directControl->OnUSBControllerChange();
13536}
13537
13538/**
13539 * @note Locks this object for reading.
13540 */
13541HRESULT SessionMachine::i_onSharedFolderChange()
13542{
13543 LogFlowThisFunc(("\n"));
13544
13545 AutoCaller autoCaller(this);
13546 AssertComRCReturnRC(autoCaller.rc());
13547
13548 ComPtr<IInternalSessionControl> directControl;
13549 {
13550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13551 directControl = mData->mSession.mDirectControl;
13552 }
13553
13554 /* ignore notifications sent after #OnSessionEnd() is called */
13555 if (!directControl)
13556 return S_OK;
13557
13558 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13559}
13560
13561/**
13562 * @note Locks this object for reading.
13563 */
13564HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13565{
13566 LogFlowThisFunc(("\n"));
13567
13568 AutoCaller autoCaller(this);
13569 AssertComRCReturnRC(autoCaller.rc());
13570
13571 ComPtr<IInternalSessionControl> directControl;
13572 {
13573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13574 directControl = mData->mSession.mDirectControl;
13575 }
13576
13577 /* ignore notifications sent after #OnSessionEnd() is called */
13578 if (!directControl)
13579 return S_OK;
13580
13581 return directControl->OnClipboardModeChange(aClipboardMode);
13582}
13583
13584/**
13585 * @note Locks this object for reading.
13586 */
13587HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13588{
13589 LogFlowThisFunc(("\n"));
13590
13591 AutoCaller autoCaller(this);
13592 AssertComRCReturnRC(autoCaller.rc());
13593
13594 ComPtr<IInternalSessionControl> directControl;
13595 {
13596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13597 directControl = mData->mSession.mDirectControl;
13598 }
13599
13600 /* ignore notifications sent after #OnSessionEnd() is called */
13601 if (!directControl)
13602 return S_OK;
13603
13604 return directControl->OnDnDModeChange(aDnDMode);
13605}
13606
13607/**
13608 * @note Locks this object for reading.
13609 */
13610HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13611{
13612 LogFlowThisFunc(("\n"));
13613
13614 AutoCaller autoCaller(this);
13615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13616
13617 ComPtr<IInternalSessionControl> directControl;
13618 {
13619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13620 directControl = mData->mSession.mDirectControl;
13621 }
13622
13623 /* ignore notifications sent after #OnSessionEnd() is called */
13624 if (!directControl)
13625 return S_OK;
13626
13627 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13628}
13629
13630/**
13631 * @note Locks this object for reading.
13632 */
13633HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13634{
13635 LogFlowThisFunc(("\n"));
13636
13637 AutoCaller autoCaller(this);
13638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13639
13640 ComPtr<IInternalSessionControl> directControl;
13641 {
13642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13643 directControl = mData->mSession.mDirectControl;
13644 }
13645
13646 /* ignore notifications sent after #OnSessionEnd() is called */
13647 if (!directControl)
13648 return S_OK;
13649
13650 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13651}
13652
13653/**
13654 * Returns @c true if this machine's USB controller reports it has a matching
13655 * filter for the given USB device and @c false otherwise.
13656 *
13657 * @note locks this object for reading.
13658 */
13659bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13660{
13661 AutoCaller autoCaller(this);
13662 /* silently return if not ready -- this method may be called after the
13663 * direct machine session has been called */
13664 if (!autoCaller.isOk())
13665 return false;
13666
13667#ifdef VBOX_WITH_USB
13668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13669
13670 switch (mData->mMachineState)
13671 {
13672 case MachineState_Starting:
13673 case MachineState_Restoring:
13674 case MachineState_TeleportingIn:
13675 case MachineState_Paused:
13676 case MachineState_Running:
13677 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13678 * elsewhere... */
13679 alock.release();
13680 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13681 default: break;
13682 }
13683#else
13684 NOREF(aDevice);
13685 NOREF(aMaskedIfs);
13686#endif
13687 return false;
13688}
13689
13690/**
13691 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13692 */
13693HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13694 IVirtualBoxErrorInfo *aError,
13695 ULONG aMaskedIfs)
13696{
13697 LogFlowThisFunc(("\n"));
13698
13699 AutoCaller autoCaller(this);
13700
13701 /* This notification may happen after the machine object has been
13702 * uninitialized (the session was closed), so don't assert. */
13703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13704
13705 ComPtr<IInternalSessionControl> directControl;
13706 {
13707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13708 directControl = mData->mSession.mDirectControl;
13709 }
13710
13711 /* fail on notifications sent after #OnSessionEnd() is called, it is
13712 * expected by the caller */
13713 if (!directControl)
13714 return E_FAIL;
13715
13716 /* No locks should be held at this point. */
13717 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13718 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13719
13720 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13721}
13722
13723/**
13724 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13725 */
13726HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13727 IVirtualBoxErrorInfo *aError)
13728{
13729 LogFlowThisFunc(("\n"));
13730
13731 AutoCaller autoCaller(this);
13732
13733 /* This notification may happen after the machine object has been
13734 * uninitialized (the session was closed), so don't assert. */
13735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13736
13737 ComPtr<IInternalSessionControl> directControl;
13738 {
13739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13740 directControl = mData->mSession.mDirectControl;
13741 }
13742
13743 /* fail on notifications sent after #OnSessionEnd() is called, it is
13744 * expected by the caller */
13745 if (!directControl)
13746 return E_FAIL;
13747
13748 /* No locks should be held at this point. */
13749 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13750 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13751
13752 return directControl->OnUSBDeviceDetach(aId, aError);
13753}
13754
13755// protected methods
13756/////////////////////////////////////////////////////////////////////////////
13757
13758/**
13759 * Helper method to finalize saving the state.
13760 *
13761 * @note Must be called from under this object's lock.
13762 *
13763 * @param aRc S_OK if the snapshot has been taken successfully
13764 * @param aErrMsg human readable error message for failure
13765 *
13766 * @note Locks mParent + this objects for writing.
13767 */
13768HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13769{
13770 LogFlowThisFuncEnter();
13771
13772 AutoCaller autoCaller(this);
13773 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13774
13775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13776
13777 HRESULT rc = S_OK;
13778
13779 if (SUCCEEDED(aRc))
13780 {
13781 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13782
13783 /* save all VM settings */
13784 rc = i_saveSettings(NULL);
13785 // no need to check whether VirtualBox.xml needs saving also since
13786 // we can't have a name change pending at this point
13787 }
13788 else
13789 {
13790 // delete the saved state file (it might have been already created);
13791 // we need not check whether this is shared with a snapshot here because
13792 // we certainly created this saved state file here anew
13793 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13794 }
13795
13796 /* notify the progress object about operation completion */
13797 Assert(mConsoleTaskData.mProgress);
13798 if (SUCCEEDED(aRc))
13799 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13800 else
13801 {
13802 if (aErrMsg.length())
13803 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13804 COM_IIDOF(ISession),
13805 getComponentName(),
13806 aErrMsg.c_str());
13807 else
13808 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13809 }
13810
13811 /* clear out the temporary saved state data */
13812 mConsoleTaskData.mLastState = MachineState_Null;
13813 mConsoleTaskData.strStateFilePath.setNull();
13814 mConsoleTaskData.mProgress.setNull();
13815
13816 LogFlowThisFuncLeave();
13817 return rc;
13818}
13819
13820/**
13821 * Deletes the given file if it is no longer in use by either the current machine state
13822 * (if the machine is "saved") or any of the machine's snapshots.
13823 *
13824 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13825 * but is different for each SnapshotMachine. When calling this, the order of calling this
13826 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13827 * is therefore critical. I know, it's all rather messy.
13828 *
13829 * @param strStateFile
13830 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13831 * the test for whether the saved state file is in use.
13832 */
13833void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13834 Snapshot *pSnapshotToIgnore)
13835{
13836 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13837 if ( (strStateFile.isNotEmpty())
13838 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13839 )
13840 // ... and it must also not be shared with other snapshots
13841 if ( !mData->mFirstSnapshot
13842 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13843 // this checks the SnapshotMachine's state file paths
13844 )
13845 RTFileDelete(strStateFile.c_str());
13846}
13847
13848/**
13849 * Locks the attached media.
13850 *
13851 * All attached hard disks are locked for writing and DVD/floppy are locked for
13852 * reading. Parents of attached hard disks (if any) are locked for reading.
13853 *
13854 * This method also performs accessibility check of all media it locks: if some
13855 * media is inaccessible, the method will return a failure and a bunch of
13856 * extended error info objects per each inaccessible medium.
13857 *
13858 * Note that this method is atomic: if it returns a success, all media are
13859 * locked as described above; on failure no media is locked at all (all
13860 * succeeded individual locks will be undone).
13861 *
13862 * The caller is responsible for doing the necessary state sanity checks.
13863 *
13864 * The locks made by this method must be undone by calling #unlockMedia() when
13865 * no more needed.
13866 */
13867HRESULT SessionMachine::lockMedia()
13868{
13869 AutoCaller autoCaller(this);
13870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13871
13872 AutoMultiWriteLock2 alock(this->lockHandle(),
13873 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13874
13875 /* bail out if trying to lock things with already set up locking */
13876 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13877
13878 MultiResult mrc(S_OK);
13879
13880 /* Collect locking information for all medium objects attached to the VM. */
13881 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13882 it != mMediaData->mAttachments.end();
13883 ++it)
13884 {
13885 MediumAttachment* pAtt = *it;
13886 DeviceType_T devType = pAtt->i_getType();
13887 Medium *pMedium = pAtt->i_getMedium();
13888
13889 MediumLockList *pMediumLockList(new MediumLockList());
13890 // There can be attachments without a medium (floppy/dvd), and thus
13891 // it's impossible to create a medium lock list. It still makes sense
13892 // to have the empty medium lock list in the map in case a medium is
13893 // attached later.
13894 if (pMedium != NULL)
13895 {
13896 MediumType_T mediumType = pMedium->i_getType();
13897 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13898 || mediumType == MediumType_Shareable;
13899 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13900
13901 alock.release();
13902 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13903 !fIsReadOnlyLock /* fMediumLockWrite */,
13904 NULL,
13905 *pMediumLockList);
13906 alock.acquire();
13907 if (FAILED(mrc))
13908 {
13909 delete pMediumLockList;
13910 mData->mSession.mLockedMedia.Clear();
13911 break;
13912 }
13913 }
13914
13915 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13916 if (FAILED(rc))
13917 {
13918 mData->mSession.mLockedMedia.Clear();
13919 mrc = setError(rc,
13920 tr("Collecting locking information for all attached media failed"));
13921 break;
13922 }
13923 }
13924
13925 if (SUCCEEDED(mrc))
13926 {
13927 /* Now lock all media. If this fails, nothing is locked. */
13928 alock.release();
13929 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13930 alock.acquire();
13931 if (FAILED(rc))
13932 {
13933 mrc = setError(rc,
13934 tr("Locking of attached media failed"));
13935 }
13936 }
13937
13938 return mrc;
13939}
13940
13941/**
13942 * Undoes the locks made by by #lockMedia().
13943 */
13944HRESULT SessionMachine::unlockMedia()
13945{
13946 AutoCaller autoCaller(this);
13947 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
13948
13949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13950
13951 /* we may be holding important error info on the current thread;
13952 * preserve it */
13953 ErrorInfoKeeper eik;
13954
13955 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13956 AssertComRC(rc);
13957 return rc;
13958}
13959
13960/**
13961 * Helper to change the machine state (reimplementation).
13962 *
13963 * @note Locks this object for writing.
13964 * @note This method must not call i_saveSettings or SaveSettings, otherwise
13965 * it can cause crashes in random places due to unexpectedly committing
13966 * the current settings. The caller is responsible for that. The call
13967 * to saveStateSettings is fine, because this method does not commit.
13968 */
13969HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
13970{
13971 LogFlowThisFuncEnter();
13972 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13973
13974 AutoCaller autoCaller(this);
13975 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13976
13977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13978
13979 MachineState_T oldMachineState = mData->mMachineState;
13980
13981 AssertMsgReturn(oldMachineState != aMachineState,
13982 ("oldMachineState=%s, aMachineState=%s\n",
13983 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13984 E_FAIL);
13985
13986 HRESULT rc = S_OK;
13987
13988 int stsFlags = 0;
13989 bool deleteSavedState = false;
13990
13991 /* detect some state transitions */
13992
13993 if ( ( oldMachineState == MachineState_Saved
13994 && aMachineState == MachineState_Restoring)
13995 || ( ( oldMachineState == MachineState_PoweredOff
13996 || oldMachineState == MachineState_Teleported
13997 || oldMachineState == MachineState_Aborted
13998 )
13999 && ( aMachineState == MachineState_TeleportingIn
14000 || aMachineState == MachineState_Starting
14001 )
14002 )
14003 )
14004 {
14005 /* The EMT thread is about to start */
14006
14007 /* Nothing to do here for now... */
14008
14009 /// @todo NEWMEDIA don't let mDVDDrive and other children
14010 /// change anything when in the Starting/Restoring state
14011 }
14012 else if ( ( oldMachineState == MachineState_Running
14013 || oldMachineState == MachineState_Paused
14014 || oldMachineState == MachineState_Teleporting
14015 || oldMachineState == MachineState_LiveSnapshotting
14016 || oldMachineState == MachineState_Stuck
14017 || oldMachineState == MachineState_Starting
14018 || oldMachineState == MachineState_Stopping
14019 || oldMachineState == MachineState_Saving
14020 || oldMachineState == MachineState_Restoring
14021 || oldMachineState == MachineState_TeleportingPausedVM
14022 || oldMachineState == MachineState_TeleportingIn
14023 )
14024 && ( aMachineState == MachineState_PoweredOff
14025 || aMachineState == MachineState_Saved
14026 || aMachineState == MachineState_Teleported
14027 || aMachineState == MachineState_Aborted
14028 )
14029 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14030 * snapshot */
14031 && ( mConsoleTaskData.mSnapshot.isNull()
14032 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14033 )
14034 )
14035 {
14036 /* The EMT thread has just stopped, unlock attached media. Note that as
14037 * opposed to locking that is done from Console, we do unlocking here
14038 * because the VM process may have aborted before having a chance to
14039 * properly unlock all media it locked. */
14040
14041 unlockMedia();
14042 }
14043
14044 if (oldMachineState == MachineState_Restoring)
14045 {
14046 if (aMachineState != MachineState_Saved)
14047 {
14048 /*
14049 * delete the saved state file once the machine has finished
14050 * restoring from it (note that Console sets the state from
14051 * Restoring to Saved if the VM couldn't restore successfully,
14052 * to give the user an ability to fix an error and retry --
14053 * we keep the saved state file in this case)
14054 */
14055 deleteSavedState = true;
14056 }
14057 }
14058 else if ( oldMachineState == MachineState_Saved
14059 && ( aMachineState == MachineState_PoweredOff
14060 || aMachineState == MachineState_Aborted
14061 || aMachineState == MachineState_Teleported
14062 )
14063 )
14064 {
14065 /*
14066 * delete the saved state after Console::ForgetSavedState() is called
14067 * or if the VM process (owning a direct VM session) crashed while the
14068 * VM was Saved
14069 */
14070
14071 /// @todo (dmik)
14072 // Not sure that deleting the saved state file just because of the
14073 // client death before it attempted to restore the VM is a good
14074 // thing. But when it crashes we need to go to the Aborted state
14075 // which cannot have the saved state file associated... The only
14076 // way to fix this is to make the Aborted condition not a VM state
14077 // but a bool flag: i.e., when a crash occurs, set it to true and
14078 // change the state to PoweredOff or Saved depending on the
14079 // saved state presence.
14080
14081 deleteSavedState = true;
14082 mData->mCurrentStateModified = TRUE;
14083 stsFlags |= SaveSTS_CurStateModified;
14084 }
14085
14086 if ( aMachineState == MachineState_Starting
14087 || aMachineState == MachineState_Restoring
14088 || aMachineState == MachineState_TeleportingIn
14089 )
14090 {
14091 /* set the current state modified flag to indicate that the current
14092 * state is no more identical to the state in the
14093 * current snapshot */
14094 if (!mData->mCurrentSnapshot.isNull())
14095 {
14096 mData->mCurrentStateModified = TRUE;
14097 stsFlags |= SaveSTS_CurStateModified;
14098 }
14099 }
14100
14101 if (deleteSavedState)
14102 {
14103 if (mRemoveSavedState)
14104 {
14105 Assert(!mSSData->strStateFilePath.isEmpty());
14106
14107 // it is safe to delete the saved state file if ...
14108 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14109 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14110 // ... none of the snapshots share the saved state file
14111 )
14112 RTFileDelete(mSSData->strStateFilePath.c_str());
14113 }
14114
14115 mSSData->strStateFilePath.setNull();
14116 stsFlags |= SaveSTS_StateFilePath;
14117 }
14118
14119 /* redirect to the underlying peer machine */
14120 mPeer->i_setMachineState(aMachineState);
14121
14122 if ( aMachineState == MachineState_PoweredOff
14123 || aMachineState == MachineState_Teleported
14124 || aMachineState == MachineState_Aborted
14125 || aMachineState == MachineState_Saved)
14126 {
14127 /* the machine has stopped execution
14128 * (or the saved state file was adopted) */
14129 stsFlags |= SaveSTS_StateTimeStamp;
14130 }
14131
14132 if ( ( oldMachineState == MachineState_PoweredOff
14133 || oldMachineState == MachineState_Aborted
14134 || oldMachineState == MachineState_Teleported
14135 )
14136 && aMachineState == MachineState_Saved)
14137 {
14138 /* the saved state file was adopted */
14139 Assert(!mSSData->strStateFilePath.isEmpty());
14140 stsFlags |= SaveSTS_StateFilePath;
14141 }
14142
14143#ifdef VBOX_WITH_GUEST_PROPS
14144 if ( aMachineState == MachineState_PoweredOff
14145 || aMachineState == MachineState_Aborted
14146 || aMachineState == MachineState_Teleported)
14147 {
14148 /* Make sure any transient guest properties get removed from the
14149 * property store on shutdown. */
14150
14151 HWData::GuestPropertyMap::const_iterator it;
14152 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14153 if (!fNeedsSaving)
14154 for (it = mHWData->mGuestProperties.begin();
14155 it != mHWData->mGuestProperties.end(); ++it)
14156 if ( (it->second.mFlags & guestProp::TRANSIENT)
14157 || (it->second.mFlags & guestProp::TRANSRESET))
14158 {
14159 fNeedsSaving = true;
14160 break;
14161 }
14162 if (fNeedsSaving)
14163 {
14164 mData->mCurrentStateModified = TRUE;
14165 stsFlags |= SaveSTS_CurStateModified;
14166 }
14167 }
14168#endif
14169
14170 rc = i_saveStateSettings(stsFlags);
14171
14172 if ( ( oldMachineState != MachineState_PoweredOff
14173 && oldMachineState != MachineState_Aborted
14174 && oldMachineState != MachineState_Teleported
14175 )
14176 && ( aMachineState == MachineState_PoweredOff
14177 || aMachineState == MachineState_Aborted
14178 || aMachineState == MachineState_Teleported
14179 )
14180 )
14181 {
14182 /* we've been shut down for any reason */
14183 /* no special action so far */
14184 }
14185
14186 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14187 LogFlowThisFuncLeave();
14188 return rc;
14189}
14190
14191/**
14192 * Sends the current machine state value to the VM process.
14193 *
14194 * @note Locks this object for reading, then calls a client process.
14195 */
14196HRESULT SessionMachine::i_updateMachineStateOnClient()
14197{
14198 AutoCaller autoCaller(this);
14199 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14200
14201 ComPtr<IInternalSessionControl> directControl;
14202 {
14203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14204 AssertReturn(!!mData, E_FAIL);
14205 directControl = mData->mSession.mDirectControl;
14206
14207 /* directControl may be already set to NULL here in #OnSessionEnd()
14208 * called too early by the direct session process while there is still
14209 * some operation (like deleting the snapshot) in progress. The client
14210 * process in this case is waiting inside Session::close() for the
14211 * "end session" process object to complete, while #uninit() called by
14212 * #checkForDeath() on the Watcher thread is waiting for the pending
14213 * operation to complete. For now, we accept this inconsistent behavior
14214 * and simply do nothing here. */
14215
14216 if (mData->mSession.mState == SessionState_Unlocking)
14217 return S_OK;
14218
14219 AssertReturn(!directControl.isNull(), E_FAIL);
14220 }
14221
14222 return directControl->UpdateMachineState(mData->mMachineState);
14223}
14224
14225HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14226{
14227 NOREF(aRemove);
14228 ReturnComNotImplemented();
14229}
14230
14231HRESULT Machine::updateState(MachineState_T aState)
14232{
14233 NOREF(aState);
14234 ReturnComNotImplemented();
14235}
14236
14237HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14238{
14239 NOREF(aProgress);
14240 ReturnComNotImplemented();
14241}
14242
14243HRESULT Machine::endPowerUp(LONG aResult)
14244{
14245 NOREF(aResult);
14246 ReturnComNotImplemented();
14247}
14248
14249HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14250{
14251 NOREF(aProgress);
14252 ReturnComNotImplemented();
14253}
14254
14255HRESULT Machine::endPoweringDown(LONG aResult,
14256 const com::Utf8Str &aErrMsg)
14257{
14258 NOREF(aResult);
14259 NOREF(aErrMsg);
14260 ReturnComNotImplemented();
14261}
14262
14263HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14264 BOOL *aMatched,
14265 ULONG *aMaskedInterfaces)
14266{
14267 NOREF(aDevice);
14268 NOREF(aMatched);
14269 NOREF(aMaskedInterfaces);
14270 ReturnComNotImplemented();
14271
14272}
14273
14274HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14275{
14276 NOREF(aId);
14277 ReturnComNotImplemented();
14278}
14279
14280HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14281 BOOL aDone)
14282{
14283 NOREF(aId);
14284 NOREF(aDone);
14285 ReturnComNotImplemented();
14286}
14287
14288HRESULT Machine::autoCaptureUSBDevices()
14289{
14290 ReturnComNotImplemented();
14291}
14292
14293HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14294{
14295 NOREF(aDone);
14296 ReturnComNotImplemented();
14297}
14298
14299HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14300 ComPtr<IProgress> &aProgress)
14301{
14302 NOREF(aSession);
14303 NOREF(aProgress);
14304 ReturnComNotImplemented();
14305}
14306
14307HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14308 com::Utf8Str &aStateFilePath)
14309{
14310 NOREF(aProgress);
14311 NOREF(aStateFilePath);
14312 ReturnComNotImplemented();
14313}
14314
14315HRESULT Machine::endSavingState(LONG aResult,
14316 const com::Utf8Str &aErrMsg)
14317{
14318 NOREF(aResult);
14319 NOREF(aErrMsg);
14320 ReturnComNotImplemented();
14321}
14322
14323HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14324{
14325 NOREF(aSavedStateFile);
14326 ReturnComNotImplemented();
14327}
14328
14329HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14330 const com::Utf8Str &aName,
14331 const com::Utf8Str &aDescription,
14332 const ComPtr<IProgress> &aConsoleProgress,
14333 BOOL aFTakingSnapshotOnline,
14334 com::Utf8Str &aStateFilePath)
14335{
14336 NOREF(aInitiator);
14337 NOREF(aName);
14338 NOREF(aDescription);
14339 NOREF(aConsoleProgress);
14340 NOREF(aFTakingSnapshotOnline);
14341 NOREF(aStateFilePath);
14342 ReturnComNotImplemented();
14343}
14344
14345HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14346{
14347 NOREF(aSuccess);
14348 ReturnComNotImplemented();
14349}
14350
14351HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14352 const com::Guid &aStartId,
14353 const com::Guid &aEndId,
14354 BOOL aDeleteAllChildren,
14355 MachineState_T *aMachineState,
14356 ComPtr<IProgress> &aProgress)
14357{
14358 NOREF(aInitiator);
14359 NOREF(aStartId);
14360 NOREF(aEndId);
14361 NOREF(aDeleteAllChildren);
14362 NOREF(aMachineState);
14363 NOREF(aProgress);
14364 ReturnComNotImplemented();
14365}
14366
14367HRESULT Machine::finishOnlineMergeMedium()
14368{
14369 ReturnComNotImplemented();
14370}
14371
14372HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14373 const ComPtr<ISnapshot> &aSnapshot,
14374 MachineState_T *aMachineState,
14375 ComPtr<IProgress> &aProgress)
14376{
14377 NOREF(aInitiator);
14378 NOREF(aSnapshot);
14379 NOREF(aMachineState);
14380 NOREF(aProgress);
14381 ReturnComNotImplemented();
14382}
14383
14384HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14385 std::vector<com::Utf8Str> &aValues,
14386 std::vector<LONG64> &aTimestamps,
14387 std::vector<com::Utf8Str> &aFlags)
14388{
14389 NOREF(aNames);
14390 NOREF(aValues);
14391 NOREF(aTimestamps);
14392 NOREF(aFlags);
14393 ReturnComNotImplemented();
14394}
14395
14396HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14397 const com::Utf8Str &aValue,
14398 LONG64 aTimestamp,
14399 const com::Utf8Str &aFlags)
14400{
14401 NOREF(aName);
14402 NOREF(aValue);
14403 NOREF(aTimestamp);
14404 NOREF(aFlags);
14405 ReturnComNotImplemented();
14406}
14407
14408HRESULT Machine::lockMedia()
14409{
14410 ReturnComNotImplemented();
14411}
14412
14413HRESULT Machine::unlockMedia()
14414{
14415 ReturnComNotImplemented();
14416}
14417
14418HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14419 ComPtr<IMediumAttachment> &aNewAttachment)
14420{
14421 NOREF(aAttachment);
14422 NOREF(aNewAttachment);
14423 ReturnComNotImplemented();
14424}
14425
14426HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14427 ULONG aCpuUser,
14428 ULONG aCpuKernel,
14429 ULONG aCpuIdle,
14430 ULONG aMemTotal,
14431 ULONG aMemFree,
14432 ULONG aMemBalloon,
14433 ULONG aMemShared,
14434 ULONG aMemCache,
14435 ULONG aPagedTotal,
14436 ULONG aMemAllocTotal,
14437 ULONG aMemFreeTotal,
14438 ULONG aMemBalloonTotal,
14439 ULONG aMemSharedTotal,
14440 ULONG aVmNetRx,
14441 ULONG aVmNetTx)
14442{
14443 NOREF(aValidStats);
14444 NOREF(aCpuUser);
14445 NOREF(aCpuKernel);
14446 NOREF(aCpuIdle);
14447 NOREF(aMemTotal);
14448 NOREF(aMemFree);
14449 NOREF(aMemBalloon);
14450 NOREF(aMemShared);
14451 NOREF(aMemCache);
14452 NOREF(aPagedTotal);
14453 NOREF(aMemAllocTotal);
14454 NOREF(aMemFreeTotal);
14455 NOREF(aMemBalloonTotal);
14456 NOREF(aMemSharedTotal);
14457 NOREF(aVmNetRx);
14458 NOREF(aVmNetTx);
14459 ReturnComNotImplemented();
14460}
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