VirtualBox

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

Last change on this file since 70582 was 70582, checked in by vboxsync, 7 years ago

bugref:8345. First draft of move VM feature. There are some issues but base logic should stay the same. This code hasn't been built on Windows OSes yet.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 523.9 KB
Line 
1/* $Id: MachineImpl.cpp 70582 2018-01-15 10:13:41Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.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
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mAPIC = true;
197 mX2APIC = false;
198 mHPETEnabled = false;
199 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
200 mCpuIdPortabilityLevel = 0;
201 mCpuProfile = "host";
202
203 /* default boot order: floppy - DVD - HDD */
204 mBootOrder[0] = DeviceType_Floppy;
205 mBootOrder[1] = DeviceType_DVD;
206 mBootOrder[2] = DeviceType_HardDisk;
207 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
208 mBootOrder[i] = DeviceType_Null;
209
210 mClipboardMode = ClipboardMode_Disabled;
211 mDnDMode = DnDMode_Disabled;
212
213 mFirmwareType = FirmwareType_BIOS;
214 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
215 mPointingHIDType = PointingHIDType_PS2Mouse;
216 mChipsetType = ChipsetType_PIIX3;
217 mParavirtProvider = ParavirtProvider_Default;
218 mEmulatedUSBCardReaderEnabled = FALSE;
219
220 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
221 mCPUAttached[i] = false;
222
223 mIOCacheEnabled = true;
224 mIOCacheSize = 5; /* 5MB */
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine class
233/////////////////////////////////////////////////////////////////////////////
234
235// constructor / destructor
236/////////////////////////////////////////////////////////////////////////////
237
238Machine::Machine() :
239#ifdef VBOX_WITH_RESOURCE_USAGE_API
240 mCollectorGuest(NULL),
241#endif
242 mPeer(NULL),
243 mParent(NULL),
244 mSerialPorts(),
245 mParallelPorts(),
246 uRegistryNeedsSaving(0)
247{}
248
249Machine::~Machine()
250{}
251
252HRESULT Machine::FinalConstruct()
253{
254 LogFlowThisFunc(("\n"));
255 return BaseFinalConstruct();
256}
257
258void Machine::FinalRelease()
259{
260 LogFlowThisFunc(("\n"));
261 uninit();
262 BaseFinalRelease();
263}
264
265/**
266 * Initializes a new machine instance; this init() variant creates a new, empty machine.
267 * This gets called from VirtualBox::CreateMachine().
268 *
269 * @param aParent Associated parent object
270 * @param strConfigFile Local file system path to the VM settings file (can
271 * be relative to the VirtualBox config directory).
272 * @param strName name for the machine
273 * @param llGroups list of groups for the machine
274 * @param aOsType OS Type of this machine or NULL.
275 * @param aId UUID for the new machine.
276 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
277 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
278 * scheme (includes the UUID).
279 *
280 * @return Success indicator. if not S_OK, the machine object is invalid
281 */
282HRESULT Machine::init(VirtualBox *aParent,
283 const Utf8Str &strConfigFile,
284 const Utf8Str &strName,
285 const StringsList &llGroups,
286 GuestOSType *aOsType,
287 const Guid &aId,
288 bool fForceOverwrite,
289 bool fDirectoryIncludesUUID)
290{
291 LogFlowThisFuncEnter();
292 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
293
294 /* Enclose the state transition NotReady->InInit->Ready */
295 AutoInitSpan autoInitSpan(this);
296 AssertReturn(autoInitSpan.isOk(), E_FAIL);
297
298 HRESULT rc = initImpl(aParent, strConfigFile);
299 if (FAILED(rc)) return rc;
300
301 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
302 if (FAILED(rc)) return rc;
303
304 if (SUCCEEDED(rc))
305 {
306 // create an empty machine config
307 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
308
309 rc = initDataAndChildObjects();
310 }
311
312 if (SUCCEEDED(rc))
313 {
314 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
315 mData->mAccessible = TRUE;
316
317 unconst(mData->mUuid) = aId;
318
319 mUserData->s.strName = strName;
320
321 mUserData->s.llGroups = llGroups;
322
323 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
324 // the "name sync" flag determines whether the machine directory gets renamed along
325 // with the machine file; say so if the settings file name is the same as the
326 // settings file parent directory (machine directory)
327 mUserData->s.fNameSync = i_isInOwnDir();
328
329 // initialize the default snapshots folder
330 rc = COMSETTER(SnapshotFolder)(NULL);
331 AssertComRC(rc);
332
333 if (aOsType)
334 {
335 /* Store OS type */
336 mUserData->s.strOsType = aOsType->i_id();
337
338 /* Apply BIOS defaults */
339 mBIOSSettings->i_applyDefaults(aOsType);
340
341 /* Let the OS type select 64-bit ness. */
342 mHWData->mLongMode = aOsType->i_is64Bit()
343 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
344
345 /* Let the OS type enable the X2APIC */
346 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
347 }
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->i_applyDefaults(aOsType);
356
357 /* Apply parallel port defaults */
358 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
359 mParallelPorts[slot]->i_applyDefaults();
360
361 /* At this point the changing of the current state modification
362 * flag is allowed. */
363 i_allowStateModification();
364
365 /* commit all changes made during the initialization */
366 i_commit();
367 }
368
369 /* Confirm a successful initialization when it's the case */
370 if (SUCCEEDED(rc))
371 {
372 if (mData->mAccessible)
373 autoInitSpan.setSucceeded();
374 else
375 autoInitSpan.setLimited();
376 }
377
378 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
379 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
380 mData->mRegistered,
381 mData->mAccessible,
382 rc));
383
384 LogFlowThisFuncLeave();
385
386 return rc;
387}
388
389/**
390 * Initializes a new instance with data from machine XML (formerly Init_Registered).
391 * Gets called in two modes:
392 *
393 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
394 * UUID is specified and we mark the machine as "registered";
395 *
396 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
397 * and the machine remains unregistered until RegisterMachine() is called.
398 *
399 * @param aParent Associated parent object
400 * @param strConfigFile Local file system path to the VM settings file (can
401 * be relative to the VirtualBox config directory).
402 * @param aId UUID of the machine or NULL (see above).
403 *
404 * @return Success indicator. if not S_OK, the machine object is invalid
405 */
406HRESULT Machine::initFromSettings(VirtualBox *aParent,
407 const Utf8Str &strConfigFile,
408 const Guid *aId)
409{
410 LogFlowThisFuncEnter();
411 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
412
413 /* Enclose the state transition NotReady->InInit->Ready */
414 AutoInitSpan autoInitSpan(this);
415 AssertReturn(autoInitSpan.isOk(), E_FAIL);
416
417 HRESULT rc = initImpl(aParent, strConfigFile);
418 if (FAILED(rc)) return rc;
419
420 if (aId)
421 {
422 // loading a registered VM:
423 unconst(mData->mUuid) = *aId;
424 mData->mRegistered = TRUE;
425 // now load the settings from XML:
426 rc = i_registeredInit();
427 // this calls initDataAndChildObjects() and loadSettings()
428 }
429 else
430 {
431 // opening an unregistered VM (VirtualBox::OpenMachine()):
432 rc = initDataAndChildObjects();
433
434 if (SUCCEEDED(rc))
435 {
436 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
437 mData->mAccessible = TRUE;
438
439 try
440 {
441 // load and parse machine XML; this will throw on XML or logic errors
442 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
443
444 // reject VM UUID duplicates, they can happen if someone
445 // tries to register an already known VM config again
446 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
447 true /* fPermitInaccessible */,
448 false /* aDoSetError */,
449 NULL) != VBOX_E_OBJECT_NOT_FOUND)
450 {
451 throw setError(E_FAIL,
452 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
453 mData->m_strConfigFile.c_str());
454 }
455
456 // use UUID from machine config
457 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
458
459 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
460 NULL /* puuidRegistry */);
461 if (FAILED(rc)) throw rc;
462
463 /* At this point the changing of the current state modification
464 * flag is allowed. */
465 i_allowStateModification();
466
467 i_commit();
468 }
469 catch (HRESULT err)
470 {
471 /* we assume that error info is set by the thrower */
472 rc = err;
473 }
474 catch (...)
475 {
476 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
477 }
478 }
479 }
480
481 /* Confirm a successful initialization when it's the case */
482 if (SUCCEEDED(rc))
483 {
484 if (mData->mAccessible)
485 autoInitSpan.setSucceeded();
486 else
487 {
488 autoInitSpan.setLimited();
489
490 // uninit media from this machine's media registry, or else
491 // reloading the settings will fail
492 mParent->i_unregisterMachineMedia(i_getId());
493 }
494 }
495
496 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
497 "rc=%08X\n",
498 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
499 mData->mRegistered, mData->mAccessible, rc));
500
501 LogFlowThisFuncLeave();
502
503 return rc;
504}
505
506/**
507 * Initializes a new instance from a machine config that is already in memory
508 * (import OVF case). Since we are importing, the UUID in the machine
509 * config is ignored and we always generate a fresh one.
510 *
511 * @param aParent Associated parent object.
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->i_getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = i_loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 i_allowStateModification();
566
567 /* commit all changes made during the initialization */
568 i_commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 /* Ignore all errors from unregistering, they would destroy
580- * the more interesting error information we already have,
581- * pinpointing the issue with the VM config. */
582 ErrorInfoKeeper eik;
583
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->i_unregisterMachineMedia(i_getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent The VirtualBox object.
605 * @param strConfigFile Settings file.
606 * @return
607 */
608HRESULT Machine::initImpl(VirtualBox *aParent,
609 const Utf8Str &strConfigFile)
610{
611 LogFlowThisFuncEnter();
612
613 AssertReturn(aParent, E_INVALIDARG);
614 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
615
616 HRESULT rc = S_OK;
617
618 /* share the parent weakly */
619 unconst(mParent) = aParent;
620
621 /* allocate the essential machine data structure (the rest will be
622 * allocated later by initDataAndChildObjects() */
623 mData.allocate();
624
625 /* memorize the config file name (as provided) */
626 mData->m_strConfigFile = strConfigFile;
627
628 /* get the full file name */
629 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
630 if (RT_FAILURE(vrc1))
631 return setError(VBOX_E_FILE_ERROR,
632 tr("Invalid machine settings file name '%s' (%Rrc)"),
633 strConfigFile.c_str(),
634 vrc1);
635
636 LogFlowThisFuncLeave();
637
638 return rc;
639}
640
641/**
642 * Tries to create a machine settings file in the path stored in the machine
643 * instance data. Used when a new machine is created to fail gracefully if
644 * the settings file could not be written (e.g. because machine dir is read-only).
645 * @return
646 */
647HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
648{
649 HRESULT rc = S_OK;
650
651 // when we create a new machine, we must be able to create the settings file
652 RTFILE f = NIL_RTFILE;
653 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
654 if ( RT_SUCCESS(vrc)
655 || vrc == VERR_SHARING_VIOLATION
656 )
657 {
658 if (RT_SUCCESS(vrc))
659 RTFileClose(f);
660 if (!fForceOverwrite)
661 rc = setError(VBOX_E_FILE_ERROR,
662 tr("Machine settings file '%s' already exists"),
663 mData->m_strConfigFileFull.c_str());
664 else
665 {
666 /* try to delete the config file, as otherwise the creation
667 * of a new settings file will fail. */
668 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
669 if (RT_FAILURE(vrc2))
670 rc = setError(VBOX_E_FILE_ERROR,
671 tr("Could not delete the existing settings file '%s' (%Rrc)"),
672 mData->m_strConfigFileFull.c_str(), vrc2);
673 }
674 }
675 else if ( vrc != VERR_FILE_NOT_FOUND
676 && vrc != VERR_PATH_NOT_FOUND
677 )
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Invalid machine settings file name '%s' (%Rrc)"),
680 mData->m_strConfigFileFull.c_str(),
681 vrc);
682 return rc;
683}
684
685/**
686 * Initializes the registered machine by loading the settings file.
687 * This method is separated from #init() in order to make it possible to
688 * retry the operation after VirtualBox startup instead of refusing to
689 * startup the whole VirtualBox server in case if the settings file of some
690 * registered VM is invalid or inaccessible.
691 *
692 * @note Must be always called from this object's write lock
693 * (unless called from #init() that doesn't need any locking).
694 * @note Locks the mUSBController method for writing.
695 * @note Subclasses must not call this method.
696 */
697HRESULT Machine::i_registeredInit()
698{
699 AssertReturn(!i_isSessionMachine(), E_FAIL);
700 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
701 AssertReturn(mData->mUuid.isValid(), E_FAIL);
702 AssertReturn(!mData->mAccessible, E_FAIL);
703
704 HRESULT rc = initDataAndChildObjects();
705
706 if (SUCCEEDED(rc))
707 {
708 /* Temporarily reset the registered flag in order to let setters
709 * potentially called from loadSettings() succeed (isMutable() used in
710 * all setters will return FALSE for a Machine instance if mRegistered
711 * is TRUE). */
712 mData->mRegistered = FALSE;
713
714 try
715 {
716 // load and parse machine XML; this will throw on XML or logic errors
717 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
718
719 if (mData->mUuid != mData->pMachineConfigFile->uuid)
720 throw setError(E_FAIL,
721 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
722 mData->pMachineConfigFile->uuid.raw(),
723 mData->m_strConfigFileFull.c_str(),
724 mData->mUuid.toString().c_str(),
725 mParent->i_settingsFilePath().c_str());
726
727 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
728 NULL /* const Guid *puuidRegistry */);
729 if (FAILED(rc)) throw rc;
730 }
731 catch (HRESULT err)
732 {
733 /* we assume that error info is set by the thrower */
734 rc = err;
735 }
736 catch (...)
737 {
738 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
739 }
740
741 /* Restore the registered flag (even on failure) */
742 mData->mRegistered = TRUE;
743 }
744
745 if (SUCCEEDED(rc))
746 {
747 /* Set mAccessible to TRUE only if we successfully locked and loaded
748 * the settings file */
749 mData->mAccessible = TRUE;
750
751 /* commit all changes made during loading the settings file */
752 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
753 /// @todo r=klaus for some reason the settings loading logic backs up
754 // the settings, and therefore a commit is needed. Should probably be changed.
755 }
756 else
757 {
758 /* If the machine is registered, then, instead of returning a
759 * failure, we mark it as inaccessible and set the result to
760 * success to give it a try later */
761
762 /* fetch the current error info */
763 mData->mAccessError = com::ErrorInfo();
764 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
765
766 /* rollback all changes */
767 i_rollback(false /* aNotify */);
768
769 // uninit media from this machine's media registry, or else
770 // reloading the settings will fail
771 mParent->i_unregisterMachineMedia(i_getId());
772
773 /* uninitialize the common part to make sure all data is reset to
774 * default (null) values */
775 uninitDataAndChildObjects();
776
777 rc = S_OK;
778 }
779
780 return rc;
781}
782
783/**
784 * Uninitializes the instance.
785 * Called either from FinalRelease() or by the parent when it gets destroyed.
786 *
787 * @note The caller of this method must make sure that this object
788 * a) doesn't have active callers on the current thread and b) is not locked
789 * by the current thread; otherwise uninit() will hang either a) due to
790 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
791 * a dead-lock caused by this thread waiting for all callers on the other
792 * threads are done but preventing them from doing so by holding a lock.
793 */
794void Machine::uninit()
795{
796 LogFlowThisFuncEnter();
797
798 Assert(!isWriteLockOnCurrentThread());
799
800 Assert(!uRegistryNeedsSaving);
801 if (uRegistryNeedsSaving)
802 {
803 AutoCaller autoCaller(this);
804 if (SUCCEEDED(autoCaller.rc()))
805 {
806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
807 i_saveSettings(NULL, Machine::SaveS_Force);
808 }
809 }
810
811 /* Enclose the state transition Ready->InUninit->NotReady */
812 AutoUninitSpan autoUninitSpan(this);
813 if (autoUninitSpan.uninitDone())
814 return;
815
816 Assert(!i_isSnapshotMachine());
817 Assert(!i_isSessionMachine());
818 Assert(!!mData);
819
820 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
821 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
822
823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
824
825 if (!mData->mSession.mMachine.isNull())
826 {
827 /* Theoretically, this can only happen if the VirtualBox server has been
828 * terminated while there were clients running that owned open direct
829 * sessions. Since in this case we are definitely called by
830 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
831 * won't happen on the client watcher thread (because it has a
832 * VirtualBox caller for the duration of the
833 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
834 * cannot happen until the VirtualBox caller is released). This is
835 * important, because SessionMachine::uninit() cannot correctly operate
836 * after we return from this method (it expects the Machine instance is
837 * still valid). We'll call it ourselves below.
838 */
839 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
840 (SessionMachine*)mData->mSession.mMachine));
841
842 if (Global::IsOnlineOrTransient(mData->mMachineState))
843 {
844 Log1WarningThisFunc(("Setting state to Aborted!\n"));
845 /* set machine state using SessionMachine reimplementation */
846 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
847 }
848
849 /*
850 * Uninitialize SessionMachine using public uninit() to indicate
851 * an unexpected uninitialization.
852 */
853 mData->mSession.mMachine->uninit();
854 /* SessionMachine::uninit() must set mSession.mMachine to null */
855 Assert(mData->mSession.mMachine.isNull());
856 }
857
858 // uninit media from this machine's media registry, if they're still there
859 Guid uuidMachine(i_getId());
860
861 /* the lock is no more necessary (SessionMachine is uninitialized) */
862 alock.release();
863
864 /* XXX This will fail with
865 * "cannot be closed because it is still attached to 1 virtual machines"
866 * because at this point we did not call uninitDataAndChildObjects() yet
867 * and therefore also removeBackReference() for all these mediums was not called! */
868
869 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
870 mParent->i_unregisterMachineMedia(uuidMachine);
871
872 // has machine been modified?
873 if (mData->flModifications)
874 {
875 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
876 i_rollback(false /* aNotify */);
877 }
878
879 if (mData->mAccessible)
880 uninitDataAndChildObjects();
881
882 /* free the essential data structure last */
883 mData.free();
884
885 LogFlowThisFuncLeave();
886}
887
888// Wrapped IMachine properties
889/////////////////////////////////////////////////////////////////////////////
890HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
891{
892 /* mParent is constant during life time, no need to lock */
893 ComObjPtr<VirtualBox> pVirtualBox(mParent);
894 aParent = pVirtualBox;
895
896 return S_OK;
897}
898
899
900HRESULT Machine::getAccessible(BOOL *aAccessible)
901{
902 /* In some cases (medium registry related), it is necessary to be able to
903 * go through the list of all machines. Happens when an inaccessible VM
904 * has a sensible medium registry. */
905 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
907
908 HRESULT rc = S_OK;
909
910 if (!mData->mAccessible)
911 {
912 /* try to initialize the VM once more if not accessible */
913
914 AutoReinitSpan autoReinitSpan(this);
915 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
916
917#ifdef DEBUG
918 LogFlowThisFunc(("Dumping media backreferences\n"));
919 mParent->i_dumpAllBackRefs();
920#endif
921
922 if (mData->pMachineConfigFile)
923 {
924 // reset the XML file to force loadSettings() (called from i_registeredInit())
925 // to parse it again; the file might have changed
926 delete mData->pMachineConfigFile;
927 mData->pMachineConfigFile = NULL;
928 }
929
930 rc = i_registeredInit();
931
932 if (SUCCEEDED(rc) && mData->mAccessible)
933 {
934 autoReinitSpan.setSucceeded();
935
936 /* make sure interesting parties will notice the accessibility
937 * state change */
938 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
939 mParent->i_onMachineDataChange(mData->mUuid);
940 }
941 }
942
943 if (SUCCEEDED(rc))
944 *aAccessible = mData->mAccessible;
945
946 LogFlowThisFuncLeave();
947
948 return rc;
949}
950
951HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
952{
953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
954
955 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
956 {
957 /* return shortly */
958 aAccessError = NULL;
959 return S_OK;
960 }
961
962 HRESULT rc = S_OK;
963
964 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
965 rc = errorInfo.createObject();
966 if (SUCCEEDED(rc))
967 {
968 errorInfo->init(mData->mAccessError.getResultCode(),
969 mData->mAccessError.getInterfaceID().ref(),
970 Utf8Str(mData->mAccessError.getComponent()).c_str(),
971 Utf8Str(mData->mAccessError.getText()));
972 aAccessError = errorInfo;
973 }
974
975 return rc;
976}
977
978HRESULT Machine::getName(com::Utf8Str &aName)
979{
980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
981
982 aName = mUserData->s.strName;
983
984 return S_OK;
985}
986
987HRESULT Machine::setName(const com::Utf8Str &aName)
988{
989 // prohibit setting a UUID only as the machine name, or else it can
990 // never be found by findMachine()
991 Guid test(aName);
992
993 if (test.isValid())
994 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
995
996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 HRESULT rc = i_checkStateDependency(MutableStateDep);
999 if (FAILED(rc)) return rc;
1000
1001 i_setModified(IsModified_MachineData);
1002 mUserData.backup();
1003 mUserData->s.strName = aName;
1004
1005 return S_OK;
1006}
1007
1008HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1009{
1010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1011
1012 aDescription = mUserData->s.strDescription;
1013
1014 return S_OK;
1015}
1016
1017HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1018{
1019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1020
1021 // this can be done in principle in any state as it doesn't affect the VM
1022 // significantly, but play safe by not messing around while complex
1023 // activities are going on
1024 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1025 if (FAILED(rc)) return rc;
1026
1027 i_setModified(IsModified_MachineData);
1028 mUserData.backup();
1029 mUserData->s.strDescription = aDescription;
1030
1031 return S_OK;
1032}
1033
1034HRESULT Machine::getId(com::Guid &aId)
1035{
1036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 aId = mData->mUuid;
1039
1040 return S_OK;
1041}
1042
1043HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1044{
1045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1046 aGroups.resize(mUserData->s.llGroups.size());
1047 size_t i = 0;
1048 for (StringsList::const_iterator
1049 it = mUserData->s.llGroups.begin();
1050 it != mUserData->s.llGroups.end();
1051 ++it, ++i)
1052 aGroups[i] = (*it);
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1058{
1059 StringsList llGroups;
1060 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1061 if (FAILED(rc))
1062 return rc;
1063
1064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1065
1066 rc = i_checkStateDependency(MutableOrSavedStateDep);
1067 if (FAILED(rc)) return rc;
1068
1069 i_setModified(IsModified_MachineData);
1070 mUserData.backup();
1071 mUserData->s.llGroups = llGroups;
1072
1073 return S_OK;
1074}
1075
1076HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1077{
1078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 aOSTypeId = mUserData->s.strOsType;
1081
1082 return S_OK;
1083}
1084
1085HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1086{
1087 /* look up the object by Id to check it is valid */
1088 ComObjPtr<GuestOSType> pGuestOSType;
1089 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1090 pGuestOSType);
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Utf8Str osTypeId = pGuestOSType->i_id();
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 rc = i_checkStateDependency(MutableStateDep);
1100 if (FAILED(rc)) return rc;
1101
1102 i_setModified(IsModified_MachineData);
1103 mUserData.backup();
1104 mUserData->s.strOsType = osTypeId;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1110{
1111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 *aFirmwareType = mHWData->mFirmwareType;
1114
1115 return S_OK;
1116}
1117
1118HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1119{
1120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 HRESULT rc = i_checkStateDependency(MutableStateDep);
1123 if (FAILED(rc)) return rc;
1124
1125 i_setModified(IsModified_MachineData);
1126 mHWData.backup();
1127 mHWData->mFirmwareType = aFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1133{
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1137
1138 return S_OK;
1139}
1140
1141HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1142{
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 HRESULT rc = i_checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 i_setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1151
1152 return S_OK;
1153}
1154
1155HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1156{
1157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 *aPointingHIDType = mHWData->mPointingHIDType;
1160
1161 return S_OK;
1162}
1163
1164HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1165{
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = i_checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 i_setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mPointingHIDType = aPointingHIDType;
1174
1175 return S_OK;
1176}
1177
1178HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1179{
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aChipsetType = mHWData->mChipsetType;
1183
1184 return S_OK;
1185}
1186
1187HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1188{
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = i_checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 if (aChipsetType != mHWData->mChipsetType)
1195 {
1196 i_setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mChipsetType = aChipsetType;
1199
1200 // Resize network adapter array, to be finalized on commit/rollback.
1201 // We must not throw away entries yet, otherwise settings are lost
1202 // without a way to roll back.
1203 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1204 size_t oldCount = mNetworkAdapters.size();
1205 if (newCount > oldCount)
1206 {
1207 mNetworkAdapters.resize(newCount);
1208 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1209 {
1210 unconst(mNetworkAdapters[slot]).createObject();
1211 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1212 }
1213 }
1214 }
1215
1216 return S_OK;
1217}
1218
1219HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1220{
1221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 aParavirtDebug = mHWData->mParavirtDebug;
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 /** @todo Parse/validate options? */
1235 if (aParavirtDebug != mHWData->mParavirtDebug)
1236 {
1237 i_setModified(IsModified_MachineData);
1238 mHWData.backup();
1239 mHWData->mParavirtDebug = aParavirtDebug;
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aParavirtProvider = mHWData->mParavirtProvider;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aParavirtProvider != mHWData->mParavirtProvider)
1262 {
1263 i_setModified(IsModified_MachineData);
1264 mHWData.backup();
1265 mHWData->mParavirtProvider = aParavirtProvider;
1266 }
1267
1268 return S_OK;
1269}
1270
1271HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1272{
1273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1274
1275 *aParavirtProvider = mHWData->mParavirtProvider;
1276 switch (mHWData->mParavirtProvider)
1277 {
1278 case ParavirtProvider_None:
1279 case ParavirtProvider_HyperV:
1280 case ParavirtProvider_KVM:
1281 case ParavirtProvider_Minimal:
1282 break;
1283
1284 /* Resolve dynamic provider types to the effective types. */
1285 default:
1286 {
1287 ComObjPtr<GuestOSType> pGuestOSType;
1288 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1289 pGuestOSType);
1290 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1291
1292 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1293 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1294
1295 switch (mHWData->mParavirtProvider)
1296 {
1297 case ParavirtProvider_Legacy:
1298 {
1299 if (fOsXGuest)
1300 *aParavirtProvider = ParavirtProvider_Minimal;
1301 else
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 case ParavirtProvider_Default:
1307 {
1308 if (fOsXGuest)
1309 *aParavirtProvider = ParavirtProvider_Minimal;
1310 else if ( mUserData->s.strOsType == "Windows10"
1311 || mUserData->s.strOsType == "Windows10_64"
1312 || mUserData->s.strOsType == "Windows81"
1313 || mUserData->s.strOsType == "Windows81_64"
1314 || mUserData->s.strOsType == "Windows8"
1315 || mUserData->s.strOsType == "Windows8_64"
1316 || mUserData->s.strOsType == "Windows7"
1317 || mUserData->s.strOsType == "Windows7_64"
1318 || mUserData->s.strOsType == "WindowsVista"
1319 || mUserData->s.strOsType == "WindowsVista_64"
1320 || mUserData->s.strOsType == "Windows2012"
1321 || mUserData->s.strOsType == "Windows2012_64"
1322 || mUserData->s.strOsType == "Windows2008"
1323 || mUserData->s.strOsType == "Windows2008_64")
1324 {
1325 *aParavirtProvider = ParavirtProvider_HyperV;
1326 }
1327 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1328 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1329 || mUserData->s.strOsType == "Linux"
1330 || mUserData->s.strOsType == "Linux_64"
1331 || mUserData->s.strOsType == "ArchLinux"
1332 || mUserData->s.strOsType == "ArchLinux_64"
1333 || mUserData->s.strOsType == "Debian"
1334 || mUserData->s.strOsType == "Debian_64"
1335 || mUserData->s.strOsType == "Fedora"
1336 || mUserData->s.strOsType == "Fedora_64"
1337 || mUserData->s.strOsType == "Gentoo"
1338 || mUserData->s.strOsType == "Gentoo_64"
1339 || mUserData->s.strOsType == "Mandriva"
1340 || mUserData->s.strOsType == "Mandriva_64"
1341 || mUserData->s.strOsType == "OpenSUSE"
1342 || mUserData->s.strOsType == "OpenSUSE_64"
1343 || mUserData->s.strOsType == "Oracle"
1344 || mUserData->s.strOsType == "Oracle_64"
1345 || mUserData->s.strOsType == "RedHat"
1346 || mUserData->s.strOsType == "RedHat_64"
1347 || mUserData->s.strOsType == "Turbolinux"
1348 || mUserData->s.strOsType == "Turbolinux_64"
1349 || mUserData->s.strOsType == "Ubuntu"
1350 || mUserData->s.strOsType == "Ubuntu_64"
1351 || mUserData->s.strOsType == "Xandros"
1352 || mUserData->s.strOsType == "Xandros_64")
1353 {
1354 *aParavirtProvider = ParavirtProvider_KVM;
1355 }
1356 else
1357 *aParavirtProvider = ParavirtProvider_None;
1358 break;
1359 }
1360
1361 default: AssertFailedBreak(); /* Shut up MSC. */
1362 }
1363 break;
1364 }
1365 }
1366
1367 Assert( *aParavirtProvider == ParavirtProvider_None
1368 || *aParavirtProvider == ParavirtProvider_Minimal
1369 || *aParavirtProvider == ParavirtProvider_HyperV
1370 || *aParavirtProvider == ParavirtProvider_KVM);
1371 return S_OK;
1372}
1373
1374HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1375{
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377
1378 aHardwareVersion = mHWData->mHWVersion;
1379
1380 return S_OK;
1381}
1382
1383HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1384{
1385 /* check known version */
1386 Utf8Str hwVersion = aHardwareVersion;
1387 if ( hwVersion.compare("1") != 0
1388 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1389 return setError(E_INVALIDARG,
1390 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = i_checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 i_setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 mHWData->mHWVersion = aHardwareVersion;
1400
1401 return S_OK;
1402}
1403
1404HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1405{
1406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 if (!mHWData->mHardwareUUID.isZero())
1409 aHardwareUUID = mHWData->mHardwareUUID;
1410 else
1411 aHardwareUUID = mData->mUuid;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1417{
1418 if (!aHardwareUUID.isValid())
1419 return E_INVALIDARG;
1420
1421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 HRESULT rc = i_checkStateDependency(MutableStateDep);
1424 if (FAILED(rc)) return rc;
1425
1426 i_setModified(IsModified_MachineData);
1427 mHWData.backup();
1428 if (aHardwareUUID == mData->mUuid)
1429 mHWData->mHardwareUUID.clear();
1430 else
1431 mHWData->mHardwareUUID = aHardwareUUID;
1432
1433 return S_OK;
1434}
1435
1436HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1437{
1438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 *aMemorySize = mHWData->mMemorySize;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::setMemorySize(ULONG aMemorySize)
1446{
1447 /* check RAM limits */
1448 if ( aMemorySize < MM_RAM_MIN_IN_MB
1449 || aMemorySize > MM_RAM_MAX_IN_MB
1450 )
1451 return setError(E_INVALIDARG,
1452 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1453 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1454
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT rc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(rc)) return rc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mMemorySize = aMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aCPUCount = mHWData->mCPUCount;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setCPUCount(ULONG aCPUCount)
1477{
1478 /* check CPU limits */
1479 if ( aCPUCount < SchemaDefs::MinCPUCount
1480 || aCPUCount > SchemaDefs::MaxCPUCount
1481 )
1482 return setError(E_INVALIDARG,
1483 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1484 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1485
1486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1489 if (mHWData->mCPUHotPlugEnabled)
1490 {
1491 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1492 {
1493 if (mHWData->mCPUAttached[idx])
1494 return setError(E_INVALIDARG,
1495 tr("There is still a CPU attached to socket %lu."
1496 "Detach the CPU before removing the socket"),
1497 aCPUCount, idx+1);
1498 }
1499 }
1500
1501 HRESULT rc = i_checkStateDependency(MutableStateDep);
1502 if (FAILED(rc)) return rc;
1503
1504 i_setModified(IsModified_MachineData);
1505 mHWData.backup();
1506 mHWData->mCPUCount = aCPUCount;
1507
1508 return S_OK;
1509}
1510
1511HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1512{
1513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1514
1515 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1521{
1522 HRESULT rc = S_OK;
1523
1524 /* check throttle limits */
1525 if ( aCPUExecutionCap < 1
1526 || aCPUExecutionCap > 100
1527 )
1528 return setError(E_INVALIDARG,
1529 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1530 aCPUExecutionCap, 1, 100);
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 alock.release();
1535 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1536 alock.acquire();
1537 if (FAILED(rc)) return rc;
1538
1539 i_setModified(IsModified_MachineData);
1540 mHWData.backup();
1541 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1542
1543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1544 if (Global::IsOnline(mData->mMachineState))
1545 i_saveSettings(NULL);
1546
1547 return S_OK;
1548}
1549
1550HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1551{
1552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1555
1556 return S_OK;
1557}
1558
1559HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1560{
1561 HRESULT rc = S_OK;
1562
1563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 rc = i_checkStateDependency(MutableStateDep);
1566 if (FAILED(rc)) return rc;
1567
1568 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1569 {
1570 if (aCPUHotPlugEnabled)
1571 {
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574
1575 /* Add the amount of CPUs currently attached */
1576 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1577 mHWData->mCPUAttached[i] = true;
1578 }
1579 else
1580 {
1581 /*
1582 * We can disable hotplug only if the amount of maximum CPUs is equal
1583 * to the amount of attached CPUs
1584 */
1585 unsigned cCpusAttached = 0;
1586 unsigned iHighestId = 0;
1587
1588 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1589 {
1590 if (mHWData->mCPUAttached[i])
1591 {
1592 cCpusAttached++;
1593 iHighestId = i;
1594 }
1595 }
1596
1597 if ( (cCpusAttached != mHWData->mCPUCount)
1598 || (iHighestId >= mHWData->mCPUCount))
1599 return setError(E_INVALIDARG,
1600 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604 }
1605 }
1606
1607 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1622{
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1626 if (SUCCEEDED(hrc))
1627 {
1628 i_setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1631 }
1632 return hrc;
1633}
1634
1635HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1636{
1637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1638 aCPUProfile = mHWData->mCpuProfile;
1639 return S_OK;
1640}
1641
1642HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1643{
1644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1645 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1646 if (SUCCEEDED(hrc))
1647 {
1648 i_setModified(IsModified_MachineData);
1649 mHWData.backup();
1650 /* Empty equals 'host'. */
1651 if (aCPUProfile.isNotEmpty())
1652 mHWData->mCpuProfile = aCPUProfile;
1653 else
1654 mHWData->mCpuProfile = "host";
1655 }
1656 return hrc;
1657}
1658
1659HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1660{
1661#ifdef VBOX_WITH_USB_CARDREADER
1662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1665
1666 return S_OK;
1667#else
1668 NOREF(aEmulatedUSBCardReaderEnabled);
1669 return E_NOTIMPL;
1670#endif
1671}
1672
1673HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1679 if (FAILED(rc)) return rc;
1680
1681 i_setModified(IsModified_MachineData);
1682 mHWData.backup();
1683 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1684
1685 return S_OK;
1686#else
1687 NOREF(aEmulatedUSBCardReaderEnabled);
1688 return E_NOTIMPL;
1689#endif
1690}
1691
1692HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1693{
1694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 *aHPETEnabled = mHWData->mHPETEnabled;
1697
1698 return S_OK;
1699}
1700
1701HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1702{
1703 HRESULT rc = S_OK;
1704
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 rc = i_checkStateDependency(MutableStateDep);
1708 if (FAILED(rc)) return rc;
1709
1710 i_setModified(IsModified_MachineData);
1711 mHWData.backup();
1712
1713 mHWData->mHPETEnabled = aHPETEnabled;
1714
1715 return rc;
1716}
1717
1718HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1719{
1720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1723 return S_OK;
1724}
1725
1726HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1727{
1728 HRESULT rc = S_OK;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 i_setModified(IsModified_MachineData);
1733 mHWData.backup();
1734 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1735
1736 alock.release();
1737 rc = i_onVideoCaptureChange();
1738 alock.acquire();
1739 if (FAILED(rc))
1740 {
1741 /*
1742 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1743 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1744 * determine if it should start or stop capturing. Therefore we need to manually
1745 * undo change.
1746 */
1747 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1748 return rc;
1749 }
1750
1751 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1752 if (Global::IsOnline(mData->mMachineState))
1753 i_saveSettings(NULL);
1754
1755 return rc;
1756}
1757
1758HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1759{
1760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1761 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1762 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1763 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1764 return S_OK;
1765}
1766
1767HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1768{
1769 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1770 bool fChanged = false;
1771
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1775 {
1776 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1777 {
1778 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1779 fChanged = true;
1780 }
1781 }
1782 if (fChanged)
1783 {
1784 alock.release();
1785 HRESULT rc = i_onVideoCaptureChange();
1786 alock.acquire();
1787 if (FAILED(rc)) return rc;
1788 i_setModified(IsModified_MachineData);
1789
1790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1791 if (Global::IsOnline(mData->mMachineState))
1792 i_saveSettings(NULL);
1793 }
1794
1795 return S_OK;
1796}
1797
1798HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801 if (mHWData->mVideoCaptureFile.isEmpty())
1802 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1803 else
1804 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1805 return S_OK;
1806}
1807
1808HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1809{
1810 Utf8Str strFile(aVideoCaptureFile);
1811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1812
1813 if ( Global::IsOnline(mData->mMachineState)
1814 && mHWData->mVideoCaptureEnabled)
1815 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1816
1817 if (!RTPathStartsWithRoot(strFile.c_str()))
1818 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1819
1820 if (!strFile.isEmpty())
1821 {
1822 Utf8Str defaultFile;
1823 i_getDefaultVideoCaptureFile(defaultFile);
1824 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1825 strFile.setNull();
1826 }
1827
1828 i_setModified(IsModified_MachineData);
1829 mHWData.backup();
1830 mHWData->mVideoCaptureFile = strFile;
1831
1832 return S_OK;
1833}
1834
1835HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1836{
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1839 return S_OK;
1840}
1841
1842HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1843{
1844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1845
1846 if ( Global::IsOnline(mData->mMachineState)
1847 && mHWData->mVideoCaptureEnabled)
1848 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1849
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1858{
1859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1860 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1861 return S_OK;
1862}
1863
1864HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1865{
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 if ( Global::IsOnline(mData->mMachineState)
1869 && mHWData->mVideoCaptureEnabled)
1870 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1871
1872 i_setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1875
1876 return S_OK;
1877}
1878
1879HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1880{
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1883 return S_OK;
1884}
1885
1886HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1887{
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 if ( Global::IsOnline(mData->mMachineState)
1891 && mHWData->mVideoCaptureEnabled)
1892 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1893
1894 i_setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1897
1898 return S_OK;
1899}
1900
1901HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1902{
1903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1904 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1905 return S_OK;
1906}
1907
1908HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1909{
1910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 if ( Global::IsOnline(mData->mMachineState)
1913 && mHWData->mVideoCaptureEnabled)
1914 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1915
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1927 return S_OK;
1928}
1929
1930HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1931{
1932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1933
1934 if ( Global::IsOnline(mData->mMachineState)
1935 && mHWData->mVideoCaptureEnabled)
1936 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1937
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1941
1942 return S_OK;
1943}
1944
1945HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1946{
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1949 return S_OK;
1950}
1951
1952HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1953{
1954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1955
1956 if ( Global::IsOnline(mData->mMachineState)
1957 && mHWData->mVideoCaptureEnabled)
1958 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1959
1960 i_setModified(IsModified_MachineData);
1961 mHWData.backup();
1962 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1963
1964 return S_OK;
1965}
1966
1967HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1968{
1969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1972 return S_OK;
1973}
1974
1975HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1976{
1977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 if ( Global::IsOnline(mData->mMachineState)
1980 && mHWData->mVideoCaptureEnabled)
1981 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1982
1983 i_setModified(IsModified_MachineData);
1984 mHWData.backup();
1985 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1986
1987 return S_OK;
1988}
1989
1990HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1991{
1992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1995
1996 return S_OK;
1997}
1998
1999HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2000{
2001 switch (aGraphicsControllerType)
2002 {
2003 case GraphicsControllerType_Null:
2004 case GraphicsControllerType_VBoxVGA:
2005#ifdef VBOX_WITH_VMSVGA
2006 case GraphicsControllerType_VMSVGA:
2007#endif
2008 break;
2009 default:
2010 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2011 }
2012
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT rc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(rc)) return rc;
2017
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2021
2022 return S_OK;
2023}
2024
2025HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2026{
2027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 *aVRAMSize = mHWData->mVRAMSize;
2030
2031 return S_OK;
2032}
2033
2034HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2035{
2036 /* check VRAM limits */
2037 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2038 return setError(E_INVALIDARG,
2039 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2040 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2041
2042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 HRESULT rc = i_checkStateDependency(MutableStateDep);
2045 if (FAILED(rc)) return rc;
2046
2047 i_setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mVRAMSize = aVRAMSize;
2050
2051 return S_OK;
2052}
2053
2054/** @todo this method should not be public */
2055HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2060
2061 return S_OK;
2062}
2063
2064/**
2065 * Set the memory balloon size.
2066 *
2067 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2068 * we have to make sure that we never call IGuest from here.
2069 */
2070HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2071{
2072 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2073#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2074 /* check limits */
2075 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2076 return setError(E_INVALIDARG,
2077 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2078 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2079
2080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2085
2086 return S_OK;
2087#else
2088 NOREF(aMemoryBalloonSize);
2089 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2090#endif
2091}
2092
2093HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2094{
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2098 return S_OK;
2099}
2100
2101HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2102{
2103#ifdef VBOX_WITH_PAGE_SHARING
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2107 i_setModified(IsModified_MachineData);
2108 mHWData.backup();
2109 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2110 return S_OK;
2111#else
2112 NOREF(aPageFusionEnabled);
2113 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2114#endif
2115}
2116
2117HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2127{
2128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 HRESULT rc = i_checkStateDependency(MutableStateDep);
2131 if (FAILED(rc)) return rc;
2132
2133 /** @todo check validity! */
2134
2135 i_setModified(IsModified_MachineData);
2136 mHWData.backup();
2137 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2138
2139 return S_OK;
2140}
2141
2142
2143HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2144{
2145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2148
2149 return S_OK;
2150}
2151
2152HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2153{
2154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2155
2156 HRESULT rc = i_checkStateDependency(MutableStateDep);
2157 if (FAILED(rc)) return rc;
2158
2159 /** @todo check validity! */
2160 i_setModified(IsModified_MachineData);
2161 mHWData.backup();
2162 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2163
2164 return S_OK;
2165}
2166
2167HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2168{
2169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2170
2171 *aMonitorCount = mHWData->mMonitorCount;
2172
2173 return S_OK;
2174}
2175
2176HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2177{
2178 /* make sure monitor count is a sensible number */
2179 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2180 return setError(E_INVALIDARG,
2181 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2182 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2183
2184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 HRESULT rc = i_checkStateDependency(MutableStateDep);
2187 if (FAILED(rc)) return rc;
2188
2189 i_setModified(IsModified_MachineData);
2190 mHWData.backup();
2191 mHWData->mMonitorCount = aMonitorCount;
2192
2193 return S_OK;
2194}
2195
2196HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2197{
2198 /* mBIOSSettings is constant during life time, no need to lock */
2199 aBIOSSettings = mBIOSSettings;
2200
2201 return S_OK;
2202}
2203
2204HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2205{
2206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 switch (aProperty)
2209 {
2210 case CPUPropertyType_PAE:
2211 *aValue = mHWData->mPAEEnabled;
2212 break;
2213
2214 case CPUPropertyType_LongMode:
2215 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2216 *aValue = TRUE;
2217 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2218 *aValue = FALSE;
2219#if HC_ARCH_BITS == 64
2220 else
2221 *aValue = TRUE;
2222#else
2223 else
2224 {
2225 *aValue = FALSE;
2226
2227 ComObjPtr<GuestOSType> pGuestOSType;
2228 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2229 pGuestOSType);
2230 if (SUCCEEDED(hrc2))
2231 {
2232 if (pGuestOSType->i_is64Bit())
2233 {
2234 ComObjPtr<Host> pHost = mParent->i_host();
2235 alock.release();
2236
2237 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2238 if (FAILED(hrc2))
2239 *aValue = FALSE;
2240 }
2241 }
2242 }
2243#endif
2244 break;
2245
2246 case CPUPropertyType_TripleFaultReset:
2247 *aValue = mHWData->mTripleFaultReset;
2248 break;
2249
2250 case CPUPropertyType_APIC:
2251 *aValue = mHWData->mAPIC;
2252 break;
2253
2254 case CPUPropertyType_X2APIC:
2255 *aValue = mHWData->mX2APIC;
2256 break;
2257
2258 default:
2259 return E_INVALIDARG;
2260 }
2261 return S_OK;
2262}
2263
2264HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2265{
2266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2267
2268 HRESULT rc = i_checkStateDependency(MutableStateDep);
2269 if (FAILED(rc)) return rc;
2270
2271 switch (aProperty)
2272 {
2273 case CPUPropertyType_PAE:
2274 i_setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mPAEEnabled = !!aValue;
2277 break;
2278
2279 case CPUPropertyType_LongMode:
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2283 break;
2284
2285 case CPUPropertyType_TripleFaultReset:
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mTripleFaultReset = !!aValue;
2289 break;
2290
2291 case CPUPropertyType_APIC:
2292 if (mHWData->mX2APIC)
2293 aValue = TRUE;
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mAPIC = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_X2APIC:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mX2APIC = !!aValue;
2303 if (aValue)
2304 mHWData->mAPIC = !!aValue;
2305 break;
2306
2307 default:
2308 return E_INVALIDARG;
2309 }
2310 return S_OK;
2311}
2312
2313HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2314 ULONG *aValEcx, ULONG *aValEdx)
2315{
2316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2317 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2318 {
2319 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2320 it != mHWData->mCpuIdLeafList.end();
2321 ++it)
2322 {
2323 if (aOrdinal == 0)
2324 {
2325 const settings::CpuIdLeaf &rLeaf= *it;
2326 *aIdx = rLeaf.idx;
2327 *aSubIdx = rLeaf.idxSub;
2328 *aValEax = rLeaf.uEax;
2329 *aValEbx = rLeaf.uEbx;
2330 *aValEcx = rLeaf.uEcx;
2331 *aValEdx = rLeaf.uEdx;
2332 return S_OK;
2333 }
2334 aOrdinal--;
2335 }
2336 }
2337 return E_INVALIDARG;
2338}
2339
2340HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2341{
2342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2343
2344 /*
2345 * Search the list.
2346 */
2347 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2348 {
2349 const settings::CpuIdLeaf &rLeaf= *it;
2350 if ( rLeaf.idx == aIdx
2351 && ( aSubIdx == UINT32_MAX
2352 || rLeaf.idxSub == aSubIdx) )
2353 {
2354 *aValEax = rLeaf.uEax;
2355 *aValEbx = rLeaf.uEbx;
2356 *aValEcx = rLeaf.uEcx;
2357 *aValEdx = rLeaf.uEdx;
2358 return S_OK;
2359 }
2360 }
2361
2362 return E_INVALIDARG;
2363}
2364
2365
2366HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2367{
2368 /*
2369 * Validate input before taking locks and checking state.
2370 */
2371 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2372 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2373 if ( aIdx >= UINT32_C(0x20)
2374 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2375 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2376 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2377
2378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2379 HRESULT rc = i_checkStateDependency(MutableStateDep);
2380 if (FAILED(rc)) return rc;
2381
2382 /*
2383 * Impose a maximum number of leaves.
2384 */
2385 if (mHWData->mCpuIdLeafList.size() > 256)
2386 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2387
2388 /*
2389 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2390 */
2391 i_setModified(IsModified_MachineData);
2392 mHWData.backup();
2393
2394 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2395 {
2396 settings::CpuIdLeaf &rLeaf= *it;
2397 if ( rLeaf.idx == aIdx
2398 && ( aSubIdx == UINT32_MAX
2399 || rLeaf.idxSub == aSubIdx) )
2400 it = mHWData->mCpuIdLeafList.erase(it);
2401 else
2402 ++it;
2403 }
2404
2405 settings::CpuIdLeaf NewLeaf;
2406 NewLeaf.idx = aIdx;
2407 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2408 NewLeaf.uEax = aValEax;
2409 NewLeaf.uEbx = aValEbx;
2410 NewLeaf.uEcx = aValEcx;
2411 NewLeaf.uEdx = aValEdx;
2412 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2413 return S_OK;
2414}
2415
2416HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2417{
2418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
2420 HRESULT rc = i_checkStateDependency(MutableStateDep);
2421 if (FAILED(rc)) return rc;
2422
2423 /*
2424 * Do the removal.
2425 */
2426 bool fModified = false;
2427 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2428 {
2429 settings::CpuIdLeaf &rLeaf= *it;
2430 if ( rLeaf.idx == aIdx
2431 && ( aSubIdx == UINT32_MAX
2432 || rLeaf.idxSub == aSubIdx) )
2433 {
2434 if (!fModified)
2435 {
2436 fModified = true;
2437 i_setModified(IsModified_MachineData);
2438 mHWData.backup();
2439 }
2440 it = mHWData->mCpuIdLeafList.erase(it);
2441 }
2442 else
2443 ++it;
2444 }
2445
2446 return S_OK;
2447}
2448
2449HRESULT Machine::removeAllCPUIDLeaves()
2450{
2451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2452
2453 HRESULT rc = i_checkStateDependency(MutableStateDep);
2454 if (FAILED(rc)) return rc;
2455
2456 if (mHWData->mCpuIdLeafList.size() > 0)
2457 {
2458 i_setModified(IsModified_MachineData);
2459 mHWData.backup();
2460
2461 mHWData->mCpuIdLeafList.clear();
2462 }
2463
2464 return S_OK;
2465}
2466HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2467{
2468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 switch(aProperty)
2471 {
2472 case HWVirtExPropertyType_Enabled:
2473 *aValue = mHWData->mHWVirtExEnabled;
2474 break;
2475
2476 case HWVirtExPropertyType_VPID:
2477 *aValue = mHWData->mHWVirtExVPIDEnabled;
2478 break;
2479
2480 case HWVirtExPropertyType_NestedPaging:
2481 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2482 break;
2483
2484 case HWVirtExPropertyType_UnrestrictedExecution:
2485 *aValue = mHWData->mHWVirtExUXEnabled;
2486 break;
2487
2488 case HWVirtExPropertyType_LargePages:
2489 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2490#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2491 *aValue = FALSE;
2492#endif
2493 break;
2494
2495 case HWVirtExPropertyType_Force:
2496 *aValue = mHWData->mHWVirtExForceEnabled;
2497 break;
2498
2499 default:
2500 return E_INVALIDARG;
2501 }
2502 return S_OK;
2503}
2504
2505HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2506{
2507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 HRESULT rc = i_checkStateDependency(MutableStateDep);
2510 if (FAILED(rc)) return rc;
2511
2512 switch(aProperty)
2513 {
2514 case HWVirtExPropertyType_Enabled:
2515 i_setModified(IsModified_MachineData);
2516 mHWData.backup();
2517 mHWData->mHWVirtExEnabled = !!aValue;
2518 break;
2519
2520 case HWVirtExPropertyType_VPID:
2521 i_setModified(IsModified_MachineData);
2522 mHWData.backup();
2523 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2524 break;
2525
2526 case HWVirtExPropertyType_NestedPaging:
2527 i_setModified(IsModified_MachineData);
2528 mHWData.backup();
2529 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2530 break;
2531
2532 case HWVirtExPropertyType_UnrestrictedExecution:
2533 i_setModified(IsModified_MachineData);
2534 mHWData.backup();
2535 mHWData->mHWVirtExUXEnabled = !!aValue;
2536 break;
2537
2538 case HWVirtExPropertyType_LargePages:
2539 i_setModified(IsModified_MachineData);
2540 mHWData.backup();
2541 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2542 break;
2543
2544 case HWVirtExPropertyType_Force:
2545 i_setModified(IsModified_MachineData);
2546 mHWData.backup();
2547 mHWData->mHWVirtExForceEnabled = !!aValue;
2548 break;
2549
2550 default:
2551 return E_INVALIDARG;
2552 }
2553
2554 return S_OK;
2555}
2556
2557HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2558{
2559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2567{
2568 /** @todo (r=dmik):
2569 * 1. Allow to change the name of the snapshot folder containing snapshots
2570 * 2. Rename the folder on disk instead of just changing the property
2571 * value (to be smart and not to leave garbage). Note that it cannot be
2572 * done here because the change may be rolled back. Thus, the right
2573 * place is #saveSettings().
2574 */
2575
2576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 HRESULT rc = i_checkStateDependency(MutableStateDep);
2579 if (FAILED(rc)) return rc;
2580
2581 if (!mData->mCurrentSnapshot.isNull())
2582 return setError(E_FAIL,
2583 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2584
2585 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2586
2587 if (strSnapshotFolder.isEmpty())
2588 strSnapshotFolder = "Snapshots";
2589 int vrc = i_calculateFullPath(strSnapshotFolder,
2590 strSnapshotFolder);
2591 if (RT_FAILURE(vrc))
2592 return setError(E_FAIL,
2593 tr("Invalid snapshot folder '%s' (%Rrc)"),
2594 strSnapshotFolder.c_str(), vrc);
2595
2596 i_setModified(IsModified_MachineData);
2597 mUserData.backup();
2598
2599 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 aMediumAttachments.resize(mMediumAttachments->size());
2609 size_t i = 0;
2610 for (MediumAttachmentList::const_iterator
2611 it = mMediumAttachments->begin();
2612 it != mMediumAttachments->end();
2613 ++it, ++i)
2614 aMediumAttachments[i] = *it;
2615
2616 return S_OK;
2617}
2618
2619HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2620{
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 Assert(!!mVRDEServer);
2624
2625 aVRDEServer = mVRDEServer;
2626
2627 return S_OK;
2628}
2629
2630HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2631{
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 aAudioAdapter = mAudioAdapter;
2635
2636 return S_OK;
2637}
2638
2639HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2640{
2641#ifdef VBOX_WITH_VUSB
2642 clearError();
2643 MultiResult rc(S_OK);
2644
2645# ifdef VBOX_WITH_USB
2646 rc = mParent->i_host()->i_checkUSBProxyService();
2647 if (FAILED(rc)) return rc;
2648# endif
2649
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 aUSBControllers.resize(mUSBControllers->size());
2653 size_t i = 0;
2654 for (USBControllerList::const_iterator
2655 it = mUSBControllers->begin();
2656 it != mUSBControllers->end();
2657 ++it, ++i)
2658 aUSBControllers[i] = *it;
2659
2660 return S_OK;
2661#else
2662 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2663 * extended error info to indicate that USB is simply not available
2664 * (w/o treating it as a failure), for example, as in OSE */
2665 NOREF(aUSBControllers);
2666 ReturnComNotImplemented();
2667#endif /* VBOX_WITH_VUSB */
2668}
2669
2670HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2671{
2672#ifdef VBOX_WITH_VUSB
2673 clearError();
2674 MultiResult rc(S_OK);
2675
2676# ifdef VBOX_WITH_USB
2677 rc = mParent->i_host()->i_checkUSBProxyService();
2678 if (FAILED(rc)) return rc;
2679# endif
2680
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aUSBDeviceFilters = mUSBDeviceFilters;
2684 return rc;
2685#else
2686 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2687 * extended error info to indicate that USB is simply not available
2688 * (w/o treating it as a failure), for example, as in OSE */
2689 NOREF(aUSBDeviceFilters);
2690 ReturnComNotImplemented();
2691#endif /* VBOX_WITH_VUSB */
2692}
2693
2694HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2695{
2696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 aSettingsFilePath = mData->m_strConfigFileFull;
2699
2700 return S_OK;
2701}
2702
2703HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2704{
2705 RT_NOREF(aSettingsFilePath);
2706 ReturnComNotImplemented();
2707}
2708
2709HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2714 if (FAILED(rc)) return rc;
2715
2716 if (!mData->pMachineConfigFile->fileExists())
2717 // this is a new machine, and no config file exists yet:
2718 *aSettingsModified = TRUE;
2719 else
2720 *aSettingsModified = (mData->flModifications != 0);
2721
2722 return S_OK;
2723}
2724
2725HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2726{
2727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 *aSessionState = mData->mSession.mState;
2730
2731 return S_OK;
2732}
2733
2734HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2735{
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 aSessionName = mData->mSession.mName;
2739
2740 return S_OK;
2741}
2742
2743HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 *aSessionPID = mData->mSession.mPID;
2748
2749 return S_OK;
2750}
2751
2752HRESULT Machine::getState(MachineState_T *aState)
2753{
2754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 *aState = mData->mMachineState;
2757 Assert(mData->mMachineState != MachineState_Null);
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2767
2768 return S_OK;
2769}
2770
2771HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 aStateFilePath = mSSData->strStateFilePath;
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2781{
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 i_getLogFolder(aLogFolder);
2785
2786 return S_OK;
2787}
2788
2789HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2790{
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 aCurrentSnapshot = mData->mCurrentSnapshot;
2794
2795 return S_OK;
2796}
2797
2798HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2799{
2800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2803 ? 0
2804 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 /* Note: for machines with no snapshots, we always return FALSE
2814 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2815 * reasons :) */
2816
2817 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2818 ? FALSE
2819 : mData->mCurrentStateModified;
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2825{
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 aSharedFolders.resize(mHWData->mSharedFolders.size());
2829 size_t i = 0;
2830 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2831 it = mHWData->mSharedFolders.begin();
2832 it != mHWData->mSharedFolders.end();
2833 ++it, ++i)
2834 aSharedFolders[i] = *it;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 *aClipboardMode = mHWData->mClipboardMode;
2844
2845 return S_OK;
2846}
2847
2848HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2849{
2850 HRESULT rc = S_OK;
2851
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
2854 alock.release();
2855 rc = i_onClipboardModeChange(aClipboardMode);
2856 alock.acquire();
2857 if (FAILED(rc)) return rc;
2858
2859 i_setModified(IsModified_MachineData);
2860 mHWData.backup();
2861 mHWData->mClipboardMode = aClipboardMode;
2862
2863 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2864 if (Global::IsOnline(mData->mMachineState))
2865 i_saveSettings(NULL);
2866
2867 return S_OK;
2868}
2869
2870HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2871{
2872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2873
2874 *aDnDMode = mHWData->mDnDMode;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2880{
2881 HRESULT rc = S_OK;
2882
2883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2884
2885 alock.release();
2886 rc = i_onDnDModeChange(aDnDMode);
2887
2888 alock.acquire();
2889 if (FAILED(rc)) return rc;
2890
2891 i_setModified(IsModified_MachineData);
2892 mHWData.backup();
2893 mHWData->mDnDMode = aDnDMode;
2894
2895 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2896 if (Global::IsOnline(mData->mMachineState))
2897 i_saveSettings(NULL);
2898
2899 return S_OK;
2900}
2901
2902HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2903{
2904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 aStorageControllers.resize(mStorageControllers->size());
2907 size_t i = 0;
2908 for (StorageControllerList::const_iterator
2909 it = mStorageControllers->begin();
2910 it != mStorageControllers->end();
2911 ++it, ++i)
2912 aStorageControllers[i] = *it;
2913
2914 return S_OK;
2915}
2916
2917HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2918{
2919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2920
2921 *aEnabled = mUserData->s.fTeleporterEnabled;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2927{
2928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* Only allow it to be set to true when PoweredOff or Aborted.
2931 (Clearing it is always permitted.) */
2932 if ( aTeleporterEnabled
2933 && mData->mRegistered
2934 && ( !i_isSessionMachine()
2935 || ( mData->mMachineState != MachineState_PoweredOff
2936 && mData->mMachineState != MachineState_Teleported
2937 && mData->mMachineState != MachineState_Aborted
2938 )
2939 )
2940 )
2941 return setError(VBOX_E_INVALID_VM_STATE,
2942 tr("The machine is not powered off (state is %s)"),
2943 Global::stringifyMachineState(mData->mMachineState));
2944
2945 i_setModified(IsModified_MachineData);
2946 mUserData.backup();
2947 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2948
2949 return S_OK;
2950}
2951
2952HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2953{
2954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2957
2958 return S_OK;
2959}
2960
2961HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2962{
2963 if (aTeleporterPort >= _64K)
2964 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2965
2966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2969 if (FAILED(rc)) return rc;
2970
2971 i_setModified(IsModified_MachineData);
2972 mUserData.backup();
2973 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2974
2975 return S_OK;
2976}
2977
2978HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2979{
2980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2981
2982 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2983
2984 return S_OK;
2985}
2986
2987HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2988{
2989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2990
2991 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2992 if (FAILED(rc)) return rc;
2993
2994 i_setModified(IsModified_MachineData);
2995 mUserData.backup();
2996 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2997
2998 return S_OK;
2999}
3000
3001HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3002{
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3005
3006 return S_OK;
3007}
3008
3009HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3010{
3011 /*
3012 * Hash the password first.
3013 */
3014 com::Utf8Str aT = aTeleporterPassword;
3015
3016 if (!aT.isEmpty())
3017 {
3018 if (VBoxIsPasswordHashed(&aT))
3019 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3020 VBoxHashPassword(&aT);
3021 }
3022
3023 /*
3024 * Do the update.
3025 */
3026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3027 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3028 if (SUCCEEDED(hrc))
3029 {
3030 i_setModified(IsModified_MachineData);
3031 mUserData.backup();
3032 mUserData->s.strTeleporterPassword = aT;
3033 }
3034
3035 return hrc;
3036}
3037
3038HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3039{
3040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3043 return S_OK;
3044}
3045
3046HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3047{
3048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3049
3050 /** @todo deal with running state change. */
3051 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3052 if (FAILED(rc)) return rc;
3053
3054 i_setModified(IsModified_MachineData);
3055 mUserData.backup();
3056 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3057 return S_OK;
3058}
3059
3060HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3061{
3062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3065 return S_OK;
3066}
3067
3068HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3069{
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 /** @todo deal with running state change. */
3073 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3074 if (FAILED(rc)) return rc;
3075
3076 i_setModified(IsModified_MachineData);
3077 mUserData.backup();
3078 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3079 return S_OK;
3080}
3081
3082HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3083{
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 /** @todo deal with running state change. */
3095 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3096 if (FAILED(rc)) return rc;
3097
3098 i_setModified(IsModified_MachineData);
3099 mUserData.backup();
3100 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3101 return S_OK;
3102}
3103
3104HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3105{
3106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3109
3110 return S_OK;
3111}
3112
3113HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3114{
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 /** @todo deal with running state change. */
3118 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3119 if (FAILED(rc)) return rc;
3120
3121 i_setModified(IsModified_MachineData);
3122 mUserData.backup();
3123 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3124
3125 return S_OK;
3126}
3127
3128HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3129{
3130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3133 return S_OK;
3134}
3135
3136HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3137{
3138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3139
3140 /** @todo deal with running state change. */
3141 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3142 if (FAILED(rc)) return rc;
3143
3144 i_setModified(IsModified_MachineData);
3145 mUserData.backup();
3146 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3147 return S_OK;
3148}
3149
3150HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3151{
3152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3155
3156 return S_OK;
3157}
3158
3159HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3160{
3161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3162
3163 /* Only allow it to be set to true when PoweredOff or Aborted.
3164 (Clearing it is always permitted.) */
3165 if ( aRTCUseUTC
3166 && mData->mRegistered
3167 && ( !i_isSessionMachine()
3168 || ( mData->mMachineState != MachineState_PoweredOff
3169 && mData->mMachineState != MachineState_Teleported
3170 && mData->mMachineState != MachineState_Aborted
3171 )
3172 )
3173 )
3174 return setError(VBOX_E_INVALID_VM_STATE,
3175 tr("The machine is not powered off (state is %s)"),
3176 Global::stringifyMachineState(mData->mMachineState));
3177
3178 i_setModified(IsModified_MachineData);
3179 mUserData.backup();
3180 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3181
3182 return S_OK;
3183}
3184
3185HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3186{
3187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3188
3189 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3190
3191 return S_OK;
3192}
3193
3194HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3195{
3196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3197
3198 HRESULT rc = i_checkStateDependency(MutableStateDep);
3199 if (FAILED(rc)) return rc;
3200
3201 i_setModified(IsModified_MachineData);
3202 mHWData.backup();
3203 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3204
3205 return S_OK;
3206}
3207
3208HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3209{
3210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3211
3212 *aIOCacheSize = mHWData->mIOCacheSize;
3213
3214 return S_OK;
3215}
3216
3217HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3218{
3219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3220
3221 HRESULT rc = i_checkStateDependency(MutableStateDep);
3222 if (FAILED(rc)) return rc;
3223
3224 i_setModified(IsModified_MachineData);
3225 mHWData.backup();
3226 mHWData->mIOCacheSize = aIOCacheSize;
3227
3228 return S_OK;
3229}
3230
3231
3232/**
3233 * @note Locks objects!
3234 */
3235HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3236 LockType_T aLockType)
3237{
3238 /* check the session state */
3239 SessionState_T state;
3240 HRESULT rc = aSession->COMGETTER(State)(&state);
3241 if (FAILED(rc)) return rc;
3242
3243 if (state != SessionState_Unlocked)
3244 return setError(VBOX_E_INVALID_OBJECT_STATE,
3245 tr("The given session is busy"));
3246
3247 // get the client's IInternalSessionControl interface
3248 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3249 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3250 E_INVALIDARG);
3251
3252 // session name (only used in some code paths)
3253 Utf8Str strSessionName;
3254
3255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3256
3257 if (!mData->mRegistered)
3258 return setError(E_UNEXPECTED,
3259 tr("The machine '%s' is not registered"),
3260 mUserData->s.strName.c_str());
3261
3262 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3263
3264 SessionState_T oldState = mData->mSession.mState;
3265 /* Hack: in case the session is closing and there is a progress object
3266 * which allows waiting for the session to be closed, take the opportunity
3267 * and do a limited wait (max. 1 second). This helps a lot when the system
3268 * is busy and thus session closing can take a little while. */
3269 if ( mData->mSession.mState == SessionState_Unlocking
3270 && mData->mSession.mProgress)
3271 {
3272 alock.release();
3273 mData->mSession.mProgress->WaitForCompletion(1000);
3274 alock.acquire();
3275 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3276 }
3277
3278 // try again now
3279 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3280 // (i.e. session machine exists)
3281 && (aLockType == LockType_Shared) // caller wants a shared link to the
3282 // existing session that holds the write lock:
3283 )
3284 {
3285 // OK, share the session... we are now dealing with three processes:
3286 // 1) VBoxSVC (where this code runs);
3287 // 2) process C: the caller's client process (who wants a shared session);
3288 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3289
3290 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3291 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3292 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3293 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3294 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3295
3296 /*
3297 * Release the lock before calling the client process. It's safe here
3298 * since the only thing to do after we get the lock again is to add
3299 * the remote control to the list (which doesn't directly influence
3300 * anything).
3301 */
3302 alock.release();
3303
3304 // get the console of the session holding the write lock (this is a remote call)
3305 ComPtr<IConsole> pConsoleW;
3306 if (mData->mSession.mLockType == LockType_VM)
3307 {
3308 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3309 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3310 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3311 if (FAILED(rc))
3312 // the failure may occur w/o any error info (from RPC), so provide one
3313 return setError(VBOX_E_VM_ERROR,
3314 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3315 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3316 }
3317
3318 // share the session machine and W's console with the caller's session
3319 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3320 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3321 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3322
3323 if (FAILED(rc))
3324 // the failure may occur w/o any error info (from RPC), so provide one
3325 return setError(VBOX_E_VM_ERROR,
3326 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3327 alock.acquire();
3328
3329 // need to revalidate the state after acquiring the lock again
3330 if (mData->mSession.mState != SessionState_Locked)
3331 {
3332 pSessionControl->Uninitialize();
3333 return setError(VBOX_E_INVALID_SESSION_STATE,
3334 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3335 mUserData->s.strName.c_str());
3336 }
3337
3338 // add the caller's session to the list
3339 mData->mSession.mRemoteControls.push_back(pSessionControl);
3340 }
3341 else if ( mData->mSession.mState == SessionState_Locked
3342 || mData->mSession.mState == SessionState_Unlocking
3343 )
3344 {
3345 // sharing not permitted, or machine still unlocking:
3346 return setError(VBOX_E_INVALID_OBJECT_STATE,
3347 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3348 mUserData->s.strName.c_str());
3349 }
3350 else
3351 {
3352 // machine is not locked: then write-lock the machine (create the session machine)
3353
3354 // must not be busy
3355 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3356
3357 // get the caller's session PID
3358 RTPROCESS pid = NIL_RTPROCESS;
3359 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3360 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3361 Assert(pid != NIL_RTPROCESS);
3362
3363 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3364
3365 if (fLaunchingVMProcess)
3366 {
3367 if (mData->mSession.mPID == NIL_RTPROCESS)
3368 {
3369 // two or more clients racing for a lock, the one which set the
3370 // session state to Spawning will win, the others will get an
3371 // error as we can't decide here if waiting a little would help
3372 // (only for shared locks this would avoid an error)
3373 return setError(VBOX_E_INVALID_OBJECT_STATE,
3374 tr("The machine '%s' already has a lock request pending"),
3375 mUserData->s.strName.c_str());
3376 }
3377
3378 // this machine is awaiting for a spawning session to be opened:
3379 // then the calling process must be the one that got started by
3380 // LaunchVMProcess()
3381
3382 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3383 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3384
3385#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3386 /* Hardened windows builds spawns three processes when a VM is
3387 launched, the 3rd one is the one that will end up here. */
3388 RTPROCESS ppid;
3389 int rc = RTProcQueryParent(pid, &ppid);
3390 if (RT_SUCCESS(rc))
3391 rc = RTProcQueryParent(ppid, &ppid);
3392 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3393 || rc == VERR_ACCESS_DENIED)
3394 {
3395 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3396 mData->mSession.mPID = pid;
3397 }
3398#endif
3399
3400 if (mData->mSession.mPID != pid)
3401 return setError(E_ACCESSDENIED,
3402 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3403 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3404 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3405 }
3406
3407 // create the mutable SessionMachine from the current machine
3408 ComObjPtr<SessionMachine> sessionMachine;
3409 sessionMachine.createObject();
3410 rc = sessionMachine->init(this);
3411 AssertComRC(rc);
3412
3413 /* NOTE: doing return from this function after this point but
3414 * before the end is forbidden since it may call SessionMachine::uninit()
3415 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3416 * lock while still holding the Machine lock in alock so that a deadlock
3417 * is possible due to the wrong lock order. */
3418
3419 if (SUCCEEDED(rc))
3420 {
3421 /*
3422 * Set the session state to Spawning to protect against subsequent
3423 * attempts to open a session and to unregister the machine after
3424 * we release the lock.
3425 */
3426 SessionState_T origState = mData->mSession.mState;
3427 mData->mSession.mState = SessionState_Spawning;
3428
3429#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3430 /* Get the client token ID to be passed to the client process */
3431 Utf8Str strTokenId;
3432 sessionMachine->i_getTokenId(strTokenId);
3433 Assert(!strTokenId.isEmpty());
3434#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3435 /* Get the client token to be passed to the client process */
3436 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3437 /* The token is now "owned" by pToken, fix refcount */
3438 if (!pToken.isNull())
3439 pToken->Release();
3440#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3441
3442 /*
3443 * Release the lock before calling the client process -- it will call
3444 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3445 * because the state is Spawning, so that LaunchVMProcess() and
3446 * LockMachine() calls will fail. This method, called before we
3447 * acquire the lock again, will fail because of the wrong PID.
3448 *
3449 * Note that mData->mSession.mRemoteControls accessed outside
3450 * the lock may not be modified when state is Spawning, so it's safe.
3451 */
3452 alock.release();
3453
3454 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3455#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3456 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3457#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3458 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3459 /* Now the token is owned by the client process. */
3460 pToken.setNull();
3461#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3462 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3463
3464 /* The failure may occur w/o any error info (from RPC), so provide one */
3465 if (FAILED(rc))
3466 setError(VBOX_E_VM_ERROR,
3467 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3468
3469 // get session name, either to remember or to compare against
3470 // the already known session name.
3471 {
3472 Bstr bstrSessionName;
3473 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3474 if (SUCCEEDED(rc2))
3475 strSessionName = bstrSessionName;
3476 }
3477
3478 if ( SUCCEEDED(rc)
3479 && fLaunchingVMProcess
3480 )
3481 {
3482 /* complete the remote session initialization */
3483
3484 /* get the console from the direct session */
3485 ComPtr<IConsole> console;
3486 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3487 ComAssertComRC(rc);
3488
3489 if (SUCCEEDED(rc) && !console)
3490 {
3491 ComAssert(!!console);
3492 rc = E_FAIL;
3493 }
3494
3495 /* assign machine & console to the remote session */
3496 if (SUCCEEDED(rc))
3497 {
3498 /*
3499 * after LaunchVMProcess(), the first and the only
3500 * entry in remoteControls is that remote session
3501 */
3502 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3503 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3504 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3505
3506 /* The failure may occur w/o any error info (from RPC), so provide one */
3507 if (FAILED(rc))
3508 setError(VBOX_E_VM_ERROR,
3509 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3510 }
3511
3512 if (FAILED(rc))
3513 pSessionControl->Uninitialize();
3514 }
3515
3516 /* acquire the lock again */
3517 alock.acquire();
3518
3519 /* Restore the session state */
3520 mData->mSession.mState = origState;
3521 }
3522
3523 // finalize spawning anyway (this is why we don't return on errors above)
3524 if (fLaunchingVMProcess)
3525 {
3526 Assert(mData->mSession.mName == strSessionName);
3527 /* Note that the progress object is finalized later */
3528 /** @todo Consider checking mData->mSession.mProgress for cancellation
3529 * around here. */
3530
3531 /* We don't reset mSession.mPID here because it is necessary for
3532 * SessionMachine::uninit() to reap the child process later. */
3533
3534 if (FAILED(rc))
3535 {
3536 /* Close the remote session, remove the remote control from the list
3537 * and reset session state to Closed (@note keep the code in sync
3538 * with the relevant part in checkForSpawnFailure()). */
3539
3540 Assert(mData->mSession.mRemoteControls.size() == 1);
3541 if (mData->mSession.mRemoteControls.size() == 1)
3542 {
3543 ErrorInfoKeeper eik;
3544 mData->mSession.mRemoteControls.front()->Uninitialize();
3545 }
3546
3547 mData->mSession.mRemoteControls.clear();
3548 mData->mSession.mState = SessionState_Unlocked;
3549 }
3550 }
3551 else
3552 {
3553 /* memorize PID of the directly opened session */
3554 if (SUCCEEDED(rc))
3555 mData->mSession.mPID = pid;
3556 }
3557
3558 if (SUCCEEDED(rc))
3559 {
3560 mData->mSession.mLockType = aLockType;
3561 /* memorize the direct session control and cache IUnknown for it */
3562 mData->mSession.mDirectControl = pSessionControl;
3563 mData->mSession.mState = SessionState_Locked;
3564 if (!fLaunchingVMProcess)
3565 mData->mSession.mName = strSessionName;
3566 /* associate the SessionMachine with this Machine */
3567 mData->mSession.mMachine = sessionMachine;
3568
3569 /* request an IUnknown pointer early from the remote party for later
3570 * identity checks (it will be internally cached within mDirectControl
3571 * at least on XPCOM) */
3572 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3573 NOREF(unk);
3574 }
3575
3576 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3577 * would break the lock order */
3578 alock.release();
3579
3580 /* uninitialize the created session machine on failure */
3581 if (FAILED(rc))
3582 sessionMachine->uninit();
3583 }
3584
3585 if (SUCCEEDED(rc))
3586 {
3587 /*
3588 * tell the client watcher thread to update the set of
3589 * machines that have open sessions
3590 */
3591 mParent->i_updateClientWatcher();
3592
3593 if (oldState != SessionState_Locked)
3594 /* fire an event */
3595 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3596 }
3597
3598 return rc;
3599}
3600
3601/**
3602 * @note Locks objects!
3603 */
3604HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3605 const com::Utf8Str &aName,
3606 const com::Utf8Str &aEnvironment,
3607 ComPtr<IProgress> &aProgress)
3608{
3609 Utf8Str strFrontend(aName);
3610 /* "emergencystop" doesn't need the session, so skip the checks/interface
3611 * retrieval. This code doesn't quite fit in here, but introducing a
3612 * special API method would be even more effort, and would require explicit
3613 * support by every API client. It's better to hide the feature a bit. */
3614 if (strFrontend != "emergencystop")
3615 CheckComArgNotNull(aSession);
3616
3617 HRESULT rc = S_OK;
3618 if (strFrontend.isEmpty())
3619 {
3620 Bstr bstrFrontend;
3621 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3622 if (FAILED(rc))
3623 return rc;
3624 strFrontend = bstrFrontend;
3625 if (strFrontend.isEmpty())
3626 {
3627 ComPtr<ISystemProperties> systemProperties;
3628 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3629 if (FAILED(rc))
3630 return rc;
3631 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3632 if (FAILED(rc))
3633 return rc;
3634 strFrontend = bstrFrontend;
3635 }
3636 /* paranoia - emergencystop is not a valid default */
3637 if (strFrontend == "emergencystop")
3638 strFrontend = Utf8Str::Empty;
3639 }
3640 /* default frontend: Qt GUI */
3641 if (strFrontend.isEmpty())
3642 strFrontend = "GUI/Qt";
3643
3644 if (strFrontend != "emergencystop")
3645 {
3646 /* check the session state */
3647 SessionState_T state;
3648 rc = aSession->COMGETTER(State)(&state);
3649 if (FAILED(rc))
3650 return rc;
3651
3652 if (state != SessionState_Unlocked)
3653 return setError(VBOX_E_INVALID_OBJECT_STATE,
3654 tr("The given session is busy"));
3655
3656 /* get the IInternalSessionControl interface */
3657 ComPtr<IInternalSessionControl> control(aSession);
3658 ComAssertMsgRet(!control.isNull(),
3659 ("No IInternalSessionControl interface"),
3660 E_INVALIDARG);
3661
3662 /* get the teleporter enable state for the progress object init. */
3663 BOOL fTeleporterEnabled;
3664 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3665 if (FAILED(rc))
3666 return rc;
3667
3668 /* create a progress object */
3669 ComObjPtr<ProgressProxy> progress;
3670 progress.createObject();
3671 rc = progress->init(mParent,
3672 static_cast<IMachine*>(this),
3673 Bstr(tr("Starting VM")).raw(),
3674 TRUE /* aCancelable */,
3675 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3676 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3677 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3678 2 /* uFirstOperationWeight */,
3679 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3680
3681 if (SUCCEEDED(rc))
3682 {
3683 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3684 if (SUCCEEDED(rc))
3685 {
3686 aProgress = progress;
3687
3688 /* signal the client watcher thread */
3689 mParent->i_updateClientWatcher();
3690
3691 /* fire an event */
3692 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3693 }
3694 }
3695 }
3696 else
3697 {
3698 /* no progress object - either instant success or failure */
3699 aProgress = NULL;
3700
3701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3702
3703 if (mData->mSession.mState != SessionState_Locked)
3704 return setError(VBOX_E_INVALID_OBJECT_STATE,
3705 tr("The machine '%s' is not locked by a session"),
3706 mUserData->s.strName.c_str());
3707
3708 /* must have a VM process associated - do not kill normal API clients
3709 * with an open session */
3710 if (!Global::IsOnline(mData->mMachineState))
3711 return setError(VBOX_E_INVALID_OBJECT_STATE,
3712 tr("The machine '%s' does not have a VM process"),
3713 mUserData->s.strName.c_str());
3714
3715 /* forcibly terminate the VM process */
3716 if (mData->mSession.mPID != NIL_RTPROCESS)
3717 RTProcTerminate(mData->mSession.mPID);
3718
3719 /* signal the client watcher thread, as most likely the client has
3720 * been terminated */
3721 mParent->i_updateClientWatcher();
3722 }
3723
3724 return rc;
3725}
3726
3727HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3728{
3729 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3730 return setError(E_INVALIDARG,
3731 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3732 aPosition, SchemaDefs::MaxBootPosition);
3733
3734 if (aDevice == DeviceType_USB)
3735 return setError(E_NOTIMPL,
3736 tr("Booting from USB device is currently not supported"));
3737
3738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3739
3740 HRESULT rc = i_checkStateDependency(MutableStateDep);
3741 if (FAILED(rc)) return rc;
3742
3743 i_setModified(IsModified_MachineData);
3744 mHWData.backup();
3745 mHWData->mBootOrder[aPosition - 1] = aDevice;
3746
3747 return S_OK;
3748}
3749
3750HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3751{
3752 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3753 return setError(E_INVALIDARG,
3754 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3755 aPosition, SchemaDefs::MaxBootPosition);
3756
3757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3758
3759 *aDevice = mHWData->mBootOrder[aPosition - 1];
3760
3761 return S_OK;
3762}
3763
3764HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3765 LONG aControllerPort,
3766 LONG aDevice,
3767 DeviceType_T aType,
3768 const ComPtr<IMedium> &aMedium)
3769{
3770 IMedium *aM = aMedium;
3771 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3772 aName.c_str(), aControllerPort, aDevice, aType, aM));
3773
3774 // request the host lock first, since might be calling Host methods for getting host drives;
3775 // next, protect the media tree all the while we're in here, as well as our member variables
3776 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3777 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3778
3779 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3780 if (FAILED(rc)) return rc;
3781
3782 /// @todo NEWMEDIA implicit machine registration
3783 if (!mData->mRegistered)
3784 return setError(VBOX_E_INVALID_OBJECT_STATE,
3785 tr("Cannot attach storage devices to an unregistered machine"));
3786
3787 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3788
3789 /* Check for an existing controller. */
3790 ComObjPtr<StorageController> ctl;
3791 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3792 if (FAILED(rc)) return rc;
3793
3794 StorageControllerType_T ctrlType;
3795 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3796 if (FAILED(rc))
3797 return setError(E_FAIL,
3798 tr("Could not get type of controller '%s'"),
3799 aName.c_str());
3800
3801 bool fSilent = false;
3802 Utf8Str strReconfig;
3803
3804 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3805 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3806 if ( mData->mMachineState == MachineState_Paused
3807 && strReconfig == "1")
3808 fSilent = true;
3809
3810 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3811 bool fHotplug = false;
3812 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3813 fHotplug = true;
3814
3815 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3816 return setError(VBOX_E_INVALID_VM_STATE,
3817 tr("Controller '%s' does not support hotplugging"),
3818 aName.c_str());
3819
3820 // check that the port and device are not out of range
3821 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3822 if (FAILED(rc)) return rc;
3823
3824 /* check if the device slot is already busy */
3825 MediumAttachment *pAttachTemp;
3826 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3827 aName,
3828 aControllerPort,
3829 aDevice)))
3830 {
3831 Medium *pMedium = pAttachTemp->i_getMedium();
3832 if (pMedium)
3833 {
3834 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3835 return setError(VBOX_E_OBJECT_IN_USE,
3836 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3837 pMedium->i_getLocationFull().c_str(),
3838 aControllerPort,
3839 aDevice,
3840 aName.c_str());
3841 }
3842 else
3843 return setError(VBOX_E_OBJECT_IN_USE,
3844 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3845 aControllerPort, aDevice, aName.c_str());
3846 }
3847
3848 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3849 if (aMedium && medium.isNull())
3850 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3851
3852 AutoCaller mediumCaller(medium);
3853 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3854
3855 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3856
3857 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3858 && !medium.isNull()
3859 )
3860 return setError(VBOX_E_OBJECT_IN_USE,
3861 tr("Medium '%s' is already attached to this virtual machine"),
3862 medium->i_getLocationFull().c_str());
3863
3864 if (!medium.isNull())
3865 {
3866 MediumType_T mtype = medium->i_getType();
3867 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3868 // For DVDs it's not written to the config file, so needs no global config
3869 // version bump. For floppies it's a new attribute "type", which is ignored
3870 // by older VirtualBox version, so needs no global config version bump either.
3871 // For hard disks this type is not accepted.
3872 if (mtype == MediumType_MultiAttach)
3873 {
3874 // This type is new with VirtualBox 4.0 and therefore requires settings
3875 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3876 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3877 // two reasons: The medium type is a property of the media registry tree, which
3878 // can reside in the global config file (for pre-4.0 media); we would therefore
3879 // possibly need to bump the global config version. We don't want to do that though
3880 // because that might make downgrading to pre-4.0 impossible.
3881 // As a result, we can only use these two new types if the medium is NOT in the
3882 // global registry:
3883 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3884 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3885 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3886 )
3887 return setError(VBOX_E_INVALID_OBJECT_STATE,
3888 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3889 "to machines that were created with VirtualBox 4.0 or later"),
3890 medium->i_getLocationFull().c_str());
3891 }
3892 }
3893
3894 bool fIndirect = false;
3895 if (!medium.isNull())
3896 fIndirect = medium->i_isReadOnly();
3897 bool associate = true;
3898
3899 do
3900 {
3901 if ( aType == DeviceType_HardDisk
3902 && mMediumAttachments.isBackedUp())
3903 {
3904 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3905
3906 /* check if the medium was attached to the VM before we started
3907 * changing attachments in which case the attachment just needs to
3908 * be restored */
3909 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3910 {
3911 AssertReturn(!fIndirect, E_FAIL);
3912
3913 /* see if it's the same bus/channel/device */
3914 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3915 {
3916 /* the simplest case: restore the whole attachment
3917 * and return, nothing else to do */
3918 mMediumAttachments->push_back(pAttachTemp);
3919
3920 /* Reattach the medium to the VM. */
3921 if (fHotplug || fSilent)
3922 {
3923 mediumLock.release();
3924 treeLock.release();
3925 alock.release();
3926
3927 MediumLockList *pMediumLockList(new MediumLockList());
3928
3929 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3930 medium /* pToLockWrite */,
3931 false /* fMediumLockWriteAll */,
3932 NULL,
3933 *pMediumLockList);
3934 alock.acquire();
3935 if (FAILED(rc))
3936 delete pMediumLockList;
3937 else
3938 {
3939 mData->mSession.mLockedMedia.Unlock();
3940 alock.release();
3941 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3942 mData->mSession.mLockedMedia.Lock();
3943 alock.acquire();
3944 }
3945 alock.release();
3946
3947 if (SUCCEEDED(rc))
3948 {
3949 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3950 /* Remove lock list in case of error. */
3951 if (FAILED(rc))
3952 {
3953 mData->mSession.mLockedMedia.Unlock();
3954 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3955 mData->mSession.mLockedMedia.Lock();
3956 }
3957 }
3958 }
3959
3960 return S_OK;
3961 }
3962
3963 /* bus/channel/device differ; we need a new attachment object,
3964 * but don't try to associate it again */
3965 associate = false;
3966 break;
3967 }
3968 }
3969
3970 /* go further only if the attachment is to be indirect */
3971 if (!fIndirect)
3972 break;
3973
3974 /* perform the so called smart attachment logic for indirect
3975 * attachments. Note that smart attachment is only applicable to base
3976 * hard disks. */
3977
3978 if (medium->i_getParent().isNull())
3979 {
3980 /* first, investigate the backup copy of the current hard disk
3981 * attachments to make it possible to re-attach existing diffs to
3982 * another device slot w/o losing their contents */
3983 if (mMediumAttachments.isBackedUp())
3984 {
3985 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3986
3987 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3988 uint32_t foundLevel = 0;
3989
3990 for (MediumAttachmentList::const_iterator
3991 it = oldAtts.begin();
3992 it != oldAtts.end();
3993 ++it)
3994 {
3995 uint32_t level = 0;
3996 MediumAttachment *pAttach = *it;
3997 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3998 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3999 if (pMedium.isNull())
4000 continue;
4001
4002 if (pMedium->i_getBase(&level) == medium)
4003 {
4004 /* skip the hard disk if its currently attached (we
4005 * cannot attach the same hard disk twice) */
4006 if (i_findAttachment(*mMediumAttachments.data(),
4007 pMedium))
4008 continue;
4009
4010 /* matched device, channel and bus (i.e. attached to the
4011 * same place) will win and immediately stop the search;
4012 * otherwise the attachment that has the youngest
4013 * descendant of medium will be used
4014 */
4015 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4016 {
4017 /* the simplest case: restore the whole attachment
4018 * and return, nothing else to do */
4019 mMediumAttachments->push_back(*it);
4020
4021 /* Reattach the medium to the VM. */
4022 if (fHotplug || fSilent)
4023 {
4024 mediumLock.release();
4025 treeLock.release();
4026 alock.release();
4027
4028 MediumLockList *pMediumLockList(new MediumLockList());
4029
4030 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4031 medium /* pToLockWrite */,
4032 false /* fMediumLockWriteAll */,
4033 NULL,
4034 *pMediumLockList);
4035 alock.acquire();
4036 if (FAILED(rc))
4037 delete pMediumLockList;
4038 else
4039 {
4040 mData->mSession.mLockedMedia.Unlock();
4041 alock.release();
4042 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4043 mData->mSession.mLockedMedia.Lock();
4044 alock.acquire();
4045 }
4046 alock.release();
4047
4048 if (SUCCEEDED(rc))
4049 {
4050 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4051 /* Remove lock list in case of error. */
4052 if (FAILED(rc))
4053 {
4054 mData->mSession.mLockedMedia.Unlock();
4055 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4056 mData->mSession.mLockedMedia.Lock();
4057 }
4058 }
4059 }
4060
4061 return S_OK;
4062 }
4063 else if ( foundIt == oldAtts.end()
4064 || level > foundLevel /* prefer younger */
4065 )
4066 {
4067 foundIt = it;
4068 foundLevel = level;
4069 }
4070 }
4071 }
4072
4073 if (foundIt != oldAtts.end())
4074 {
4075 /* use the previously attached hard disk */
4076 medium = (*foundIt)->i_getMedium();
4077 mediumCaller.attach(medium);
4078 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4079 mediumLock.attach(medium);
4080 /* not implicit, doesn't require association with this VM */
4081 fIndirect = false;
4082 associate = false;
4083 /* go right to the MediumAttachment creation */
4084 break;
4085 }
4086 }
4087
4088 /* must give up the medium lock and medium tree lock as below we
4089 * go over snapshots, which needs a lock with higher lock order. */
4090 mediumLock.release();
4091 treeLock.release();
4092
4093 /* then, search through snapshots for the best diff in the given
4094 * hard disk's chain to base the new diff on */
4095
4096 ComObjPtr<Medium> base;
4097 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4098 while (snap)
4099 {
4100 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4101
4102 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4103
4104 MediumAttachment *pAttachFound = NULL;
4105 uint32_t foundLevel = 0;
4106
4107 for (MediumAttachmentList::const_iterator
4108 it = snapAtts.begin();
4109 it != snapAtts.end();
4110 ++it)
4111 {
4112 MediumAttachment *pAttach = *it;
4113 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4114 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4115 if (pMedium.isNull())
4116 continue;
4117
4118 uint32_t level = 0;
4119 if (pMedium->i_getBase(&level) == medium)
4120 {
4121 /* matched device, channel and bus (i.e. attached to the
4122 * same place) will win and immediately stop the search;
4123 * otherwise the attachment that has the youngest
4124 * descendant of medium will be used
4125 */
4126 if ( pAttach->i_getDevice() == aDevice
4127 && pAttach->i_getPort() == aControllerPort
4128 && pAttach->i_getControllerName() == aName
4129 )
4130 {
4131 pAttachFound = pAttach;
4132 break;
4133 }
4134 else if ( !pAttachFound
4135 || level > foundLevel /* prefer younger */
4136 )
4137 {
4138 pAttachFound = pAttach;
4139 foundLevel = level;
4140 }
4141 }
4142 }
4143
4144 if (pAttachFound)
4145 {
4146 base = pAttachFound->i_getMedium();
4147 break;
4148 }
4149
4150 snap = snap->i_getParent();
4151 }
4152
4153 /* re-lock medium tree and the medium, as we need it below */
4154 treeLock.acquire();
4155 mediumLock.acquire();
4156
4157 /* found a suitable diff, use it as a base */
4158 if (!base.isNull())
4159 {
4160 medium = base;
4161 mediumCaller.attach(medium);
4162 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4163 mediumLock.attach(medium);
4164 }
4165 }
4166
4167 Utf8Str strFullSnapshotFolder;
4168 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4169
4170 ComObjPtr<Medium> diff;
4171 diff.createObject();
4172 // store this diff in the same registry as the parent
4173 Guid uuidRegistryParent;
4174 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4175 {
4176 // parent image has no registry: this can happen if we're attaching a new immutable
4177 // image that has not yet been attached (medium then points to the base and we're
4178 // creating the diff image for the immutable, and the parent is not yet registered);
4179 // put the parent in the machine registry then
4180 mediumLock.release();
4181 treeLock.release();
4182 alock.release();
4183 i_addMediumToRegistry(medium);
4184 alock.acquire();
4185 treeLock.acquire();
4186 mediumLock.acquire();
4187 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4188 }
4189 rc = diff->init(mParent,
4190 medium->i_getPreferredDiffFormat(),
4191 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4192 uuidRegistryParent,
4193 DeviceType_HardDisk);
4194 if (FAILED(rc)) return rc;
4195
4196 /* Apply the normal locking logic to the entire chain. */
4197 MediumLockList *pMediumLockList(new MediumLockList());
4198 mediumLock.release();
4199 treeLock.release();
4200 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4201 diff /* pToLockWrite */,
4202 false /* fMediumLockWriteAll */,
4203 medium,
4204 *pMediumLockList);
4205 treeLock.acquire();
4206 mediumLock.acquire();
4207 if (SUCCEEDED(rc))
4208 {
4209 mediumLock.release();
4210 treeLock.release();
4211 rc = pMediumLockList->Lock();
4212 treeLock.acquire();
4213 mediumLock.acquire();
4214 if (FAILED(rc))
4215 setError(rc,
4216 tr("Could not lock medium when creating diff '%s'"),
4217 diff->i_getLocationFull().c_str());
4218 else
4219 {
4220 /* will release the lock before the potentially lengthy
4221 * operation, so protect with the special state */
4222 MachineState_T oldState = mData->mMachineState;
4223 i_setMachineState(MachineState_SettingUp);
4224
4225 mediumLock.release();
4226 treeLock.release();
4227 alock.release();
4228
4229 rc = medium->i_createDiffStorage(diff,
4230 medium->i_getPreferredDiffVariant(),
4231 pMediumLockList,
4232 NULL /* aProgress */,
4233 true /* aWait */);
4234
4235 alock.acquire();
4236 treeLock.acquire();
4237 mediumLock.acquire();
4238
4239 i_setMachineState(oldState);
4240 }
4241 }
4242
4243 /* Unlock the media and free the associated memory. */
4244 delete pMediumLockList;
4245
4246 if (FAILED(rc)) return rc;
4247
4248 /* use the created diff for the actual attachment */
4249 medium = diff;
4250 mediumCaller.attach(medium);
4251 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4252 mediumLock.attach(medium);
4253 }
4254 while (0);
4255
4256 ComObjPtr<MediumAttachment> attachment;
4257 attachment.createObject();
4258 rc = attachment->init(this,
4259 medium,
4260 aName,
4261 aControllerPort,
4262 aDevice,
4263 aType,
4264 fIndirect,
4265 false /* fPassthrough */,
4266 false /* fTempEject */,
4267 false /* fNonRotational */,
4268 false /* fDiscard */,
4269 fHotplug /* fHotPluggable */,
4270 Utf8Str::Empty);
4271 if (FAILED(rc)) return rc;
4272
4273 if (associate && !medium.isNull())
4274 {
4275 // as the last step, associate the medium to the VM
4276 rc = medium->i_addBackReference(mData->mUuid);
4277 // here we can fail because of Deleting, or being in process of creating a Diff
4278 if (FAILED(rc)) return rc;
4279
4280 mediumLock.release();
4281 treeLock.release();
4282 alock.release();
4283 i_addMediumToRegistry(medium);
4284 alock.acquire();
4285 treeLock.acquire();
4286 mediumLock.acquire();
4287 }
4288
4289 /* success: finally remember the attachment */
4290 i_setModified(IsModified_Storage);
4291 mMediumAttachments.backup();
4292 mMediumAttachments->push_back(attachment);
4293
4294 mediumLock.release();
4295 treeLock.release();
4296 alock.release();
4297
4298 if (fHotplug || fSilent)
4299 {
4300 if (!medium.isNull())
4301 {
4302 MediumLockList *pMediumLockList(new MediumLockList());
4303
4304 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4305 medium /* pToLockWrite */,
4306 false /* fMediumLockWriteAll */,
4307 NULL,
4308 *pMediumLockList);
4309 alock.acquire();
4310 if (FAILED(rc))
4311 delete pMediumLockList;
4312 else
4313 {
4314 mData->mSession.mLockedMedia.Unlock();
4315 alock.release();
4316 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4317 mData->mSession.mLockedMedia.Lock();
4318 alock.acquire();
4319 }
4320 alock.release();
4321 }
4322
4323 if (SUCCEEDED(rc))
4324 {
4325 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4326 /* Remove lock list in case of error. */
4327 if (FAILED(rc))
4328 {
4329 mData->mSession.mLockedMedia.Unlock();
4330 mData->mSession.mLockedMedia.Remove(attachment);
4331 mData->mSession.mLockedMedia.Lock();
4332 }
4333 }
4334 }
4335
4336 /* Save modified registries, but skip this machine as it's the caller's
4337 * job to save its settings like all other settings changes. */
4338 mParent->i_unmarkRegistryModified(i_getId());
4339 mParent->i_saveModifiedRegistries();
4340
4341 return rc;
4342}
4343
4344HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4345 LONG aDevice)
4346{
4347 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4348 aName.c_str(), aControllerPort, aDevice));
4349
4350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4351
4352 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4353 if (FAILED(rc)) return rc;
4354
4355 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4356
4357 /* Check for an existing controller. */
4358 ComObjPtr<StorageController> ctl;
4359 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4360 if (FAILED(rc)) return rc;
4361
4362 StorageControllerType_T ctrlType;
4363 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4364 if (FAILED(rc))
4365 return setError(E_FAIL,
4366 tr("Could not get type of controller '%s'"),
4367 aName.c_str());
4368
4369 bool fSilent = false;
4370 Utf8Str strReconfig;
4371
4372 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4373 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4374 if ( mData->mMachineState == MachineState_Paused
4375 && strReconfig == "1")
4376 fSilent = true;
4377
4378 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4379 bool fHotplug = false;
4380 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4381 fHotplug = true;
4382
4383 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4384 return setError(VBOX_E_INVALID_VM_STATE,
4385 tr("Controller '%s' does not support hotplugging"),
4386 aName.c_str());
4387
4388 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4389 aName,
4390 aControllerPort,
4391 aDevice);
4392 if (!pAttach)
4393 return setError(VBOX_E_OBJECT_NOT_FOUND,
4394 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4395 aDevice, aControllerPort, aName.c_str());
4396
4397 if (fHotplug && !pAttach->i_getHotPluggable())
4398 return setError(VBOX_E_NOT_SUPPORTED,
4399 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4400 aDevice, aControllerPort, aName.c_str());
4401
4402 /*
4403 * The VM has to detach the device before we delete any implicit diffs.
4404 * If this fails we can roll back without loosing data.
4405 */
4406 if (fHotplug || fSilent)
4407 {
4408 alock.release();
4409 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4410 alock.acquire();
4411 }
4412 if (FAILED(rc)) return rc;
4413
4414 /* If we are here everything went well and we can delete the implicit now. */
4415 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4416
4417 alock.release();
4418
4419 /* Save modified registries, but skip this machine as it's the caller's
4420 * job to save its settings like all other settings changes. */
4421 mParent->i_unmarkRegistryModified(i_getId());
4422 mParent->i_saveModifiedRegistries();
4423
4424 return rc;
4425}
4426
4427HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4428 LONG aDevice, BOOL aPassthrough)
4429{
4430 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4431 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4432
4433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4434
4435 HRESULT rc = i_checkStateDependency(MutableStateDep);
4436 if (FAILED(rc)) return rc;
4437
4438 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4439
4440 if (Global::IsOnlineOrTransient(mData->mMachineState))
4441 return setError(VBOX_E_INVALID_VM_STATE,
4442 tr("Invalid machine state: %s"),
4443 Global::stringifyMachineState(mData->mMachineState));
4444
4445 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4446 aName,
4447 aControllerPort,
4448 aDevice);
4449 if (!pAttach)
4450 return setError(VBOX_E_OBJECT_NOT_FOUND,
4451 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4452 aDevice, aControllerPort, aName.c_str());
4453
4454
4455 i_setModified(IsModified_Storage);
4456 mMediumAttachments.backup();
4457
4458 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4459
4460 if (pAttach->i_getType() != DeviceType_DVD)
4461 return setError(E_INVALIDARG,
4462 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4463 aDevice, aControllerPort, aName.c_str());
4464 pAttach->i_updatePassthrough(!!aPassthrough);
4465
4466 return S_OK;
4467}
4468
4469HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4470 LONG aDevice, BOOL aTemporaryEject)
4471{
4472
4473 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4474 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4475
4476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4477
4478 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4479 if (FAILED(rc)) return rc;
4480
4481 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4482 aName,
4483 aControllerPort,
4484 aDevice);
4485 if (!pAttach)
4486 return setError(VBOX_E_OBJECT_NOT_FOUND,
4487 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4488 aDevice, aControllerPort, aName.c_str());
4489
4490
4491 i_setModified(IsModified_Storage);
4492 mMediumAttachments.backup();
4493
4494 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4495
4496 if (pAttach->i_getType() != DeviceType_DVD)
4497 return setError(E_INVALIDARG,
4498 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4499 aDevice, aControllerPort, aName.c_str());
4500 pAttach->i_updateTempEject(!!aTemporaryEject);
4501
4502 return S_OK;
4503}
4504
4505HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4506 LONG aDevice, BOOL aNonRotational)
4507{
4508
4509 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4510 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4511
4512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4513
4514 HRESULT rc = i_checkStateDependency(MutableStateDep);
4515 if (FAILED(rc)) return rc;
4516
4517 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4518
4519 if (Global::IsOnlineOrTransient(mData->mMachineState))
4520 return setError(VBOX_E_INVALID_VM_STATE,
4521 tr("Invalid machine state: %s"),
4522 Global::stringifyMachineState(mData->mMachineState));
4523
4524 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4525 aName,
4526 aControllerPort,
4527 aDevice);
4528 if (!pAttach)
4529 return setError(VBOX_E_OBJECT_NOT_FOUND,
4530 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4531 aDevice, aControllerPort, aName.c_str());
4532
4533
4534 i_setModified(IsModified_Storage);
4535 mMediumAttachments.backup();
4536
4537 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4538
4539 if (pAttach->i_getType() != DeviceType_HardDisk)
4540 return setError(E_INVALIDARG,
4541 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"),
4542 aDevice, aControllerPort, aName.c_str());
4543 pAttach->i_updateNonRotational(!!aNonRotational);
4544
4545 return S_OK;
4546}
4547
4548HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4549 LONG aDevice, BOOL aDiscard)
4550{
4551
4552 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4553 aName.c_str(), aControllerPort, aDevice, aDiscard));
4554
4555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4556
4557 HRESULT rc = i_checkStateDependency(MutableStateDep);
4558 if (FAILED(rc)) return rc;
4559
4560 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4561
4562 if (Global::IsOnlineOrTransient(mData->mMachineState))
4563 return setError(VBOX_E_INVALID_VM_STATE,
4564 tr("Invalid machine state: %s"),
4565 Global::stringifyMachineState(mData->mMachineState));
4566
4567 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4568 aName,
4569 aControllerPort,
4570 aDevice);
4571 if (!pAttach)
4572 return setError(VBOX_E_OBJECT_NOT_FOUND,
4573 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4574 aDevice, aControllerPort, aName.c_str());
4575
4576
4577 i_setModified(IsModified_Storage);
4578 mMediumAttachments.backup();
4579
4580 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4581
4582 if (pAttach->i_getType() != DeviceType_HardDisk)
4583 return setError(E_INVALIDARG,
4584 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"),
4585 aDevice, aControllerPort, aName.c_str());
4586 pAttach->i_updateDiscard(!!aDiscard);
4587
4588 return S_OK;
4589}
4590
4591HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4592 LONG aDevice, BOOL aHotPluggable)
4593{
4594 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4595 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4596
4597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4598
4599 HRESULT rc = i_checkStateDependency(MutableStateDep);
4600 if (FAILED(rc)) return rc;
4601
4602 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4603
4604 if (Global::IsOnlineOrTransient(mData->mMachineState))
4605 return setError(VBOX_E_INVALID_VM_STATE,
4606 tr("Invalid machine state: %s"),
4607 Global::stringifyMachineState(mData->mMachineState));
4608
4609 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4610 aName,
4611 aControllerPort,
4612 aDevice);
4613 if (!pAttach)
4614 return setError(VBOX_E_OBJECT_NOT_FOUND,
4615 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4616 aDevice, aControllerPort, aName.c_str());
4617
4618 /* Check for an existing controller. */
4619 ComObjPtr<StorageController> ctl;
4620 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4621 if (FAILED(rc)) return rc;
4622
4623 StorageControllerType_T ctrlType;
4624 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4625 if (FAILED(rc))
4626 return setError(E_FAIL,
4627 tr("Could not get type of controller '%s'"),
4628 aName.c_str());
4629
4630 if (!i_isControllerHotplugCapable(ctrlType))
4631 return setError(VBOX_E_NOT_SUPPORTED,
4632 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4633 aName.c_str());
4634
4635 i_setModified(IsModified_Storage);
4636 mMediumAttachments.backup();
4637
4638 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4639
4640 if (pAttach->i_getType() == DeviceType_Floppy)
4641 return setError(E_INVALIDARG,
4642 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"),
4643 aDevice, aControllerPort, aName.c_str());
4644 pAttach->i_updateHotPluggable(!!aHotPluggable);
4645
4646 return S_OK;
4647}
4648
4649HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4650 LONG aDevice)
4651{
4652 int rc = S_OK;
4653 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4654 aName.c_str(), aControllerPort, aDevice));
4655
4656 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4657
4658 return rc;
4659}
4660
4661HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4662 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4663{
4664 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4665 aName.c_str(), aControllerPort, aDevice));
4666
4667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4668
4669 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4670 if (FAILED(rc)) return rc;
4671
4672 if (Global::IsOnlineOrTransient(mData->mMachineState))
4673 return setError(VBOX_E_INVALID_VM_STATE,
4674 tr("Invalid machine state: %s"),
4675 Global::stringifyMachineState(mData->mMachineState));
4676
4677 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4678 aName,
4679 aControllerPort,
4680 aDevice);
4681 if (!pAttach)
4682 return setError(VBOX_E_OBJECT_NOT_FOUND,
4683 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4684 aDevice, aControllerPort, aName.c_str());
4685
4686
4687 i_setModified(IsModified_Storage);
4688 mMediumAttachments.backup();
4689
4690 IBandwidthGroup *iB = aBandwidthGroup;
4691 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4692 if (aBandwidthGroup && group.isNull())
4693 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4694
4695 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4696
4697 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4698 if (strBandwidthGroupOld.isNotEmpty())
4699 {
4700 /* Get the bandwidth group object and release it - this must not fail. */
4701 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4702 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4703 Assert(SUCCEEDED(rc));
4704
4705 pBandwidthGroupOld->i_release();
4706 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4707 }
4708
4709 if (!group.isNull())
4710 {
4711 group->i_reference();
4712 pAttach->i_updateBandwidthGroup(group->i_getName());
4713 }
4714
4715 return S_OK;
4716}
4717
4718HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4719 LONG aControllerPort,
4720 LONG aDevice,
4721 DeviceType_T aType)
4722{
4723 HRESULT rc = S_OK;
4724
4725 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4726 aName.c_str(), aControllerPort, aDevice, aType));
4727
4728 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4729
4730 return rc;
4731}
4732
4733
4734HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4735 LONG aControllerPort,
4736 LONG aDevice,
4737 BOOL aForce)
4738{
4739 int rc = S_OK;
4740 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4741 aName.c_str(), aControllerPort, aForce));
4742
4743 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4744
4745 return rc;
4746}
4747
4748HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4749 LONG aControllerPort,
4750 LONG aDevice,
4751 const ComPtr<IMedium> &aMedium,
4752 BOOL aForce)
4753{
4754 int rc = S_OK;
4755 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4756 aName.c_str(), aControllerPort, aDevice, aForce));
4757
4758 // request the host lock first, since might be calling Host methods for getting host drives;
4759 // next, protect the media tree all the while we're in here, as well as our member variables
4760 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4761 this->lockHandle(),
4762 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4763
4764 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4765 aName,
4766 aControllerPort,
4767 aDevice);
4768 if (pAttach.isNull())
4769 return setError(VBOX_E_OBJECT_NOT_FOUND,
4770 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4771 aDevice, aControllerPort, aName.c_str());
4772
4773 /* Remember previously mounted medium. The medium before taking the
4774 * backup is not necessarily the same thing. */
4775 ComObjPtr<Medium> oldmedium;
4776 oldmedium = pAttach->i_getMedium();
4777
4778 IMedium *iM = aMedium;
4779 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4780 if (aMedium && pMedium.isNull())
4781 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4782
4783 AutoCaller mediumCaller(pMedium);
4784 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4785
4786 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4787 if (pMedium)
4788 {
4789 DeviceType_T mediumType = pAttach->i_getType();
4790 switch (mediumType)
4791 {
4792 case DeviceType_DVD:
4793 case DeviceType_Floppy:
4794 break;
4795
4796 default:
4797 return setError(VBOX_E_INVALID_OBJECT_STATE,
4798 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4799 aControllerPort,
4800 aDevice,
4801 aName.c_str());
4802 }
4803 }
4804
4805 i_setModified(IsModified_Storage);
4806 mMediumAttachments.backup();
4807
4808 {
4809 // The backup operation makes the pAttach reference point to the
4810 // old settings. Re-get the correct reference.
4811 pAttach = i_findAttachment(*mMediumAttachments.data(),
4812 aName,
4813 aControllerPort,
4814 aDevice);
4815 if (!oldmedium.isNull())
4816 oldmedium->i_removeBackReference(mData->mUuid);
4817 if (!pMedium.isNull())
4818 {
4819 pMedium->i_addBackReference(mData->mUuid);
4820
4821 mediumLock.release();
4822 multiLock.release();
4823 i_addMediumToRegistry(pMedium);
4824 multiLock.acquire();
4825 mediumLock.acquire();
4826 }
4827
4828 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4829 pAttach->i_updateMedium(pMedium);
4830 }
4831
4832 i_setModified(IsModified_Storage);
4833
4834 mediumLock.release();
4835 multiLock.release();
4836 rc = i_onMediumChange(pAttach, aForce);
4837 multiLock.acquire();
4838 mediumLock.acquire();
4839
4840 /* On error roll back this change only. */
4841 if (FAILED(rc))
4842 {
4843 if (!pMedium.isNull())
4844 pMedium->i_removeBackReference(mData->mUuid);
4845 pAttach = i_findAttachment(*mMediumAttachments.data(),
4846 aName,
4847 aControllerPort,
4848 aDevice);
4849 /* If the attachment is gone in the meantime, bail out. */
4850 if (pAttach.isNull())
4851 return rc;
4852 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4853 if (!oldmedium.isNull())
4854 oldmedium->i_addBackReference(mData->mUuid);
4855 pAttach->i_updateMedium(oldmedium);
4856 }
4857
4858 mediumLock.release();
4859 multiLock.release();
4860
4861 /* Save modified registries, but skip this machine as it's the caller's
4862 * job to save its settings like all other settings changes. */
4863 mParent->i_unmarkRegistryModified(i_getId());
4864 mParent->i_saveModifiedRegistries();
4865
4866 return rc;
4867}
4868HRESULT Machine::getMedium(const com::Utf8Str &aName,
4869 LONG aControllerPort,
4870 LONG aDevice,
4871 ComPtr<IMedium> &aMedium)
4872{
4873 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4874 aName.c_str(), aControllerPort, aDevice));
4875
4876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4877
4878 aMedium = NULL;
4879
4880 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4881 aName,
4882 aControllerPort,
4883 aDevice);
4884 if (pAttach.isNull())
4885 return setError(VBOX_E_OBJECT_NOT_FOUND,
4886 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4887 aDevice, aControllerPort, aName.c_str());
4888
4889 aMedium = pAttach->i_getMedium();
4890
4891 return S_OK;
4892}
4893
4894HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4895{
4896
4897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4898
4899 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4900
4901 return S_OK;
4902}
4903
4904HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4905{
4906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4907
4908 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4909
4910 return S_OK;
4911}
4912
4913HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4914{
4915 /* Do not assert if slot is out of range, just return the advertised
4916 status. testdriver/vbox.py triggers this in logVmInfo. */
4917 if (aSlot >= mNetworkAdapters.size())
4918 return setError(E_INVALIDARG,
4919 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4920 aSlot, mNetworkAdapters.size());
4921
4922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4923
4924 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4925
4926 return S_OK;
4927}
4928
4929HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4930{
4931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4934 size_t i = 0;
4935 for (settings::StringsMap::const_iterator
4936 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4937 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4938 ++it, ++i)
4939 aKeys[i] = it->first;
4940
4941 return S_OK;
4942}
4943
4944 /**
4945 * @note Locks this object for reading.
4946 */
4947HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4948 com::Utf8Str &aValue)
4949{
4950 /* start with nothing found */
4951 aValue = "";
4952
4953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4954
4955 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4956 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4957 // found:
4958 aValue = it->second; // source is a Utf8Str
4959
4960 /* return the result to caller (may be empty) */
4961 return S_OK;
4962}
4963
4964 /**
4965 * @note Locks mParent for writing + this object for writing.
4966 */
4967HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4968{
4969 Utf8Str strOldValue; // empty
4970
4971 // locking note: we only hold the read lock briefly to look up the old value,
4972 // then release it and call the onExtraCanChange callbacks. There is a small
4973 // chance of a race insofar as the callback might be called twice if two callers
4974 // change the same key at the same time, but that's a much better solution
4975 // than the deadlock we had here before. The actual changing of the extradata
4976 // is then performed under the write lock and race-free.
4977
4978 // look up the old value first; if nothing has changed then we need not do anything
4979 {
4980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4981
4982 // For snapshots don't even think about allowing changes, extradata
4983 // is global for a machine, so there is nothing snapshot specific.
4984 if (i_isSnapshotMachine())
4985 return setError(VBOX_E_INVALID_VM_STATE,
4986 tr("Cannot set extradata for a snapshot"));
4987
4988 // check if the right IMachine instance is used
4989 if (mData->mRegistered && !i_isSessionMachine())
4990 return setError(VBOX_E_INVALID_VM_STATE,
4991 tr("Cannot set extradata for an immutable machine"));
4992
4993 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4994 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4995 strOldValue = it->second;
4996 }
4997
4998 bool fChanged;
4999 if ((fChanged = (strOldValue != aValue)))
5000 {
5001 // ask for permission from all listeners outside the locks;
5002 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5003 // lock to copy the list of callbacks to invoke
5004 Bstr error;
5005 Bstr bstrValue(aValue);
5006
5007 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5008 {
5009 const char *sep = error.isEmpty() ? "" : ": ";
5010 CBSTR err = error.raw();
5011 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5012 return setError(E_ACCESSDENIED,
5013 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5014 aKey.c_str(),
5015 aValue.c_str(),
5016 sep,
5017 err);
5018 }
5019
5020 // data is changing and change not vetoed: then write it out under the lock
5021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5022
5023 if (aValue.isEmpty())
5024 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5025 else
5026 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5027 // creates a new key if needed
5028
5029 bool fNeedsGlobalSaveSettings = false;
5030 // This saving of settings is tricky: there is no "old state" for the
5031 // extradata items at all (unlike all other settings), so the old/new
5032 // settings comparison would give a wrong result!
5033 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5034
5035 if (fNeedsGlobalSaveSettings)
5036 {
5037 // save the global settings; for that we should hold only the VirtualBox lock
5038 alock.release();
5039 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5040 mParent->i_saveSettings();
5041 }
5042 }
5043
5044 // fire notification outside the lock
5045 if (fChanged)
5046 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5047
5048 return S_OK;
5049}
5050
5051HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5052{
5053 aProgress = NULL;
5054 NOREF(aSettingsFilePath);
5055 ReturnComNotImplemented();
5056}
5057
5058HRESULT Machine::saveSettings()
5059{
5060 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5061
5062 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5063 if (FAILED(rc)) return rc;
5064
5065 /* the settings file path may never be null */
5066 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5067
5068 /* save all VM data excluding snapshots */
5069 bool fNeedsGlobalSaveSettings = false;
5070 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5071 mlock.release();
5072
5073 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5074 {
5075 // save the global settings; for that we should hold only the VirtualBox lock
5076 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5077 rc = mParent->i_saveSettings();
5078 }
5079
5080 return rc;
5081}
5082
5083
5084HRESULT Machine::discardSettings()
5085{
5086 /*
5087 * We need to take the machine list lock here as well as the machine one
5088 * or we'll get into trouble should any media stuff require rolling back.
5089 *
5090 * Details:
5091 *
5092 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5093 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5094 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5095 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5096 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5097 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5098 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5099 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5100 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5101 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5102 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5103 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5104 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5105 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5106 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5107 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5108 * 0:005> k
5109 * # Child-SP RetAddr Call Site
5110 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5111 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5112 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5113 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5114 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5115 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5116 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5117 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5118 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5119 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5120 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5121 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5122 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5123 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5124 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5125 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5126 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5127 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5128 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5129 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5130 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5131 *
5132 */
5133 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5135
5136 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5137 if (FAILED(rc)) return rc;
5138
5139 /*
5140 * during this rollback, the session will be notified if data has
5141 * been actually changed
5142 */
5143 i_rollback(true /* aNotify */);
5144
5145 return S_OK;
5146}
5147
5148/** @note Locks objects! */
5149HRESULT Machine::unregister(AutoCaller &autoCaller,
5150 CleanupMode_T aCleanupMode,
5151 std::vector<ComPtr<IMedium> > &aMedia)
5152{
5153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 Guid id(i_getId());
5156
5157 if (mData->mSession.mState != SessionState_Unlocked)
5158 return setError(VBOX_E_INVALID_OBJECT_STATE,
5159 tr("Cannot unregister the machine '%s' while it is locked"),
5160 mUserData->s.strName.c_str());
5161
5162 // wait for state dependents to drop to zero
5163 i_ensureNoStateDependencies();
5164
5165 if (!mData->mAccessible)
5166 {
5167 // inaccessible maschines can only be unregistered; uninitialize ourselves
5168 // here because currently there may be no unregistered that are inaccessible
5169 // (this state combination is not supported). Note releasing the caller and
5170 // leaving the lock before calling uninit()
5171 alock.release();
5172 autoCaller.release();
5173
5174 uninit();
5175
5176 mParent->i_unregisterMachine(this, id);
5177 // calls VirtualBox::i_saveSettings()
5178
5179 return S_OK;
5180 }
5181
5182 HRESULT rc = S_OK;
5183
5184 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5185 // discard saved state
5186 if (mData->mMachineState == MachineState_Saved)
5187 {
5188 // add the saved state file to the list of files the caller should delete
5189 Assert(!mSSData->strStateFilePath.isEmpty());
5190 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5191
5192 mSSData->strStateFilePath.setNull();
5193
5194 // unconditionally set the machine state to powered off, we now
5195 // know no session has locked the machine
5196 mData->mMachineState = MachineState_PoweredOff;
5197 }
5198
5199 size_t cSnapshots = 0;
5200 if (mData->mFirstSnapshot)
5201 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5202 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5203 // fail now before we start detaching media
5204 return setError(VBOX_E_INVALID_OBJECT_STATE,
5205 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5206 mUserData->s.strName.c_str(), cSnapshots);
5207
5208 // This list collects the medium objects from all medium attachments
5209 // which we will detach from the machine and its snapshots, in a specific
5210 // order which allows for closing all media without getting "media in use"
5211 // errors, simply by going through the list from the front to the back:
5212 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5213 // and must be closed before the parent media from the snapshots, or closing the parents
5214 // will fail because they still have children);
5215 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5216 // the root ("first") snapshot of the machine.
5217 MediaList llMedia;
5218
5219 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5220 && mMediumAttachments->size()
5221 )
5222 {
5223 // we have media attachments: detach them all and add the Medium objects to our list
5224 if (aCleanupMode != CleanupMode_UnregisterOnly)
5225 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5226 else
5227 return setError(VBOX_E_INVALID_OBJECT_STATE,
5228 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5229 mUserData->s.strName.c_str(), mMediumAttachments->size());
5230 }
5231
5232 if (cSnapshots)
5233 {
5234 // add the media from the medium attachments of the snapshots to llMedia
5235 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5236 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5237 // into the children first
5238
5239 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5240 MachineState_T oldState = mData->mMachineState;
5241 mData->mMachineState = MachineState_DeletingSnapshot;
5242
5243 // make a copy of the first snapshot so the refcount does not drop to 0
5244 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5245 // because of the AutoCaller voodoo)
5246 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5247
5248 // GO!
5249 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5250
5251 mData->mMachineState = oldState;
5252 }
5253
5254 if (FAILED(rc))
5255 {
5256 i_rollbackMedia();
5257 return rc;
5258 }
5259
5260 // commit all the media changes made above
5261 i_commitMedia();
5262
5263 mData->mRegistered = false;
5264
5265 // machine lock no longer needed
5266 alock.release();
5267
5268 // return media to caller
5269 aMedia.resize(llMedia.size());
5270 size_t i = 0;
5271 for (MediaList::const_iterator
5272 it = llMedia.begin();
5273 it != llMedia.end();
5274 ++it, ++i)
5275 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5276
5277 mParent->i_unregisterMachine(this, id);
5278 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5279
5280 return S_OK;
5281}
5282
5283/**
5284 * Task record for deleting a machine config.
5285 */
5286class Machine::DeleteConfigTask
5287 : public Machine::Task
5288{
5289public:
5290 DeleteConfigTask(Machine *m,
5291 Progress *p,
5292 const Utf8Str &t,
5293 const RTCList<ComPtr<IMedium> > &llMediums,
5294 const StringsList &llFilesToDelete)
5295 : Task(m, p, t),
5296 m_llMediums(llMediums),
5297 m_llFilesToDelete(llFilesToDelete)
5298 {}
5299
5300private:
5301 void handler()
5302 {
5303 try
5304 {
5305 m_pMachine->i_deleteConfigHandler(*this);
5306 }
5307 catch (...)
5308 {
5309 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5310 }
5311 }
5312
5313 RTCList<ComPtr<IMedium> > m_llMediums;
5314 StringsList m_llFilesToDelete;
5315
5316 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5317};
5318
5319/**
5320 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5321 * SessionMachine::taskHandler().
5322 *
5323 * @note Locks this object for writing.
5324 *
5325 * @param task
5326 * @return
5327 */
5328void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5329{
5330 LogFlowThisFuncEnter();
5331
5332 AutoCaller autoCaller(this);
5333 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5334 if (FAILED(autoCaller.rc()))
5335 {
5336 /* we might have been uninitialized because the session was accidentally
5337 * closed by the client, so don't assert */
5338 HRESULT rc = setError(E_FAIL,
5339 tr("The session has been accidentally closed"));
5340 task.m_pProgress->i_notifyComplete(rc);
5341 LogFlowThisFuncLeave();
5342 return;
5343 }
5344
5345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5346
5347 HRESULT rc = S_OK;
5348
5349 try
5350 {
5351 ULONG uLogHistoryCount = 3;
5352 ComPtr<ISystemProperties> systemProperties;
5353 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5354 if (FAILED(rc)) throw rc;
5355
5356 if (!systemProperties.isNull())
5357 {
5358 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5359 if (FAILED(rc)) throw rc;
5360 }
5361
5362 MachineState_T oldState = mData->mMachineState;
5363 i_setMachineState(MachineState_SettingUp);
5364 alock.release();
5365 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5366 {
5367 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5368 {
5369 AutoCaller mac(pMedium);
5370 if (FAILED(mac.rc())) throw mac.rc();
5371 Utf8Str strLocation = pMedium->i_getLocationFull();
5372 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5373 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5374 if (FAILED(rc)) throw rc;
5375 }
5376 if (pMedium->i_isMediumFormatFile())
5377 {
5378 ComPtr<IProgress> pProgress2;
5379 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5380 if (FAILED(rc)) throw rc;
5381 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5382 if (FAILED(rc)) throw rc;
5383 /* Check the result of the asynchronous process. */
5384 LONG iRc;
5385 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5386 if (FAILED(rc)) throw rc;
5387 /* If the thread of the progress object has an error, then
5388 * retrieve the error info from there, or it'll be lost. */
5389 if (FAILED(iRc))
5390 throw setError(ProgressErrorInfo(pProgress2));
5391 }
5392
5393 /* Close the medium, deliberately without checking the return
5394 * code, and without leaving any trace in the error info, as
5395 * a failure here is a very minor issue, which shouldn't happen
5396 * as above we even managed to delete the medium. */
5397 {
5398 ErrorInfoKeeper eik;
5399 pMedium->Close();
5400 }
5401 }
5402 i_setMachineState(oldState);
5403 alock.acquire();
5404
5405 // delete the files pushed on the task list by Machine::Delete()
5406 // (this includes saved states of the machine and snapshots and
5407 // medium storage files from the IMedium list passed in, and the
5408 // machine XML file)
5409 for (StringsList::const_iterator
5410 it = task.m_llFilesToDelete.begin();
5411 it != task.m_llFilesToDelete.end();
5412 ++it)
5413 {
5414 const Utf8Str &strFile = *it;
5415 LogFunc(("Deleting file %s\n", strFile.c_str()));
5416 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5417 if (FAILED(rc)) throw rc;
5418
5419 int vrc = RTFileDelete(strFile.c_str());
5420 if (RT_FAILURE(vrc))
5421 throw setError(VBOX_E_IPRT_ERROR,
5422 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5423 }
5424
5425 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5426 if (FAILED(rc)) throw rc;
5427
5428 /* delete the settings only when the file actually exists */
5429 if (mData->pMachineConfigFile->fileExists())
5430 {
5431 /* Delete any backup or uncommitted XML files. Ignore failures.
5432 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5433 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5434 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5435 RTFileDelete(otherXml.c_str());
5436 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5437 RTFileDelete(otherXml.c_str());
5438
5439 /* delete the Logs folder, nothing important should be left
5440 * there (we don't check for errors because the user might have
5441 * some private files there that we don't want to delete) */
5442 Utf8Str logFolder;
5443 getLogFolder(logFolder);
5444 Assert(logFolder.length());
5445 if (RTDirExists(logFolder.c_str()))
5446 {
5447 /* Delete all VBox.log[.N] files from the Logs folder
5448 * (this must be in sync with the rotation logic in
5449 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5450 * files that may have been created by the GUI. */
5451 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5452 logFolder.c_str(), RTPATH_DELIMITER);
5453 RTFileDelete(log.c_str());
5454 log = Utf8StrFmt("%s%cVBox.png",
5455 logFolder.c_str(), RTPATH_DELIMITER);
5456 RTFileDelete(log.c_str());
5457 for (int i = uLogHistoryCount; i > 0; i--)
5458 {
5459 log = Utf8StrFmt("%s%cVBox.log.%d",
5460 logFolder.c_str(), RTPATH_DELIMITER, i);
5461 RTFileDelete(log.c_str());
5462 log = Utf8StrFmt("%s%cVBox.png.%d",
5463 logFolder.c_str(), RTPATH_DELIMITER, i);
5464 RTFileDelete(log.c_str());
5465 }
5466#if defined(RT_OS_WINDOWS)
5467 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5468 RTFileDelete(log.c_str());
5469 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5470 RTFileDelete(log.c_str());
5471#endif
5472
5473 RTDirRemove(logFolder.c_str());
5474 }
5475
5476 /* delete the Snapshots folder, nothing important should be left
5477 * there (we don't check for errors because the user might have
5478 * some private files there that we don't want to delete) */
5479 Utf8Str strFullSnapshotFolder;
5480 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5481 Assert(!strFullSnapshotFolder.isEmpty());
5482 if (RTDirExists(strFullSnapshotFolder.c_str()))
5483 RTDirRemove(strFullSnapshotFolder.c_str());
5484
5485 // delete the directory that contains the settings file, but only
5486 // if it matches the VM name
5487 Utf8Str settingsDir;
5488 if (i_isInOwnDir(&settingsDir))
5489 RTDirRemove(settingsDir.c_str());
5490 }
5491
5492 alock.release();
5493
5494 mParent->i_saveModifiedRegistries();
5495 }
5496 catch (HRESULT aRC) { rc = aRC; }
5497
5498 task.m_pProgress->i_notifyComplete(rc);
5499
5500 LogFlowThisFuncLeave();
5501}
5502
5503HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5504{
5505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5506
5507 HRESULT rc = i_checkStateDependency(MutableStateDep);
5508 if (FAILED(rc)) return rc;
5509
5510 if (mData->mRegistered)
5511 return setError(VBOX_E_INVALID_VM_STATE,
5512 tr("Cannot delete settings of a registered machine"));
5513
5514 // collect files to delete
5515 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5516 if (mData->pMachineConfigFile->fileExists())
5517 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5518
5519 RTCList<ComPtr<IMedium> > llMediums;
5520 for (size_t i = 0; i < aMedia.size(); ++i)
5521 {
5522 IMedium *pIMedium(aMedia[i]);
5523 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5524 if (pMedium.isNull())
5525 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5526 SafeArray<BSTR> ids;
5527 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5528 if (FAILED(rc)) return rc;
5529 /* At this point the medium should not have any back references
5530 * anymore. If it has it is attached to another VM and *must* not
5531 * deleted. */
5532 if (ids.size() < 1)
5533 llMediums.append(pMedium);
5534 }
5535
5536 ComObjPtr<Progress> pProgress;
5537 pProgress.createObject();
5538 rc = pProgress->init(i_getVirtualBox(),
5539 static_cast<IMachine*>(this) /* aInitiator */,
5540 tr("Deleting files"),
5541 true /* fCancellable */,
5542 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5543 tr("Collecting file inventory"));
5544 if (FAILED(rc))
5545 return rc;
5546
5547 /* create and start the task on a separate thread (note that it will not
5548 * start working until we release alock) */
5549 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5550 rc = pTask->createThread();
5551 if (FAILED(rc))
5552 return rc;
5553
5554 pProgress.queryInterfaceTo(aProgress.asOutParam());
5555
5556 LogFlowFuncLeave();
5557
5558 return S_OK;
5559}
5560
5561HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5562{
5563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5564
5565 ComObjPtr<Snapshot> pSnapshot;
5566 HRESULT rc;
5567
5568 if (aNameOrId.isEmpty())
5569 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5570 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5571 else
5572 {
5573 Guid uuid(aNameOrId);
5574 if (uuid.isValid())
5575 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5576 else
5577 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5578 }
5579 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5580
5581 return rc;
5582}
5583
5584HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5585{
5586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5587
5588 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5589 if (FAILED(rc)) return rc;
5590
5591 ComObjPtr<SharedFolder> sharedFolder;
5592 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5593 if (SUCCEEDED(rc))
5594 return setError(VBOX_E_OBJECT_IN_USE,
5595 tr("Shared folder named '%s' already exists"),
5596 aName.c_str());
5597
5598 sharedFolder.createObject();
5599 rc = sharedFolder->init(i_getMachine(),
5600 aName,
5601 aHostPath,
5602 !!aWritable,
5603 !!aAutomount,
5604 true /* fFailOnError */);
5605 if (FAILED(rc)) return rc;
5606
5607 i_setModified(IsModified_SharedFolders);
5608 mHWData.backup();
5609 mHWData->mSharedFolders.push_back(sharedFolder);
5610
5611 /* inform the direct session if any */
5612 alock.release();
5613 i_onSharedFolderChange();
5614
5615 return S_OK;
5616}
5617
5618HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5619{
5620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5621
5622 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5623 if (FAILED(rc)) return rc;
5624
5625 ComObjPtr<SharedFolder> sharedFolder;
5626 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5627 if (FAILED(rc)) return rc;
5628
5629 i_setModified(IsModified_SharedFolders);
5630 mHWData.backup();
5631 mHWData->mSharedFolders.remove(sharedFolder);
5632
5633 /* inform the direct session if any */
5634 alock.release();
5635 i_onSharedFolderChange();
5636
5637 return S_OK;
5638}
5639
5640HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5641{
5642 /* start with No */
5643 *aCanShow = FALSE;
5644
5645 ComPtr<IInternalSessionControl> directControl;
5646 {
5647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5648
5649 if (mData->mSession.mState != SessionState_Locked)
5650 return setError(VBOX_E_INVALID_VM_STATE,
5651 tr("Machine is not locked for session (session state: %s)"),
5652 Global::stringifySessionState(mData->mSession.mState));
5653
5654 if (mData->mSession.mLockType == LockType_VM)
5655 directControl = mData->mSession.mDirectControl;
5656 }
5657
5658 /* ignore calls made after #OnSessionEnd() is called */
5659 if (!directControl)
5660 return S_OK;
5661
5662 LONG64 dummy;
5663 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5664}
5665
5666HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5667{
5668 ComPtr<IInternalSessionControl> directControl;
5669 {
5670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5671
5672 if (mData->mSession.mState != SessionState_Locked)
5673 return setError(E_FAIL,
5674 tr("Machine is not locked for session (session state: %s)"),
5675 Global::stringifySessionState(mData->mSession.mState));
5676
5677 if (mData->mSession.mLockType == LockType_VM)
5678 directControl = mData->mSession.mDirectControl;
5679 }
5680
5681 /* ignore calls made after #OnSessionEnd() is called */
5682 if (!directControl)
5683 return S_OK;
5684
5685 BOOL dummy;
5686 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5687}
5688
5689#ifdef VBOX_WITH_GUEST_PROPS
5690/**
5691 * Look up a guest property in VBoxSVC's internal structures.
5692 */
5693HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5694 com::Utf8Str &aValue,
5695 LONG64 *aTimestamp,
5696 com::Utf8Str &aFlags) const
5697{
5698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5699
5700 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5701 if (it != mHWData->mGuestProperties.end())
5702 {
5703 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5704 aValue = it->second.strValue;
5705 *aTimestamp = it->second.mTimestamp;
5706 GuestPropWriteFlags(it->second.mFlags, szFlags);
5707 aFlags = Utf8Str(szFlags);
5708 }
5709
5710 return S_OK;
5711}
5712
5713/**
5714 * Query the VM that a guest property belongs to for the property.
5715 * @returns E_ACCESSDENIED if the VM process is not available or not
5716 * currently handling queries and the lookup should then be done in
5717 * VBoxSVC.
5718 */
5719HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5720 com::Utf8Str &aValue,
5721 LONG64 *aTimestamp,
5722 com::Utf8Str &aFlags) const
5723{
5724 HRESULT rc = S_OK;
5725 BSTR bValue = NULL;
5726 BSTR bFlags = NULL;
5727
5728 ComPtr<IInternalSessionControl> directControl;
5729 {
5730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5731 if (mData->mSession.mLockType == LockType_VM)
5732 directControl = mData->mSession.mDirectControl;
5733 }
5734
5735 /* ignore calls made after #OnSessionEnd() is called */
5736 if (!directControl)
5737 rc = E_ACCESSDENIED;
5738 else
5739 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5740 0 /* accessMode */,
5741 &bValue, aTimestamp, &bFlags);
5742
5743 aValue = bValue;
5744 aFlags = bFlags;
5745
5746 return rc;
5747}
5748#endif // VBOX_WITH_GUEST_PROPS
5749
5750HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5751 com::Utf8Str &aValue,
5752 LONG64 *aTimestamp,
5753 com::Utf8Str &aFlags)
5754{
5755#ifndef VBOX_WITH_GUEST_PROPS
5756 ReturnComNotImplemented();
5757#else // VBOX_WITH_GUEST_PROPS
5758
5759 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5760
5761 if (rc == E_ACCESSDENIED)
5762 /* The VM is not running or the service is not (yet) accessible */
5763 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5764 return rc;
5765#endif // VBOX_WITH_GUEST_PROPS
5766}
5767
5768HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5769{
5770 LONG64 dummyTimestamp;
5771 com::Utf8Str dummyFlags;
5772 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5773 return rc;
5774
5775}
5776HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5777{
5778 com::Utf8Str dummyFlags;
5779 com::Utf8Str dummyValue;
5780 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5781 return rc;
5782}
5783
5784#ifdef VBOX_WITH_GUEST_PROPS
5785/**
5786 * Set a guest property in VBoxSVC's internal structures.
5787 */
5788HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5789 const com::Utf8Str &aFlags, bool fDelete)
5790{
5791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5792 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5793 if (FAILED(rc)) return rc;
5794
5795 try
5796 {
5797 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5798 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5799 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5800
5801 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5802 if (it == mHWData->mGuestProperties.end())
5803 {
5804 if (!fDelete)
5805 {
5806 i_setModified(IsModified_MachineData);
5807 mHWData.backupEx();
5808
5809 RTTIMESPEC time;
5810 HWData::GuestProperty prop;
5811 prop.strValue = Bstr(aValue).raw();
5812 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5813 prop.mFlags = fFlags;
5814 mHWData->mGuestProperties[aName] = prop;
5815 }
5816 }
5817 else
5818 {
5819 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5820 {
5821 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5822 }
5823 else
5824 {
5825 i_setModified(IsModified_MachineData);
5826 mHWData.backupEx();
5827
5828 /* The backupEx() operation invalidates our iterator,
5829 * so get a new one. */
5830 it = mHWData->mGuestProperties.find(aName);
5831 Assert(it != mHWData->mGuestProperties.end());
5832
5833 if (!fDelete)
5834 {
5835 RTTIMESPEC time;
5836 it->second.strValue = aValue;
5837 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5838 it->second.mFlags = fFlags;
5839 }
5840 else
5841 mHWData->mGuestProperties.erase(it);
5842 }
5843 }
5844
5845 if (SUCCEEDED(rc))
5846 {
5847 alock.release();
5848
5849 mParent->i_onGuestPropertyChange(mData->mUuid,
5850 Bstr(aName).raw(),
5851 Bstr(aValue).raw(),
5852 Bstr(aFlags).raw());
5853 }
5854 }
5855 catch (std::bad_alloc &)
5856 {
5857 rc = E_OUTOFMEMORY;
5858 }
5859
5860 return rc;
5861}
5862
5863/**
5864 * Set a property on the VM that that property belongs to.
5865 * @returns E_ACCESSDENIED if the VM process is not available or not
5866 * currently handling queries and the setting should then be done in
5867 * VBoxSVC.
5868 */
5869HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5870 const com::Utf8Str &aFlags, bool fDelete)
5871{
5872 HRESULT rc;
5873
5874 try
5875 {
5876 ComPtr<IInternalSessionControl> directControl;
5877 {
5878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5879 if (mData->mSession.mLockType == LockType_VM)
5880 directControl = mData->mSession.mDirectControl;
5881 }
5882
5883 BSTR dummy = NULL; /* will not be changed (setter) */
5884 LONG64 dummy64;
5885 if (!directControl)
5886 rc = E_ACCESSDENIED;
5887 else
5888 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5889 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5890 fDelete? 2: 1 /* accessMode */,
5891 &dummy, &dummy64, &dummy);
5892 }
5893 catch (std::bad_alloc &)
5894 {
5895 rc = E_OUTOFMEMORY;
5896 }
5897
5898 return rc;
5899}
5900#endif // VBOX_WITH_GUEST_PROPS
5901
5902HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5903 const com::Utf8Str &aFlags)
5904{
5905#ifndef VBOX_WITH_GUEST_PROPS
5906 ReturnComNotImplemented();
5907#else // VBOX_WITH_GUEST_PROPS
5908 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5909 if (rc == E_ACCESSDENIED)
5910 /* The VM is not running or the service is not (yet) accessible */
5911 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5912 return rc;
5913#endif // VBOX_WITH_GUEST_PROPS
5914}
5915
5916HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5917{
5918 return setGuestProperty(aProperty, aValue, "");
5919}
5920
5921HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5922{
5923#ifndef VBOX_WITH_GUEST_PROPS
5924 ReturnComNotImplemented();
5925#else // VBOX_WITH_GUEST_PROPS
5926 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5927 if (rc == E_ACCESSDENIED)
5928 /* The VM is not running or the service is not (yet) accessible */
5929 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5930 return rc;
5931#endif // VBOX_WITH_GUEST_PROPS
5932}
5933
5934#ifdef VBOX_WITH_GUEST_PROPS
5935/**
5936 * Enumerate the guest properties in VBoxSVC's internal structures.
5937 */
5938HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5939 std::vector<com::Utf8Str> &aNames,
5940 std::vector<com::Utf8Str> &aValues,
5941 std::vector<LONG64> &aTimestamps,
5942 std::vector<com::Utf8Str> &aFlags)
5943{
5944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5945 Utf8Str strPatterns(aPatterns);
5946
5947 /*
5948 * Look for matching patterns and build up a list.
5949 */
5950 HWData::GuestPropertyMap propMap;
5951 for (HWData::GuestPropertyMap::const_iterator
5952 it = mHWData->mGuestProperties.begin();
5953 it != mHWData->mGuestProperties.end();
5954 ++it)
5955 {
5956 if ( strPatterns.isEmpty()
5957 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5958 RTSTR_MAX,
5959 it->first.c_str(),
5960 RTSTR_MAX,
5961 NULL)
5962 )
5963 propMap.insert(*it);
5964 }
5965
5966 alock.release();
5967
5968 /*
5969 * And build up the arrays for returning the property information.
5970 */
5971 size_t cEntries = propMap.size();
5972
5973 aNames.resize(cEntries);
5974 aValues.resize(cEntries);
5975 aTimestamps.resize(cEntries);
5976 aFlags.resize(cEntries);
5977
5978 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5979 size_t i = 0;
5980 for (HWData::GuestPropertyMap::const_iterator
5981 it = propMap.begin();
5982 it != propMap.end();
5983 ++it, ++i)
5984 {
5985 aNames[i] = it->first;
5986 aValues[i] = it->second.strValue;
5987 aTimestamps[i] = it->second.mTimestamp;
5988 GuestPropWriteFlags(it->second.mFlags, szFlags);
5989 aFlags[i] = Utf8Str(szFlags);
5990 }
5991
5992 return S_OK;
5993}
5994
5995/**
5996 * Enumerate the properties managed by a VM.
5997 * @returns E_ACCESSDENIED if the VM process is not available or not
5998 * currently handling queries and the setting should then be done in
5999 * VBoxSVC.
6000 */
6001HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6002 std::vector<com::Utf8Str> &aNames,
6003 std::vector<com::Utf8Str> &aValues,
6004 std::vector<LONG64> &aTimestamps,
6005 std::vector<com::Utf8Str> &aFlags)
6006{
6007 HRESULT rc;
6008 ComPtr<IInternalSessionControl> directControl;
6009 {
6010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6011 if (mData->mSession.mLockType == LockType_VM)
6012 directControl = mData->mSession.mDirectControl;
6013 }
6014
6015 com::SafeArray<BSTR> bNames;
6016 com::SafeArray<BSTR> bValues;
6017 com::SafeArray<LONG64> bTimestamps;
6018 com::SafeArray<BSTR> bFlags;
6019
6020 if (!directControl)
6021 rc = E_ACCESSDENIED;
6022 else
6023 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6024 ComSafeArrayAsOutParam(bNames),
6025 ComSafeArrayAsOutParam(bValues),
6026 ComSafeArrayAsOutParam(bTimestamps),
6027 ComSafeArrayAsOutParam(bFlags));
6028 size_t i;
6029 aNames.resize(bNames.size());
6030 for (i = 0; i < bNames.size(); ++i)
6031 aNames[i] = Utf8Str(bNames[i]);
6032 aValues.resize(bValues.size());
6033 for (i = 0; i < bValues.size(); ++i)
6034 aValues[i] = Utf8Str(bValues[i]);
6035 aTimestamps.resize(bTimestamps.size());
6036 for (i = 0; i < bTimestamps.size(); ++i)
6037 aTimestamps[i] = bTimestamps[i];
6038 aFlags.resize(bFlags.size());
6039 for (i = 0; i < bFlags.size(); ++i)
6040 aFlags[i] = Utf8Str(bFlags[i]);
6041
6042 return rc;
6043}
6044#endif // VBOX_WITH_GUEST_PROPS
6045HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6046 std::vector<com::Utf8Str> &aNames,
6047 std::vector<com::Utf8Str> &aValues,
6048 std::vector<LONG64> &aTimestamps,
6049 std::vector<com::Utf8Str> &aFlags)
6050{
6051#ifndef VBOX_WITH_GUEST_PROPS
6052 ReturnComNotImplemented();
6053#else // VBOX_WITH_GUEST_PROPS
6054
6055 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6056
6057 if (rc == E_ACCESSDENIED)
6058 /* The VM is not running or the service is not (yet) accessible */
6059 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6060 return rc;
6061#endif // VBOX_WITH_GUEST_PROPS
6062}
6063
6064HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6065 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6066{
6067 MediumAttachmentList atts;
6068
6069 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6070 if (FAILED(rc)) return rc;
6071
6072 aMediumAttachments.resize(atts.size());
6073 size_t i = 0;
6074 for (MediumAttachmentList::const_iterator
6075 it = atts.begin();
6076 it != atts.end();
6077 ++it, ++i)
6078 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6079
6080 return S_OK;
6081}
6082
6083HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6084 LONG aControllerPort,
6085 LONG aDevice,
6086 ComPtr<IMediumAttachment> &aAttachment)
6087{
6088 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6089 aName.c_str(), aControllerPort, aDevice));
6090
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 aAttachment = NULL;
6094
6095 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6096 aName,
6097 aControllerPort,
6098 aDevice);
6099 if (pAttach.isNull())
6100 return setError(VBOX_E_OBJECT_NOT_FOUND,
6101 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6102 aDevice, aControllerPort, aName.c_str());
6103
6104 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6105
6106 return S_OK;
6107}
6108
6109
6110HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6111 StorageBus_T aConnectionType,
6112 ComPtr<IStorageController> &aController)
6113{
6114 if ( (aConnectionType <= StorageBus_Null)
6115 || (aConnectionType > StorageBus_PCIe))
6116 return setError(E_INVALIDARG,
6117 tr("Invalid connection type: %d"),
6118 aConnectionType);
6119
6120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6121
6122 HRESULT rc = i_checkStateDependency(MutableStateDep);
6123 if (FAILED(rc)) return rc;
6124
6125 /* try to find one with the name first. */
6126 ComObjPtr<StorageController> ctrl;
6127
6128 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6129 if (SUCCEEDED(rc))
6130 return setError(VBOX_E_OBJECT_IN_USE,
6131 tr("Storage controller named '%s' already exists"),
6132 aName.c_str());
6133
6134 ctrl.createObject();
6135
6136 /* get a new instance number for the storage controller */
6137 ULONG ulInstance = 0;
6138 bool fBootable = true;
6139 for (StorageControllerList::const_iterator
6140 it = mStorageControllers->begin();
6141 it != mStorageControllers->end();
6142 ++it)
6143 {
6144 if ((*it)->i_getStorageBus() == aConnectionType)
6145 {
6146 ULONG ulCurInst = (*it)->i_getInstance();
6147
6148 if (ulCurInst >= ulInstance)
6149 ulInstance = ulCurInst + 1;
6150
6151 /* Only one controller of each type can be marked as bootable. */
6152 if ((*it)->i_getBootable())
6153 fBootable = false;
6154 }
6155 }
6156
6157 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6158 if (FAILED(rc)) return rc;
6159
6160 i_setModified(IsModified_Storage);
6161 mStorageControllers.backup();
6162 mStorageControllers->push_back(ctrl);
6163
6164 ctrl.queryInterfaceTo(aController.asOutParam());
6165
6166 /* inform the direct session if any */
6167 alock.release();
6168 i_onStorageControllerChange();
6169
6170 return S_OK;
6171}
6172
6173HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6174 ComPtr<IStorageController> &aStorageController)
6175{
6176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6177
6178 ComObjPtr<StorageController> ctrl;
6179
6180 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6181 if (SUCCEEDED(rc))
6182 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6183
6184 return rc;
6185}
6186
6187HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6188 ULONG aInstance,
6189 ComPtr<IStorageController> &aStorageController)
6190{
6191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6192
6193 for (StorageControllerList::const_iterator
6194 it = mStorageControllers->begin();
6195 it != mStorageControllers->end();
6196 ++it)
6197 {
6198 if ( (*it)->i_getStorageBus() == aConnectionType
6199 && (*it)->i_getInstance() == aInstance)
6200 {
6201 (*it).queryInterfaceTo(aStorageController.asOutParam());
6202 return S_OK;
6203 }
6204 }
6205
6206 return setError(VBOX_E_OBJECT_NOT_FOUND,
6207 tr("Could not find a storage controller with instance number '%lu'"),
6208 aInstance);
6209}
6210
6211HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6212{
6213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6214
6215 HRESULT rc = i_checkStateDependency(MutableStateDep);
6216 if (FAILED(rc)) return rc;
6217
6218 ComObjPtr<StorageController> ctrl;
6219
6220 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6221 if (SUCCEEDED(rc))
6222 {
6223 /* Ensure that only one controller of each type is marked as bootable. */
6224 if (aBootable == TRUE)
6225 {
6226 for (StorageControllerList::const_iterator
6227 it = mStorageControllers->begin();
6228 it != mStorageControllers->end();
6229 ++it)
6230 {
6231 ComObjPtr<StorageController> aCtrl = (*it);
6232
6233 if ( (aCtrl->i_getName() != aName)
6234 && aCtrl->i_getBootable() == TRUE
6235 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6236 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6237 {
6238 aCtrl->i_setBootable(FALSE);
6239 break;
6240 }
6241 }
6242 }
6243
6244 if (SUCCEEDED(rc))
6245 {
6246 ctrl->i_setBootable(aBootable);
6247 i_setModified(IsModified_Storage);
6248 }
6249 }
6250
6251 if (SUCCEEDED(rc))
6252 {
6253 /* inform the direct session if any */
6254 alock.release();
6255 i_onStorageControllerChange();
6256 }
6257
6258 return rc;
6259}
6260
6261HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6262{
6263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6264
6265 HRESULT rc = i_checkStateDependency(MutableStateDep);
6266 if (FAILED(rc)) return rc;
6267
6268 ComObjPtr<StorageController> ctrl;
6269 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6270 if (FAILED(rc)) return rc;
6271
6272 {
6273 /* find all attached devices to the appropriate storage controller and detach them all */
6274 // make a temporary list because detachDevice invalidates iterators into
6275 // mMediumAttachments
6276 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6277
6278 for (MediumAttachmentList::const_iterator
6279 it = llAttachments2.begin();
6280 it != llAttachments2.end();
6281 ++it)
6282 {
6283 MediumAttachment *pAttachTemp = *it;
6284
6285 AutoCaller localAutoCaller(pAttachTemp);
6286 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6287
6288 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6289
6290 if (pAttachTemp->i_getControllerName() == aName)
6291 {
6292 rc = i_detachDevice(pAttachTemp, alock, NULL);
6293 if (FAILED(rc)) return rc;
6294 }
6295 }
6296 }
6297
6298 /* We can remove it now. */
6299 i_setModified(IsModified_Storage);
6300 mStorageControllers.backup();
6301
6302 ctrl->i_unshare();
6303
6304 mStorageControllers->remove(ctrl);
6305
6306 /* inform the direct session if any */
6307 alock.release();
6308 i_onStorageControllerChange();
6309
6310 return S_OK;
6311}
6312
6313HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6314 ComPtr<IUSBController> &aController)
6315{
6316 if ( (aType <= USBControllerType_Null)
6317 || (aType >= USBControllerType_Last))
6318 return setError(E_INVALIDARG,
6319 tr("Invalid USB controller type: %d"),
6320 aType);
6321
6322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6323
6324 HRESULT rc = i_checkStateDependency(MutableStateDep);
6325 if (FAILED(rc)) return rc;
6326
6327 /* try to find one with the same type first. */
6328 ComObjPtr<USBController> ctrl;
6329
6330 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6331 if (SUCCEEDED(rc))
6332 return setError(VBOX_E_OBJECT_IN_USE,
6333 tr("USB controller named '%s' already exists"),
6334 aName.c_str());
6335
6336 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6337 ULONG maxInstances;
6338 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6339 if (FAILED(rc))
6340 return rc;
6341
6342 ULONG cInstances = i_getUSBControllerCountByType(aType);
6343 if (cInstances >= maxInstances)
6344 return setError(E_INVALIDARG,
6345 tr("Too many USB controllers of this type"));
6346
6347 ctrl.createObject();
6348
6349 rc = ctrl->init(this, aName, aType);
6350 if (FAILED(rc)) return rc;
6351
6352 i_setModified(IsModified_USB);
6353 mUSBControllers.backup();
6354 mUSBControllers->push_back(ctrl);
6355
6356 ctrl.queryInterfaceTo(aController.asOutParam());
6357
6358 /* inform the direct session if any */
6359 alock.release();
6360 i_onUSBControllerChange();
6361
6362 return S_OK;
6363}
6364
6365HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6366{
6367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6368
6369 ComObjPtr<USBController> ctrl;
6370
6371 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6372 if (SUCCEEDED(rc))
6373 ctrl.queryInterfaceTo(aController.asOutParam());
6374
6375 return rc;
6376}
6377
6378HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6379 ULONG *aControllers)
6380{
6381 if ( (aType <= USBControllerType_Null)
6382 || (aType >= USBControllerType_Last))
6383 return setError(E_INVALIDARG,
6384 tr("Invalid USB controller type: %d"),
6385 aType);
6386
6387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6388
6389 ComObjPtr<USBController> ctrl;
6390
6391 *aControllers = i_getUSBControllerCountByType(aType);
6392
6393 return S_OK;
6394}
6395
6396HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6397{
6398
6399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6400
6401 HRESULT rc = i_checkStateDependency(MutableStateDep);
6402 if (FAILED(rc)) return rc;
6403
6404 ComObjPtr<USBController> ctrl;
6405 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6406 if (FAILED(rc)) return rc;
6407
6408 i_setModified(IsModified_USB);
6409 mUSBControllers.backup();
6410
6411 ctrl->i_unshare();
6412
6413 mUSBControllers->remove(ctrl);
6414
6415 /* inform the direct session if any */
6416 alock.release();
6417 i_onUSBControllerChange();
6418
6419 return S_OK;
6420}
6421
6422HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6423 ULONG *aOriginX,
6424 ULONG *aOriginY,
6425 ULONG *aWidth,
6426 ULONG *aHeight,
6427 BOOL *aEnabled)
6428{
6429 uint32_t u32OriginX= 0;
6430 uint32_t u32OriginY= 0;
6431 uint32_t u32Width = 0;
6432 uint32_t u32Height = 0;
6433 uint16_t u16Flags = 0;
6434
6435 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6436 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6437 if (RT_FAILURE(vrc))
6438 {
6439#ifdef RT_OS_WINDOWS
6440 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6441 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6442 * So just assign fEnable to TRUE again.
6443 * The right fix would be to change GUI API wrappers to make sure that parameters
6444 * are changed only if API succeeds.
6445 */
6446 *aEnabled = TRUE;
6447#endif
6448 return setError(VBOX_E_IPRT_ERROR,
6449 tr("Saved guest size is not available (%Rrc)"),
6450 vrc);
6451 }
6452
6453 *aOriginX = u32OriginX;
6454 *aOriginY = u32OriginY;
6455 *aWidth = u32Width;
6456 *aHeight = u32Height;
6457 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6458
6459 return S_OK;
6460}
6461
6462HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6463 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6464{
6465 if (aScreenId != 0)
6466 return E_NOTIMPL;
6467
6468 if ( aBitmapFormat != BitmapFormat_BGR0
6469 && aBitmapFormat != BitmapFormat_BGRA
6470 && aBitmapFormat != BitmapFormat_RGBA
6471 && aBitmapFormat != BitmapFormat_PNG)
6472 return setError(E_NOTIMPL,
6473 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6474
6475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6476
6477 uint8_t *pu8Data = NULL;
6478 uint32_t cbData = 0;
6479 uint32_t u32Width = 0;
6480 uint32_t u32Height = 0;
6481
6482 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6483
6484 if (RT_FAILURE(vrc))
6485 return setError(VBOX_E_IPRT_ERROR,
6486 tr("Saved thumbnail data is not available (%Rrc)"),
6487 vrc);
6488
6489 HRESULT hr = S_OK;
6490
6491 *aWidth = u32Width;
6492 *aHeight = u32Height;
6493
6494 if (cbData > 0)
6495 {
6496 /* Convert pixels to the format expected by the API caller. */
6497 if (aBitmapFormat == BitmapFormat_BGR0)
6498 {
6499 /* [0] B, [1] G, [2] R, [3] 0. */
6500 aData.resize(cbData);
6501 memcpy(&aData.front(), pu8Data, cbData);
6502 }
6503 else if (aBitmapFormat == BitmapFormat_BGRA)
6504 {
6505 /* [0] B, [1] G, [2] R, [3] A. */
6506 aData.resize(cbData);
6507 for (uint32_t i = 0; i < cbData; i += 4)
6508 {
6509 aData[i] = pu8Data[i];
6510 aData[i + 1] = pu8Data[i + 1];
6511 aData[i + 2] = pu8Data[i + 2];
6512 aData[i + 3] = 0xff;
6513 }
6514 }
6515 else if (aBitmapFormat == BitmapFormat_RGBA)
6516 {
6517 /* [0] R, [1] G, [2] B, [3] A. */
6518 aData.resize(cbData);
6519 for (uint32_t i = 0; i < cbData; i += 4)
6520 {
6521 aData[i] = pu8Data[i + 2];
6522 aData[i + 1] = pu8Data[i + 1];
6523 aData[i + 2] = pu8Data[i];
6524 aData[i + 3] = 0xff;
6525 }
6526 }
6527 else if (aBitmapFormat == BitmapFormat_PNG)
6528 {
6529 uint8_t *pu8PNG = NULL;
6530 uint32_t cbPNG = 0;
6531 uint32_t cxPNG = 0;
6532 uint32_t cyPNG = 0;
6533
6534 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6535
6536 if (RT_SUCCESS(vrc))
6537 {
6538 aData.resize(cbPNG);
6539 if (cbPNG)
6540 memcpy(&aData.front(), pu8PNG, cbPNG);
6541 }
6542 else
6543 hr = setError(VBOX_E_IPRT_ERROR,
6544 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6545 vrc);
6546
6547 RTMemFree(pu8PNG);
6548 }
6549 }
6550
6551 freeSavedDisplayScreenshot(pu8Data);
6552
6553 return hr;
6554}
6555
6556HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6557 ULONG *aWidth,
6558 ULONG *aHeight,
6559 std::vector<BitmapFormat_T> &aBitmapFormats)
6560{
6561 if (aScreenId != 0)
6562 return E_NOTIMPL;
6563
6564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6565
6566 uint8_t *pu8Data = NULL;
6567 uint32_t cbData = 0;
6568 uint32_t u32Width = 0;
6569 uint32_t u32Height = 0;
6570
6571 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6572
6573 if (RT_FAILURE(vrc))
6574 return setError(VBOX_E_IPRT_ERROR,
6575 tr("Saved screenshot data is not available (%Rrc)"),
6576 vrc);
6577
6578 *aWidth = u32Width;
6579 *aHeight = u32Height;
6580 aBitmapFormats.resize(1);
6581 aBitmapFormats[0] = BitmapFormat_PNG;
6582
6583 freeSavedDisplayScreenshot(pu8Data);
6584
6585 return S_OK;
6586}
6587
6588HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6589 BitmapFormat_T aBitmapFormat,
6590 ULONG *aWidth,
6591 ULONG *aHeight,
6592 std::vector<BYTE> &aData)
6593{
6594 if (aScreenId != 0)
6595 return E_NOTIMPL;
6596
6597 if (aBitmapFormat != BitmapFormat_PNG)
6598 return E_NOTIMPL;
6599
6600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6601
6602 uint8_t *pu8Data = NULL;
6603 uint32_t cbData = 0;
6604 uint32_t u32Width = 0;
6605 uint32_t u32Height = 0;
6606
6607 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6608
6609 if (RT_FAILURE(vrc))
6610 return setError(VBOX_E_IPRT_ERROR,
6611 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6612 vrc);
6613
6614 *aWidth = u32Width;
6615 *aHeight = u32Height;
6616
6617 aData.resize(cbData);
6618 if (cbData)
6619 memcpy(&aData.front(), pu8Data, cbData);
6620
6621 freeSavedDisplayScreenshot(pu8Data);
6622
6623 return S_OK;
6624}
6625
6626HRESULT Machine::hotPlugCPU(ULONG aCpu)
6627{
6628 HRESULT rc = S_OK;
6629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6630
6631 if (!mHWData->mCPUHotPlugEnabled)
6632 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6633
6634 if (aCpu >= mHWData->mCPUCount)
6635 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6636
6637 if (mHWData->mCPUAttached[aCpu])
6638 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6639
6640 alock.release();
6641 rc = i_onCPUChange(aCpu, false);
6642 alock.acquire();
6643 if (FAILED(rc)) return rc;
6644
6645 i_setModified(IsModified_MachineData);
6646 mHWData.backup();
6647 mHWData->mCPUAttached[aCpu] = true;
6648
6649 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6650 if (Global::IsOnline(mData->mMachineState))
6651 i_saveSettings(NULL);
6652
6653 return S_OK;
6654}
6655
6656HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6657{
6658 HRESULT rc = S_OK;
6659
6660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6661
6662 if (!mHWData->mCPUHotPlugEnabled)
6663 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6664
6665 if (aCpu >= SchemaDefs::MaxCPUCount)
6666 return setError(E_INVALIDARG,
6667 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6668 SchemaDefs::MaxCPUCount);
6669
6670 if (!mHWData->mCPUAttached[aCpu])
6671 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6672
6673 /* CPU 0 can't be detached */
6674 if (aCpu == 0)
6675 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6676
6677 alock.release();
6678 rc = i_onCPUChange(aCpu, true);
6679 alock.acquire();
6680 if (FAILED(rc)) return rc;
6681
6682 i_setModified(IsModified_MachineData);
6683 mHWData.backup();
6684 mHWData->mCPUAttached[aCpu] = false;
6685
6686 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6687 if (Global::IsOnline(mData->mMachineState))
6688 i_saveSettings(NULL);
6689
6690 return S_OK;
6691}
6692
6693HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6694{
6695 *aAttached = false;
6696
6697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6698
6699 /* If hotplug is enabled the CPU is always enabled. */
6700 if (!mHWData->mCPUHotPlugEnabled)
6701 {
6702 if (aCpu < mHWData->mCPUCount)
6703 *aAttached = true;
6704 }
6705 else
6706 {
6707 if (aCpu < SchemaDefs::MaxCPUCount)
6708 *aAttached = mHWData->mCPUAttached[aCpu];
6709 }
6710
6711 return S_OK;
6712}
6713
6714HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6715{
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 Utf8Str log = i_getLogFilename(aIdx);
6719 if (!RTFileExists(log.c_str()))
6720 log.setNull();
6721 aFilename = log;
6722
6723 return S_OK;
6724}
6725
6726HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6727{
6728 if (aSize < 0)
6729 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6730
6731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 HRESULT rc = S_OK;
6734 Utf8Str log = i_getLogFilename(aIdx);
6735
6736 /* do not unnecessarily hold the lock while doing something which does
6737 * not need the lock and potentially takes a long time. */
6738 alock.release();
6739
6740 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6741 * keeps the SOAP reply size under 1M for the webservice (we're using
6742 * base64 encoded strings for binary data for years now, avoiding the
6743 * expansion of each byte array element to approx. 25 bytes of XML. */
6744 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6745 aData.resize(cbData);
6746
6747 RTFILE LogFile;
6748 int vrc = RTFileOpen(&LogFile, log.c_str(),
6749 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6750 if (RT_SUCCESS(vrc))
6751 {
6752 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6753 if (RT_SUCCESS(vrc))
6754 aData.resize(cbData);
6755 else
6756 rc = setError(VBOX_E_IPRT_ERROR,
6757 tr("Could not read log file '%s' (%Rrc)"),
6758 log.c_str(), vrc);
6759 RTFileClose(LogFile);
6760 }
6761 else
6762 rc = setError(VBOX_E_IPRT_ERROR,
6763 tr("Could not open log file '%s' (%Rrc)"),
6764 log.c_str(), vrc);
6765
6766 if (FAILED(rc))
6767 aData.resize(0);
6768
6769 return rc;
6770}
6771
6772
6773/**
6774 * Currently this method doesn't attach device to the running VM,
6775 * just makes sure it's plugged on next VM start.
6776 */
6777HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6778{
6779 // lock scope
6780 {
6781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6782
6783 HRESULT rc = i_checkStateDependency(MutableStateDep);
6784 if (FAILED(rc)) return rc;
6785
6786 ChipsetType_T aChipset = ChipsetType_PIIX3;
6787 COMGETTER(ChipsetType)(&aChipset);
6788
6789 if (aChipset != ChipsetType_ICH9)
6790 {
6791 return setError(E_INVALIDARG,
6792 tr("Host PCI attachment only supported with ICH9 chipset"));
6793 }
6794
6795 // check if device with this host PCI address already attached
6796 for (HWData::PCIDeviceAssignmentList::const_iterator
6797 it = mHWData->mPCIDeviceAssignments.begin();
6798 it != mHWData->mPCIDeviceAssignments.end();
6799 ++it)
6800 {
6801 LONG iHostAddress = -1;
6802 ComPtr<PCIDeviceAttachment> pAttach;
6803 pAttach = *it;
6804 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6805 if (iHostAddress == aHostAddress)
6806 return setError(E_INVALIDARG,
6807 tr("Device with host PCI address already attached to this VM"));
6808 }
6809
6810 ComObjPtr<PCIDeviceAttachment> pda;
6811 char name[32];
6812
6813 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6814 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6815 pda.createObject();
6816 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6817 i_setModified(IsModified_MachineData);
6818 mHWData.backup();
6819 mHWData->mPCIDeviceAssignments.push_back(pda);
6820 }
6821
6822 return S_OK;
6823}
6824
6825/**
6826 * Currently this method doesn't detach device from the running VM,
6827 * just makes sure it's not plugged on next VM start.
6828 */
6829HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6830{
6831 ComObjPtr<PCIDeviceAttachment> pAttach;
6832 bool fRemoved = false;
6833 HRESULT rc;
6834
6835 // lock scope
6836 {
6837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838
6839 rc = i_checkStateDependency(MutableStateDep);
6840 if (FAILED(rc)) return rc;
6841
6842 for (HWData::PCIDeviceAssignmentList::const_iterator
6843 it = mHWData->mPCIDeviceAssignments.begin();
6844 it != mHWData->mPCIDeviceAssignments.end();
6845 ++it)
6846 {
6847 LONG iHostAddress = -1;
6848 pAttach = *it;
6849 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6850 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6851 {
6852 i_setModified(IsModified_MachineData);
6853 mHWData.backup();
6854 mHWData->mPCIDeviceAssignments.remove(pAttach);
6855 fRemoved = true;
6856 break;
6857 }
6858 }
6859 }
6860
6861
6862 /* Fire event outside of the lock */
6863 if (fRemoved)
6864 {
6865 Assert(!pAttach.isNull());
6866 ComPtr<IEventSource> es;
6867 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6868 Assert(SUCCEEDED(rc));
6869 Bstr mid;
6870 rc = this->COMGETTER(Id)(mid.asOutParam());
6871 Assert(SUCCEEDED(rc));
6872 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6873 }
6874
6875 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6876 tr("No host PCI device %08x attached"),
6877 aHostAddress
6878 );
6879}
6880
6881HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6882{
6883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6884
6885 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6886 size_t i = 0;
6887 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6888 it = mHWData->mPCIDeviceAssignments.begin();
6889 it != mHWData->mPCIDeviceAssignments.end();
6890 ++it, ++i)
6891 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6892
6893 return S_OK;
6894}
6895
6896HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6897{
6898 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6899
6900 return S_OK;
6901}
6902
6903HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6904{
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6908
6909 return S_OK;
6910}
6911
6912HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6913{
6914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6915 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6916 if (SUCCEEDED(hrc))
6917 {
6918 hrc = mHWData.backupEx();
6919 if (SUCCEEDED(hrc))
6920 {
6921 i_setModified(IsModified_MachineData);
6922 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6923 }
6924 }
6925 return hrc;
6926}
6927
6928HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6929{
6930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6931 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6932 return S_OK;
6933}
6934
6935HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6936{
6937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6938 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6939 if (SUCCEEDED(hrc))
6940 {
6941 hrc = mHWData.backupEx();
6942 if (SUCCEEDED(hrc))
6943 {
6944 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6945 if (SUCCEEDED(hrc))
6946 i_setModified(IsModified_MachineData);
6947 }
6948 }
6949 return hrc;
6950}
6951
6952HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6953{
6954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6955
6956 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6957
6958 return S_OK;
6959}
6960
6961HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6962{
6963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6964 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6965 if (SUCCEEDED(hrc))
6966 {
6967 hrc = mHWData.backupEx();
6968 if (SUCCEEDED(hrc))
6969 {
6970 i_setModified(IsModified_MachineData);
6971 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6972 }
6973 }
6974 return hrc;
6975}
6976
6977HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6978{
6979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6982
6983 return S_OK;
6984}
6985
6986HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6987{
6988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6989
6990 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6991 if ( SUCCEEDED(hrc)
6992 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6993 {
6994 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6995 int vrc;
6996
6997 if (aAutostartEnabled)
6998 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6999 else
7000 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7001
7002 if (RT_SUCCESS(vrc))
7003 {
7004 hrc = mHWData.backupEx();
7005 if (SUCCEEDED(hrc))
7006 {
7007 i_setModified(IsModified_MachineData);
7008 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7009 }
7010 }
7011 else if (vrc == VERR_NOT_SUPPORTED)
7012 hrc = setError(VBOX_E_NOT_SUPPORTED,
7013 tr("The VM autostart feature is not supported on this platform"));
7014 else if (vrc == VERR_PATH_NOT_FOUND)
7015 hrc = setError(E_FAIL,
7016 tr("The path to the autostart database is not set"));
7017 else
7018 hrc = setError(E_UNEXPECTED,
7019 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7020 aAutostartEnabled ? "Adding" : "Removing",
7021 mUserData->s.strName.c_str(), vrc);
7022 }
7023 return hrc;
7024}
7025
7026HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7027{
7028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7029
7030 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7031
7032 return S_OK;
7033}
7034
7035HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7036{
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7039 if (SUCCEEDED(hrc))
7040 {
7041 hrc = mHWData.backupEx();
7042 if (SUCCEEDED(hrc))
7043 {
7044 i_setModified(IsModified_MachineData);
7045 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7046 }
7047 }
7048 return hrc;
7049}
7050
7051HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7052{
7053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7054
7055 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7056
7057 return S_OK;
7058}
7059
7060HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7061{
7062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7063 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7064 if ( SUCCEEDED(hrc)
7065 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7066 {
7067 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7068 int vrc;
7069
7070 if (aAutostopType != AutostopType_Disabled)
7071 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7072 else
7073 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7074
7075 if (RT_SUCCESS(vrc))
7076 {
7077 hrc = mHWData.backupEx();
7078 if (SUCCEEDED(hrc))
7079 {
7080 i_setModified(IsModified_MachineData);
7081 mHWData->mAutostart.enmAutostopType = aAutostopType;
7082 }
7083 }
7084 else if (vrc == VERR_NOT_SUPPORTED)
7085 hrc = setError(VBOX_E_NOT_SUPPORTED,
7086 tr("The VM autostop feature is not supported on this platform"));
7087 else if (vrc == VERR_PATH_NOT_FOUND)
7088 hrc = setError(E_FAIL,
7089 tr("The path to the autostart database is not set"));
7090 else
7091 hrc = setError(E_UNEXPECTED,
7092 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7093 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7094 mUserData->s.strName.c_str(), vrc);
7095 }
7096 return hrc;
7097}
7098
7099HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7100{
7101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7102
7103 aDefaultFrontend = mHWData->mDefaultFrontend;
7104
7105 return S_OK;
7106}
7107
7108HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7109{
7110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7111 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7112 if (SUCCEEDED(hrc))
7113 {
7114 hrc = mHWData.backupEx();
7115 if (SUCCEEDED(hrc))
7116 {
7117 i_setModified(IsModified_MachineData);
7118 mHWData->mDefaultFrontend = aDefaultFrontend;
7119 }
7120 }
7121 return hrc;
7122}
7123
7124HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7125{
7126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7127 size_t cbIcon = mUserData->s.ovIcon.size();
7128 aIcon.resize(cbIcon);
7129 if (cbIcon)
7130 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7131 return S_OK;
7132}
7133
7134HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7135{
7136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7137 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7138 if (SUCCEEDED(hrc))
7139 {
7140 i_setModified(IsModified_MachineData);
7141 mUserData.backup();
7142 size_t cbIcon = aIcon.size();
7143 mUserData->s.ovIcon.resize(cbIcon);
7144 if (cbIcon)
7145 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7146 }
7147 return hrc;
7148}
7149
7150HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7151{
7152#ifdef VBOX_WITH_USB
7153 *aUSBProxyAvailable = true;
7154#else
7155 *aUSBProxyAvailable = false;
7156#endif
7157 return S_OK;
7158}
7159
7160HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7161{
7162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7163
7164 aVMProcessPriority = mUserData->s.strVMPriority;
7165
7166 return S_OK;
7167}
7168
7169HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7170{
7171 RT_NOREF(aVMProcessPriority);
7172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7173 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7174 if (SUCCEEDED(hrc))
7175 {
7176 /** @todo r=klaus: currently this is marked as not implemented, as
7177 * the code for setting the priority of the process is not there
7178 * (neither when starting the VM nor at runtime). */
7179 ReturnComNotImplemented();
7180#if 0
7181 hrc = mUserData.backupEx();
7182 if (SUCCEEDED(hrc))
7183 {
7184 i_setModified(IsModified_MachineData);
7185 mUserData->s.strVMPriority = aVMProcessPriority;
7186 }
7187#endif
7188 }
7189 return hrc;
7190}
7191
7192HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7193 ComPtr<IProgress> &aProgress)
7194{
7195 ComObjPtr<Progress> pP;
7196 Progress *ppP = pP;
7197 IProgress *iP = static_cast<IProgress *>(ppP);
7198 IProgress **pProgress = &iP;
7199
7200 IMachine *pTarget = aTarget;
7201
7202 /* Convert the options. */
7203 RTCList<CloneOptions_T> optList;
7204 if (aOptions.size())
7205 for (size_t i = 0; i < aOptions.size(); ++i)
7206 optList.append(aOptions[i]);
7207
7208 if (optList.contains(CloneOptions_Link))
7209 {
7210 if (!i_isSnapshotMachine())
7211 return setError(E_INVALIDARG,
7212 tr("Linked clone can only be created from a snapshot"));
7213 if (aMode != CloneMode_MachineState)
7214 return setError(E_INVALIDARG,
7215 tr("Linked clone can only be created for a single machine state"));
7216 }
7217 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7218
7219 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7220
7221 HRESULT rc = pWorker->start(pProgress);
7222
7223 pP = static_cast<Progress *>(*pProgress);
7224 pP.queryInterfaceTo(aProgress.asOutParam());
7225
7226 return rc;
7227
7228}
7229
7230HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7231 const com::Utf8Str &aType,
7232 ComPtr<IProgress> &aProgress)
7233{
7234 LogFlowThisFuncEnter();
7235
7236 ComObjPtr<Progress> progress;
7237
7238 progress.createObject();
7239
7240 HRESULT rc = S_OK;
7241 Utf8Str targetPath = aTargetPath;
7242 Utf8Str type = aType;
7243
7244 /* Initialize our worker task */
7245 MachineMoveVM* task = NULL;
7246 try
7247 {
7248 task = new MachineMoveVM(this, targetPath, type, progress);
7249 }
7250 catch(...)
7251 {
7252 delete task;
7253 return rc;
7254 }
7255
7256 /*
7257 * task pointer will be owned by the ThreadTask class.
7258 * There is no need to call operator "delete" in the end.
7259 */
7260 rc = task->init();
7261 if (SUCCEEDED(rc))
7262 {
7263 rc = task->createThread();
7264 if (FAILED(rc))
7265 {
7266 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7267 }
7268
7269 /* Return progress to the caller */
7270 progress.queryInterfaceTo(aProgress.asOutParam());
7271 }
7272
7273 LogFlowThisFuncLeave();
7274 return rc;
7275
7276}
7277
7278HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7279{
7280 NOREF(aProgress);
7281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 // This check should always fail.
7284 HRESULT rc = i_checkStateDependency(MutableStateDep);
7285 if (FAILED(rc)) return rc;
7286
7287 AssertFailedReturn(E_NOTIMPL);
7288}
7289
7290HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7291{
7292 NOREF(aSavedStateFile);
7293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7294
7295 // This check should always fail.
7296 HRESULT rc = i_checkStateDependency(MutableStateDep);
7297 if (FAILED(rc)) return rc;
7298
7299 AssertFailedReturn(E_NOTIMPL);
7300}
7301
7302HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7303{
7304 NOREF(aFRemoveFile);
7305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7306
7307 // This check should always fail.
7308 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7309 if (FAILED(rc)) return rc;
7310
7311 AssertFailedReturn(E_NOTIMPL);
7312}
7313
7314// public methods for internal purposes
7315/////////////////////////////////////////////////////////////////////////////
7316
7317/**
7318 * Adds the given IsModified_* flag to the dirty flags of the machine.
7319 * This must be called either during i_loadSettings or under the machine write lock.
7320 * @param fl Flag
7321 * @param fAllowStateModification If state modifications are allowed.
7322 */
7323void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7324{
7325 mData->flModifications |= fl;
7326 if (fAllowStateModification && i_isStateModificationAllowed())
7327 mData->mCurrentStateModified = true;
7328}
7329
7330/**
7331 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7332 * care of the write locking.
7333 *
7334 * @param fModification The flag to add.
7335 * @param fAllowStateModification If state modifications are allowed.
7336 */
7337void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7338{
7339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7340 i_setModified(fModification, fAllowStateModification);
7341}
7342
7343/**
7344 * Saves the registry entry of this machine to the given configuration node.
7345 *
7346 * @param data Machine registry data.
7347 *
7348 * @note locks this object for reading.
7349 */
7350HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7351{
7352 AutoLimitedCaller autoCaller(this);
7353 AssertComRCReturnRC(autoCaller.rc());
7354
7355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7356
7357 data.uuid = mData->mUuid;
7358 data.strSettingsFile = mData->m_strConfigFile;
7359
7360 return S_OK;
7361}
7362
7363/**
7364 * Calculates the absolute path of the given path taking the directory of the
7365 * machine settings file as the current directory.
7366 *
7367 * @param strPath Path to calculate the absolute path for.
7368 * @param aResult Where to put the result (used only on success, can be the
7369 * same Utf8Str instance as passed in @a aPath).
7370 * @return IPRT result.
7371 *
7372 * @note Locks this object for reading.
7373 */
7374int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7375{
7376 AutoCaller autoCaller(this);
7377 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7378
7379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7380
7381 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7382
7383 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7384
7385 strSettingsDir.stripFilename();
7386 char folder[RTPATH_MAX];
7387 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7388 if (RT_SUCCESS(vrc))
7389 aResult = folder;
7390
7391 return vrc;
7392}
7393
7394/**
7395 * Copies strSource to strTarget, making it relative to the machine folder
7396 * if it is a subdirectory thereof, or simply copying it otherwise.
7397 *
7398 * @param strSource Path to evaluate and copy.
7399 * @param strTarget Buffer to receive target path.
7400 *
7401 * @note Locks this object for reading.
7402 */
7403void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7404 Utf8Str &strTarget)
7405{
7406 AutoCaller autoCaller(this);
7407 AssertComRCReturn(autoCaller.rc(), (void)0);
7408
7409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7410
7411 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7412 // use strTarget as a temporary buffer to hold the machine settings dir
7413 strTarget = mData->m_strConfigFileFull;
7414 strTarget.stripFilename();
7415 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7416 {
7417 // is relative: then append what's left
7418 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7419 // for empty paths (only possible for subdirs) use "." to avoid
7420 // triggering default settings for not present config attributes.
7421 if (strTarget.isEmpty())
7422 strTarget = ".";
7423 }
7424 else
7425 // is not relative: then overwrite
7426 strTarget = strSource;
7427}
7428
7429/**
7430 * Returns the full path to the machine's log folder in the
7431 * \a aLogFolder argument.
7432 */
7433void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7434{
7435 AutoCaller autoCaller(this);
7436 AssertComRCReturnVoid(autoCaller.rc());
7437
7438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7439
7440 char szTmp[RTPATH_MAX];
7441 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7442 if (RT_SUCCESS(vrc))
7443 {
7444 if (szTmp[0] && !mUserData.isNull())
7445 {
7446 char szTmp2[RTPATH_MAX];
7447 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7448 if (RT_SUCCESS(vrc))
7449 aLogFolder = Utf8StrFmt("%s%c%s",
7450 szTmp2,
7451 RTPATH_DELIMITER,
7452 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7453 }
7454 else
7455 vrc = VERR_PATH_IS_RELATIVE;
7456 }
7457
7458 if (RT_FAILURE(vrc))
7459 {
7460 // fallback if VBOX_USER_LOGHOME is not set or invalid
7461 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7462 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7463 aLogFolder.append(RTPATH_DELIMITER);
7464 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7465 }
7466}
7467
7468/**
7469 * Returns the full path to the machine's log file for an given index.
7470 */
7471Utf8Str Machine::i_getLogFilename(ULONG idx)
7472{
7473 Utf8Str logFolder;
7474 getLogFolder(logFolder);
7475 Assert(logFolder.length());
7476
7477 Utf8Str log;
7478 if (idx == 0)
7479 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7480#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7481 else if (idx == 1)
7482 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7483 else
7484 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7485#else
7486 else
7487 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7488#endif
7489 return log;
7490}
7491
7492/**
7493 * Returns the full path to the machine's hardened log file.
7494 */
7495Utf8Str Machine::i_getHardeningLogFilename(void)
7496{
7497 Utf8Str strFilename;
7498 getLogFolder(strFilename);
7499 Assert(strFilename.length());
7500 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7501 return strFilename;
7502}
7503
7504
7505/**
7506 * Composes a unique saved state filename based on the current system time. The filename is
7507 * granular to the second so this will work so long as no more than one snapshot is taken on
7508 * a machine per second.
7509 *
7510 * Before version 4.1, we used this formula for saved state files:
7511 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7512 * which no longer works because saved state files can now be shared between the saved state of the
7513 * "saved" machine and an online snapshot, and the following would cause problems:
7514 * 1) save machine
7515 * 2) create online snapshot from that machine state --> reusing saved state file
7516 * 3) save machine again --> filename would be reused, breaking the online snapshot
7517 *
7518 * So instead we now use a timestamp.
7519 *
7520 * @param strStateFilePath
7521 */
7522
7523void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7524{
7525 AutoCaller autoCaller(this);
7526 AssertComRCReturnVoid(autoCaller.rc());
7527
7528 {
7529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7530 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7531 }
7532
7533 RTTIMESPEC ts;
7534 RTTimeNow(&ts);
7535 RTTIME time;
7536 RTTimeExplode(&time, &ts);
7537
7538 strStateFilePath += RTPATH_DELIMITER;
7539 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7540 time.i32Year, time.u8Month, time.u8MonthDay,
7541 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7542}
7543
7544/**
7545 * Returns the full path to the default video capture file.
7546 */
7547void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7548{
7549 AutoCaller autoCaller(this);
7550 AssertComRCReturnVoid(autoCaller.rc());
7551
7552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7553
7554 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7555 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7556 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7557}
7558
7559/**
7560 * Returns whether at least one USB controller is present for the VM.
7561 */
7562bool Machine::i_isUSBControllerPresent()
7563{
7564 AutoCaller autoCaller(this);
7565 AssertComRCReturn(autoCaller.rc(), false);
7566
7567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7568
7569 return (mUSBControllers->size() > 0);
7570}
7571
7572/**
7573 * @note Locks this object for writing, calls the client process
7574 * (inside the lock).
7575 */
7576HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7577 const Utf8Str &strFrontend,
7578 const Utf8Str &strEnvironment,
7579 ProgressProxy *aProgress)
7580{
7581 LogFlowThisFuncEnter();
7582
7583 AssertReturn(aControl, E_FAIL);
7584 AssertReturn(aProgress, E_FAIL);
7585 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7586
7587 AutoCaller autoCaller(this);
7588 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7589
7590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7591
7592 if (!mData->mRegistered)
7593 return setError(E_UNEXPECTED,
7594 tr("The machine '%s' is not registered"),
7595 mUserData->s.strName.c_str());
7596
7597 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7598
7599 /* The process started when launching a VM with separate UI/VM processes is always
7600 * the UI process, i.e. needs special handling as it won't claim the session. */
7601 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7602
7603 if (fSeparate)
7604 {
7605 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7606 return setError(VBOX_E_INVALID_OBJECT_STATE,
7607 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7608 mUserData->s.strName.c_str());
7609 }
7610 else
7611 {
7612 if ( mData->mSession.mState == SessionState_Locked
7613 || mData->mSession.mState == SessionState_Spawning
7614 || mData->mSession.mState == SessionState_Unlocking)
7615 return setError(VBOX_E_INVALID_OBJECT_STATE,
7616 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7617 mUserData->s.strName.c_str());
7618
7619 /* may not be busy */
7620 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7621 }
7622
7623 /* get the path to the executable */
7624 char szPath[RTPATH_MAX];
7625 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7626 size_t cchBufLeft = strlen(szPath);
7627 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7628 szPath[cchBufLeft] = 0;
7629 char *pszNamePart = szPath + cchBufLeft;
7630 cchBufLeft = sizeof(szPath) - cchBufLeft;
7631
7632 int vrc = VINF_SUCCESS;
7633 RTPROCESS pid = NIL_RTPROCESS;
7634
7635 RTENV env = RTENV_DEFAULT;
7636
7637 if (!strEnvironment.isEmpty())
7638 {
7639 char *newEnvStr = NULL;
7640
7641 do
7642 {
7643 /* clone the current environment */
7644 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7645 AssertRCBreakStmt(vrc2, vrc = vrc2);
7646
7647 newEnvStr = RTStrDup(strEnvironment.c_str());
7648 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7649
7650 /* put new variables to the environment
7651 * (ignore empty variable names here since RTEnv API
7652 * intentionally doesn't do that) */
7653 char *var = newEnvStr;
7654 for (char *p = newEnvStr; *p; ++p)
7655 {
7656 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7657 {
7658 *p = '\0';
7659 if (*var)
7660 {
7661 char *val = strchr(var, '=');
7662 if (val)
7663 {
7664 *val++ = '\0';
7665 vrc2 = RTEnvSetEx(env, var, val);
7666 }
7667 else
7668 vrc2 = RTEnvUnsetEx(env, var);
7669 if (RT_FAILURE(vrc2))
7670 break;
7671 }
7672 var = p + 1;
7673 }
7674 }
7675 if (RT_SUCCESS(vrc2) && *var)
7676 vrc2 = RTEnvPutEx(env, var);
7677
7678 AssertRCBreakStmt(vrc2, vrc = vrc2);
7679 }
7680 while (0);
7681
7682 if (newEnvStr != NULL)
7683 RTStrFree(newEnvStr);
7684 }
7685
7686 /* Hardening logging */
7687#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7688 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7689 {
7690 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7691 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7692 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7693 {
7694 Utf8Str strStartupLogDir = strHardeningLogFile;
7695 strStartupLogDir.stripFilename();
7696 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7697 file without stripping the file. */
7698 }
7699 strSupHardeningLogArg.append(strHardeningLogFile);
7700
7701 /* Remove legacy log filename to avoid confusion. */
7702 Utf8Str strOldStartupLogFile;
7703 getLogFolder(strOldStartupLogFile);
7704 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7705 RTFileDelete(strOldStartupLogFile.c_str());
7706 }
7707 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7708#else
7709 const char *pszSupHardeningLogArg = NULL;
7710#endif
7711
7712 Utf8Str strCanonicalName;
7713
7714#ifdef VBOX_WITH_QTGUI
7715 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7716 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7717 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7718 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7719 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7720 {
7721 strCanonicalName = "GUI/Qt";
7722# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7723 /* Modify the base path so that we don't need to use ".." below. */
7724 RTPathStripTrailingSlash(szPath);
7725 RTPathStripFilename(szPath);
7726 cchBufLeft = strlen(szPath);
7727 pszNamePart = szPath + cchBufLeft;
7728 cchBufLeft = sizeof(szPath) - cchBufLeft;
7729
7730# define OSX_APP_NAME "VirtualBoxVM"
7731# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7732
7733 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7734 if ( strAppOverride.contains(".")
7735 || strAppOverride.contains("/")
7736 || strAppOverride.contains("\\")
7737 || strAppOverride.contains(":"))
7738 strAppOverride.setNull();
7739 Utf8Str strAppPath;
7740 if (!strAppOverride.isEmpty())
7741 {
7742 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7743 Utf8Str strFullPath(szPath);
7744 strFullPath.append(strAppPath);
7745 /* there is a race, but people using this deserve the failure */
7746 if (!RTFileExists(strFullPath.c_str()))
7747 strAppOverride.setNull();
7748 }
7749 if (strAppOverride.isEmpty())
7750 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7751 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7752 strcpy(pszNamePart, strAppPath.c_str());
7753# else
7754 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7755 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7756 strcpy(pszNamePart, s_szVirtualBox_exe);
7757# endif
7758
7759 Utf8Str idStr = mData->mUuid.toString();
7760 const char *apszArgs[] =
7761 {
7762 szPath,
7763 "--comment", mUserData->s.strName.c_str(),
7764 "--startvm", idStr.c_str(),
7765 "--no-startvm-errormsgbox",
7766 NULL, /* For "--separate". */
7767 NULL, /* For "--sup-startup-log". */
7768 NULL
7769 };
7770 unsigned iArg = 6;
7771 if (fSeparate)
7772 apszArgs[iArg++] = "--separate";
7773 apszArgs[iArg++] = pszSupHardeningLogArg;
7774
7775 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7776 }
7777#else /* !VBOX_WITH_QTGUI */
7778 if (0)
7779 ;
7780#endif /* VBOX_WITH_QTGUI */
7781
7782 else
7783
7784#ifdef VBOX_WITH_VBOXSDL
7785 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7786 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7787 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7788 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7789 {
7790 strCanonicalName = "GUI/SDL";
7791 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7792 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7793 strcpy(pszNamePart, s_szVBoxSDL_exe);
7794
7795 Utf8Str idStr = mData->mUuid.toString();
7796 const char *apszArgs[] =
7797 {
7798 szPath,
7799 "--comment", mUserData->s.strName.c_str(),
7800 "--startvm", idStr.c_str(),
7801 NULL, /* For "--separate". */
7802 NULL, /* For "--sup-startup-log". */
7803 NULL
7804 };
7805 unsigned iArg = 5;
7806 if (fSeparate)
7807 apszArgs[iArg++] = "--separate";
7808 apszArgs[iArg++] = pszSupHardeningLogArg;
7809
7810 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7811 }
7812#else /* !VBOX_WITH_VBOXSDL */
7813 if (0)
7814 ;
7815#endif /* !VBOX_WITH_VBOXSDL */
7816
7817 else
7818
7819#ifdef VBOX_WITH_HEADLESS
7820 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7821 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7822 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7823 )
7824 {
7825 strCanonicalName = "headless";
7826 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7827 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7828 * and a VM works even if the server has not been installed.
7829 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7830 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7831 * differently in 4.0 and 3.x.
7832 */
7833 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7834 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7835 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7836
7837 Utf8Str idStr = mData->mUuid.toString();
7838 const char *apszArgs[] =
7839 {
7840 szPath,
7841 "--comment", mUserData->s.strName.c_str(),
7842 "--startvm", idStr.c_str(),
7843 "--vrde", "config",
7844 NULL, /* For "--capture". */
7845 NULL, /* For "--sup-startup-log". */
7846 NULL
7847 };
7848 unsigned iArg = 7;
7849 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7850 apszArgs[iArg++] = "--capture";
7851 apszArgs[iArg++] = pszSupHardeningLogArg;
7852
7853# ifdef RT_OS_WINDOWS
7854 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7855# else
7856 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7857# endif
7858 }
7859#else /* !VBOX_WITH_HEADLESS */
7860 if (0)
7861 ;
7862#endif /* !VBOX_WITH_HEADLESS */
7863 else
7864 {
7865 RTEnvDestroy(env);
7866 return setError(E_INVALIDARG,
7867 tr("Invalid frontend name: '%s'"),
7868 strFrontend.c_str());
7869 }
7870
7871 RTEnvDestroy(env);
7872
7873 if (RT_FAILURE(vrc))
7874 return setError(VBOX_E_IPRT_ERROR,
7875 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7876 mUserData->s.strName.c_str(), vrc);
7877
7878 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7879
7880 if (!fSeparate)
7881 {
7882 /*
7883 * Note that we don't release the lock here before calling the client,
7884 * because it doesn't need to call us back if called with a NULL argument.
7885 * Releasing the lock here is dangerous because we didn't prepare the
7886 * launch data yet, but the client we've just started may happen to be
7887 * too fast and call LockMachine() that will fail (because of PID, etc.),
7888 * so that the Machine will never get out of the Spawning session state.
7889 */
7890
7891 /* inform the session that it will be a remote one */
7892 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7893#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7894 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7895#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7896 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7897#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7898 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7899
7900 if (FAILED(rc))
7901 {
7902 /* restore the session state */
7903 mData->mSession.mState = SessionState_Unlocked;
7904 alock.release();
7905 mParent->i_addProcessToReap(pid);
7906 /* The failure may occur w/o any error info (from RPC), so provide one */
7907 return setError(VBOX_E_VM_ERROR,
7908 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7909 }
7910
7911 /* attach launch data to the machine */
7912 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7913 mData->mSession.mRemoteControls.push_back(aControl);
7914 mData->mSession.mProgress = aProgress;
7915 mData->mSession.mPID = pid;
7916 mData->mSession.mState = SessionState_Spawning;
7917 Assert(strCanonicalName.isNotEmpty());
7918 mData->mSession.mName = strCanonicalName;
7919 }
7920 else
7921 {
7922 /* For separate UI process we declare the launch as completed instantly, as the
7923 * actual headless VM start may or may not come. No point in remembering anything
7924 * yet, as what matters for us is when the headless VM gets started. */
7925 aProgress->i_notifyComplete(S_OK);
7926 }
7927
7928 alock.release();
7929 mParent->i_addProcessToReap(pid);
7930
7931 LogFlowThisFuncLeave();
7932 return S_OK;
7933}
7934
7935/**
7936 * Returns @c true if the given session machine instance has an open direct
7937 * session (and optionally also for direct sessions which are closing) and
7938 * returns the session control machine instance if so.
7939 *
7940 * Note that when the method returns @c false, the arguments remain unchanged.
7941 *
7942 * @param aMachine Session machine object.
7943 * @param aControl Direct session control object (optional).
7944 * @param aRequireVM If true then only allow VM sessions.
7945 * @param aAllowClosing If true then additionally a session which is currently
7946 * being closed will also be allowed.
7947 *
7948 * @note locks this object for reading.
7949 */
7950bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7951 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7952 bool aRequireVM /*= false*/,
7953 bool aAllowClosing /*= false*/)
7954{
7955 AutoLimitedCaller autoCaller(this);
7956 AssertComRCReturn(autoCaller.rc(), false);
7957
7958 /* just return false for inaccessible machines */
7959 if (getObjectState().getState() != ObjectState::Ready)
7960 return false;
7961
7962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7963
7964 if ( ( mData->mSession.mState == SessionState_Locked
7965 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7966 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7967 )
7968 {
7969 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7970
7971 aMachine = mData->mSession.mMachine;
7972
7973 if (aControl != NULL)
7974 *aControl = mData->mSession.mDirectControl;
7975
7976 return true;
7977 }
7978
7979 return false;
7980}
7981
7982/**
7983 * Returns @c true if the given machine has an spawning direct session.
7984 *
7985 * @note locks this object for reading.
7986 */
7987bool Machine::i_isSessionSpawning()
7988{
7989 AutoLimitedCaller autoCaller(this);
7990 AssertComRCReturn(autoCaller.rc(), false);
7991
7992 /* just return false for inaccessible machines */
7993 if (getObjectState().getState() != ObjectState::Ready)
7994 return false;
7995
7996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7997
7998 if (mData->mSession.mState == SessionState_Spawning)
7999 return true;
8000
8001 return false;
8002}
8003
8004/**
8005 * Called from the client watcher thread to check for unexpected client process
8006 * death during Session_Spawning state (e.g. before it successfully opened a
8007 * direct session).
8008 *
8009 * On Win32 and on OS/2, this method is called only when we've got the
8010 * direct client's process termination notification, so it always returns @c
8011 * true.
8012 *
8013 * On other platforms, this method returns @c true if the client process is
8014 * terminated and @c false if it's still alive.
8015 *
8016 * @note Locks this object for writing.
8017 */
8018bool Machine::i_checkForSpawnFailure()
8019{
8020 AutoCaller autoCaller(this);
8021 if (!autoCaller.isOk())
8022 {
8023 /* nothing to do */
8024 LogFlowThisFunc(("Already uninitialized!\n"));
8025 return true;
8026 }
8027
8028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8029
8030 if (mData->mSession.mState != SessionState_Spawning)
8031 {
8032 /* nothing to do */
8033 LogFlowThisFunc(("Not spawning any more!\n"));
8034 return true;
8035 }
8036
8037 HRESULT rc = S_OK;
8038
8039 /* PID not yet initialized, skip check. */
8040 if (mData->mSession.mPID == NIL_RTPROCESS)
8041 return false;
8042
8043 RTPROCSTATUS status;
8044 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8045
8046 if (vrc != VERR_PROCESS_RUNNING)
8047 {
8048 Utf8Str strExtraInfo;
8049
8050#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8051 /* If the startup logfile exists and is of non-zero length, tell the
8052 user to look there for more details to encourage them to attach it
8053 when reporting startup issues. */
8054 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8055 uint64_t cbStartupLogFile = 0;
8056 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8057 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8058 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8059#endif
8060
8061 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8062 rc = setError(E_FAIL,
8063 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8064 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8065 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8066 rc = setError(E_FAIL,
8067 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8068 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8069 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8070 rc = setError(E_FAIL,
8071 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8072 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8073 else
8074 rc = setError(E_FAIL,
8075 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8076 i_getName().c_str(), vrc, strExtraInfo.c_str());
8077 }
8078
8079 if (FAILED(rc))
8080 {
8081 /* Close the remote session, remove the remote control from the list
8082 * and reset session state to Closed (@note keep the code in sync with
8083 * the relevant part in LockMachine()). */
8084
8085 Assert(mData->mSession.mRemoteControls.size() == 1);
8086 if (mData->mSession.mRemoteControls.size() == 1)
8087 {
8088 ErrorInfoKeeper eik;
8089 mData->mSession.mRemoteControls.front()->Uninitialize();
8090 }
8091
8092 mData->mSession.mRemoteControls.clear();
8093 mData->mSession.mState = SessionState_Unlocked;
8094
8095 /* finalize the progress after setting the state */
8096 if (!mData->mSession.mProgress.isNull())
8097 {
8098 mData->mSession.mProgress->notifyComplete(rc);
8099 mData->mSession.mProgress.setNull();
8100 }
8101
8102 mData->mSession.mPID = NIL_RTPROCESS;
8103
8104 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8105 return true;
8106 }
8107
8108 return false;
8109}
8110
8111/**
8112 * Checks whether the machine can be registered. If so, commits and saves
8113 * all settings.
8114 *
8115 * @note Must be called from mParent's write lock. Locks this object and
8116 * children for writing.
8117 */
8118HRESULT Machine::i_prepareRegister()
8119{
8120 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8121
8122 AutoLimitedCaller autoCaller(this);
8123 AssertComRCReturnRC(autoCaller.rc());
8124
8125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8126
8127 /* wait for state dependents to drop to zero */
8128 i_ensureNoStateDependencies();
8129
8130 if (!mData->mAccessible)
8131 return setError(VBOX_E_INVALID_OBJECT_STATE,
8132 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8133 mUserData->s.strName.c_str(),
8134 mData->mUuid.toString().c_str());
8135
8136 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8137
8138 if (mData->mRegistered)
8139 return setError(VBOX_E_INVALID_OBJECT_STATE,
8140 tr("The machine '%s' with UUID {%s} is already registered"),
8141 mUserData->s.strName.c_str(),
8142 mData->mUuid.toString().c_str());
8143
8144 HRESULT rc = S_OK;
8145
8146 // Ensure the settings are saved. If we are going to be registered and
8147 // no config file exists yet, create it by calling i_saveSettings() too.
8148 if ( (mData->flModifications)
8149 || (!mData->pMachineConfigFile->fileExists())
8150 )
8151 {
8152 rc = i_saveSettings(NULL);
8153 // no need to check whether VirtualBox.xml needs saving too since
8154 // we can't have a machine XML file rename pending
8155 if (FAILED(rc)) return rc;
8156 }
8157
8158 /* more config checking goes here */
8159
8160 if (SUCCEEDED(rc))
8161 {
8162 /* we may have had implicit modifications we want to fix on success */
8163 i_commit();
8164
8165 mData->mRegistered = true;
8166 }
8167 else
8168 {
8169 /* we may have had implicit modifications we want to cancel on failure*/
8170 i_rollback(false /* aNotify */);
8171 }
8172
8173 return rc;
8174}
8175
8176/**
8177 * Increases the number of objects dependent on the machine state or on the
8178 * registered state. Guarantees that these two states will not change at least
8179 * until #i_releaseStateDependency() is called.
8180 *
8181 * Depending on the @a aDepType value, additional state checks may be made.
8182 * These checks will set extended error info on failure. See
8183 * #i_checkStateDependency() for more info.
8184 *
8185 * If this method returns a failure, the dependency is not added and the caller
8186 * is not allowed to rely on any particular machine state or registration state
8187 * value and may return the failed result code to the upper level.
8188 *
8189 * @param aDepType Dependency type to add.
8190 * @param aState Current machine state (NULL if not interested).
8191 * @param aRegistered Current registered state (NULL if not interested).
8192 *
8193 * @note Locks this object for writing.
8194 */
8195HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8196 MachineState_T *aState /* = NULL */,
8197 BOOL *aRegistered /* = NULL */)
8198{
8199 AutoCaller autoCaller(this);
8200 AssertComRCReturnRC(autoCaller.rc());
8201
8202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8203
8204 HRESULT rc = i_checkStateDependency(aDepType);
8205 if (FAILED(rc)) return rc;
8206
8207 {
8208 if (mData->mMachineStateChangePending != 0)
8209 {
8210 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8211 * drop to zero so don't add more. It may make sense to wait a bit
8212 * and retry before reporting an error (since the pending state
8213 * transition should be really quick) but let's just assert for
8214 * now to see if it ever happens on practice. */
8215
8216 AssertFailed();
8217
8218 return setError(E_ACCESSDENIED,
8219 tr("Machine state change is in progress. Please retry the operation later."));
8220 }
8221
8222 ++mData->mMachineStateDeps;
8223 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8224 }
8225
8226 if (aState)
8227 *aState = mData->mMachineState;
8228 if (aRegistered)
8229 *aRegistered = mData->mRegistered;
8230
8231 return S_OK;
8232}
8233
8234/**
8235 * Decreases the number of objects dependent on the machine state.
8236 * Must always complete the #i_addStateDependency() call after the state
8237 * dependency is no more necessary.
8238 */
8239void Machine::i_releaseStateDependency()
8240{
8241 AutoCaller autoCaller(this);
8242 AssertComRCReturnVoid(autoCaller.rc());
8243
8244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8245
8246 /* releaseStateDependency() w/o addStateDependency()? */
8247 AssertReturnVoid(mData->mMachineStateDeps != 0);
8248 -- mData->mMachineStateDeps;
8249
8250 if (mData->mMachineStateDeps == 0)
8251 {
8252 /* inform i_ensureNoStateDependencies() that there are no more deps */
8253 if (mData->mMachineStateChangePending != 0)
8254 {
8255 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8256 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8257 }
8258 }
8259}
8260
8261Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8262{
8263 /* start with nothing found */
8264 Utf8Str strResult("");
8265
8266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8267
8268 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8269 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8270 // found:
8271 strResult = it->second; // source is a Utf8Str
8272
8273 return strResult;
8274}
8275
8276// protected methods
8277/////////////////////////////////////////////////////////////////////////////
8278
8279/**
8280 * Performs machine state checks based on the @a aDepType value. If a check
8281 * fails, this method will set extended error info, otherwise it will return
8282 * S_OK. It is supposed, that on failure, the caller will immediately return
8283 * the return value of this method to the upper level.
8284 *
8285 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8286 *
8287 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8288 * current state of this machine object allows to change settings of the
8289 * machine (i.e. the machine is not registered, or registered but not running
8290 * and not saved). It is useful to call this method from Machine setters
8291 * before performing any change.
8292 *
8293 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8294 * as for MutableStateDep except that if the machine is saved, S_OK is also
8295 * returned. This is useful in setters which allow changing machine
8296 * properties when it is in the saved state.
8297 *
8298 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8299 * if the current state of this machine object allows to change runtime
8300 * changeable settings of the machine (i.e. the machine is not registered, or
8301 * registered but either running or not running and not saved). It is useful
8302 * to call this method from Machine setters before performing any changes to
8303 * runtime changeable settings.
8304 *
8305 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8306 * the same as for MutableOrRunningStateDep except that if the machine is
8307 * saved, S_OK is also returned. This is useful in setters which allow
8308 * changing runtime and saved state changeable machine properties.
8309 *
8310 * @param aDepType Dependency type to check.
8311 *
8312 * @note Non Machine based classes should use #i_addStateDependency() and
8313 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8314 * template.
8315 *
8316 * @note This method must be called from under this object's read or write
8317 * lock.
8318 */
8319HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8320{
8321 switch (aDepType)
8322 {
8323 case AnyStateDep:
8324 {
8325 break;
8326 }
8327 case MutableStateDep:
8328 {
8329 if ( mData->mRegistered
8330 && ( !i_isSessionMachine()
8331 || ( mData->mMachineState != MachineState_Aborted
8332 && mData->mMachineState != MachineState_Teleported
8333 && mData->mMachineState != MachineState_PoweredOff
8334 )
8335 )
8336 )
8337 return setError(VBOX_E_INVALID_VM_STATE,
8338 tr("The machine is not mutable (state is %s)"),
8339 Global::stringifyMachineState(mData->mMachineState));
8340 break;
8341 }
8342 case MutableOrSavedStateDep:
8343 {
8344 if ( mData->mRegistered
8345 && ( !i_isSessionMachine()
8346 || ( mData->mMachineState != MachineState_Aborted
8347 && mData->mMachineState != MachineState_Teleported
8348 && mData->mMachineState != MachineState_Saved
8349 && mData->mMachineState != MachineState_PoweredOff
8350 )
8351 )
8352 )
8353 return setError(VBOX_E_INVALID_VM_STATE,
8354 tr("The machine is not mutable or saved (state is %s)"),
8355 Global::stringifyMachineState(mData->mMachineState));
8356 break;
8357 }
8358 case MutableOrRunningStateDep:
8359 {
8360 if ( mData->mRegistered
8361 && ( !i_isSessionMachine()
8362 || ( mData->mMachineState != MachineState_Aborted
8363 && mData->mMachineState != MachineState_Teleported
8364 && mData->mMachineState != MachineState_PoweredOff
8365 && !Global::IsOnline(mData->mMachineState)
8366 )
8367 )
8368 )
8369 return setError(VBOX_E_INVALID_VM_STATE,
8370 tr("The machine is not mutable or running (state is %s)"),
8371 Global::stringifyMachineState(mData->mMachineState));
8372 break;
8373 }
8374 case MutableOrSavedOrRunningStateDep:
8375 {
8376 if ( mData->mRegistered
8377 && ( !i_isSessionMachine()
8378 || ( mData->mMachineState != MachineState_Aborted
8379 && mData->mMachineState != MachineState_Teleported
8380 && mData->mMachineState != MachineState_Saved
8381 && mData->mMachineState != MachineState_PoweredOff
8382 && !Global::IsOnline(mData->mMachineState)
8383 )
8384 )
8385 )
8386 return setError(VBOX_E_INVALID_VM_STATE,
8387 tr("The machine is not mutable, saved or running (state is %s)"),
8388 Global::stringifyMachineState(mData->mMachineState));
8389 break;
8390 }
8391 }
8392
8393 return S_OK;
8394}
8395
8396/**
8397 * Helper to initialize all associated child objects and allocate data
8398 * structures.
8399 *
8400 * This method must be called as a part of the object's initialization procedure
8401 * (usually done in the #init() method).
8402 *
8403 * @note Must be called only from #init() or from #i_registeredInit().
8404 */
8405HRESULT Machine::initDataAndChildObjects()
8406{
8407 AutoCaller autoCaller(this);
8408 AssertComRCReturnRC(autoCaller.rc());
8409 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8410 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8411
8412 AssertReturn(!mData->mAccessible, E_FAIL);
8413
8414 /* allocate data structures */
8415 mSSData.allocate();
8416 mUserData.allocate();
8417 mHWData.allocate();
8418 mMediumAttachments.allocate();
8419 mStorageControllers.allocate();
8420 mUSBControllers.allocate();
8421
8422 /* initialize mOSTypeId */
8423 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8424
8425/** @todo r=bird: init() methods never fails, right? Why don't we make them
8426 * return void then! */
8427
8428 /* create associated BIOS settings object */
8429 unconst(mBIOSSettings).createObject();
8430 mBIOSSettings->init(this);
8431
8432 /* create an associated VRDE object (default is disabled) */
8433 unconst(mVRDEServer).createObject();
8434 mVRDEServer->init(this);
8435
8436 /* create associated serial port objects */
8437 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8438 {
8439 unconst(mSerialPorts[slot]).createObject();
8440 mSerialPorts[slot]->init(this, slot);
8441 }
8442
8443 /* create associated parallel port objects */
8444 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8445 {
8446 unconst(mParallelPorts[slot]).createObject();
8447 mParallelPorts[slot]->init(this, slot);
8448 }
8449
8450 /* create the audio adapter object (always present, default is disabled) */
8451 unconst(mAudioAdapter).createObject();
8452 mAudioAdapter->init(this);
8453
8454 /* create the USB device filters object (always present) */
8455 unconst(mUSBDeviceFilters).createObject();
8456 mUSBDeviceFilters->init(this);
8457
8458 /* create associated network adapter objects */
8459 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8460 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8461 {
8462 unconst(mNetworkAdapters[slot]).createObject();
8463 mNetworkAdapters[slot]->init(this, slot);
8464 }
8465
8466 /* create the bandwidth control */
8467 unconst(mBandwidthControl).createObject();
8468 mBandwidthControl->init(this);
8469
8470 return S_OK;
8471}
8472
8473/**
8474 * Helper to uninitialize all associated child objects and to free all data
8475 * structures.
8476 *
8477 * This method must be called as a part of the object's uninitialization
8478 * procedure (usually done in the #uninit() method).
8479 *
8480 * @note Must be called only from #uninit() or from #i_registeredInit().
8481 */
8482void Machine::uninitDataAndChildObjects()
8483{
8484 AutoCaller autoCaller(this);
8485 AssertComRCReturnVoid(autoCaller.rc());
8486 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8487 || getObjectState().getState() == ObjectState::Limited);
8488
8489 /* tell all our other child objects we've been uninitialized */
8490 if (mBandwidthControl)
8491 {
8492 mBandwidthControl->uninit();
8493 unconst(mBandwidthControl).setNull();
8494 }
8495
8496 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8497 {
8498 if (mNetworkAdapters[slot])
8499 {
8500 mNetworkAdapters[slot]->uninit();
8501 unconst(mNetworkAdapters[slot]).setNull();
8502 }
8503 }
8504
8505 if (mUSBDeviceFilters)
8506 {
8507 mUSBDeviceFilters->uninit();
8508 unconst(mUSBDeviceFilters).setNull();
8509 }
8510
8511 if (mAudioAdapter)
8512 {
8513 mAudioAdapter->uninit();
8514 unconst(mAudioAdapter).setNull();
8515 }
8516
8517 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8518 {
8519 if (mParallelPorts[slot])
8520 {
8521 mParallelPorts[slot]->uninit();
8522 unconst(mParallelPorts[slot]).setNull();
8523 }
8524 }
8525
8526 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8527 {
8528 if (mSerialPorts[slot])
8529 {
8530 mSerialPorts[slot]->uninit();
8531 unconst(mSerialPorts[slot]).setNull();
8532 }
8533 }
8534
8535 if (mVRDEServer)
8536 {
8537 mVRDEServer->uninit();
8538 unconst(mVRDEServer).setNull();
8539 }
8540
8541 if (mBIOSSettings)
8542 {
8543 mBIOSSettings->uninit();
8544 unconst(mBIOSSettings).setNull();
8545 }
8546
8547 /* Deassociate media (only when a real Machine or a SnapshotMachine
8548 * instance is uninitialized; SessionMachine instances refer to real
8549 * Machine media). This is necessary for a clean re-initialization of
8550 * the VM after successfully re-checking the accessibility state. Note
8551 * that in case of normal Machine or SnapshotMachine uninitialization (as
8552 * a result of unregistering or deleting the snapshot), outdated media
8553 * attachments will already be uninitialized and deleted, so this
8554 * code will not affect them. */
8555 if ( !mMediumAttachments.isNull()
8556 && !i_isSessionMachine()
8557 )
8558 {
8559 for (MediumAttachmentList::const_iterator
8560 it = mMediumAttachments->begin();
8561 it != mMediumAttachments->end();
8562 ++it)
8563 {
8564 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8565 if (pMedium.isNull())
8566 continue;
8567 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8568 AssertComRC(rc);
8569 }
8570 }
8571
8572 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8573 {
8574 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8575 if (mData->mFirstSnapshot)
8576 {
8577 // snapshots tree is protected by machine write lock; strictly
8578 // this isn't necessary here since we're deleting the entire
8579 // machine, but otherwise we assert in Snapshot::uninit()
8580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8581 mData->mFirstSnapshot->uninit();
8582 mData->mFirstSnapshot.setNull();
8583 }
8584
8585 mData->mCurrentSnapshot.setNull();
8586 }
8587
8588 /* free data structures (the essential mData structure is not freed here
8589 * since it may be still in use) */
8590 mMediumAttachments.free();
8591 mStorageControllers.free();
8592 mUSBControllers.free();
8593 mHWData.free();
8594 mUserData.free();
8595 mSSData.free();
8596}
8597
8598/**
8599 * Returns a pointer to the Machine object for this machine that acts like a
8600 * parent for complex machine data objects such as shared folders, etc.
8601 *
8602 * For primary Machine objects and for SnapshotMachine objects, returns this
8603 * object's pointer itself. For SessionMachine objects, returns the peer
8604 * (primary) machine pointer.
8605 */
8606Machine *Machine::i_getMachine()
8607{
8608 if (i_isSessionMachine())
8609 return (Machine*)mPeer;
8610 return this;
8611}
8612
8613/**
8614 * Makes sure that there are no machine state dependents. If necessary, waits
8615 * for the number of dependents to drop to zero.
8616 *
8617 * Make sure this method is called from under this object's write lock to
8618 * guarantee that no new dependents may be added when this method returns
8619 * control to the caller.
8620 *
8621 * @note Locks this object for writing. The lock will be released while waiting
8622 * (if necessary).
8623 *
8624 * @warning To be used only in methods that change the machine state!
8625 */
8626void Machine::i_ensureNoStateDependencies()
8627{
8628 AssertReturnVoid(isWriteLockOnCurrentThread());
8629
8630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8631
8632 /* Wait for all state dependents if necessary */
8633 if (mData->mMachineStateDeps != 0)
8634 {
8635 /* lazy semaphore creation */
8636 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8637 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8638
8639 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8640 mData->mMachineStateDeps));
8641
8642 ++mData->mMachineStateChangePending;
8643
8644 /* reset the semaphore before waiting, the last dependent will signal
8645 * it */
8646 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8647
8648 alock.release();
8649
8650 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8651
8652 alock.acquire();
8653
8654 -- mData->mMachineStateChangePending;
8655 }
8656}
8657
8658/**
8659 * Changes the machine state and informs callbacks.
8660 *
8661 * This method is not intended to fail so it either returns S_OK or asserts (and
8662 * returns a failure).
8663 *
8664 * @note Locks this object for writing.
8665 */
8666HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8667{
8668 LogFlowThisFuncEnter();
8669 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8670 Assert(aMachineState != MachineState_Null);
8671
8672 AutoCaller autoCaller(this);
8673 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8674
8675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8676
8677 /* wait for state dependents to drop to zero */
8678 i_ensureNoStateDependencies();
8679
8680 MachineState_T const enmOldState = mData->mMachineState;
8681 if (enmOldState != aMachineState)
8682 {
8683 mData->mMachineState = aMachineState;
8684 RTTimeNow(&mData->mLastStateChange);
8685
8686#ifdef VBOX_WITH_DTRACE_R3_MAIN
8687 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8688#endif
8689 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8690 }
8691
8692 LogFlowThisFuncLeave();
8693 return S_OK;
8694}
8695
8696/**
8697 * Searches for a shared folder with the given logical name
8698 * in the collection of shared folders.
8699 *
8700 * @param aName logical name of the shared folder
8701 * @param aSharedFolder where to return the found object
8702 * @param aSetError whether to set the error info if the folder is
8703 * not found
8704 * @return
8705 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8706 *
8707 * @note
8708 * must be called from under the object's lock!
8709 */
8710HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8711 ComObjPtr<SharedFolder> &aSharedFolder,
8712 bool aSetError /* = false */)
8713{
8714 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8715 for (HWData::SharedFolderList::const_iterator
8716 it = mHWData->mSharedFolders.begin();
8717 it != mHWData->mSharedFolders.end();
8718 ++it)
8719 {
8720 SharedFolder *pSF = *it;
8721 AutoCaller autoCaller(pSF);
8722 if (pSF->i_getName() == aName)
8723 {
8724 aSharedFolder = pSF;
8725 rc = S_OK;
8726 break;
8727 }
8728 }
8729
8730 if (aSetError && FAILED(rc))
8731 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8732
8733 return rc;
8734}
8735
8736/**
8737 * Initializes all machine instance data from the given settings structures
8738 * from XML. The exception is the machine UUID which needs special handling
8739 * depending on the caller's use case, so the caller needs to set that herself.
8740 *
8741 * This gets called in several contexts during machine initialization:
8742 *
8743 * -- When machine XML exists on disk already and needs to be loaded into memory,
8744 * for example, from #i_registeredInit() to load all registered machines on
8745 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8746 * attached to the machine should be part of some media registry already.
8747 *
8748 * -- During OVF import, when a machine config has been constructed from an
8749 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8750 * ensure that the media listed as attachments in the config (which have
8751 * been imported from the OVF) receive the correct registry ID.
8752 *
8753 * -- During VM cloning.
8754 *
8755 * @param config Machine settings from XML.
8756 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8757 * for each attached medium in the config.
8758 * @return
8759 */
8760HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8761 const Guid *puuidRegistry)
8762{
8763 // copy name, description, OS type, teleporter, UTC etc.
8764 mUserData->s = config.machineUserData;
8765
8766 // look up the object by Id to check it is valid
8767 ComObjPtr<GuestOSType> pGuestOSType;
8768 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8769 pGuestOSType);
8770 if (FAILED(rc)) return rc;
8771 mUserData->s.strOsType = pGuestOSType->i_id();
8772
8773 // stateFile (optional)
8774 if (config.strStateFile.isEmpty())
8775 mSSData->strStateFilePath.setNull();
8776 else
8777 {
8778 Utf8Str stateFilePathFull(config.strStateFile);
8779 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8780 if (RT_FAILURE(vrc))
8781 return setError(E_FAIL,
8782 tr("Invalid saved state file path '%s' (%Rrc)"),
8783 config.strStateFile.c_str(),
8784 vrc);
8785 mSSData->strStateFilePath = stateFilePathFull;
8786 }
8787
8788 // snapshot folder needs special processing so set it again
8789 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8790 if (FAILED(rc)) return rc;
8791
8792 /* Copy the extra data items (config may or may not be the same as
8793 * mData->pMachineConfigFile) if necessary. When loading the XML files
8794 * from disk they are the same, but not for OVF import. */
8795 if (mData->pMachineConfigFile != &config)
8796 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8797
8798 /* currentStateModified (optional, default is true) */
8799 mData->mCurrentStateModified = config.fCurrentStateModified;
8800
8801 mData->mLastStateChange = config.timeLastStateChange;
8802
8803 /*
8804 * note: all mUserData members must be assigned prior this point because
8805 * we need to commit changes in order to let mUserData be shared by all
8806 * snapshot machine instances.
8807 */
8808 mUserData.commitCopy();
8809
8810 // machine registry, if present (must be loaded before snapshots)
8811 if (config.canHaveOwnMediaRegistry())
8812 {
8813 // determine machine folder
8814 Utf8Str strMachineFolder = i_getSettingsFileFull();
8815 strMachineFolder.stripFilename();
8816 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8817 config.mediaRegistry,
8818 strMachineFolder);
8819 if (FAILED(rc)) return rc;
8820 }
8821
8822 /* Snapshot node (optional) */
8823 size_t cRootSnapshots;
8824 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8825 {
8826 // there must be only one root snapshot
8827 Assert(cRootSnapshots == 1);
8828
8829 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8830
8831 rc = i_loadSnapshot(snap,
8832 config.uuidCurrentSnapshot,
8833 NULL); // no parent == first snapshot
8834 if (FAILED(rc)) return rc;
8835 }
8836
8837 // hardware data
8838 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8839 if (FAILED(rc)) return rc;
8840
8841 /*
8842 * NOTE: the assignment below must be the last thing to do,
8843 * otherwise it will be not possible to change the settings
8844 * somewhere in the code above because all setters will be
8845 * blocked by i_checkStateDependency(MutableStateDep).
8846 */
8847
8848 /* set the machine state to Aborted or Saved when appropriate */
8849 if (config.fAborted)
8850 {
8851 mSSData->strStateFilePath.setNull();
8852
8853 /* no need to use i_setMachineState() during init() */
8854 mData->mMachineState = MachineState_Aborted;
8855 }
8856 else if (!mSSData->strStateFilePath.isEmpty())
8857 {
8858 /* no need to use i_setMachineState() during init() */
8859 mData->mMachineState = MachineState_Saved;
8860 }
8861
8862 // after loading settings, we are no longer different from the XML on disk
8863 mData->flModifications = 0;
8864
8865 return S_OK;
8866}
8867
8868/**
8869 * Recursively loads all snapshots starting from the given.
8870 *
8871 * @param data snapshot settings.
8872 * @param aCurSnapshotId Current snapshot ID from the settings file.
8873 * @param aParentSnapshot Parent snapshot.
8874 */
8875HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8876 const Guid &aCurSnapshotId,
8877 Snapshot *aParentSnapshot)
8878{
8879 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8880 AssertReturn(!i_isSessionMachine(), E_FAIL);
8881
8882 HRESULT rc = S_OK;
8883
8884 Utf8Str strStateFile;
8885 if (!data.strStateFile.isEmpty())
8886 {
8887 /* optional */
8888 strStateFile = data.strStateFile;
8889 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8890 if (RT_FAILURE(vrc))
8891 return setError(E_FAIL,
8892 tr("Invalid saved state file path '%s' (%Rrc)"),
8893 strStateFile.c_str(),
8894 vrc);
8895 }
8896
8897 /* create a snapshot machine object */
8898 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8899 pSnapshotMachine.createObject();
8900 rc = pSnapshotMachine->initFromSettings(this,
8901 data.hardware,
8902 &data.debugging,
8903 &data.autostart,
8904 data.uuid.ref(),
8905 strStateFile);
8906 if (FAILED(rc)) return rc;
8907
8908 /* create a snapshot object */
8909 ComObjPtr<Snapshot> pSnapshot;
8910 pSnapshot.createObject();
8911 /* initialize the snapshot */
8912 rc = pSnapshot->init(mParent, // VirtualBox object
8913 data.uuid,
8914 data.strName,
8915 data.strDescription,
8916 data.timestamp,
8917 pSnapshotMachine,
8918 aParentSnapshot);
8919 if (FAILED(rc)) return rc;
8920
8921 /* memorize the first snapshot if necessary */
8922 if (!mData->mFirstSnapshot)
8923 mData->mFirstSnapshot = pSnapshot;
8924
8925 /* memorize the current snapshot when appropriate */
8926 if ( !mData->mCurrentSnapshot
8927 && pSnapshot->i_getId() == aCurSnapshotId
8928 )
8929 mData->mCurrentSnapshot = pSnapshot;
8930
8931 // now create the children
8932 for (settings::SnapshotsList::const_iterator
8933 it = data.llChildSnapshots.begin();
8934 it != data.llChildSnapshots.end();
8935 ++it)
8936 {
8937 const settings::Snapshot &childData = *it;
8938 // recurse
8939 rc = i_loadSnapshot(childData,
8940 aCurSnapshotId,
8941 pSnapshot); // parent = the one we created above
8942 if (FAILED(rc)) return rc;
8943 }
8944
8945 return rc;
8946}
8947
8948/**
8949 * Loads settings into mHWData.
8950 *
8951 * @param puuidRegistry Registry ID.
8952 * @param puuidSnapshot Snapshot ID
8953 * @param data Reference to the hardware settings.
8954 * @param pDbg Pointer to the debugging settings.
8955 * @param pAutostart Pointer to the autostart settings.
8956 */
8957HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8958 const Guid *puuidSnapshot,
8959 const settings::Hardware &data,
8960 const settings::Debugging *pDbg,
8961 const settings::Autostart *pAutostart)
8962{
8963 AssertReturn(!i_isSessionMachine(), E_FAIL);
8964
8965 HRESULT rc = S_OK;
8966
8967 try
8968 {
8969 ComObjPtr<GuestOSType> pGuestOSType;
8970 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8971 pGuestOSType);
8972 if (FAILED(rc))
8973 return rc;
8974
8975 /* The hardware version attribute (optional). */
8976 mHWData->mHWVersion = data.strVersion;
8977 mHWData->mHardwareUUID = data.uuid;
8978
8979 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8980 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8981 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8982 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8983 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8984 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8985 mHWData->mPAEEnabled = data.fPAE;
8986 mHWData->mLongMode = data.enmLongMode;
8987 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8988 mHWData->mAPIC = data.fAPIC;
8989 mHWData->mX2APIC = data.fX2APIC;
8990 mHWData->mCPUCount = data.cCPUs;
8991 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8992 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8993 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8994 mHWData->mCpuProfile = data.strCpuProfile;
8995
8996 // cpu
8997 if (mHWData->mCPUHotPlugEnabled)
8998 {
8999 for (settings::CpuList::const_iterator
9000 it = data.llCpus.begin();
9001 it != data.llCpus.end();
9002 ++it)
9003 {
9004 const settings::Cpu &cpu = *it;
9005
9006 mHWData->mCPUAttached[cpu.ulId] = true;
9007 }
9008 }
9009
9010 // cpuid leafs
9011 for (settings::CpuIdLeafsList::const_iterator
9012 it = data.llCpuIdLeafs.begin();
9013 it != data.llCpuIdLeafs.end();
9014 ++it)
9015 {
9016 const settings::CpuIdLeaf &rLeaf= *it;
9017 if ( rLeaf.idx < UINT32_C(0x20)
9018 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9019 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9020 mHWData->mCpuIdLeafList.push_back(rLeaf);
9021 /* else: just ignore */
9022 }
9023
9024 mHWData->mMemorySize = data.ulMemorySizeMB;
9025 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9026
9027 // boot order
9028 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9029 {
9030 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9031 if (it == data.mapBootOrder.end())
9032 mHWData->mBootOrder[i] = DeviceType_Null;
9033 else
9034 mHWData->mBootOrder[i] = it->second;
9035 }
9036
9037 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9038 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9039 mHWData->mMonitorCount = data.cMonitors;
9040 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9041 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9042 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9043 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9044 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9045 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9046 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9047 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9048 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9049 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9050 if (!data.strVideoCaptureFile.isEmpty())
9051 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9052 else
9053 mHWData->mVideoCaptureFile.setNull();
9054 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9055 mHWData->mFirmwareType = data.firmwareType;
9056 mHWData->mPointingHIDType = data.pointingHIDType;
9057 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9058 mHWData->mChipsetType = data.chipsetType;
9059 mHWData->mParavirtProvider = data.paravirtProvider;
9060 mHWData->mParavirtDebug = data.strParavirtDebug;
9061 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9062 mHWData->mHPETEnabled = data.fHPETEnabled;
9063
9064 /* VRDEServer */
9065 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9066 if (FAILED(rc)) return rc;
9067
9068 /* BIOS */
9069 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9070 if (FAILED(rc)) return rc;
9071
9072 // Bandwidth control (must come before network adapters)
9073 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9074 if (FAILED(rc)) return rc;
9075
9076 /* Shared folders */
9077 for (settings::USBControllerList::const_iterator
9078 it = data.usbSettings.llUSBControllers.begin();
9079 it != data.usbSettings.llUSBControllers.end();
9080 ++it)
9081 {
9082 const settings::USBController &settingsCtrl = *it;
9083 ComObjPtr<USBController> newCtrl;
9084
9085 newCtrl.createObject();
9086 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9087 mUSBControllers->push_back(newCtrl);
9088 }
9089
9090 /* USB device filters */
9091 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9092 if (FAILED(rc)) return rc;
9093
9094 // network adapters (establish array size first and apply defaults, to
9095 // ensure reading the same settings as we saved, since the list skips
9096 // adapters having defaults)
9097 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9098 size_t oldCount = mNetworkAdapters.size();
9099 if (newCount > oldCount)
9100 {
9101 mNetworkAdapters.resize(newCount);
9102 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9103 {
9104 unconst(mNetworkAdapters[slot]).createObject();
9105 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9106 }
9107 }
9108 else if (newCount < oldCount)
9109 mNetworkAdapters.resize(newCount);
9110 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9111 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9112 for (settings::NetworkAdaptersList::const_iterator
9113 it = data.llNetworkAdapters.begin();
9114 it != data.llNetworkAdapters.end();
9115 ++it)
9116 {
9117 const settings::NetworkAdapter &nic = *it;
9118
9119 /* slot uniqueness is guaranteed by XML Schema */
9120 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9121 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9122 if (FAILED(rc)) return rc;
9123 }
9124
9125 // serial ports (establish defaults first, to ensure reading the same
9126 // settings as we saved, since the list skips ports having defaults)
9127 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9128 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9129 for (settings::SerialPortsList::const_iterator
9130 it = data.llSerialPorts.begin();
9131 it != data.llSerialPorts.end();
9132 ++it)
9133 {
9134 const settings::SerialPort &s = *it;
9135
9136 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9137 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9138 if (FAILED(rc)) return rc;
9139 }
9140
9141 // parallel ports (establish defaults first, to ensure reading the same
9142 // settings as we saved, since the list skips ports having defaults)
9143 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9144 mParallelPorts[i]->i_applyDefaults();
9145 for (settings::ParallelPortsList::const_iterator
9146 it = data.llParallelPorts.begin();
9147 it != data.llParallelPorts.end();
9148 ++it)
9149 {
9150 const settings::ParallelPort &p = *it;
9151
9152 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9153 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9154 if (FAILED(rc)) return rc;
9155 }
9156
9157 /* AudioAdapter */
9158 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9159 if (FAILED(rc)) return rc;
9160
9161 /* storage controllers */
9162 rc = i_loadStorageControllers(data.storage,
9163 puuidRegistry,
9164 puuidSnapshot);
9165 if (FAILED(rc)) return rc;
9166
9167 /* Shared folders */
9168 for (settings::SharedFoldersList::const_iterator
9169 it = data.llSharedFolders.begin();
9170 it != data.llSharedFolders.end();
9171 ++it)
9172 {
9173 const settings::SharedFolder &sf = *it;
9174
9175 ComObjPtr<SharedFolder> sharedFolder;
9176 /* Check for double entries. Not allowed! */
9177 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9178 if (SUCCEEDED(rc))
9179 return setError(VBOX_E_OBJECT_IN_USE,
9180 tr("Shared folder named '%s' already exists"),
9181 sf.strName.c_str());
9182
9183 /* Create the new shared folder. Don't break on error. This will be
9184 * reported when the machine starts. */
9185 sharedFolder.createObject();
9186 rc = sharedFolder->init(i_getMachine(),
9187 sf.strName,
9188 sf.strHostPath,
9189 RT_BOOL(sf.fWritable),
9190 RT_BOOL(sf.fAutoMount),
9191 false /* fFailOnError */);
9192 if (FAILED(rc)) return rc;
9193 mHWData->mSharedFolders.push_back(sharedFolder);
9194 }
9195
9196 // Clipboard
9197 mHWData->mClipboardMode = data.clipboardMode;
9198
9199 // drag'n'drop
9200 mHWData->mDnDMode = data.dndMode;
9201
9202 // guest settings
9203 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9204
9205 // IO settings
9206 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9207 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9208
9209 // Host PCI devices
9210 for (settings::HostPCIDeviceAttachmentList::const_iterator
9211 it = data.pciAttachments.begin();
9212 it != data.pciAttachments.end();
9213 ++it)
9214 {
9215 const settings::HostPCIDeviceAttachment &hpda = *it;
9216 ComObjPtr<PCIDeviceAttachment> pda;
9217
9218 pda.createObject();
9219 pda->i_loadSettings(this, hpda);
9220 mHWData->mPCIDeviceAssignments.push_back(pda);
9221 }
9222
9223 /*
9224 * (The following isn't really real hardware, but it lives in HWData
9225 * for reasons of convenience.)
9226 */
9227
9228#ifdef VBOX_WITH_GUEST_PROPS
9229 /* Guest properties (optional) */
9230
9231 /* Only load transient guest properties for configs which have saved
9232 * state, because there shouldn't be any for powered off VMs. The same
9233 * logic applies for snapshots, as offline snapshots shouldn't have
9234 * any such properties. They confuse the code in various places.
9235 * Note: can't rely on the machine state, as it isn't set yet. */
9236 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9237 /* apologies for the hacky unconst() usage, but this needs hacking
9238 * actually inconsistent settings into consistency, otherwise there
9239 * will be some corner cases where the inconsistency survives
9240 * surprisingly long without getting fixed, especially for snapshots
9241 * as there are no config changes. */
9242 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9243 for (settings::GuestPropertiesList::iterator
9244 it = llGuestProperties.begin();
9245 it != llGuestProperties.end();
9246 /*nothing*/)
9247 {
9248 const settings::GuestProperty &prop = *it;
9249 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9250 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9251 if ( fSkipTransientGuestProperties
9252 && ( fFlags & GUEST_PROP_F_TRANSIENT
9253 || fFlags & GUEST_PROP_F_TRANSRESET))
9254 {
9255 it = llGuestProperties.erase(it);
9256 continue;
9257 }
9258 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9259 mHWData->mGuestProperties[prop.strName] = property;
9260 ++it;
9261 }
9262#endif /* VBOX_WITH_GUEST_PROPS defined */
9263
9264 rc = i_loadDebugging(pDbg);
9265 if (FAILED(rc))
9266 return rc;
9267
9268 mHWData->mAutostart = *pAutostart;
9269
9270 /* default frontend */
9271 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9272 }
9273 catch (std::bad_alloc &)
9274 {
9275 return E_OUTOFMEMORY;
9276 }
9277
9278 AssertComRC(rc);
9279 return rc;
9280}
9281
9282/**
9283 * Called from i_loadHardware() to load the debugging settings of the
9284 * machine.
9285 *
9286 * @param pDbg Pointer to the settings.
9287 */
9288HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9289{
9290 mHWData->mDebugging = *pDbg;
9291 /* no more processing currently required, this will probably change. */
9292 return S_OK;
9293}
9294
9295/**
9296 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9297 *
9298 * @param data storage settings.
9299 * @param puuidRegistry media registry ID to set media to or NULL;
9300 * see Machine::i_loadMachineDataFromSettings()
9301 * @param puuidSnapshot snapshot ID
9302 * @return
9303 */
9304HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9305 const Guid *puuidRegistry,
9306 const Guid *puuidSnapshot)
9307{
9308 AssertReturn(!i_isSessionMachine(), E_FAIL);
9309
9310 HRESULT rc = S_OK;
9311
9312 for (settings::StorageControllersList::const_iterator
9313 it = data.llStorageControllers.begin();
9314 it != data.llStorageControllers.end();
9315 ++it)
9316 {
9317 const settings::StorageController &ctlData = *it;
9318
9319 ComObjPtr<StorageController> pCtl;
9320 /* Try to find one with the name first. */
9321 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9322 if (SUCCEEDED(rc))
9323 return setError(VBOX_E_OBJECT_IN_USE,
9324 tr("Storage controller named '%s' already exists"),
9325 ctlData.strName.c_str());
9326
9327 pCtl.createObject();
9328 rc = pCtl->init(this,
9329 ctlData.strName,
9330 ctlData.storageBus,
9331 ctlData.ulInstance,
9332 ctlData.fBootable);
9333 if (FAILED(rc)) return rc;
9334
9335 mStorageControllers->push_back(pCtl);
9336
9337 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9338 if (FAILED(rc)) return rc;
9339
9340 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9341 if (FAILED(rc)) return rc;
9342
9343 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9344 if (FAILED(rc)) return rc;
9345
9346 /* Load the attached devices now. */
9347 rc = i_loadStorageDevices(pCtl,
9348 ctlData,
9349 puuidRegistry,
9350 puuidSnapshot);
9351 if (FAILED(rc)) return rc;
9352 }
9353
9354 return S_OK;
9355}
9356
9357/**
9358 * Called from i_loadStorageControllers for a controller's devices.
9359 *
9360 * @param aStorageController
9361 * @param data
9362 * @param puuidRegistry media registry ID to set media to or NULL; see
9363 * Machine::i_loadMachineDataFromSettings()
9364 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9365 * @return
9366 */
9367HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9368 const settings::StorageController &data,
9369 const Guid *puuidRegistry,
9370 const Guid *puuidSnapshot)
9371{
9372 HRESULT rc = S_OK;
9373
9374 /* paranoia: detect duplicate attachments */
9375 for (settings::AttachedDevicesList::const_iterator
9376 it = data.llAttachedDevices.begin();
9377 it != data.llAttachedDevices.end();
9378 ++it)
9379 {
9380 const settings::AttachedDevice &ad = *it;
9381
9382 for (settings::AttachedDevicesList::const_iterator it2 = it;
9383 it2 != data.llAttachedDevices.end();
9384 ++it2)
9385 {
9386 if (it == it2)
9387 continue;
9388
9389 const settings::AttachedDevice &ad2 = *it2;
9390
9391 if ( ad.lPort == ad2.lPort
9392 && ad.lDevice == ad2.lDevice)
9393 {
9394 return setError(E_FAIL,
9395 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9396 aStorageController->i_getName().c_str(),
9397 ad.lPort,
9398 ad.lDevice,
9399 mUserData->s.strName.c_str());
9400 }
9401 }
9402 }
9403
9404 for (settings::AttachedDevicesList::const_iterator
9405 it = data.llAttachedDevices.begin();
9406 it != data.llAttachedDevices.end();
9407 ++it)
9408 {
9409 const settings::AttachedDevice &dev = *it;
9410 ComObjPtr<Medium> medium;
9411
9412 switch (dev.deviceType)
9413 {
9414 case DeviceType_Floppy:
9415 case DeviceType_DVD:
9416 if (dev.strHostDriveSrc.isNotEmpty())
9417 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9418 false /* fRefresh */, medium);
9419 else
9420 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9421 dev.uuid,
9422 false /* fRefresh */,
9423 false /* aSetError */,
9424 medium);
9425 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9426 // This is not an error. The host drive or UUID might have vanished, so just go
9427 // ahead without this removeable medium attachment
9428 rc = S_OK;
9429 break;
9430
9431 case DeviceType_HardDisk:
9432 {
9433 /* find a hard disk by UUID */
9434 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9435 if (FAILED(rc))
9436 {
9437 if (i_isSnapshotMachine())
9438 {
9439 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9440 // so the user knows that the bad disk is in a snapshot somewhere
9441 com::ErrorInfo info;
9442 return setError(E_FAIL,
9443 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9444 puuidSnapshot->raw(),
9445 info.getText().raw());
9446 }
9447 else
9448 return rc;
9449 }
9450
9451 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9452
9453 if (medium->i_getType() == MediumType_Immutable)
9454 {
9455 if (i_isSnapshotMachine())
9456 return setError(E_FAIL,
9457 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9458 "of the virtual machine '%s' ('%s')"),
9459 medium->i_getLocationFull().c_str(),
9460 dev.uuid.raw(),
9461 puuidSnapshot->raw(),
9462 mUserData->s.strName.c_str(),
9463 mData->m_strConfigFileFull.c_str());
9464
9465 return setError(E_FAIL,
9466 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9467 medium->i_getLocationFull().c_str(),
9468 dev.uuid.raw(),
9469 mUserData->s.strName.c_str(),
9470 mData->m_strConfigFileFull.c_str());
9471 }
9472
9473 if (medium->i_getType() == MediumType_MultiAttach)
9474 {
9475 if (i_isSnapshotMachine())
9476 return setError(E_FAIL,
9477 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9478 "of the virtual machine '%s' ('%s')"),
9479 medium->i_getLocationFull().c_str(),
9480 dev.uuid.raw(),
9481 puuidSnapshot->raw(),
9482 mUserData->s.strName.c_str(),
9483 mData->m_strConfigFileFull.c_str());
9484
9485 return setError(E_FAIL,
9486 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9487 medium->i_getLocationFull().c_str(),
9488 dev.uuid.raw(),
9489 mUserData->s.strName.c_str(),
9490 mData->m_strConfigFileFull.c_str());
9491 }
9492
9493 if ( !i_isSnapshotMachine()
9494 && medium->i_getChildren().size() != 0
9495 )
9496 return setError(E_FAIL,
9497 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9498 "because it has %d differencing child hard disks"),
9499 medium->i_getLocationFull().c_str(),
9500 dev.uuid.raw(),
9501 mUserData->s.strName.c_str(),
9502 mData->m_strConfigFileFull.c_str(),
9503 medium->i_getChildren().size());
9504
9505 if (i_findAttachment(*mMediumAttachments.data(),
9506 medium))
9507 return setError(E_FAIL,
9508 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9509 medium->i_getLocationFull().c_str(),
9510 dev.uuid.raw(),
9511 mUserData->s.strName.c_str(),
9512 mData->m_strConfigFileFull.c_str());
9513
9514 break;
9515 }
9516
9517 default:
9518 return setError(E_FAIL,
9519 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9520 medium->i_getLocationFull().c_str(),
9521 mUserData->s.strName.c_str(),
9522 mData->m_strConfigFileFull.c_str());
9523 }
9524
9525 if (FAILED(rc))
9526 break;
9527
9528 /* Bandwidth groups are loaded at this point. */
9529 ComObjPtr<BandwidthGroup> pBwGroup;
9530
9531 if (!dev.strBwGroup.isEmpty())
9532 {
9533 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9534 if (FAILED(rc))
9535 return setError(E_FAIL,
9536 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9537 medium->i_getLocationFull().c_str(),
9538 dev.strBwGroup.c_str(),
9539 mUserData->s.strName.c_str(),
9540 mData->m_strConfigFileFull.c_str());
9541 pBwGroup->i_reference();
9542 }
9543
9544 const Utf8Str controllerName = aStorageController->i_getName();
9545 ComObjPtr<MediumAttachment> pAttachment;
9546 pAttachment.createObject();
9547 rc = pAttachment->init(this,
9548 medium,
9549 controllerName,
9550 dev.lPort,
9551 dev.lDevice,
9552 dev.deviceType,
9553 false,
9554 dev.fPassThrough,
9555 dev.fTempEject,
9556 dev.fNonRotational,
9557 dev.fDiscard,
9558 dev.fHotPluggable,
9559 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9560 if (FAILED(rc)) break;
9561
9562 /* associate the medium with this machine and snapshot */
9563 if (!medium.isNull())
9564 {
9565 AutoCaller medCaller(medium);
9566 if (FAILED(medCaller.rc())) return medCaller.rc();
9567 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9568
9569 if (i_isSnapshotMachine())
9570 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9571 else
9572 rc = medium->i_addBackReference(mData->mUuid);
9573 /* If the medium->addBackReference fails it sets an appropriate
9574 * error message, so no need to do any guesswork here. */
9575
9576 if (puuidRegistry)
9577 // caller wants registry ID to be set on all attached media (OVF import case)
9578 medium->i_addRegistry(*puuidRegistry);
9579 }
9580
9581 if (FAILED(rc))
9582 break;
9583
9584 /* back up mMediumAttachments to let registeredInit() properly rollback
9585 * on failure (= limited accessibility) */
9586 i_setModified(IsModified_Storage);
9587 mMediumAttachments.backup();
9588 mMediumAttachments->push_back(pAttachment);
9589 }
9590
9591 return rc;
9592}
9593
9594/**
9595 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9596 *
9597 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9598 * @param aSnapshot where to return the found snapshot
9599 * @param aSetError true to set extended error info on failure
9600 */
9601HRESULT Machine::i_findSnapshotById(const Guid &aId,
9602 ComObjPtr<Snapshot> &aSnapshot,
9603 bool aSetError /* = false */)
9604{
9605 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9606
9607 if (!mData->mFirstSnapshot)
9608 {
9609 if (aSetError)
9610 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9611 return E_FAIL;
9612 }
9613
9614 if (aId.isZero())
9615 aSnapshot = mData->mFirstSnapshot;
9616 else
9617 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9618
9619 if (!aSnapshot)
9620 {
9621 if (aSetError)
9622 return setError(E_FAIL,
9623 tr("Could not find a snapshot with UUID {%s}"),
9624 aId.toString().c_str());
9625 return E_FAIL;
9626 }
9627
9628 return S_OK;
9629}
9630
9631/**
9632 * Returns the snapshot with the given name or fails of no such snapshot.
9633 *
9634 * @param strName snapshot name to find
9635 * @param aSnapshot where to return the found snapshot
9636 * @param aSetError true to set extended error info on failure
9637 */
9638HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9639 ComObjPtr<Snapshot> &aSnapshot,
9640 bool aSetError /* = false */)
9641{
9642 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9643
9644 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9645
9646 if (!mData->mFirstSnapshot)
9647 {
9648 if (aSetError)
9649 return setError(VBOX_E_OBJECT_NOT_FOUND,
9650 tr("This machine does not have any snapshots"));
9651 return VBOX_E_OBJECT_NOT_FOUND;
9652 }
9653
9654 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9655
9656 if (!aSnapshot)
9657 {
9658 if (aSetError)
9659 return setError(VBOX_E_OBJECT_NOT_FOUND,
9660 tr("Could not find a snapshot named '%s'"), strName.c_str());
9661 return VBOX_E_OBJECT_NOT_FOUND;
9662 }
9663
9664 return S_OK;
9665}
9666
9667/**
9668 * Returns a storage controller object with the given name.
9669 *
9670 * @param aName storage controller name to find
9671 * @param aStorageController where to return the found storage controller
9672 * @param aSetError true to set extended error info on failure
9673 */
9674HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9675 ComObjPtr<StorageController> &aStorageController,
9676 bool aSetError /* = false */)
9677{
9678 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9679
9680 for (StorageControllerList::const_iterator
9681 it = mStorageControllers->begin();
9682 it != mStorageControllers->end();
9683 ++it)
9684 {
9685 if ((*it)->i_getName() == aName)
9686 {
9687 aStorageController = (*it);
9688 return S_OK;
9689 }
9690 }
9691
9692 if (aSetError)
9693 return setError(VBOX_E_OBJECT_NOT_FOUND,
9694 tr("Could not find a storage controller named '%s'"),
9695 aName.c_str());
9696 return VBOX_E_OBJECT_NOT_FOUND;
9697}
9698
9699/**
9700 * Returns a USB controller object with the given name.
9701 *
9702 * @param aName USB controller name to find
9703 * @param aUSBController where to return the found USB controller
9704 * @param aSetError true to set extended error info on failure
9705 */
9706HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9707 ComObjPtr<USBController> &aUSBController,
9708 bool aSetError /* = false */)
9709{
9710 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9711
9712 for (USBControllerList::const_iterator
9713 it = mUSBControllers->begin();
9714 it != mUSBControllers->end();
9715 ++it)
9716 {
9717 if ((*it)->i_getName() == aName)
9718 {
9719 aUSBController = (*it);
9720 return S_OK;
9721 }
9722 }
9723
9724 if (aSetError)
9725 return setError(VBOX_E_OBJECT_NOT_FOUND,
9726 tr("Could not find a storage controller named '%s'"),
9727 aName.c_str());
9728 return VBOX_E_OBJECT_NOT_FOUND;
9729}
9730
9731/**
9732 * Returns the number of USB controller instance of the given type.
9733 *
9734 * @param enmType USB controller type.
9735 */
9736ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9737{
9738 ULONG cCtrls = 0;
9739
9740 for (USBControllerList::const_iterator
9741 it = mUSBControllers->begin();
9742 it != mUSBControllers->end();
9743 ++it)
9744 {
9745 if ((*it)->i_getControllerType() == enmType)
9746 cCtrls++;
9747 }
9748
9749 return cCtrls;
9750}
9751
9752HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9753 MediumAttachmentList &atts)
9754{
9755 AutoCaller autoCaller(this);
9756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9757
9758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9759
9760 for (MediumAttachmentList::const_iterator
9761 it = mMediumAttachments->begin();
9762 it != mMediumAttachments->end();
9763 ++it)
9764 {
9765 const ComObjPtr<MediumAttachment> &pAtt = *it;
9766 // should never happen, but deal with NULL pointers in the list.
9767 AssertContinue(!pAtt.isNull());
9768
9769 // getControllerName() needs caller+read lock
9770 AutoCaller autoAttCaller(pAtt);
9771 if (FAILED(autoAttCaller.rc()))
9772 {
9773 atts.clear();
9774 return autoAttCaller.rc();
9775 }
9776 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9777
9778 if (pAtt->i_getControllerName() == aName)
9779 atts.push_back(pAtt);
9780 }
9781
9782 return S_OK;
9783}
9784
9785
9786/**
9787 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9788 * file if the machine name was changed and about creating a new settings file
9789 * if this is a new machine.
9790 *
9791 * @note Must be never called directly but only from #saveSettings().
9792 */
9793HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9794{
9795 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9796
9797 HRESULT rc = S_OK;
9798
9799 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9800
9801 /// @todo need to handle primary group change, too
9802
9803 /* attempt to rename the settings file if machine name is changed */
9804 if ( mUserData->s.fNameSync
9805 && mUserData.isBackedUp()
9806 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9807 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9808 )
9809 {
9810 bool dirRenamed = false;
9811 bool fileRenamed = false;
9812
9813 Utf8Str configFile, newConfigFile;
9814 Utf8Str configFilePrev, newConfigFilePrev;
9815 Utf8Str configDir, newConfigDir;
9816
9817 do
9818 {
9819 int vrc = VINF_SUCCESS;
9820
9821 Utf8Str name = mUserData.backedUpData()->s.strName;
9822 Utf8Str newName = mUserData->s.strName;
9823 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9824 if (group == "/")
9825 group.setNull();
9826 Utf8Str newGroup = mUserData->s.llGroups.front();
9827 if (newGroup == "/")
9828 newGroup.setNull();
9829
9830 configFile = mData->m_strConfigFileFull;
9831
9832 /* first, rename the directory if it matches the group and machine name */
9833 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9834 group.c_str(), RTPATH_DELIMITER, name.c_str());
9835 /** @todo hack, make somehow use of ComposeMachineFilename */
9836 if (mUserData->s.fDirectoryIncludesUUID)
9837 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9838 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9839 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9840 /** @todo hack, make somehow use of ComposeMachineFilename */
9841 if (mUserData->s.fDirectoryIncludesUUID)
9842 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9843 configDir = configFile;
9844 configDir.stripFilename();
9845 newConfigDir = configDir;
9846 if ( configDir.length() >= groupPlusName.length()
9847 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9848 groupPlusName.c_str()))
9849 {
9850 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9851 Utf8Str newConfigBaseDir(newConfigDir);
9852 newConfigDir.append(newGroupPlusName);
9853 /* consistency: use \ if appropriate on the platform */
9854 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9855 /* new dir and old dir cannot be equal here because of 'if'
9856 * above and because name != newName */
9857 Assert(configDir != newConfigDir);
9858 if (!fSettingsFileIsNew)
9859 {
9860 /* perform real rename only if the machine is not new */
9861 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9862 if ( vrc == VERR_FILE_NOT_FOUND
9863 || vrc == VERR_PATH_NOT_FOUND)
9864 {
9865 /* create the parent directory, then retry renaming */
9866 Utf8Str parent(newConfigDir);
9867 parent.stripFilename();
9868 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9869 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9870 }
9871 if (RT_FAILURE(vrc))
9872 {
9873 rc = setError(E_FAIL,
9874 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9875 configDir.c_str(),
9876 newConfigDir.c_str(),
9877 vrc);
9878 break;
9879 }
9880 /* delete subdirectories which are no longer needed */
9881 Utf8Str dir(configDir);
9882 dir.stripFilename();
9883 while (dir != newConfigBaseDir && dir != ".")
9884 {
9885 vrc = RTDirRemove(dir.c_str());
9886 if (RT_FAILURE(vrc))
9887 break;
9888 dir.stripFilename();
9889 }
9890 dirRenamed = true;
9891 }
9892 }
9893
9894 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9895 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9896
9897 /* then try to rename the settings file itself */
9898 if (newConfigFile != configFile)
9899 {
9900 /* get the path to old settings file in renamed directory */
9901 configFile = Utf8StrFmt("%s%c%s",
9902 newConfigDir.c_str(),
9903 RTPATH_DELIMITER,
9904 RTPathFilename(configFile.c_str()));
9905 if (!fSettingsFileIsNew)
9906 {
9907 /* perform real rename only if the machine is not new */
9908 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9909 if (RT_FAILURE(vrc))
9910 {
9911 rc = setError(E_FAIL,
9912 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9913 configFile.c_str(),
9914 newConfigFile.c_str(),
9915 vrc);
9916 break;
9917 }
9918 fileRenamed = true;
9919 configFilePrev = configFile;
9920 configFilePrev += "-prev";
9921 newConfigFilePrev = newConfigFile;
9922 newConfigFilePrev += "-prev";
9923 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9924 }
9925 }
9926
9927 // update m_strConfigFileFull amd mConfigFile
9928 mData->m_strConfigFileFull = newConfigFile;
9929 // compute the relative path too
9930 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9931
9932 // store the old and new so that VirtualBox::i_saveSettings() can update
9933 // the media registry
9934 if ( mData->mRegistered
9935 && (configDir != newConfigDir || configFile != newConfigFile))
9936 {
9937 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9938
9939 if (pfNeedsGlobalSaveSettings)
9940 *pfNeedsGlobalSaveSettings = true;
9941 }
9942
9943 // in the saved state file path, replace the old directory with the new directory
9944 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9945 {
9946 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9947 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9948 }
9949
9950 // and do the same thing for the saved state file paths of all the online snapshots
9951 if (mData->mFirstSnapshot)
9952 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9953 newConfigDir.c_str());
9954 }
9955 while (0);
9956
9957 if (FAILED(rc))
9958 {
9959 /* silently try to rename everything back */
9960 if (fileRenamed)
9961 {
9962 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9963 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9964 }
9965 if (dirRenamed)
9966 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9967 }
9968
9969 if (FAILED(rc)) return rc;
9970 }
9971
9972 if (fSettingsFileIsNew)
9973 {
9974 /* create a virgin config file */
9975 int vrc = VINF_SUCCESS;
9976
9977 /* ensure the settings directory exists */
9978 Utf8Str path(mData->m_strConfigFileFull);
9979 path.stripFilename();
9980 if (!RTDirExists(path.c_str()))
9981 {
9982 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9983 if (RT_FAILURE(vrc))
9984 {
9985 return setError(E_FAIL,
9986 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9987 path.c_str(),
9988 vrc);
9989 }
9990 }
9991
9992 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9993 path = Utf8Str(mData->m_strConfigFileFull);
9994 RTFILE f = NIL_RTFILE;
9995 vrc = RTFileOpen(&f, path.c_str(),
9996 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9997 if (RT_FAILURE(vrc))
9998 return setError(E_FAIL,
9999 tr("Could not create the settings file '%s' (%Rrc)"),
10000 path.c_str(),
10001 vrc);
10002 RTFileClose(f);
10003 }
10004
10005 return rc;
10006}
10007
10008/**
10009 * Saves and commits machine data, user data and hardware data.
10010 *
10011 * Note that on failure, the data remains uncommitted.
10012 *
10013 * @a aFlags may combine the following flags:
10014 *
10015 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10016 * Used when saving settings after an operation that makes them 100%
10017 * correspond to the settings from the current snapshot.
10018 * - SaveS_Force: settings will be saved without doing a deep compare of the
10019 * settings structures. This is used when this is called because snapshots
10020 * have changed to avoid the overhead of the deep compare.
10021 *
10022 * @note Must be called from under this object's write lock. Locks children for
10023 * writing.
10024 *
10025 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10026 * initialized to false and that will be set to true by this function if
10027 * the caller must invoke VirtualBox::i_saveSettings() because the global
10028 * settings have changed. This will happen if a machine rename has been
10029 * saved and the global machine and media registries will therefore need
10030 * updating.
10031 * @param aFlags Flags.
10032 */
10033HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10034 int aFlags /*= 0*/)
10035{
10036 LogFlowThisFuncEnter();
10037
10038 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10039
10040 /* make sure child objects are unable to modify the settings while we are
10041 * saving them */
10042 i_ensureNoStateDependencies();
10043
10044 AssertReturn(!i_isSnapshotMachine(),
10045 E_FAIL);
10046
10047 HRESULT rc = S_OK;
10048 bool fNeedsWrite = false;
10049
10050 /* First, prepare to save settings. It will care about renaming the
10051 * settings directory and file if the machine name was changed and about
10052 * creating a new settings file if this is a new machine. */
10053 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10054 if (FAILED(rc)) return rc;
10055
10056 // keep a pointer to the current settings structures
10057 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10058 settings::MachineConfigFile *pNewConfig = NULL;
10059
10060 try
10061 {
10062 // make a fresh one to have everyone write stuff into
10063 pNewConfig = new settings::MachineConfigFile(NULL);
10064 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10065
10066 // now go and copy all the settings data from COM to the settings structures
10067 // (this calls i_saveSettings() on all the COM objects in the machine)
10068 i_copyMachineDataToSettings(*pNewConfig);
10069
10070 if (aFlags & SaveS_ResetCurStateModified)
10071 {
10072 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10073 mData->mCurrentStateModified = FALSE;
10074 fNeedsWrite = true; // always, no need to compare
10075 }
10076 else if (aFlags & SaveS_Force)
10077 {
10078 fNeedsWrite = true; // always, no need to compare
10079 }
10080 else
10081 {
10082 if (!mData->mCurrentStateModified)
10083 {
10084 // do a deep compare of the settings that we just saved with the settings
10085 // previously stored in the config file; this invokes MachineConfigFile::operator==
10086 // which does a deep compare of all the settings, which is expensive but less expensive
10087 // than writing out XML in vain
10088 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10089
10090 // could still be modified if any settings changed
10091 mData->mCurrentStateModified = fAnySettingsChanged;
10092
10093 fNeedsWrite = fAnySettingsChanged;
10094 }
10095 else
10096 fNeedsWrite = true;
10097 }
10098
10099 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10100
10101 if (fNeedsWrite)
10102 // now spit it all out!
10103 pNewConfig->write(mData->m_strConfigFileFull);
10104
10105 mData->pMachineConfigFile = pNewConfig;
10106 delete pOldConfig;
10107 i_commit();
10108
10109 // after saving settings, we are no longer different from the XML on disk
10110 mData->flModifications = 0;
10111 }
10112 catch (HRESULT err)
10113 {
10114 // we assume that error info is set by the thrower
10115 rc = err;
10116
10117 // restore old config
10118 delete pNewConfig;
10119 mData->pMachineConfigFile = pOldConfig;
10120 }
10121 catch (...)
10122 {
10123 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10124 }
10125
10126 if (fNeedsWrite)
10127 {
10128 /* Fire the data change event, even on failure (since we've already
10129 * committed all data). This is done only for SessionMachines because
10130 * mutable Machine instances are always not registered (i.e. private
10131 * to the client process that creates them) and thus don't need to
10132 * inform callbacks. */
10133 if (i_isSessionMachine())
10134 mParent->i_onMachineDataChange(mData->mUuid);
10135 }
10136
10137 LogFlowThisFunc(("rc=%08X\n", rc));
10138 LogFlowThisFuncLeave();
10139 return rc;
10140}
10141
10142/**
10143 * Implementation for saving the machine settings into the given
10144 * settings::MachineConfigFile instance. This copies machine extradata
10145 * from the previous machine config file in the instance data, if any.
10146 *
10147 * This gets called from two locations:
10148 *
10149 * -- Machine::i_saveSettings(), during the regular XML writing;
10150 *
10151 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10152 * exported to OVF and we write the VirtualBox proprietary XML
10153 * into a <vbox:Machine> tag.
10154 *
10155 * This routine fills all the fields in there, including snapshots, *except*
10156 * for the following:
10157 *
10158 * -- fCurrentStateModified. There is some special logic associated with that.
10159 *
10160 * The caller can then call MachineConfigFile::write() or do something else
10161 * with it.
10162 *
10163 * Caller must hold the machine lock!
10164 *
10165 * This throws XML errors and HRESULT, so the caller must have a catch block!
10166 */
10167void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10168{
10169 // deep copy extradata, being extra careful with self assignment (the STL
10170 // map assignment on Mac OS X clang based Xcode isn't checking)
10171 if (&config != mData->pMachineConfigFile)
10172 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10173
10174 config.uuid = mData->mUuid;
10175
10176 // copy name, description, OS type, teleport, UTC etc.
10177 config.machineUserData = mUserData->s;
10178
10179 if ( mData->mMachineState == MachineState_Saved
10180 || mData->mMachineState == MachineState_Restoring
10181 // when doing certain snapshot operations we may or may not have
10182 // a saved state in the current state, so keep everything as is
10183 || ( ( mData->mMachineState == MachineState_Snapshotting
10184 || mData->mMachineState == MachineState_DeletingSnapshot
10185 || mData->mMachineState == MachineState_RestoringSnapshot)
10186 && (!mSSData->strStateFilePath.isEmpty())
10187 )
10188 )
10189 {
10190 Assert(!mSSData->strStateFilePath.isEmpty());
10191 /* try to make the file name relative to the settings file dir */
10192 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10193 }
10194 else
10195 {
10196 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10197 config.strStateFile.setNull();
10198 }
10199
10200 if (mData->mCurrentSnapshot)
10201 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10202 else
10203 config.uuidCurrentSnapshot.clear();
10204
10205 config.timeLastStateChange = mData->mLastStateChange;
10206 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10207 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10208
10209 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10210 if (FAILED(rc)) throw rc;
10211
10212 // save machine's media registry if this is VirtualBox 4.0 or later
10213 if (config.canHaveOwnMediaRegistry())
10214 {
10215 // determine machine folder
10216 Utf8Str strMachineFolder = i_getSettingsFileFull();
10217 strMachineFolder.stripFilename();
10218 mParent->i_saveMediaRegistry(config.mediaRegistry,
10219 i_getId(), // only media with registry ID == machine UUID
10220 strMachineFolder);
10221 // this throws HRESULT
10222 }
10223
10224 // save snapshots
10225 rc = i_saveAllSnapshots(config);
10226 if (FAILED(rc)) throw rc;
10227}
10228
10229/**
10230 * Saves all snapshots of the machine into the given machine config file. Called
10231 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10232 * @param config
10233 * @return
10234 */
10235HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10236{
10237 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10238
10239 HRESULT rc = S_OK;
10240
10241 try
10242 {
10243 config.llFirstSnapshot.clear();
10244
10245 if (mData->mFirstSnapshot)
10246 {
10247 // the settings use a list for "the first snapshot"
10248 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10249
10250 // get reference to the snapshot on the list and work on that
10251 // element straight in the list to avoid excessive copying later
10252 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10253 if (FAILED(rc)) throw rc;
10254 }
10255
10256// if (mType == IsSessionMachine)
10257// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10258
10259 }
10260 catch (HRESULT err)
10261 {
10262 /* we assume that error info is set by the thrower */
10263 rc = err;
10264 }
10265 catch (...)
10266 {
10267 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10268 }
10269
10270 return rc;
10271}
10272
10273/**
10274 * Saves the VM hardware configuration. It is assumed that the
10275 * given node is empty.
10276 *
10277 * @param data Reference to the settings object for the hardware config.
10278 * @param pDbg Pointer to the settings object for the debugging config
10279 * which happens to live in mHWData.
10280 * @param pAutostart Pointer to the settings object for the autostart config
10281 * which happens to live in mHWData.
10282 */
10283HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10284 settings::Autostart *pAutostart)
10285{
10286 HRESULT rc = S_OK;
10287
10288 try
10289 {
10290 /* The hardware version attribute (optional).
10291 Automatically upgrade from 1 to current default hardware version
10292 when there is no saved state. (ugly!) */
10293 if ( mHWData->mHWVersion == "1"
10294 && mSSData->strStateFilePath.isEmpty()
10295 )
10296 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10297
10298 data.strVersion = mHWData->mHWVersion;
10299 data.uuid = mHWData->mHardwareUUID;
10300
10301 // CPU
10302 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10303 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10304 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10305 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10306 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10307 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10308 data.fPAE = !!mHWData->mPAEEnabled;
10309 data.enmLongMode = mHWData->mLongMode;
10310 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10311 data.fAPIC = !!mHWData->mAPIC;
10312 data.fX2APIC = !!mHWData->mX2APIC;
10313 data.cCPUs = mHWData->mCPUCount;
10314 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10315 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10316 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10317 data.strCpuProfile = mHWData->mCpuProfile;
10318
10319 data.llCpus.clear();
10320 if (data.fCpuHotPlug)
10321 {
10322 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10323 {
10324 if (mHWData->mCPUAttached[idx])
10325 {
10326 settings::Cpu cpu;
10327 cpu.ulId = idx;
10328 data.llCpus.push_back(cpu);
10329 }
10330 }
10331 }
10332
10333 /* Standard and Extended CPUID leafs. */
10334 data.llCpuIdLeafs.clear();
10335 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10336
10337 // memory
10338 data.ulMemorySizeMB = mHWData->mMemorySize;
10339 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10340
10341 // firmware
10342 data.firmwareType = mHWData->mFirmwareType;
10343
10344 // HID
10345 data.pointingHIDType = mHWData->mPointingHIDType;
10346 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10347
10348 // chipset
10349 data.chipsetType = mHWData->mChipsetType;
10350
10351 // paravirt
10352 data.paravirtProvider = mHWData->mParavirtProvider;
10353 data.strParavirtDebug = mHWData->mParavirtDebug;
10354
10355 // emulated USB card reader
10356 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10357
10358 // HPET
10359 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10360
10361 // boot order
10362 data.mapBootOrder.clear();
10363 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10364 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10365
10366 // display
10367 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10368 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10369 data.cMonitors = mHWData->mMonitorCount;
10370 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10371 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10372 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10373 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10374 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10375 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10376 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10377 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10378 {
10379 if (mHWData->maVideoCaptureScreens[i])
10380 ASMBitSet(&data.u64VideoCaptureScreens, i);
10381 else
10382 ASMBitClear(&data.u64VideoCaptureScreens, i);
10383 }
10384 /* store relative video capture file if possible */
10385 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10386 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10387
10388 /* VRDEServer settings (optional) */
10389 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10390 if (FAILED(rc)) throw rc;
10391
10392 /* BIOS (required) */
10393 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10394 if (FAILED(rc)) throw rc;
10395
10396 /* USB Controller (required) */
10397 data.usbSettings.llUSBControllers.clear();
10398 for (USBControllerList::const_iterator
10399 it = mUSBControllers->begin();
10400 it != mUSBControllers->end();
10401 ++it)
10402 {
10403 ComObjPtr<USBController> ctrl = *it;
10404 settings::USBController settingsCtrl;
10405
10406 settingsCtrl.strName = ctrl->i_getName();
10407 settingsCtrl.enmType = ctrl->i_getControllerType();
10408
10409 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10410 }
10411
10412 /* USB device filters (required) */
10413 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10414 if (FAILED(rc)) throw rc;
10415
10416 /* Network adapters (required) */
10417 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10418 data.llNetworkAdapters.clear();
10419 /* Write out only the nominal number of network adapters for this
10420 * chipset type. Since Machine::commit() hasn't been called there
10421 * may be extra NIC settings in the vector. */
10422 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10423 {
10424 settings::NetworkAdapter nic;
10425 nic.ulSlot = (uint32_t)slot;
10426 /* paranoia check... must not be NULL, but must not crash either. */
10427 if (mNetworkAdapters[slot])
10428 {
10429 if (mNetworkAdapters[slot]->i_hasDefaults())
10430 continue;
10431
10432 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10433 if (FAILED(rc)) throw rc;
10434
10435 data.llNetworkAdapters.push_back(nic);
10436 }
10437 }
10438
10439 /* Serial ports */
10440 data.llSerialPorts.clear();
10441 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10442 {
10443 if (mSerialPorts[slot]->i_hasDefaults())
10444 continue;
10445
10446 settings::SerialPort s;
10447 s.ulSlot = slot;
10448 rc = mSerialPorts[slot]->i_saveSettings(s);
10449 if (FAILED(rc)) return rc;
10450
10451 data.llSerialPorts.push_back(s);
10452 }
10453
10454 /* Parallel ports */
10455 data.llParallelPorts.clear();
10456 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10457 {
10458 if (mParallelPorts[slot]->i_hasDefaults())
10459 continue;
10460
10461 settings::ParallelPort p;
10462 p.ulSlot = slot;
10463 rc = mParallelPorts[slot]->i_saveSettings(p);
10464 if (FAILED(rc)) return rc;
10465
10466 data.llParallelPorts.push_back(p);
10467 }
10468
10469 /* Audio adapter */
10470 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10471 if (FAILED(rc)) return rc;
10472
10473 rc = i_saveStorageControllers(data.storage);
10474 if (FAILED(rc)) return rc;
10475
10476 /* Shared folders */
10477 data.llSharedFolders.clear();
10478 for (HWData::SharedFolderList::const_iterator
10479 it = mHWData->mSharedFolders.begin();
10480 it != mHWData->mSharedFolders.end();
10481 ++it)
10482 {
10483 SharedFolder *pSF = *it;
10484 AutoCaller sfCaller(pSF);
10485 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10486 settings::SharedFolder sf;
10487 sf.strName = pSF->i_getName();
10488 sf.strHostPath = pSF->i_getHostPath();
10489 sf.fWritable = !!pSF->i_isWritable();
10490 sf.fAutoMount = !!pSF->i_isAutoMounted();
10491
10492 data.llSharedFolders.push_back(sf);
10493 }
10494
10495 // clipboard
10496 data.clipboardMode = mHWData->mClipboardMode;
10497
10498 // drag'n'drop
10499 data.dndMode = mHWData->mDnDMode;
10500
10501 /* Guest */
10502 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10503
10504 // IO settings
10505 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10506 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10507
10508 /* BandwidthControl (required) */
10509 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10510 if (FAILED(rc)) throw rc;
10511
10512 /* Host PCI devices */
10513 data.pciAttachments.clear();
10514 for (HWData::PCIDeviceAssignmentList::const_iterator
10515 it = mHWData->mPCIDeviceAssignments.begin();
10516 it != mHWData->mPCIDeviceAssignments.end();
10517 ++it)
10518 {
10519 ComObjPtr<PCIDeviceAttachment> pda = *it;
10520 settings::HostPCIDeviceAttachment hpda;
10521
10522 rc = pda->i_saveSettings(hpda);
10523 if (FAILED(rc)) throw rc;
10524
10525 data.pciAttachments.push_back(hpda);
10526 }
10527
10528 // guest properties
10529 data.llGuestProperties.clear();
10530#ifdef VBOX_WITH_GUEST_PROPS
10531 for (HWData::GuestPropertyMap::const_iterator
10532 it = mHWData->mGuestProperties.begin();
10533 it != mHWData->mGuestProperties.end();
10534 ++it)
10535 {
10536 HWData::GuestProperty property = it->second;
10537
10538 /* Remove transient guest properties at shutdown unless we
10539 * are saving state. Note that restoring snapshot intentionally
10540 * keeps them, they will be removed if appropriate once the final
10541 * machine state is set (as crashes etc. need to work). */
10542 if ( ( mData->mMachineState == MachineState_PoweredOff
10543 || mData->mMachineState == MachineState_Aborted
10544 || mData->mMachineState == MachineState_Teleported)
10545 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10546 continue;
10547 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10548 prop.strName = it->first;
10549 prop.strValue = property.strValue;
10550 prop.timestamp = property.mTimestamp;
10551 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10552 GuestPropWriteFlags(property.mFlags, szFlags);
10553 prop.strFlags = szFlags;
10554
10555 data.llGuestProperties.push_back(prop);
10556 }
10557
10558 /* I presume this doesn't require a backup(). */
10559 mData->mGuestPropertiesModified = FALSE;
10560#endif /* VBOX_WITH_GUEST_PROPS defined */
10561
10562 *pDbg = mHWData->mDebugging;
10563 *pAutostart = mHWData->mAutostart;
10564
10565 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10566 }
10567 catch (std::bad_alloc &)
10568 {
10569 return E_OUTOFMEMORY;
10570 }
10571
10572 AssertComRC(rc);
10573 return rc;
10574}
10575
10576/**
10577 * Saves the storage controller configuration.
10578 *
10579 * @param data storage settings.
10580 */
10581HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10582{
10583 data.llStorageControllers.clear();
10584
10585 for (StorageControllerList::const_iterator
10586 it = mStorageControllers->begin();
10587 it != mStorageControllers->end();
10588 ++it)
10589 {
10590 HRESULT rc;
10591 ComObjPtr<StorageController> pCtl = *it;
10592
10593 settings::StorageController ctl;
10594 ctl.strName = pCtl->i_getName();
10595 ctl.controllerType = pCtl->i_getControllerType();
10596 ctl.storageBus = pCtl->i_getStorageBus();
10597 ctl.ulInstance = pCtl->i_getInstance();
10598 ctl.fBootable = pCtl->i_getBootable();
10599
10600 /* Save the port count. */
10601 ULONG portCount;
10602 rc = pCtl->COMGETTER(PortCount)(&portCount);
10603 ComAssertComRCRet(rc, rc);
10604 ctl.ulPortCount = portCount;
10605
10606 /* Save fUseHostIOCache */
10607 BOOL fUseHostIOCache;
10608 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10609 ComAssertComRCRet(rc, rc);
10610 ctl.fUseHostIOCache = !!fUseHostIOCache;
10611
10612 /* save the devices now. */
10613 rc = i_saveStorageDevices(pCtl, ctl);
10614 ComAssertComRCRet(rc, rc);
10615
10616 data.llStorageControllers.push_back(ctl);
10617 }
10618
10619 return S_OK;
10620}
10621
10622/**
10623 * Saves the hard disk configuration.
10624 */
10625HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10626 settings::StorageController &data)
10627{
10628 MediumAttachmentList atts;
10629
10630 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10631 if (FAILED(rc)) return rc;
10632
10633 data.llAttachedDevices.clear();
10634 for (MediumAttachmentList::const_iterator
10635 it = atts.begin();
10636 it != atts.end();
10637 ++it)
10638 {
10639 settings::AttachedDevice dev;
10640 IMediumAttachment *iA = *it;
10641 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10642 Medium *pMedium = pAttach->i_getMedium();
10643
10644 dev.deviceType = pAttach->i_getType();
10645 dev.lPort = pAttach->i_getPort();
10646 dev.lDevice = pAttach->i_getDevice();
10647 dev.fPassThrough = pAttach->i_getPassthrough();
10648 dev.fHotPluggable = pAttach->i_getHotPluggable();
10649 if (pMedium)
10650 {
10651 if (pMedium->i_isHostDrive())
10652 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10653 else
10654 dev.uuid = pMedium->i_getId();
10655 dev.fTempEject = pAttach->i_getTempEject();
10656 dev.fNonRotational = pAttach->i_getNonRotational();
10657 dev.fDiscard = pAttach->i_getDiscard();
10658 }
10659
10660 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10661
10662 data.llAttachedDevices.push_back(dev);
10663 }
10664
10665 return S_OK;
10666}
10667
10668/**
10669 * Saves machine state settings as defined by aFlags
10670 * (SaveSTS_* values).
10671 *
10672 * @param aFlags Combination of SaveSTS_* flags.
10673 *
10674 * @note Locks objects for writing.
10675 */
10676HRESULT Machine::i_saveStateSettings(int aFlags)
10677{
10678 if (aFlags == 0)
10679 return S_OK;
10680
10681 AutoCaller autoCaller(this);
10682 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10683
10684 /* This object's write lock is also necessary to serialize file access
10685 * (prevent concurrent reads and writes) */
10686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10687
10688 HRESULT rc = S_OK;
10689
10690 Assert(mData->pMachineConfigFile);
10691
10692 try
10693 {
10694 if (aFlags & SaveSTS_CurStateModified)
10695 mData->pMachineConfigFile->fCurrentStateModified = true;
10696
10697 if (aFlags & SaveSTS_StateFilePath)
10698 {
10699 if (!mSSData->strStateFilePath.isEmpty())
10700 /* try to make the file name relative to the settings file dir */
10701 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10702 else
10703 mData->pMachineConfigFile->strStateFile.setNull();
10704 }
10705
10706 if (aFlags & SaveSTS_StateTimeStamp)
10707 {
10708 Assert( mData->mMachineState != MachineState_Aborted
10709 || mSSData->strStateFilePath.isEmpty());
10710
10711 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10712
10713 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10714/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10715 }
10716
10717 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10718 }
10719 catch (...)
10720 {
10721 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10722 }
10723
10724 return rc;
10725}
10726
10727/**
10728 * Ensures that the given medium is added to a media registry. If this machine
10729 * was created with 4.0 or later, then the machine registry is used. Otherwise
10730 * the global VirtualBox media registry is used.
10731 *
10732 * Caller must NOT hold machine lock, media tree or any medium locks!
10733 *
10734 * @param pMedium
10735 */
10736void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10737{
10738 /* Paranoia checks: do not hold machine or media tree locks. */
10739 AssertReturnVoid(!isWriteLockOnCurrentThread());
10740 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10741
10742 ComObjPtr<Medium> pBase;
10743 {
10744 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10745 pBase = pMedium->i_getBase();
10746 }
10747
10748 /* Paranoia checks: do not hold medium locks. */
10749 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10750 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10751
10752 // decide which medium registry to use now that the medium is attached:
10753 Guid uuid;
10754 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10755 // machine XML is VirtualBox 4.0 or higher:
10756 uuid = i_getId(); // machine UUID
10757 else
10758 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10759
10760 if (pMedium->i_addRegistry(uuid))
10761 mParent->i_markRegistryModified(uuid);
10762
10763 /* For more complex hard disk structures it can happen that the base
10764 * medium isn't yet associated with any medium registry. Do that now. */
10765 if (pMedium != pBase)
10766 {
10767 /* Tree lock needed by Medium::addRegistry when recursing. */
10768 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10769 if (pBase->i_addRegistryRecursive(uuid))
10770 {
10771 treeLock.release();
10772 mParent->i_markRegistryModified(uuid);
10773 }
10774 }
10775}
10776
10777/**
10778 * Creates differencing hard disks for all normal hard disks attached to this
10779 * machine and a new set of attachments to refer to created disks.
10780 *
10781 * Used when taking a snapshot or when deleting the current state. Gets called
10782 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10783 *
10784 * This method assumes that mMediumAttachments contains the original hard disk
10785 * attachments it needs to create diffs for. On success, these attachments will
10786 * be replaced with the created diffs.
10787 *
10788 * Attachments with non-normal hard disks are left as is.
10789 *
10790 * If @a aOnline is @c false then the original hard disks that require implicit
10791 * diffs will be locked for reading. Otherwise it is assumed that they are
10792 * already locked for writing (when the VM was started). Note that in the latter
10793 * case it is responsibility of the caller to lock the newly created diffs for
10794 * writing if this method succeeds.
10795 *
10796 * @param aProgress Progress object to run (must contain at least as
10797 * many operations left as the number of hard disks
10798 * attached).
10799 * @param aWeight Weight of this operation.
10800 * @param aOnline Whether the VM was online prior to this operation.
10801 *
10802 * @note The progress object is not marked as completed, neither on success nor
10803 * on failure. This is a responsibility of the caller.
10804 *
10805 * @note Locks this object and the media tree for writing.
10806 */
10807HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10808 ULONG aWeight,
10809 bool aOnline)
10810{
10811 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10812
10813 AutoCaller autoCaller(this);
10814 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10815
10816 AutoMultiWriteLock2 alock(this->lockHandle(),
10817 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10818
10819 /* must be in a protective state because we release the lock below */
10820 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10821 || mData->mMachineState == MachineState_OnlineSnapshotting
10822 || mData->mMachineState == MachineState_LiveSnapshotting
10823 || mData->mMachineState == MachineState_RestoringSnapshot
10824 || mData->mMachineState == MachineState_DeletingSnapshot
10825 , E_FAIL);
10826
10827 HRESULT rc = S_OK;
10828
10829 // use appropriate locked media map (online or offline)
10830 MediumLockListMap lockedMediaOffline;
10831 MediumLockListMap *lockedMediaMap;
10832 if (aOnline)
10833 lockedMediaMap = &mData->mSession.mLockedMedia;
10834 else
10835 lockedMediaMap = &lockedMediaOffline;
10836
10837 try
10838 {
10839 if (!aOnline)
10840 {
10841 /* lock all attached hard disks early to detect "in use"
10842 * situations before creating actual diffs */
10843 for (MediumAttachmentList::const_iterator
10844 it = mMediumAttachments->begin();
10845 it != mMediumAttachments->end();
10846 ++it)
10847 {
10848 MediumAttachment *pAtt = *it;
10849 if (pAtt->i_getType() == DeviceType_HardDisk)
10850 {
10851 Medium *pMedium = pAtt->i_getMedium();
10852 Assert(pMedium);
10853
10854 MediumLockList *pMediumLockList(new MediumLockList());
10855 alock.release();
10856 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10857 NULL /* pToLockWrite */,
10858 false /* fMediumLockWriteAll */,
10859 NULL,
10860 *pMediumLockList);
10861 alock.acquire();
10862 if (FAILED(rc))
10863 {
10864 delete pMediumLockList;
10865 throw rc;
10866 }
10867 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10868 if (FAILED(rc))
10869 {
10870 throw setError(rc,
10871 tr("Collecting locking information for all attached media failed"));
10872 }
10873 }
10874 }
10875
10876 /* Now lock all media. If this fails, nothing is locked. */
10877 alock.release();
10878 rc = lockedMediaMap->Lock();
10879 alock.acquire();
10880 if (FAILED(rc))
10881 {
10882 throw setError(rc,
10883 tr("Locking of attached media failed"));
10884 }
10885 }
10886
10887 /* remember the current list (note that we don't use backup() since
10888 * mMediumAttachments may be already backed up) */
10889 MediumAttachmentList atts = *mMediumAttachments.data();
10890
10891 /* start from scratch */
10892 mMediumAttachments->clear();
10893
10894 /* go through remembered attachments and create diffs for normal hard
10895 * disks and attach them */
10896 for (MediumAttachmentList::const_iterator
10897 it = atts.begin();
10898 it != atts.end();
10899 ++it)
10900 {
10901 MediumAttachment *pAtt = *it;
10902
10903 DeviceType_T devType = pAtt->i_getType();
10904 Medium *pMedium = pAtt->i_getMedium();
10905
10906 if ( devType != DeviceType_HardDisk
10907 || pMedium == NULL
10908 || pMedium->i_getType() != MediumType_Normal)
10909 {
10910 /* copy the attachment as is */
10911
10912 /** @todo the progress object created in SessionMachine::TakeSnaphot
10913 * only expects operations for hard disks. Later other
10914 * device types need to show up in the progress as well. */
10915 if (devType == DeviceType_HardDisk)
10916 {
10917 if (pMedium == NULL)
10918 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10919 aWeight); // weight
10920 else
10921 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10922 pMedium->i_getBase()->i_getName().c_str()).raw(),
10923 aWeight); // weight
10924 }
10925
10926 mMediumAttachments->push_back(pAtt);
10927 continue;
10928 }
10929
10930 /* need a diff */
10931 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10932 pMedium->i_getBase()->i_getName().c_str()).raw(),
10933 aWeight); // weight
10934
10935 Utf8Str strFullSnapshotFolder;
10936 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10937
10938 ComObjPtr<Medium> diff;
10939 diff.createObject();
10940 // store the diff in the same registry as the parent
10941 // (this cannot fail here because we can't create implicit diffs for
10942 // unregistered images)
10943 Guid uuidRegistryParent;
10944 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10945 Assert(fInRegistry); NOREF(fInRegistry);
10946 rc = diff->init(mParent,
10947 pMedium->i_getPreferredDiffFormat(),
10948 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10949 uuidRegistryParent,
10950 DeviceType_HardDisk);
10951 if (FAILED(rc)) throw rc;
10952
10953 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10954 * the push_back? Looks like we're going to release medium with the
10955 * wrong kind of lock (general issue with if we fail anywhere at all)
10956 * and an orphaned VDI in the snapshots folder. */
10957
10958 /* update the appropriate lock list */
10959 MediumLockList *pMediumLockList;
10960 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10961 AssertComRCThrowRC(rc);
10962 if (aOnline)
10963 {
10964 alock.release();
10965 /* The currently attached medium will be read-only, change
10966 * the lock type to read. */
10967 rc = pMediumLockList->Update(pMedium, false);
10968 alock.acquire();
10969 AssertComRCThrowRC(rc);
10970 }
10971
10972 /* release the locks before the potentially lengthy operation */
10973 alock.release();
10974 rc = pMedium->i_createDiffStorage(diff,
10975 pMedium->i_getPreferredDiffVariant(),
10976 pMediumLockList,
10977 NULL /* aProgress */,
10978 true /* aWait */);
10979 alock.acquire();
10980 if (FAILED(rc)) throw rc;
10981
10982 /* actual lock list update is done in Machine::i_commitMedia */
10983
10984 rc = diff->i_addBackReference(mData->mUuid);
10985 AssertComRCThrowRC(rc);
10986
10987 /* add a new attachment */
10988 ComObjPtr<MediumAttachment> attachment;
10989 attachment.createObject();
10990 rc = attachment->init(this,
10991 diff,
10992 pAtt->i_getControllerName(),
10993 pAtt->i_getPort(),
10994 pAtt->i_getDevice(),
10995 DeviceType_HardDisk,
10996 true /* aImplicit */,
10997 false /* aPassthrough */,
10998 false /* aTempEject */,
10999 pAtt->i_getNonRotational(),
11000 pAtt->i_getDiscard(),
11001 pAtt->i_getHotPluggable(),
11002 pAtt->i_getBandwidthGroup());
11003 if (FAILED(rc)) throw rc;
11004
11005 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11006 AssertComRCThrowRC(rc);
11007 mMediumAttachments->push_back(attachment);
11008 }
11009 }
11010 catch (HRESULT aRC) { rc = aRC; }
11011
11012 /* unlock all hard disks we locked when there is no VM */
11013 if (!aOnline)
11014 {
11015 ErrorInfoKeeper eik;
11016
11017 HRESULT rc1 = lockedMediaMap->Clear();
11018 AssertComRC(rc1);
11019 }
11020
11021 return rc;
11022}
11023
11024/**
11025 * Deletes implicit differencing hard disks created either by
11026 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11027 * mMediumAttachments.
11028 *
11029 * Note that to delete hard disks created by #attachDevice() this method is
11030 * called from #i_rollbackMedia() when the changes are rolled back.
11031 *
11032 * @note Locks this object and the media tree for writing.
11033 */
11034HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11035{
11036 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11037
11038 AutoCaller autoCaller(this);
11039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11040
11041 AutoMultiWriteLock2 alock(this->lockHandle(),
11042 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11043
11044 /* We absolutely must have backed up state. */
11045 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11046
11047 /* Check if there are any implicitly created diff images. */
11048 bool fImplicitDiffs = false;
11049 for (MediumAttachmentList::const_iterator
11050 it = mMediumAttachments->begin();
11051 it != mMediumAttachments->end();
11052 ++it)
11053 {
11054 const ComObjPtr<MediumAttachment> &pAtt = *it;
11055 if (pAtt->i_isImplicit())
11056 {
11057 fImplicitDiffs = true;
11058 break;
11059 }
11060 }
11061 /* If there is nothing to do, leave early. This saves lots of image locking
11062 * effort. It also avoids a MachineStateChanged event without real reason.
11063 * This is important e.g. when loading a VM config, because there should be
11064 * no events. Otherwise API clients can become thoroughly confused for
11065 * inaccessible VMs (the code for loading VM configs uses this method for
11066 * cleanup if the config makes no sense), as they take such events as an
11067 * indication that the VM is alive, and they would force the VM config to
11068 * be reread, leading to an endless loop. */
11069 if (!fImplicitDiffs)
11070 return S_OK;
11071
11072 HRESULT rc = S_OK;
11073 MachineState_T oldState = mData->mMachineState;
11074
11075 /* will release the lock before the potentially lengthy operation,
11076 * so protect with the special state (unless already protected) */
11077 if ( oldState != MachineState_Snapshotting
11078 && oldState != MachineState_OnlineSnapshotting
11079 && oldState != MachineState_LiveSnapshotting
11080 && oldState != MachineState_RestoringSnapshot
11081 && oldState != MachineState_DeletingSnapshot
11082 && oldState != MachineState_DeletingSnapshotOnline
11083 && oldState != MachineState_DeletingSnapshotPaused
11084 )
11085 i_setMachineState(MachineState_SettingUp);
11086
11087 // use appropriate locked media map (online or offline)
11088 MediumLockListMap lockedMediaOffline;
11089 MediumLockListMap *lockedMediaMap;
11090 if (aOnline)
11091 lockedMediaMap = &mData->mSession.mLockedMedia;
11092 else
11093 lockedMediaMap = &lockedMediaOffline;
11094
11095 try
11096 {
11097 if (!aOnline)
11098 {
11099 /* lock all attached hard disks early to detect "in use"
11100 * situations before deleting actual diffs */
11101 for (MediumAttachmentList::const_iterator
11102 it = mMediumAttachments->begin();
11103 it != mMediumAttachments->end();
11104 ++it)
11105 {
11106 MediumAttachment *pAtt = *it;
11107 if (pAtt->i_getType() == DeviceType_HardDisk)
11108 {
11109 Medium *pMedium = pAtt->i_getMedium();
11110 Assert(pMedium);
11111
11112 MediumLockList *pMediumLockList(new MediumLockList());
11113 alock.release();
11114 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11115 NULL /* pToLockWrite */,
11116 false /* fMediumLockWriteAll */,
11117 NULL,
11118 *pMediumLockList);
11119 alock.acquire();
11120
11121 if (FAILED(rc))
11122 {
11123 delete pMediumLockList;
11124 throw rc;
11125 }
11126
11127 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11128 if (FAILED(rc))
11129 throw rc;
11130 }
11131 }
11132
11133 if (FAILED(rc))
11134 throw rc;
11135 } // end of offline
11136
11137 /* Lock lists are now up to date and include implicitly created media */
11138
11139 /* Go through remembered attachments and delete all implicitly created
11140 * diffs and fix up the attachment information */
11141 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11142 MediumAttachmentList implicitAtts;
11143 for (MediumAttachmentList::const_iterator
11144 it = mMediumAttachments->begin();
11145 it != mMediumAttachments->end();
11146 ++it)
11147 {
11148 ComObjPtr<MediumAttachment> pAtt = *it;
11149 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11150 if (pMedium.isNull())
11151 continue;
11152
11153 // Implicit attachments go on the list for deletion and back references are removed.
11154 if (pAtt->i_isImplicit())
11155 {
11156 /* Deassociate and mark for deletion */
11157 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11158 rc = pMedium->i_removeBackReference(mData->mUuid);
11159 if (FAILED(rc))
11160 throw rc;
11161 implicitAtts.push_back(pAtt);
11162 continue;
11163 }
11164
11165 /* Was this medium attached before? */
11166 if (!i_findAttachment(oldAtts, pMedium))
11167 {
11168 /* no: de-associate */
11169 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11170 rc = pMedium->i_removeBackReference(mData->mUuid);
11171 if (FAILED(rc))
11172 throw rc;
11173 continue;
11174 }
11175 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11176 }
11177
11178 /* If there are implicit attachments to delete, throw away the lock
11179 * map contents (which will unlock all media) since the medium
11180 * attachments will be rolled back. Below we need to completely
11181 * recreate the lock map anyway since it is infinitely complex to
11182 * do this incrementally (would need reconstructing each attachment
11183 * change, which would be extremely hairy). */
11184 if (implicitAtts.size() != 0)
11185 {
11186 ErrorInfoKeeper eik;
11187
11188 HRESULT rc1 = lockedMediaMap->Clear();
11189 AssertComRC(rc1);
11190 }
11191
11192 /* rollback hard disk changes */
11193 mMediumAttachments.rollback();
11194
11195 MultiResult mrc(S_OK);
11196
11197 // Delete unused implicit diffs.
11198 if (implicitAtts.size() != 0)
11199 {
11200 alock.release();
11201
11202 for (MediumAttachmentList::const_iterator
11203 it = implicitAtts.begin();
11204 it != implicitAtts.end();
11205 ++it)
11206 {
11207 // Remove medium associated with this attachment.
11208 ComObjPtr<MediumAttachment> pAtt = *it;
11209 Assert(pAtt);
11210 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11211 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11212 Assert(pMedium);
11213
11214 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11215 // continue on delete failure, just collect error messages
11216 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11217 pMedium->i_getLocationFull().c_str() ));
11218 mrc = rc;
11219 }
11220 // Clear the list of deleted implicit attachments now, while not
11221 // holding the lock, as it will ultimately trigger Medium::uninit()
11222 // calls which assume that the media tree lock isn't held.
11223 implicitAtts.clear();
11224
11225 alock.acquire();
11226
11227 /* if there is a VM recreate media lock map as mentioned above,
11228 * otherwise it is a waste of time and we leave things unlocked */
11229 if (aOnline)
11230 {
11231 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11232 /* must never be NULL, but better safe than sorry */
11233 if (!pMachine.isNull())
11234 {
11235 alock.release();
11236 rc = mData->mSession.mMachine->i_lockMedia();
11237 alock.acquire();
11238 if (FAILED(rc))
11239 throw rc;
11240 }
11241 }
11242 }
11243 }
11244 catch (HRESULT aRC) {rc = aRC;}
11245
11246 if (mData->mMachineState == MachineState_SettingUp)
11247 i_setMachineState(oldState);
11248
11249 /* unlock all hard disks we locked when there is no VM */
11250 if (!aOnline)
11251 {
11252 ErrorInfoKeeper eik;
11253
11254 HRESULT rc1 = lockedMediaMap->Clear();
11255 AssertComRC(rc1);
11256 }
11257
11258 return rc;
11259}
11260
11261
11262/**
11263 * Looks through the given list of media attachments for one with the given parameters
11264 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11265 * can be searched as well if needed.
11266 *
11267 * @param ll
11268 * @param aControllerName
11269 * @param aControllerPort
11270 * @param aDevice
11271 * @return
11272 */
11273MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11274 const Utf8Str &aControllerName,
11275 LONG aControllerPort,
11276 LONG aDevice)
11277{
11278 for (MediumAttachmentList::const_iterator
11279 it = ll.begin();
11280 it != ll.end();
11281 ++it)
11282 {
11283 MediumAttachment *pAttach = *it;
11284 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11285 return pAttach;
11286 }
11287
11288 return NULL;
11289}
11290
11291/**
11292 * Looks through the given list of media attachments for one with the given parameters
11293 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11294 * can be searched as well if needed.
11295 *
11296 * @param ll
11297 * @param pMedium
11298 * @return
11299 */
11300MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11301 ComObjPtr<Medium> pMedium)
11302{
11303 for (MediumAttachmentList::const_iterator
11304 it = ll.begin();
11305 it != ll.end();
11306 ++it)
11307 {
11308 MediumAttachment *pAttach = *it;
11309 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11310 if (pMediumThis == pMedium)
11311 return pAttach;
11312 }
11313
11314 return NULL;
11315}
11316
11317/**
11318 * Looks through the given list of media attachments for one with the given parameters
11319 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11320 * can be searched as well if needed.
11321 *
11322 * @param ll
11323 * @param id
11324 * @return
11325 */
11326MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11327 Guid &id)
11328{
11329 for (MediumAttachmentList::const_iterator
11330 it = ll.begin();
11331 it != ll.end();
11332 ++it)
11333 {
11334 MediumAttachment *pAttach = *it;
11335 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11336 if (pMediumThis->i_getId() == id)
11337 return pAttach;
11338 }
11339
11340 return NULL;
11341}
11342
11343/**
11344 * Main implementation for Machine::DetachDevice. This also gets called
11345 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11346 *
11347 * @param pAttach Medium attachment to detach.
11348 * @param writeLock Machine write lock which the caller must have locked once.
11349 * This may be released temporarily in here.
11350 * @param pSnapshot If NULL, then the detachment is for the current machine.
11351 * Otherwise this is for a SnapshotMachine, and this must be
11352 * its snapshot.
11353 * @return
11354 */
11355HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11356 AutoWriteLock &writeLock,
11357 Snapshot *pSnapshot)
11358{
11359 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11360 DeviceType_T mediumType = pAttach->i_getType();
11361
11362 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11363
11364 if (pAttach->i_isImplicit())
11365 {
11366 /* attempt to implicitly delete the implicitly created diff */
11367
11368 /// @todo move the implicit flag from MediumAttachment to Medium
11369 /// and forbid any hard disk operation when it is implicit. Or maybe
11370 /// a special media state for it to make it even more simple.
11371
11372 Assert(mMediumAttachments.isBackedUp());
11373
11374 /* will release the lock before the potentially lengthy operation, so
11375 * protect with the special state */
11376 MachineState_T oldState = mData->mMachineState;
11377 i_setMachineState(MachineState_SettingUp);
11378
11379 writeLock.release();
11380
11381 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11382 true /*aWait*/);
11383
11384 writeLock.acquire();
11385
11386 i_setMachineState(oldState);
11387
11388 if (FAILED(rc)) return rc;
11389 }
11390
11391 i_setModified(IsModified_Storage);
11392 mMediumAttachments.backup();
11393 mMediumAttachments->remove(pAttach);
11394
11395 if (!oldmedium.isNull())
11396 {
11397 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11398 if (pSnapshot)
11399 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11400 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11401 else if (mediumType != DeviceType_HardDisk)
11402 oldmedium->i_removeBackReference(mData->mUuid);
11403 }
11404
11405 return S_OK;
11406}
11407
11408/**
11409 * Goes thru all media of the given list and
11410 *
11411 * 1) calls i_detachDevice() on each of them for this machine and
11412 * 2) adds all Medium objects found in the process to the given list,
11413 * depending on cleanupMode.
11414 *
11415 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11416 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11417 * media to the list.
11418 *
11419 * This gets called from Machine::Unregister, both for the actual Machine and
11420 * the SnapshotMachine objects that might be found in the snapshots.
11421 *
11422 * Requires caller and locking. The machine lock must be passed in because it
11423 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11424 *
11425 * @param writeLock Machine lock from top-level caller; this gets passed to
11426 * i_detachDevice.
11427 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11428 * object if called for a SnapshotMachine.
11429 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11430 * added to llMedia; if Full, then all media get added;
11431 * otherwise no media get added.
11432 * @param llMedia Caller's list to receive Medium objects which got detached so
11433 * caller can close() them, depending on cleanupMode.
11434 * @return
11435 */
11436HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11437 Snapshot *pSnapshot,
11438 CleanupMode_T cleanupMode,
11439 MediaList &llMedia)
11440{
11441 Assert(isWriteLockOnCurrentThread());
11442
11443 HRESULT rc;
11444
11445 // make a temporary list because i_detachDevice invalidates iterators into
11446 // mMediumAttachments
11447 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11448
11449 for (MediumAttachmentList::iterator
11450 it = llAttachments2.begin();
11451 it != llAttachments2.end();
11452 ++it)
11453 {
11454 ComObjPtr<MediumAttachment> &pAttach = *it;
11455 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11456
11457 if (!pMedium.isNull())
11458 {
11459 AutoCaller mac(pMedium);
11460 if (FAILED(mac.rc())) return mac.rc();
11461 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11462 DeviceType_T devType = pMedium->i_getDeviceType();
11463 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11464 && devType == DeviceType_HardDisk)
11465 || (cleanupMode == CleanupMode_Full)
11466 )
11467 {
11468 llMedia.push_back(pMedium);
11469 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11470 /* Not allowed to keep this lock as below we need the parent
11471 * medium lock, and the lock order is parent to child. */
11472 lock.release();
11473 /*
11474 * Search for medias which are not attached to any machine, but
11475 * in the chain to an attached disk. Mediums are only consided
11476 * if they are:
11477 * - have only one child
11478 * - no references to any machines
11479 * - are of normal medium type
11480 */
11481 while (!pParent.isNull())
11482 {
11483 AutoCaller mac1(pParent);
11484 if (FAILED(mac1.rc())) return mac1.rc();
11485 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11486 if (pParent->i_getChildren().size() == 1)
11487 {
11488 if ( pParent->i_getMachineBackRefCount() == 0
11489 && pParent->i_getType() == MediumType_Normal
11490 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11491 llMedia.push_back(pParent);
11492 }
11493 else
11494 break;
11495 pParent = pParent->i_getParent();
11496 }
11497 }
11498 }
11499
11500 // real machine: then we need to use the proper method
11501 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11502
11503 if (FAILED(rc))
11504 return rc;
11505 }
11506
11507 return S_OK;
11508}
11509
11510/**
11511 * Perform deferred hard disk detachments.
11512 *
11513 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11514 * changed (not backed up).
11515 *
11516 * If @a aOnline is @c true then this method will also unlock the old hard
11517 * disks for which the new implicit diffs were created and will lock these new
11518 * diffs for writing.
11519 *
11520 * @param aOnline Whether the VM was online prior to this operation.
11521 *
11522 * @note Locks this object for writing!
11523 */
11524void Machine::i_commitMedia(bool aOnline /*= false*/)
11525{
11526 AutoCaller autoCaller(this);
11527 AssertComRCReturnVoid(autoCaller.rc());
11528
11529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11530
11531 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11532
11533 HRESULT rc = S_OK;
11534
11535 /* no attach/detach operations -- nothing to do */
11536 if (!mMediumAttachments.isBackedUp())
11537 return;
11538
11539 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11540 bool fMediaNeedsLocking = false;
11541
11542 /* enumerate new attachments */
11543 for (MediumAttachmentList::const_iterator
11544 it = mMediumAttachments->begin();
11545 it != mMediumAttachments->end();
11546 ++it)
11547 {
11548 MediumAttachment *pAttach = *it;
11549
11550 pAttach->i_commit();
11551
11552 Medium *pMedium = pAttach->i_getMedium();
11553 bool fImplicit = pAttach->i_isImplicit();
11554
11555 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11556 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11557 fImplicit));
11558
11559 /** @todo convert all this Machine-based voodoo to MediumAttachment
11560 * based commit logic. */
11561 if (fImplicit)
11562 {
11563 /* convert implicit attachment to normal */
11564 pAttach->i_setImplicit(false);
11565
11566 if ( aOnline
11567 && pMedium
11568 && pAttach->i_getType() == DeviceType_HardDisk
11569 )
11570 {
11571 /* update the appropriate lock list */
11572 MediumLockList *pMediumLockList;
11573 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11574 AssertComRC(rc);
11575 if (pMediumLockList)
11576 {
11577 /* unlock if there's a need to change the locking */
11578 if (!fMediaNeedsLocking)
11579 {
11580 rc = mData->mSession.mLockedMedia.Unlock();
11581 AssertComRC(rc);
11582 fMediaNeedsLocking = true;
11583 }
11584 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11585 AssertComRC(rc);
11586 rc = pMediumLockList->Append(pMedium, true);
11587 AssertComRC(rc);
11588 }
11589 }
11590
11591 continue;
11592 }
11593
11594 if (pMedium)
11595 {
11596 /* was this medium attached before? */
11597 for (MediumAttachmentList::iterator
11598 oldIt = oldAtts.begin();
11599 oldIt != oldAtts.end();
11600 ++oldIt)
11601 {
11602 MediumAttachment *pOldAttach = *oldIt;
11603 if (pOldAttach->i_getMedium() == pMedium)
11604 {
11605 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11606
11607 /* yes: remove from old to avoid de-association */
11608 oldAtts.erase(oldIt);
11609 break;
11610 }
11611 }
11612 }
11613 }
11614
11615 /* enumerate remaining old attachments and de-associate from the
11616 * current machine state */
11617 for (MediumAttachmentList::const_iterator
11618 it = oldAtts.begin();
11619 it != oldAtts.end();
11620 ++it)
11621 {
11622 MediumAttachment *pAttach = *it;
11623 Medium *pMedium = pAttach->i_getMedium();
11624
11625 /* Detach only hard disks, since DVD/floppy media is detached
11626 * instantly in MountMedium. */
11627 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11628 {
11629 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11630
11631 /* now de-associate from the current machine state */
11632 rc = pMedium->i_removeBackReference(mData->mUuid);
11633 AssertComRC(rc);
11634
11635 if (aOnline)
11636 {
11637 /* unlock since medium is not used anymore */
11638 MediumLockList *pMediumLockList;
11639 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11640 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11641 {
11642 /* this happens for online snapshots, there the attachment
11643 * is changing, but only to a diff image created under
11644 * the old one, so there is no separate lock list */
11645 Assert(!pMediumLockList);
11646 }
11647 else
11648 {
11649 AssertComRC(rc);
11650 if (pMediumLockList)
11651 {
11652 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11653 AssertComRC(rc);
11654 }
11655 }
11656 }
11657 }
11658 }
11659
11660 /* take media locks again so that the locking state is consistent */
11661 if (fMediaNeedsLocking)
11662 {
11663 Assert(aOnline);
11664 rc = mData->mSession.mLockedMedia.Lock();
11665 AssertComRC(rc);
11666 }
11667
11668 /* commit the hard disk changes */
11669 mMediumAttachments.commit();
11670
11671 if (i_isSessionMachine())
11672 {
11673 /*
11674 * Update the parent machine to point to the new owner.
11675 * This is necessary because the stored parent will point to the
11676 * session machine otherwise and cause crashes or errors later
11677 * when the session machine gets invalid.
11678 */
11679 /** @todo Change the MediumAttachment class to behave like any other
11680 * class in this regard by creating peer MediumAttachment
11681 * objects for session machines and share the data with the peer
11682 * machine.
11683 */
11684 for (MediumAttachmentList::const_iterator
11685 it = mMediumAttachments->begin();
11686 it != mMediumAttachments->end();
11687 ++it)
11688 (*it)->i_updateParentMachine(mPeer);
11689
11690 /* attach new data to the primary machine and reshare it */
11691 mPeer->mMediumAttachments.attach(mMediumAttachments);
11692 }
11693
11694 return;
11695}
11696
11697/**
11698 * Perform deferred deletion of implicitly created diffs.
11699 *
11700 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11701 * changed (not backed up).
11702 *
11703 * @note Locks this object for writing!
11704 */
11705void Machine::i_rollbackMedia()
11706{
11707 AutoCaller autoCaller(this);
11708 AssertComRCReturnVoid(autoCaller.rc());
11709
11710 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11711 LogFlowThisFunc(("Entering rollbackMedia\n"));
11712
11713 HRESULT rc = S_OK;
11714
11715 /* no attach/detach operations -- nothing to do */
11716 if (!mMediumAttachments.isBackedUp())
11717 return;
11718
11719 /* enumerate new attachments */
11720 for (MediumAttachmentList::const_iterator
11721 it = mMediumAttachments->begin();
11722 it != mMediumAttachments->end();
11723 ++it)
11724 {
11725 MediumAttachment *pAttach = *it;
11726 /* Fix up the backrefs for DVD/floppy media. */
11727 if (pAttach->i_getType() != DeviceType_HardDisk)
11728 {
11729 Medium *pMedium = pAttach->i_getMedium();
11730 if (pMedium)
11731 {
11732 rc = pMedium->i_removeBackReference(mData->mUuid);
11733 AssertComRC(rc);
11734 }
11735 }
11736
11737 (*it)->i_rollback();
11738
11739 pAttach = *it;
11740 /* Fix up the backrefs for DVD/floppy media. */
11741 if (pAttach->i_getType() != DeviceType_HardDisk)
11742 {
11743 Medium *pMedium = pAttach->i_getMedium();
11744 if (pMedium)
11745 {
11746 rc = pMedium->i_addBackReference(mData->mUuid);
11747 AssertComRC(rc);
11748 }
11749 }
11750 }
11751
11752 /** @todo convert all this Machine-based voodoo to MediumAttachment
11753 * based rollback logic. */
11754 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11755
11756 return;
11757}
11758
11759/**
11760 * Returns true if the settings file is located in the directory named exactly
11761 * as the machine; this means, among other things, that the machine directory
11762 * should be auto-renamed.
11763 *
11764 * @param aSettingsDir if not NULL, the full machine settings file directory
11765 * name will be assigned there.
11766 *
11767 * @note Doesn't lock anything.
11768 * @note Not thread safe (must be called from this object's lock).
11769 */
11770bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11771{
11772 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11773 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11774 if (aSettingsDir)
11775 *aSettingsDir = strMachineDirName;
11776 strMachineDirName.stripPath(); // vmname
11777 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11778 strConfigFileOnly.stripPath() // vmname.vbox
11779 .stripSuffix(); // vmname
11780 /** @todo hack, make somehow use of ComposeMachineFilename */
11781 if (mUserData->s.fDirectoryIncludesUUID)
11782 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11783
11784 AssertReturn(!strMachineDirName.isEmpty(), false);
11785 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11786
11787 return strMachineDirName == strConfigFileOnly;
11788}
11789
11790/**
11791 * Discards all changes to machine settings.
11792 *
11793 * @param aNotify Whether to notify the direct session about changes or not.
11794 *
11795 * @note Locks objects for writing!
11796 */
11797void Machine::i_rollback(bool aNotify)
11798{
11799 AutoCaller autoCaller(this);
11800 AssertComRCReturn(autoCaller.rc(), (void)0);
11801
11802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11803
11804 if (!mStorageControllers.isNull())
11805 {
11806 if (mStorageControllers.isBackedUp())
11807 {
11808 /* unitialize all new devices (absent in the backed up list). */
11809 StorageControllerList *backedList = mStorageControllers.backedUpData();
11810 for (StorageControllerList::const_iterator
11811 it = mStorageControllers->begin();
11812 it != mStorageControllers->end();
11813 ++it)
11814 {
11815 if ( std::find(backedList->begin(), backedList->end(), *it)
11816 == backedList->end()
11817 )
11818 {
11819 (*it)->uninit();
11820 }
11821 }
11822
11823 /* restore the list */
11824 mStorageControllers.rollback();
11825 }
11826
11827 /* rollback any changes to devices after restoring the list */
11828 if (mData->flModifications & IsModified_Storage)
11829 {
11830 for (StorageControllerList::const_iterator
11831 it = mStorageControllers->begin();
11832 it != mStorageControllers->end();
11833 ++it)
11834 {
11835 (*it)->i_rollback();
11836 }
11837 }
11838 }
11839
11840 if (!mUSBControllers.isNull())
11841 {
11842 if (mUSBControllers.isBackedUp())
11843 {
11844 /* unitialize all new devices (absent in the backed up list). */
11845 USBControllerList *backedList = mUSBControllers.backedUpData();
11846 for (USBControllerList::const_iterator
11847 it = mUSBControllers->begin();
11848 it != mUSBControllers->end();
11849 ++it)
11850 {
11851 if ( std::find(backedList->begin(), backedList->end(), *it)
11852 == backedList->end()
11853 )
11854 {
11855 (*it)->uninit();
11856 }
11857 }
11858
11859 /* restore the list */
11860 mUSBControllers.rollback();
11861 }
11862
11863 /* rollback any changes to devices after restoring the list */
11864 if (mData->flModifications & IsModified_USB)
11865 {
11866 for (USBControllerList::const_iterator
11867 it = mUSBControllers->begin();
11868 it != mUSBControllers->end();
11869 ++it)
11870 {
11871 (*it)->i_rollback();
11872 }
11873 }
11874 }
11875
11876 mUserData.rollback();
11877
11878 mHWData.rollback();
11879
11880 if (mData->flModifications & IsModified_Storage)
11881 i_rollbackMedia();
11882
11883 if (mBIOSSettings)
11884 mBIOSSettings->i_rollback();
11885
11886 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11887 mVRDEServer->i_rollback();
11888
11889 if (mAudioAdapter)
11890 mAudioAdapter->i_rollback();
11891
11892 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11893 mUSBDeviceFilters->i_rollback();
11894
11895 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11896 mBandwidthControl->i_rollback();
11897
11898 if (!mHWData.isNull())
11899 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11900 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11901 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11902 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11903
11904 if (mData->flModifications & IsModified_NetworkAdapters)
11905 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11906 if ( mNetworkAdapters[slot]
11907 && mNetworkAdapters[slot]->i_isModified())
11908 {
11909 mNetworkAdapters[slot]->i_rollback();
11910 networkAdapters[slot] = mNetworkAdapters[slot];
11911 }
11912
11913 if (mData->flModifications & IsModified_SerialPorts)
11914 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11915 if ( mSerialPorts[slot]
11916 && mSerialPorts[slot]->i_isModified())
11917 {
11918 mSerialPorts[slot]->i_rollback();
11919 serialPorts[slot] = mSerialPorts[slot];
11920 }
11921
11922 if (mData->flModifications & IsModified_ParallelPorts)
11923 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11924 if ( mParallelPorts[slot]
11925 && mParallelPorts[slot]->i_isModified())
11926 {
11927 mParallelPorts[slot]->i_rollback();
11928 parallelPorts[slot] = mParallelPorts[slot];
11929 }
11930
11931 if (aNotify)
11932 {
11933 /* inform the direct session about changes */
11934
11935 ComObjPtr<Machine> that = this;
11936 uint32_t flModifications = mData->flModifications;
11937 alock.release();
11938
11939 if (flModifications & IsModified_SharedFolders)
11940 that->i_onSharedFolderChange();
11941
11942 if (flModifications & IsModified_VRDEServer)
11943 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11944 if (flModifications & IsModified_USB)
11945 that->i_onUSBControllerChange();
11946
11947 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11948 if (networkAdapters[slot])
11949 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11950 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11951 if (serialPorts[slot])
11952 that->i_onSerialPortChange(serialPorts[slot]);
11953 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11954 if (parallelPorts[slot])
11955 that->i_onParallelPortChange(parallelPorts[slot]);
11956
11957 if (flModifications & IsModified_Storage)
11958 that->i_onStorageControllerChange();
11959
11960#if 0
11961 if (flModifications & IsModified_BandwidthControl)
11962 that->onBandwidthControlChange();
11963#endif
11964 }
11965}
11966
11967/**
11968 * Commits all the changes to machine settings.
11969 *
11970 * Note that this operation is supposed to never fail.
11971 *
11972 * @note Locks this object and children for writing.
11973 */
11974void Machine::i_commit()
11975{
11976 AutoCaller autoCaller(this);
11977 AssertComRCReturnVoid(autoCaller.rc());
11978
11979 AutoCaller peerCaller(mPeer);
11980 AssertComRCReturnVoid(peerCaller.rc());
11981
11982 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11983
11984 /*
11985 * use safe commit to ensure Snapshot machines (that share mUserData)
11986 * will still refer to a valid memory location
11987 */
11988 mUserData.commitCopy();
11989
11990 mHWData.commit();
11991
11992 if (mMediumAttachments.isBackedUp())
11993 i_commitMedia(Global::IsOnline(mData->mMachineState));
11994
11995 mBIOSSettings->i_commit();
11996 mVRDEServer->i_commit();
11997 mAudioAdapter->i_commit();
11998 mUSBDeviceFilters->i_commit();
11999 mBandwidthControl->i_commit();
12000
12001 /* Since mNetworkAdapters is a list which might have been changed (resized)
12002 * without using the Backupable<> template we need to handle the copying
12003 * of the list entries manually, including the creation of peers for the
12004 * new objects. */
12005 bool commitNetworkAdapters = false;
12006 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12007 if (mPeer)
12008 {
12009 /* commit everything, even the ones which will go away */
12010 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12011 mNetworkAdapters[slot]->i_commit();
12012 /* copy over the new entries, creating a peer and uninit the original */
12013 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12014 for (size_t slot = 0; slot < newSize; slot++)
12015 {
12016 /* look if this adapter has a peer device */
12017 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12018 if (!peer)
12019 {
12020 /* no peer means the adapter is a newly created one;
12021 * create a peer owning data this data share it with */
12022 peer.createObject();
12023 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12024 }
12025 mPeer->mNetworkAdapters[slot] = peer;
12026 }
12027 /* uninit any no longer needed network adapters */
12028 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12029 mNetworkAdapters[slot]->uninit();
12030 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12031 {
12032 if (mPeer->mNetworkAdapters[slot])
12033 mPeer->mNetworkAdapters[slot]->uninit();
12034 }
12035 /* Keep the original network adapter count until this point, so that
12036 * discarding a chipset type change will not lose settings. */
12037 mNetworkAdapters.resize(newSize);
12038 mPeer->mNetworkAdapters.resize(newSize);
12039 }
12040 else
12041 {
12042 /* we have no peer (our parent is the newly created machine);
12043 * just commit changes to the network adapters */
12044 commitNetworkAdapters = true;
12045 }
12046 if (commitNetworkAdapters)
12047 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12048 mNetworkAdapters[slot]->i_commit();
12049
12050 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12051 mSerialPorts[slot]->i_commit();
12052 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12053 mParallelPorts[slot]->i_commit();
12054
12055 bool commitStorageControllers = false;
12056
12057 if (mStorageControllers.isBackedUp())
12058 {
12059 mStorageControllers.commit();
12060
12061 if (mPeer)
12062 {
12063 /* Commit all changes to new controllers (this will reshare data with
12064 * peers for those who have peers) */
12065 StorageControllerList *newList = new StorageControllerList();
12066 for (StorageControllerList::const_iterator
12067 it = mStorageControllers->begin();
12068 it != mStorageControllers->end();
12069 ++it)
12070 {
12071 (*it)->i_commit();
12072
12073 /* look if this controller has a peer device */
12074 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12075 if (!peer)
12076 {
12077 /* no peer means the device is a newly created one;
12078 * create a peer owning data this device share it with */
12079 peer.createObject();
12080 peer->init(mPeer, *it, true /* aReshare */);
12081 }
12082 else
12083 {
12084 /* remove peer from the old list */
12085 mPeer->mStorageControllers->remove(peer);
12086 }
12087 /* and add it to the new list */
12088 newList->push_back(peer);
12089 }
12090
12091 /* uninit old peer's controllers that are left */
12092 for (StorageControllerList::const_iterator
12093 it = mPeer->mStorageControllers->begin();
12094 it != mPeer->mStorageControllers->end();
12095 ++it)
12096 {
12097 (*it)->uninit();
12098 }
12099
12100 /* attach new list of controllers to our peer */
12101 mPeer->mStorageControllers.attach(newList);
12102 }
12103 else
12104 {
12105 /* we have no peer (our parent is the newly created machine);
12106 * just commit changes to devices */
12107 commitStorageControllers = true;
12108 }
12109 }
12110 else
12111 {
12112 /* the list of controllers itself is not changed,
12113 * just commit changes to controllers themselves */
12114 commitStorageControllers = true;
12115 }
12116
12117 if (commitStorageControllers)
12118 {
12119 for (StorageControllerList::const_iterator
12120 it = mStorageControllers->begin();
12121 it != mStorageControllers->end();
12122 ++it)
12123 {
12124 (*it)->i_commit();
12125 }
12126 }
12127
12128 bool commitUSBControllers = false;
12129
12130 if (mUSBControllers.isBackedUp())
12131 {
12132 mUSBControllers.commit();
12133
12134 if (mPeer)
12135 {
12136 /* Commit all changes to new controllers (this will reshare data with
12137 * peers for those who have peers) */
12138 USBControllerList *newList = new USBControllerList();
12139 for (USBControllerList::const_iterator
12140 it = mUSBControllers->begin();
12141 it != mUSBControllers->end();
12142 ++it)
12143 {
12144 (*it)->i_commit();
12145
12146 /* look if this controller has a peer device */
12147 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12148 if (!peer)
12149 {
12150 /* no peer means the device is a newly created one;
12151 * create a peer owning data this device share it with */
12152 peer.createObject();
12153 peer->init(mPeer, *it, true /* aReshare */);
12154 }
12155 else
12156 {
12157 /* remove peer from the old list */
12158 mPeer->mUSBControllers->remove(peer);
12159 }
12160 /* and add it to the new list */
12161 newList->push_back(peer);
12162 }
12163
12164 /* uninit old peer's controllers that are left */
12165 for (USBControllerList::const_iterator
12166 it = mPeer->mUSBControllers->begin();
12167 it != mPeer->mUSBControllers->end();
12168 ++it)
12169 {
12170 (*it)->uninit();
12171 }
12172
12173 /* attach new list of controllers to our peer */
12174 mPeer->mUSBControllers.attach(newList);
12175 }
12176 else
12177 {
12178 /* we have no peer (our parent is the newly created machine);
12179 * just commit changes to devices */
12180 commitUSBControllers = true;
12181 }
12182 }
12183 else
12184 {
12185 /* the list of controllers itself is not changed,
12186 * just commit changes to controllers themselves */
12187 commitUSBControllers = true;
12188 }
12189
12190 if (commitUSBControllers)
12191 {
12192 for (USBControllerList::const_iterator
12193 it = mUSBControllers->begin();
12194 it != mUSBControllers->end();
12195 ++it)
12196 {
12197 (*it)->i_commit();
12198 }
12199 }
12200
12201 if (i_isSessionMachine())
12202 {
12203 /* attach new data to the primary machine and reshare it */
12204 mPeer->mUserData.attach(mUserData);
12205 mPeer->mHWData.attach(mHWData);
12206 /* mmMediumAttachments is reshared by fixupMedia */
12207 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12208 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12209 }
12210}
12211
12212/**
12213 * Copies all the hardware data from the given machine.
12214 *
12215 * Currently, only called when the VM is being restored from a snapshot. In
12216 * particular, this implies that the VM is not running during this method's
12217 * call.
12218 *
12219 * @note This method must be called from under this object's lock.
12220 *
12221 * @note This method doesn't call #i_commit(), so all data remains backed up and
12222 * unsaved.
12223 */
12224void Machine::i_copyFrom(Machine *aThat)
12225{
12226 AssertReturnVoid(!i_isSnapshotMachine());
12227 AssertReturnVoid(aThat->i_isSnapshotMachine());
12228
12229 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12230
12231 mHWData.assignCopy(aThat->mHWData);
12232
12233 // create copies of all shared folders (mHWData after attaching a copy
12234 // contains just references to original objects)
12235 for (HWData::SharedFolderList::iterator
12236 it = mHWData->mSharedFolders.begin();
12237 it != mHWData->mSharedFolders.end();
12238 ++it)
12239 {
12240 ComObjPtr<SharedFolder> folder;
12241 folder.createObject();
12242 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12243 AssertComRC(rc);
12244 *it = folder;
12245 }
12246
12247 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12248 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12249 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12250 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12251 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12252
12253 /* create private copies of all controllers */
12254 mStorageControllers.backup();
12255 mStorageControllers->clear();
12256 for (StorageControllerList::const_iterator
12257 it = aThat->mStorageControllers->begin();
12258 it != aThat->mStorageControllers->end();
12259 ++it)
12260 {
12261 ComObjPtr<StorageController> ctrl;
12262 ctrl.createObject();
12263 ctrl->initCopy(this, *it);
12264 mStorageControllers->push_back(ctrl);
12265 }
12266
12267 /* create private copies of all USB controllers */
12268 mUSBControllers.backup();
12269 mUSBControllers->clear();
12270 for (USBControllerList::const_iterator
12271 it = aThat->mUSBControllers->begin();
12272 it != aThat->mUSBControllers->end();
12273 ++it)
12274 {
12275 ComObjPtr<USBController> ctrl;
12276 ctrl.createObject();
12277 ctrl->initCopy(this, *it);
12278 mUSBControllers->push_back(ctrl);
12279 }
12280
12281 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12282 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12283 {
12284 if (mNetworkAdapters[slot].isNotNull())
12285 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12286 else
12287 {
12288 unconst(mNetworkAdapters[slot]).createObject();
12289 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12290 }
12291 }
12292 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12293 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12294 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12295 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12296}
12297
12298/**
12299 * Returns whether the given storage controller is hotplug capable.
12300 *
12301 * @returns true if the controller supports hotplugging
12302 * false otherwise.
12303 * @param enmCtrlType The controller type to check for.
12304 */
12305bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12306{
12307 ComPtr<ISystemProperties> systemProperties;
12308 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12309 if (FAILED(rc))
12310 return false;
12311
12312 BOOL aHotplugCapable = FALSE;
12313 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12314
12315 return RT_BOOL(aHotplugCapable);
12316}
12317
12318#ifdef VBOX_WITH_RESOURCE_USAGE_API
12319
12320void Machine::i_getDiskList(MediaList &list)
12321{
12322 for (MediumAttachmentList::const_iterator
12323 it = mMediumAttachments->begin();
12324 it != mMediumAttachments->end();
12325 ++it)
12326 {
12327 MediumAttachment *pAttach = *it;
12328 /* just in case */
12329 AssertContinue(pAttach);
12330
12331 AutoCaller localAutoCallerA(pAttach);
12332 if (FAILED(localAutoCallerA.rc())) continue;
12333
12334 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12335
12336 if (pAttach->i_getType() == DeviceType_HardDisk)
12337 list.push_back(pAttach->i_getMedium());
12338 }
12339}
12340
12341void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12342{
12343 AssertReturnVoid(isWriteLockOnCurrentThread());
12344 AssertPtrReturnVoid(aCollector);
12345
12346 pm::CollectorHAL *hal = aCollector->getHAL();
12347 /* Create sub metrics */
12348 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12349 "Percentage of processor time spent in user mode by the VM process.");
12350 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12351 "Percentage of processor time spent in kernel mode by the VM process.");
12352 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12353 "Size of resident portion of VM process in memory.");
12354 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12355 "Actual size of all VM disks combined.");
12356 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12357 "Network receive rate.");
12358 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12359 "Network transmit rate.");
12360 /* Create and register base metrics */
12361 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12362 cpuLoadUser, cpuLoadKernel);
12363 aCollector->registerBaseMetric(cpuLoad);
12364 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12365 ramUsageUsed);
12366 aCollector->registerBaseMetric(ramUsage);
12367 MediaList disks;
12368 i_getDiskList(disks);
12369 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12370 diskUsageUsed);
12371 aCollector->registerBaseMetric(diskUsage);
12372
12373 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12374 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12375 new pm::AggregateAvg()));
12376 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12377 new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12379 new pm::AggregateMax()));
12380 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12381 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12382 new pm::AggregateAvg()));
12383 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12384 new pm::AggregateMin()));
12385 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12386 new pm::AggregateMax()));
12387
12388 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12389 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12390 new pm::AggregateAvg()));
12391 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12392 new pm::AggregateMin()));
12393 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12394 new pm::AggregateMax()));
12395
12396 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12397 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12398 new pm::AggregateAvg()));
12399 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12400 new pm::AggregateMin()));
12401 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12402 new pm::AggregateMax()));
12403
12404
12405 /* Guest metrics collector */
12406 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12407 aCollector->registerGuest(mCollectorGuest);
12408 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12409
12410 /* Create sub metrics */
12411 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12412 "Percentage of processor time spent in user mode as seen by the guest.");
12413 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12414 "Percentage of processor time spent in kernel mode as seen by the guest.");
12415 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12416 "Percentage of processor time spent idling as seen by the guest.");
12417
12418 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12419 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12420 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12421 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12422 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12423 pm::SubMetric *guestMemCache = new pm::SubMetric(
12424 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12425
12426 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12427 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12428
12429 /* Create and register base metrics */
12430 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12431 machineNetRx, machineNetTx);
12432 aCollector->registerBaseMetric(machineNetRate);
12433
12434 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12435 guestLoadUser, guestLoadKernel, guestLoadIdle);
12436 aCollector->registerBaseMetric(guestCpuLoad);
12437
12438 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12439 guestMemTotal, guestMemFree,
12440 guestMemBalloon, guestMemShared,
12441 guestMemCache, guestPagedTotal);
12442 aCollector->registerBaseMetric(guestCpuMem);
12443
12444 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12445 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12446 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12447 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12448
12449 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12450 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12451 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12452 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12453
12454 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12455 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12456 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12457 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12458
12459 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12460 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12461 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12462 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12463
12464 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12465 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12466 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12467 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12468
12469 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12470 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12471 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12472 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12473
12474 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12475 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12476 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12477 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12478
12479 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12480 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12481 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12482 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12483
12484 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12485 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12486 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12487 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12488
12489 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12490 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12491 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12492 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12493
12494 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12495 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12496 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12497 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12498}
12499
12500void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12501{
12502 AssertReturnVoid(isWriteLockOnCurrentThread());
12503
12504 if (aCollector)
12505 {
12506 aCollector->unregisterMetricsFor(aMachine);
12507 aCollector->unregisterBaseMetricsFor(aMachine);
12508 }
12509}
12510
12511#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12512
12513
12514////////////////////////////////////////////////////////////////////////////////
12515
12516DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12517
12518HRESULT SessionMachine::FinalConstruct()
12519{
12520 LogFlowThisFunc(("\n"));
12521
12522 mClientToken = NULL;
12523
12524 return BaseFinalConstruct();
12525}
12526
12527void SessionMachine::FinalRelease()
12528{
12529 LogFlowThisFunc(("\n"));
12530
12531 Assert(!mClientToken);
12532 /* paranoia, should not hang around any more */
12533 if (mClientToken)
12534 {
12535 delete mClientToken;
12536 mClientToken = NULL;
12537 }
12538
12539 uninit(Uninit::Unexpected);
12540
12541 BaseFinalRelease();
12542}
12543
12544/**
12545 * @note Must be called only by Machine::LockMachine() from its own write lock.
12546 */
12547HRESULT SessionMachine::init(Machine *aMachine)
12548{
12549 LogFlowThisFuncEnter();
12550 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12551
12552 AssertReturn(aMachine, E_INVALIDARG);
12553
12554 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12555
12556 /* Enclose the state transition NotReady->InInit->Ready */
12557 AutoInitSpan autoInitSpan(this);
12558 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12559
12560 HRESULT rc = S_OK;
12561
12562 RT_ZERO(mAuthLibCtx);
12563
12564 /* create the machine client token */
12565 try
12566 {
12567 mClientToken = new ClientToken(aMachine, this);
12568 if (!mClientToken->isReady())
12569 {
12570 delete mClientToken;
12571 mClientToken = NULL;
12572 rc = E_FAIL;
12573 }
12574 }
12575 catch (std::bad_alloc &)
12576 {
12577 rc = E_OUTOFMEMORY;
12578 }
12579 if (FAILED(rc))
12580 return rc;
12581
12582 /* memorize the peer Machine */
12583 unconst(mPeer) = aMachine;
12584 /* share the parent pointer */
12585 unconst(mParent) = aMachine->mParent;
12586
12587 /* take the pointers to data to share */
12588 mData.share(aMachine->mData);
12589 mSSData.share(aMachine->mSSData);
12590
12591 mUserData.share(aMachine->mUserData);
12592 mHWData.share(aMachine->mHWData);
12593 mMediumAttachments.share(aMachine->mMediumAttachments);
12594
12595 mStorageControllers.allocate();
12596 for (StorageControllerList::const_iterator
12597 it = aMachine->mStorageControllers->begin();
12598 it != aMachine->mStorageControllers->end();
12599 ++it)
12600 {
12601 ComObjPtr<StorageController> ctl;
12602 ctl.createObject();
12603 ctl->init(this, *it);
12604 mStorageControllers->push_back(ctl);
12605 }
12606
12607 mUSBControllers.allocate();
12608 for (USBControllerList::const_iterator
12609 it = aMachine->mUSBControllers->begin();
12610 it != aMachine->mUSBControllers->end();
12611 ++it)
12612 {
12613 ComObjPtr<USBController> ctl;
12614 ctl.createObject();
12615 ctl->init(this, *it);
12616 mUSBControllers->push_back(ctl);
12617 }
12618
12619 unconst(mBIOSSettings).createObject();
12620 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12621 /* create another VRDEServer object that will be mutable */
12622 unconst(mVRDEServer).createObject();
12623 mVRDEServer->init(this, aMachine->mVRDEServer);
12624 /* create another audio adapter object that will be mutable */
12625 unconst(mAudioAdapter).createObject();
12626 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12627 /* create a list of serial ports that will be mutable */
12628 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12629 {
12630 unconst(mSerialPorts[slot]).createObject();
12631 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12632 }
12633 /* create a list of parallel ports that will be mutable */
12634 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12635 {
12636 unconst(mParallelPorts[slot]).createObject();
12637 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12638 }
12639
12640 /* create another USB device filters object that will be mutable */
12641 unconst(mUSBDeviceFilters).createObject();
12642 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12643
12644 /* create a list of network adapters that will be mutable */
12645 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12646 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12647 {
12648 unconst(mNetworkAdapters[slot]).createObject();
12649 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12650 }
12651
12652 /* create another bandwidth control object that will be mutable */
12653 unconst(mBandwidthControl).createObject();
12654 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12655
12656 /* default is to delete saved state on Saved -> PoweredOff transition */
12657 mRemoveSavedState = true;
12658
12659 /* Confirm a successful initialization when it's the case */
12660 autoInitSpan.setSucceeded();
12661
12662 miNATNetworksStarted = 0;
12663
12664 LogFlowThisFuncLeave();
12665 return rc;
12666}
12667
12668/**
12669 * Uninitializes this session object. If the reason is other than
12670 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12671 * or the client watcher code.
12672 *
12673 * @param aReason uninitialization reason
12674 *
12675 * @note Locks mParent + this object for writing.
12676 */
12677void SessionMachine::uninit(Uninit::Reason aReason)
12678{
12679 LogFlowThisFuncEnter();
12680 LogFlowThisFunc(("reason=%d\n", aReason));
12681
12682 /*
12683 * Strongly reference ourselves to prevent this object deletion after
12684 * mData->mSession.mMachine.setNull() below (which can release the last
12685 * reference and call the destructor). Important: this must be done before
12686 * accessing any members (and before AutoUninitSpan that does it as well).
12687 * This self reference will be released as the very last step on return.
12688 */
12689 ComObjPtr<SessionMachine> selfRef;
12690 if (aReason != Uninit::Unexpected)
12691 selfRef = this;
12692
12693 /* Enclose the state transition Ready->InUninit->NotReady */
12694 AutoUninitSpan autoUninitSpan(this);
12695 if (autoUninitSpan.uninitDone())
12696 {
12697 LogFlowThisFunc(("Already uninitialized\n"));
12698 LogFlowThisFuncLeave();
12699 return;
12700 }
12701
12702 if (autoUninitSpan.initFailed())
12703 {
12704 /* We've been called by init() because it's failed. It's not really
12705 * necessary (nor it's safe) to perform the regular uninit sequence
12706 * below, the following is enough.
12707 */
12708 LogFlowThisFunc(("Initialization failed.\n"));
12709 /* destroy the machine client token */
12710 if (mClientToken)
12711 {
12712 delete mClientToken;
12713 mClientToken = NULL;
12714 }
12715 uninitDataAndChildObjects();
12716 mData.free();
12717 unconst(mParent) = NULL;
12718 unconst(mPeer) = NULL;
12719 LogFlowThisFuncLeave();
12720 return;
12721 }
12722
12723 MachineState_T lastState;
12724 {
12725 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12726 lastState = mData->mMachineState;
12727 }
12728 NOREF(lastState);
12729
12730#ifdef VBOX_WITH_USB
12731 // release all captured USB devices, but do this before requesting the locks below
12732 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12733 {
12734 /* Console::captureUSBDevices() is called in the VM process only after
12735 * setting the machine state to Starting or Restoring.
12736 * Console::detachAllUSBDevices() will be called upon successful
12737 * termination. So, we need to release USB devices only if there was
12738 * an abnormal termination of a running VM.
12739 *
12740 * This is identical to SessionMachine::DetachAllUSBDevices except
12741 * for the aAbnormal argument. */
12742 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12743 AssertComRC(rc);
12744 NOREF(rc);
12745
12746 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12747 if (service)
12748 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12749 }
12750#endif /* VBOX_WITH_USB */
12751
12752 // we need to lock this object in uninit() because the lock is shared
12753 // with mPeer (as well as data we modify below). mParent lock is needed
12754 // by several calls to it.
12755 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12756
12757#ifdef VBOX_WITH_RESOURCE_USAGE_API
12758 /*
12759 * It is safe to call Machine::i_unregisterMetrics() here because
12760 * PerformanceCollector::samplerCallback no longer accesses guest methods
12761 * holding the lock.
12762 */
12763 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12764 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12765 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12766 if (mCollectorGuest)
12767 {
12768 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12769 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12770 mCollectorGuest = NULL;
12771 }
12772#endif
12773
12774 if (aReason == Uninit::Abnormal)
12775 {
12776 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12777
12778 /* reset the state to Aborted */
12779 if (mData->mMachineState != MachineState_Aborted)
12780 i_setMachineState(MachineState_Aborted);
12781 }
12782
12783 // any machine settings modified?
12784 if (mData->flModifications)
12785 {
12786 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12787 i_rollback(false /* aNotify */);
12788 }
12789
12790 mData->mSession.mPID = NIL_RTPROCESS;
12791
12792 if (aReason == Uninit::Unexpected)
12793 {
12794 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12795 * client watcher thread to update the set of machines that have open
12796 * sessions. */
12797 mParent->i_updateClientWatcher();
12798 }
12799
12800 /* uninitialize all remote controls */
12801 if (mData->mSession.mRemoteControls.size())
12802 {
12803 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12804 mData->mSession.mRemoteControls.size()));
12805
12806 /* Always restart a the beginning, since the iterator is invalidated
12807 * by using erase(). */
12808 for (Data::Session::RemoteControlList::iterator
12809 it = mData->mSession.mRemoteControls.begin();
12810 it != mData->mSession.mRemoteControls.end();
12811 it = mData->mSession.mRemoteControls.begin())
12812 {
12813 ComPtr<IInternalSessionControl> pControl = *it;
12814 mData->mSession.mRemoteControls.erase(it);
12815 multilock.release();
12816 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12817 HRESULT rc = pControl->Uninitialize();
12818 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12819 if (FAILED(rc))
12820 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12821 multilock.acquire();
12822 }
12823 mData->mSession.mRemoteControls.clear();
12824 }
12825
12826 /* Remove all references to the NAT network service. The service will stop
12827 * if all references (also from other VMs) are removed. */
12828 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12829 {
12830 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12831 {
12832 BOOL enabled;
12833 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12834 if ( FAILED(hrc)
12835 || !enabled)
12836 continue;
12837
12838 NetworkAttachmentType_T type;
12839 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12840 if ( SUCCEEDED(hrc)
12841 && type == NetworkAttachmentType_NATNetwork)
12842 {
12843 Bstr name;
12844 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12845 if (SUCCEEDED(hrc))
12846 {
12847 multilock.release();
12848 Utf8Str strName(name);
12849 LogRel(("VM '%s' stops using NAT network '%s'\n",
12850 mUserData->s.strName.c_str(), strName.c_str()));
12851 mParent->i_natNetworkRefDec(strName);
12852 multilock.acquire();
12853 }
12854 }
12855 }
12856 }
12857
12858 /*
12859 * An expected uninitialization can come only from #i_checkForDeath().
12860 * Otherwise it means that something's gone really wrong (for example,
12861 * the Session implementation has released the VirtualBox reference
12862 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12863 * etc). However, it's also possible, that the client releases the IPC
12864 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12865 * but the VirtualBox release event comes first to the server process.
12866 * This case is practically possible, so we should not assert on an
12867 * unexpected uninit, just log a warning.
12868 */
12869
12870 if (aReason == Uninit::Unexpected)
12871 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12872
12873 if (aReason != Uninit::Normal)
12874 {
12875 mData->mSession.mDirectControl.setNull();
12876 }
12877 else
12878 {
12879 /* this must be null here (see #OnSessionEnd()) */
12880 Assert(mData->mSession.mDirectControl.isNull());
12881 Assert(mData->mSession.mState == SessionState_Unlocking);
12882 Assert(!mData->mSession.mProgress.isNull());
12883 }
12884 if (mData->mSession.mProgress)
12885 {
12886 if (aReason == Uninit::Normal)
12887 mData->mSession.mProgress->i_notifyComplete(S_OK);
12888 else
12889 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12890 COM_IIDOF(ISession),
12891 getComponentName(),
12892 tr("The VM session was aborted"));
12893 mData->mSession.mProgress.setNull();
12894 }
12895
12896 if (mConsoleTaskData.mProgress)
12897 {
12898 Assert(aReason == Uninit::Abnormal);
12899 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12900 COM_IIDOF(ISession),
12901 getComponentName(),
12902 tr("The VM session was aborted"));
12903 mConsoleTaskData.mProgress.setNull();
12904 }
12905
12906 /* remove the association between the peer machine and this session machine */
12907 Assert( (SessionMachine*)mData->mSession.mMachine == this
12908 || aReason == Uninit::Unexpected);
12909
12910 /* reset the rest of session data */
12911 mData->mSession.mLockType = LockType_Null;
12912 mData->mSession.mMachine.setNull();
12913 mData->mSession.mState = SessionState_Unlocked;
12914 mData->mSession.mName.setNull();
12915
12916 /* destroy the machine client token before leaving the exclusive lock */
12917 if (mClientToken)
12918 {
12919 delete mClientToken;
12920 mClientToken = NULL;
12921 }
12922
12923 /* fire an event */
12924 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12925
12926 uninitDataAndChildObjects();
12927
12928 /* free the essential data structure last */
12929 mData.free();
12930
12931 /* release the exclusive lock before setting the below two to NULL */
12932 multilock.release();
12933
12934 unconst(mParent) = NULL;
12935 unconst(mPeer) = NULL;
12936
12937 AuthLibUnload(&mAuthLibCtx);
12938
12939 LogFlowThisFuncLeave();
12940}
12941
12942// util::Lockable interface
12943////////////////////////////////////////////////////////////////////////////////
12944
12945/**
12946 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12947 * with the primary Machine instance (mPeer).
12948 */
12949RWLockHandle *SessionMachine::lockHandle() const
12950{
12951 AssertReturn(mPeer != NULL, NULL);
12952 return mPeer->lockHandle();
12953}
12954
12955// IInternalMachineControl methods
12956////////////////////////////////////////////////////////////////////////////////
12957
12958/**
12959 * Passes collected guest statistics to performance collector object
12960 */
12961HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12962 ULONG aCpuKernel, ULONG aCpuIdle,
12963 ULONG aMemTotal, ULONG aMemFree,
12964 ULONG aMemBalloon, ULONG aMemShared,
12965 ULONG aMemCache, ULONG aPageTotal,
12966 ULONG aAllocVMM, ULONG aFreeVMM,
12967 ULONG aBalloonedVMM, ULONG aSharedVMM,
12968 ULONG aVmNetRx, ULONG aVmNetTx)
12969{
12970#ifdef VBOX_WITH_RESOURCE_USAGE_API
12971 if (mCollectorGuest)
12972 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12973 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12974 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12975 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12976
12977 return S_OK;
12978#else
12979 NOREF(aValidStats);
12980 NOREF(aCpuUser);
12981 NOREF(aCpuKernel);
12982 NOREF(aCpuIdle);
12983 NOREF(aMemTotal);
12984 NOREF(aMemFree);
12985 NOREF(aMemBalloon);
12986 NOREF(aMemShared);
12987 NOREF(aMemCache);
12988 NOREF(aPageTotal);
12989 NOREF(aAllocVMM);
12990 NOREF(aFreeVMM);
12991 NOREF(aBalloonedVMM);
12992 NOREF(aSharedVMM);
12993 NOREF(aVmNetRx);
12994 NOREF(aVmNetTx);
12995 return E_NOTIMPL;
12996#endif
12997}
12998
12999////////////////////////////////////////////////////////////////////////////////
13000//
13001// SessionMachine task records
13002//
13003////////////////////////////////////////////////////////////////////////////////
13004
13005/**
13006 * Task record for saving the machine state.
13007 */
13008class SessionMachine::SaveStateTask
13009 : public Machine::Task
13010{
13011public:
13012 SaveStateTask(SessionMachine *m,
13013 Progress *p,
13014 const Utf8Str &t,
13015 Reason_T enmReason,
13016 const Utf8Str &strStateFilePath)
13017 : Task(m, p, t),
13018 m_enmReason(enmReason),
13019 m_strStateFilePath(strStateFilePath)
13020 {}
13021
13022private:
13023 void handler()
13024 {
13025 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13026 }
13027
13028 Reason_T m_enmReason;
13029 Utf8Str m_strStateFilePath;
13030
13031 friend class SessionMachine;
13032};
13033
13034/**
13035 * Task thread implementation for SessionMachine::SaveState(), called from
13036 * SessionMachine::taskHandler().
13037 *
13038 * @note Locks this object for writing.
13039 *
13040 * @param task
13041 * @return
13042 */
13043void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13044{
13045 LogFlowThisFuncEnter();
13046
13047 AutoCaller autoCaller(this);
13048 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13049 if (FAILED(autoCaller.rc()))
13050 {
13051 /* we might have been uninitialized because the session was accidentally
13052 * closed by the client, so don't assert */
13053 HRESULT rc = setError(E_FAIL,
13054 tr("The session has been accidentally closed"));
13055 task.m_pProgress->i_notifyComplete(rc);
13056 LogFlowThisFuncLeave();
13057 return;
13058 }
13059
13060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13061
13062 HRESULT rc = S_OK;
13063
13064 try
13065 {
13066 ComPtr<IInternalSessionControl> directControl;
13067 if (mData->mSession.mLockType == LockType_VM)
13068 directControl = mData->mSession.mDirectControl;
13069 if (directControl.isNull())
13070 throw setError(VBOX_E_INVALID_VM_STATE,
13071 tr("Trying to save state without a running VM"));
13072 alock.release();
13073 BOOL fSuspendedBySave;
13074 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13075 Assert(!fSuspendedBySave);
13076 alock.acquire();
13077
13078 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13079 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13080 throw E_FAIL);
13081
13082 if (SUCCEEDED(rc))
13083 {
13084 mSSData->strStateFilePath = task.m_strStateFilePath;
13085
13086 /* save all VM settings */
13087 rc = i_saveSettings(NULL);
13088 // no need to check whether VirtualBox.xml needs saving also since
13089 // we can't have a name change pending at this point
13090 }
13091 else
13092 {
13093 // On failure, set the state to the state we had at the beginning.
13094 i_setMachineState(task.m_machineStateBackup);
13095 i_updateMachineStateOnClient();
13096
13097 // Delete the saved state file (might have been already created).
13098 // No need to check whether this is shared with a snapshot here
13099 // because we certainly created a fresh saved state file here.
13100 RTFileDelete(task.m_strStateFilePath.c_str());
13101 }
13102 }
13103 catch (HRESULT aRC) { rc = aRC; }
13104
13105 task.m_pProgress->i_notifyComplete(rc);
13106
13107 LogFlowThisFuncLeave();
13108}
13109
13110/**
13111 * @note Locks this object for writing.
13112 */
13113HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13114{
13115 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13116}
13117
13118HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13119{
13120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13121
13122 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13123 if (FAILED(rc)) return rc;
13124
13125 if ( mData->mMachineState != MachineState_Running
13126 && mData->mMachineState != MachineState_Paused
13127 )
13128 return setError(VBOX_E_INVALID_VM_STATE,
13129 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13130 Global::stringifyMachineState(mData->mMachineState));
13131
13132 ComObjPtr<Progress> pProgress;
13133 pProgress.createObject();
13134 rc = pProgress->init(i_getVirtualBox(),
13135 static_cast<IMachine *>(this) /* aInitiator */,
13136 tr("Saving the execution state of the virtual machine"),
13137 FALSE /* aCancelable */);
13138 if (FAILED(rc))
13139 return rc;
13140
13141 Utf8Str strStateFilePath;
13142 i_composeSavedStateFilename(strStateFilePath);
13143
13144 /* create and start the task on a separate thread (note that it will not
13145 * start working until we release alock) */
13146 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13147 rc = pTask->createThread();
13148 if (FAILED(rc))
13149 return rc;
13150
13151 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13152 i_setMachineState(MachineState_Saving);
13153 i_updateMachineStateOnClient();
13154
13155 pProgress.queryInterfaceTo(aProgress.asOutParam());
13156
13157 return S_OK;
13158}
13159
13160/**
13161 * @note Locks this object for writing.
13162 */
13163HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13164{
13165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13166
13167 HRESULT rc = i_checkStateDependency(MutableStateDep);
13168 if (FAILED(rc)) return rc;
13169
13170 if ( mData->mMachineState != MachineState_PoweredOff
13171 && mData->mMachineState != MachineState_Teleported
13172 && mData->mMachineState != MachineState_Aborted
13173 )
13174 return setError(VBOX_E_INVALID_VM_STATE,
13175 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13176 Global::stringifyMachineState(mData->mMachineState));
13177
13178 com::Utf8Str stateFilePathFull;
13179 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13180 if (RT_FAILURE(vrc))
13181 return setError(VBOX_E_FILE_ERROR,
13182 tr("Invalid saved state file path '%s' (%Rrc)"),
13183 aSavedStateFile.c_str(),
13184 vrc);
13185
13186 mSSData->strStateFilePath = stateFilePathFull;
13187
13188 /* The below i_setMachineState() will detect the state transition and will
13189 * update the settings file */
13190
13191 return i_setMachineState(MachineState_Saved);
13192}
13193
13194/**
13195 * @note Locks this object for writing.
13196 */
13197HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13198{
13199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13200
13201 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13202 if (FAILED(rc)) return rc;
13203
13204 if (mData->mMachineState != MachineState_Saved)
13205 return setError(VBOX_E_INVALID_VM_STATE,
13206 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13207 Global::stringifyMachineState(mData->mMachineState));
13208
13209 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13210
13211 /*
13212 * Saved -> PoweredOff transition will be detected in the SessionMachine
13213 * and properly handled.
13214 */
13215 rc = i_setMachineState(MachineState_PoweredOff);
13216 return rc;
13217}
13218
13219
13220/**
13221 * @note Locks the same as #i_setMachineState() does.
13222 */
13223HRESULT SessionMachine::updateState(MachineState_T aState)
13224{
13225 return i_setMachineState(aState);
13226}
13227
13228/**
13229 * @note Locks this object for writing.
13230 */
13231HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13232{
13233 IProgress *pProgress(aProgress);
13234
13235 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13236
13237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13238
13239 if (mData->mSession.mState != SessionState_Locked)
13240 return VBOX_E_INVALID_OBJECT_STATE;
13241
13242 if (!mData->mSession.mProgress.isNull())
13243 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13244
13245 /* If we didn't reference the NAT network service yet, add a reference to
13246 * force a start */
13247 if (miNATNetworksStarted < 1)
13248 {
13249 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13250 {
13251 BOOL enabled;
13252 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13253 if ( FAILED(hrc)
13254 || !enabled)
13255 continue;
13256
13257 NetworkAttachmentType_T type;
13258 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13259 if ( SUCCEEDED(hrc)
13260 && type == NetworkAttachmentType_NATNetwork)
13261 {
13262 Bstr name;
13263 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13264 if (SUCCEEDED(hrc))
13265 {
13266 Utf8Str strName(name);
13267 LogRel(("VM '%s' starts using NAT network '%s'\n",
13268 mUserData->s.strName.c_str(), strName.c_str()));
13269 mPeer->lockHandle()->unlockWrite();
13270 mParent->i_natNetworkRefInc(strName);
13271#ifdef RT_LOCK_STRICT
13272 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13273#else
13274 mPeer->lockHandle()->lockWrite();
13275#endif
13276 }
13277 }
13278 }
13279 miNATNetworksStarted++;
13280 }
13281
13282 LogFlowThisFunc(("returns S_OK.\n"));
13283 return S_OK;
13284}
13285
13286/**
13287 * @note Locks this object for writing.
13288 */
13289HRESULT SessionMachine::endPowerUp(LONG aResult)
13290{
13291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13292
13293 if (mData->mSession.mState != SessionState_Locked)
13294 return VBOX_E_INVALID_OBJECT_STATE;
13295
13296 /* Finalize the LaunchVMProcess progress object. */
13297 if (mData->mSession.mProgress)
13298 {
13299 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13300 mData->mSession.mProgress.setNull();
13301 }
13302
13303 if (SUCCEEDED((HRESULT)aResult))
13304 {
13305#ifdef VBOX_WITH_RESOURCE_USAGE_API
13306 /* The VM has been powered up successfully, so it makes sense
13307 * now to offer the performance metrics for a running machine
13308 * object. Doing it earlier wouldn't be safe. */
13309 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13310 mData->mSession.mPID);
13311#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13312 }
13313
13314 return S_OK;
13315}
13316
13317/**
13318 * @note Locks this object for writing.
13319 */
13320HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13321{
13322 LogFlowThisFuncEnter();
13323
13324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13325
13326 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13327 E_FAIL);
13328
13329 /* create a progress object to track operation completion */
13330 ComObjPtr<Progress> pProgress;
13331 pProgress.createObject();
13332 pProgress->init(i_getVirtualBox(),
13333 static_cast<IMachine *>(this) /* aInitiator */,
13334 tr("Stopping the virtual machine"),
13335 FALSE /* aCancelable */);
13336
13337 /* fill in the console task data */
13338 mConsoleTaskData.mLastState = mData->mMachineState;
13339 mConsoleTaskData.mProgress = pProgress;
13340
13341 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13342 i_setMachineState(MachineState_Stopping);
13343
13344 pProgress.queryInterfaceTo(aProgress.asOutParam());
13345
13346 return S_OK;
13347}
13348
13349/**
13350 * @note Locks this object for writing.
13351 */
13352HRESULT SessionMachine::endPoweringDown(LONG aResult,
13353 const com::Utf8Str &aErrMsg)
13354{
13355 LogFlowThisFuncEnter();
13356
13357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13358
13359 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13360 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13361 && mConsoleTaskData.mLastState != MachineState_Null,
13362 E_FAIL);
13363
13364 /*
13365 * On failure, set the state to the state we had when BeginPoweringDown()
13366 * was called (this is expected by Console::PowerDown() and the associated
13367 * task). On success the VM process already changed the state to
13368 * MachineState_PoweredOff, so no need to do anything.
13369 */
13370 if (FAILED(aResult))
13371 i_setMachineState(mConsoleTaskData.mLastState);
13372
13373 /* notify the progress object about operation completion */
13374 Assert(mConsoleTaskData.mProgress);
13375 if (SUCCEEDED(aResult))
13376 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13377 else
13378 {
13379 if (aErrMsg.length())
13380 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13381 COM_IIDOF(ISession),
13382 getComponentName(),
13383 aErrMsg.c_str());
13384 else
13385 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13386 }
13387
13388 /* clear out the temporary saved state data */
13389 mConsoleTaskData.mLastState = MachineState_Null;
13390 mConsoleTaskData.mProgress.setNull();
13391
13392 LogFlowThisFuncLeave();
13393 return S_OK;
13394}
13395
13396
13397/**
13398 * Goes through the USB filters of the given machine to see if the given
13399 * device matches any filter or not.
13400 *
13401 * @note Locks the same as USBController::hasMatchingFilter() does.
13402 */
13403HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13404 BOOL *aMatched,
13405 ULONG *aMaskedInterfaces)
13406{
13407 LogFlowThisFunc(("\n"));
13408
13409#ifdef VBOX_WITH_USB
13410 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13411#else
13412 NOREF(aDevice);
13413 NOREF(aMaskedInterfaces);
13414 *aMatched = FALSE;
13415#endif
13416
13417 return S_OK;
13418}
13419
13420/**
13421 * @note Locks the same as Host::captureUSBDevice() does.
13422 */
13423HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13424{
13425 LogFlowThisFunc(("\n"));
13426
13427#ifdef VBOX_WITH_USB
13428 /* if captureDeviceForVM() fails, it must have set extended error info */
13429 clearError();
13430 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13431 if (FAILED(rc)) return rc;
13432
13433 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13434 AssertReturn(service, E_FAIL);
13435 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13436#else
13437 NOREF(aId);
13438 return E_NOTIMPL;
13439#endif
13440}
13441
13442/**
13443 * @note Locks the same as Host::detachUSBDevice() does.
13444 */
13445HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13446 BOOL aDone)
13447{
13448 LogFlowThisFunc(("\n"));
13449
13450#ifdef VBOX_WITH_USB
13451 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13452 AssertReturn(service, E_FAIL);
13453 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13454#else
13455 NOREF(aId);
13456 NOREF(aDone);
13457 return E_NOTIMPL;
13458#endif
13459}
13460
13461/**
13462 * Inserts all machine filters to the USB proxy service and then calls
13463 * Host::autoCaptureUSBDevices().
13464 *
13465 * Called by Console from the VM process upon VM startup.
13466 *
13467 * @note Locks what called methods lock.
13468 */
13469HRESULT SessionMachine::autoCaptureUSBDevices()
13470{
13471 LogFlowThisFunc(("\n"));
13472
13473#ifdef VBOX_WITH_USB
13474 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13475 AssertComRC(rc);
13476 NOREF(rc);
13477
13478 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13479 AssertReturn(service, E_FAIL);
13480 return service->autoCaptureDevicesForVM(this);
13481#else
13482 return S_OK;
13483#endif
13484}
13485
13486/**
13487 * Removes all machine filters from the USB proxy service and then calls
13488 * Host::detachAllUSBDevices().
13489 *
13490 * Called by Console from the VM process upon normal VM termination or by
13491 * SessionMachine::uninit() upon abnormal VM termination (from under the
13492 * Machine/SessionMachine lock).
13493 *
13494 * @note Locks what called methods lock.
13495 */
13496HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13497{
13498 LogFlowThisFunc(("\n"));
13499
13500#ifdef VBOX_WITH_USB
13501 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13502 AssertComRC(rc);
13503 NOREF(rc);
13504
13505 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13506 AssertReturn(service, E_FAIL);
13507 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13508#else
13509 NOREF(aDone);
13510 return S_OK;
13511#endif
13512}
13513
13514/**
13515 * @note Locks this object for writing.
13516 */
13517HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13518 ComPtr<IProgress> &aProgress)
13519{
13520 LogFlowThisFuncEnter();
13521
13522 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13523 /*
13524 * We don't assert below because it might happen that a non-direct session
13525 * informs us it is closed right after we've been uninitialized -- it's ok.
13526 */
13527
13528 /* get IInternalSessionControl interface */
13529 ComPtr<IInternalSessionControl> control(aSession);
13530
13531 ComAssertRet(!control.isNull(), E_INVALIDARG);
13532
13533 /* Creating a Progress object requires the VirtualBox lock, and
13534 * thus locking it here is required by the lock order rules. */
13535 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13536
13537 if (control == mData->mSession.mDirectControl)
13538 {
13539 /* The direct session is being normally closed by the client process
13540 * ----------------------------------------------------------------- */
13541
13542 /* go to the closing state (essential for all open*Session() calls and
13543 * for #i_checkForDeath()) */
13544 Assert(mData->mSession.mState == SessionState_Locked);
13545 mData->mSession.mState = SessionState_Unlocking;
13546
13547 /* set direct control to NULL to release the remote instance */
13548 mData->mSession.mDirectControl.setNull();
13549 LogFlowThisFunc(("Direct control is set to NULL\n"));
13550
13551 if (mData->mSession.mProgress)
13552 {
13553 /* finalize the progress, someone might wait if a frontend
13554 * closes the session before powering on the VM. */
13555 mData->mSession.mProgress->notifyComplete(E_FAIL,
13556 COM_IIDOF(ISession),
13557 getComponentName(),
13558 tr("The VM session was closed before any attempt to power it on"));
13559 mData->mSession.mProgress.setNull();
13560 }
13561
13562 /* Create the progress object the client will use to wait until
13563 * #i_checkForDeath() is called to uninitialize this session object after
13564 * it releases the IPC semaphore.
13565 * Note! Because we're "reusing" mProgress here, this must be a proxy
13566 * object just like for LaunchVMProcess. */
13567 Assert(mData->mSession.mProgress.isNull());
13568 ComObjPtr<ProgressProxy> progress;
13569 progress.createObject();
13570 ComPtr<IUnknown> pPeer(mPeer);
13571 progress->init(mParent, pPeer,
13572 Bstr(tr("Closing session")).raw(),
13573 FALSE /* aCancelable */);
13574 progress.queryInterfaceTo(aProgress.asOutParam());
13575 mData->mSession.mProgress = progress;
13576 }
13577 else
13578 {
13579 /* the remote session is being normally closed */
13580 bool found = false;
13581 for (Data::Session::RemoteControlList::iterator
13582 it = mData->mSession.mRemoteControls.begin();
13583 it != mData->mSession.mRemoteControls.end();
13584 ++it)
13585 {
13586 if (control == *it)
13587 {
13588 found = true;
13589 // This MUST be erase(it), not remove(*it) as the latter
13590 // triggers a very nasty use after free due to the place where
13591 // the value "lives".
13592 mData->mSession.mRemoteControls.erase(it);
13593 break;
13594 }
13595 }
13596 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13597 E_INVALIDARG);
13598 }
13599
13600 /* signal the client watcher thread, because the client is going away */
13601 mParent->i_updateClientWatcher();
13602
13603 LogFlowThisFuncLeave();
13604 return S_OK;
13605}
13606
13607HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13608 std::vector<com::Utf8Str> &aValues,
13609 std::vector<LONG64> &aTimestamps,
13610 std::vector<com::Utf8Str> &aFlags)
13611{
13612 LogFlowThisFunc(("\n"));
13613
13614#ifdef VBOX_WITH_GUEST_PROPS
13615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13616
13617 size_t cEntries = mHWData->mGuestProperties.size();
13618 aNames.resize(cEntries);
13619 aValues.resize(cEntries);
13620 aTimestamps.resize(cEntries);
13621 aFlags.resize(cEntries);
13622
13623 size_t i = 0;
13624 for (HWData::GuestPropertyMap::const_iterator
13625 it = mHWData->mGuestProperties.begin();
13626 it != mHWData->mGuestProperties.end();
13627 ++it, ++i)
13628 {
13629 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13630 aNames[i] = it->first;
13631 aValues[i] = it->second.strValue;
13632 aTimestamps[i] = it->second.mTimestamp;
13633
13634 /* If it is NULL, keep it NULL. */
13635 if (it->second.mFlags)
13636 {
13637 GuestPropWriteFlags(it->second.mFlags, szFlags);
13638 aFlags[i] = szFlags;
13639 }
13640 else
13641 aFlags[i] = "";
13642 }
13643 return S_OK;
13644#else
13645 ReturnComNotImplemented();
13646#endif
13647}
13648
13649HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13650 const com::Utf8Str &aValue,
13651 LONG64 aTimestamp,
13652 const com::Utf8Str &aFlags)
13653{
13654 LogFlowThisFunc(("\n"));
13655
13656#ifdef VBOX_WITH_GUEST_PROPS
13657 try
13658 {
13659 /*
13660 * Convert input up front.
13661 */
13662 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13663 if (aFlags.length())
13664 {
13665 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13666 AssertRCReturn(vrc, E_INVALIDARG);
13667 }
13668
13669 /*
13670 * Now grab the object lock, validate the state and do the update.
13671 */
13672
13673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13674
13675 if (!Global::IsOnline(mData->mMachineState))
13676 {
13677 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13678 VBOX_E_INVALID_VM_STATE);
13679 }
13680
13681 i_setModified(IsModified_MachineData);
13682 mHWData.backup();
13683
13684 bool fDelete = !aValue.length();
13685 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13686 if (it != mHWData->mGuestProperties.end())
13687 {
13688 if (!fDelete)
13689 {
13690 it->second.strValue = aValue;
13691 it->second.mTimestamp = aTimestamp;
13692 it->second.mFlags = fFlags;
13693 }
13694 else
13695 mHWData->mGuestProperties.erase(it);
13696
13697 mData->mGuestPropertiesModified = TRUE;
13698 }
13699 else if (!fDelete)
13700 {
13701 HWData::GuestProperty prop;
13702 prop.strValue = aValue;
13703 prop.mTimestamp = aTimestamp;
13704 prop.mFlags = fFlags;
13705
13706 mHWData->mGuestProperties[aName] = prop;
13707 mData->mGuestPropertiesModified = TRUE;
13708 }
13709
13710 alock.release();
13711
13712 mParent->i_onGuestPropertyChange(mData->mUuid,
13713 Bstr(aName).raw(),
13714 Bstr(aValue).raw(),
13715 Bstr(aFlags).raw());
13716 }
13717 catch (...)
13718 {
13719 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13720 }
13721 return S_OK;
13722#else
13723 ReturnComNotImplemented();
13724#endif
13725}
13726
13727
13728HRESULT SessionMachine::lockMedia()
13729{
13730 AutoMultiWriteLock2 alock(this->lockHandle(),
13731 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13732
13733 AssertReturn( mData->mMachineState == MachineState_Starting
13734 || mData->mMachineState == MachineState_Restoring
13735 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13736
13737 clearError();
13738 alock.release();
13739 return i_lockMedia();
13740}
13741
13742HRESULT SessionMachine::unlockMedia()
13743{
13744 HRESULT hrc = i_unlockMedia();
13745 return hrc;
13746}
13747
13748HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13749 ComPtr<IMediumAttachment> &aNewAttachment)
13750{
13751 // request the host lock first, since might be calling Host methods for getting host drives;
13752 // next, protect the media tree all the while we're in here, as well as our member variables
13753 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13754 this->lockHandle(),
13755 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13756
13757 IMediumAttachment *iAttach = aAttachment;
13758 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13759
13760 Utf8Str ctrlName;
13761 LONG lPort;
13762 LONG lDevice;
13763 bool fTempEject;
13764 {
13765 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13766
13767 /* Need to query the details first, as the IMediumAttachment reference
13768 * might be to the original settings, which we are going to change. */
13769 ctrlName = pAttach->i_getControllerName();
13770 lPort = pAttach->i_getPort();
13771 lDevice = pAttach->i_getDevice();
13772 fTempEject = pAttach->i_getTempEject();
13773 }
13774
13775 if (!fTempEject)
13776 {
13777 /* Remember previously mounted medium. The medium before taking the
13778 * backup is not necessarily the same thing. */
13779 ComObjPtr<Medium> oldmedium;
13780 oldmedium = pAttach->i_getMedium();
13781
13782 i_setModified(IsModified_Storage);
13783 mMediumAttachments.backup();
13784
13785 // The backup operation makes the pAttach reference point to the
13786 // old settings. Re-get the correct reference.
13787 pAttach = i_findAttachment(*mMediumAttachments.data(),
13788 ctrlName,
13789 lPort,
13790 lDevice);
13791
13792 {
13793 AutoCaller autoAttachCaller(this);
13794 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13795
13796 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13797 if (!oldmedium.isNull())
13798 oldmedium->i_removeBackReference(mData->mUuid);
13799
13800 pAttach->i_updateMedium(NULL);
13801 pAttach->i_updateEjected();
13802 }
13803
13804 i_setModified(IsModified_Storage);
13805 }
13806 else
13807 {
13808 {
13809 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13810 pAttach->i_updateEjected();
13811 }
13812 }
13813
13814 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13815
13816 return S_OK;
13817}
13818
13819HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13820 com::Utf8Str &aResult)
13821{
13822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13823
13824 HRESULT hr = S_OK;
13825
13826 if (!mAuthLibCtx.hAuthLibrary)
13827 {
13828 /* Load the external authentication library. */
13829 Bstr authLibrary;
13830 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13831
13832 Utf8Str filename = authLibrary;
13833
13834 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13835 if (RT_FAILURE(rc))
13836 {
13837 hr = setError(E_FAIL,
13838 tr("Could not load the external authentication library '%s' (%Rrc)"),
13839 filename.c_str(), rc);
13840 }
13841 }
13842
13843 /* The auth library might need the machine lock. */
13844 alock.release();
13845
13846 if (FAILED(hr))
13847 return hr;
13848
13849 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13850 {
13851 enum VRDEAuthParams
13852 {
13853 parmUuid = 1,
13854 parmGuestJudgement,
13855 parmUser,
13856 parmPassword,
13857 parmDomain,
13858 parmClientId
13859 };
13860
13861 AuthResult result = AuthResultAccessDenied;
13862
13863 Guid uuid(aAuthParams[parmUuid]);
13864 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13865 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13866
13867 result = AuthLibAuthenticate(&mAuthLibCtx,
13868 uuid.raw(), guestJudgement,
13869 aAuthParams[parmUser].c_str(),
13870 aAuthParams[parmPassword].c_str(),
13871 aAuthParams[parmDomain].c_str(),
13872 u32ClientId);
13873
13874 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13875 size_t cbPassword = aAuthParams[parmPassword].length();
13876 if (cbPassword)
13877 {
13878 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13879 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13880 }
13881
13882 if (result == AuthResultAccessGranted)
13883 aResult = "granted";
13884 else
13885 aResult = "denied";
13886
13887 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13888 aAuthParams[parmUser].c_str(), aResult.c_str()));
13889 }
13890 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13891 {
13892 enum VRDEAuthDisconnectParams
13893 {
13894 parmUuid = 1,
13895 parmClientId
13896 };
13897
13898 Guid uuid(aAuthParams[parmUuid]);
13899 uint32_t u32ClientId = 0;
13900 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13901 }
13902 else
13903 {
13904 hr = E_INVALIDARG;
13905 }
13906
13907 return hr;
13908}
13909
13910// public methods only for internal purposes
13911/////////////////////////////////////////////////////////////////////////////
13912
13913#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13914/**
13915 * Called from the client watcher thread to check for expected or unexpected
13916 * death of the client process that has a direct session to this machine.
13917 *
13918 * On Win32 and on OS/2, this method is called only when we've got the
13919 * mutex (i.e. the client has either died or terminated normally) so it always
13920 * returns @c true (the client is terminated, the session machine is
13921 * uninitialized).
13922 *
13923 * On other platforms, the method returns @c true if the client process has
13924 * terminated normally or abnormally and the session machine was uninitialized,
13925 * and @c false if the client process is still alive.
13926 *
13927 * @note Locks this object for writing.
13928 */
13929bool SessionMachine::i_checkForDeath()
13930{
13931 Uninit::Reason reason;
13932 bool terminated = false;
13933
13934 /* Enclose autoCaller with a block because calling uninit() from under it
13935 * will deadlock. */
13936 {
13937 AutoCaller autoCaller(this);
13938 if (!autoCaller.isOk())
13939 {
13940 /* return true if not ready, to cause the client watcher to exclude
13941 * the corresponding session from watching */
13942 LogFlowThisFunc(("Already uninitialized!\n"));
13943 return true;
13944 }
13945
13946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13947
13948 /* Determine the reason of death: if the session state is Closing here,
13949 * everything is fine. Otherwise it means that the client did not call
13950 * OnSessionEnd() before it released the IPC semaphore. This may happen
13951 * either because the client process has abnormally terminated, or
13952 * because it simply forgot to call ISession::Close() before exiting. We
13953 * threat the latter also as an abnormal termination (see
13954 * Session::uninit() for details). */
13955 reason = mData->mSession.mState == SessionState_Unlocking ?
13956 Uninit::Normal :
13957 Uninit::Abnormal;
13958
13959 if (mClientToken)
13960 terminated = mClientToken->release();
13961 } /* AutoCaller block */
13962
13963 if (terminated)
13964 uninit(reason);
13965
13966 return terminated;
13967}
13968
13969void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973 strTokenId.setNull();
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturnVoid(autoCaller.rc());
13977
13978 Assert(mClientToken);
13979 if (mClientToken)
13980 mClientToken->getId(strTokenId);
13981}
13982#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13983IToken *SessionMachine::i_getToken()
13984{
13985 LogFlowThisFunc(("\n"));
13986
13987 AutoCaller autoCaller(this);
13988 AssertComRCReturn(autoCaller.rc(), NULL);
13989
13990 Assert(mClientToken);
13991 if (mClientToken)
13992 return mClientToken->getToken();
13993 else
13994 return NULL;
13995}
13996#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13997
13998Machine::ClientToken *SessionMachine::i_getClientToken()
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), NULL);
14004
14005 return mClientToken;
14006}
14007
14008
14009/**
14010 * @note Locks this object for reading.
14011 */
14012HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14013{
14014 LogFlowThisFunc(("\n"));
14015
14016 AutoCaller autoCaller(this);
14017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14018
14019 ComPtr<IInternalSessionControl> directControl;
14020 {
14021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14022 if (mData->mSession.mLockType == LockType_VM)
14023 directControl = mData->mSession.mDirectControl;
14024 }
14025
14026 /* ignore notifications sent after #OnSessionEnd() is called */
14027 if (!directControl)
14028 return S_OK;
14029
14030 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14031}
14032
14033/**
14034 * @note Locks this object for reading.
14035 */
14036HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14037 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14038 IN_BSTR aGuestIp, LONG aGuestPort)
14039{
14040 LogFlowThisFunc(("\n"));
14041
14042 AutoCaller autoCaller(this);
14043 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14044
14045 ComPtr<IInternalSessionControl> directControl;
14046 {
14047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14048 if (mData->mSession.mLockType == LockType_VM)
14049 directControl = mData->mSession.mDirectControl;
14050 }
14051
14052 /* ignore notifications sent after #OnSessionEnd() is called */
14053 if (!directControl)
14054 return S_OK;
14055 /*
14056 * instead acting like callback we ask IVirtualBox deliver corresponding event
14057 */
14058
14059 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14060 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14061 return S_OK;
14062}
14063
14064/**
14065 * @note Locks this object for reading.
14066 */
14067HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14068{
14069 LogFlowThisFunc(("\n"));
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 if (mData->mSession.mLockType == LockType_VM)
14078 directControl = mData->mSession.mDirectControl;
14079 }
14080
14081 /* ignore notifications sent after #OnSessionEnd() is called */
14082 if (!directControl)
14083 return S_OK;
14084
14085 return directControl->OnAudioAdapterChange(audioAdapter);
14086}
14087
14088/**
14089 * @note Locks this object for reading.
14090 */
14091HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14092{
14093 LogFlowThisFunc(("\n"));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14097
14098 ComPtr<IInternalSessionControl> directControl;
14099 {
14100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14101 if (mData->mSession.mLockType == LockType_VM)
14102 directControl = mData->mSession.mDirectControl;
14103 }
14104
14105 /* ignore notifications sent after #OnSessionEnd() is called */
14106 if (!directControl)
14107 return S_OK;
14108
14109 return directControl->OnSerialPortChange(serialPort);
14110}
14111
14112/**
14113 * @note Locks this object for reading.
14114 */
14115HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14116{
14117 LogFlowThisFunc(("\n"));
14118
14119 AutoCaller autoCaller(this);
14120 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14121
14122 ComPtr<IInternalSessionControl> directControl;
14123 {
14124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14125 if (mData->mSession.mLockType == LockType_VM)
14126 directControl = mData->mSession.mDirectControl;
14127 }
14128
14129 /* ignore notifications sent after #OnSessionEnd() is called */
14130 if (!directControl)
14131 return S_OK;
14132
14133 return directControl->OnParallelPortChange(parallelPort);
14134}
14135
14136/**
14137 * @note Locks this object for reading.
14138 */
14139HRESULT SessionMachine::i_onStorageControllerChange()
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 if (mData->mSession.mLockType == LockType_VM)
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 /* ignore notifications sent after #OnSessionEnd() is called */
14154 if (!directControl)
14155 return S_OK;
14156
14157 return directControl->OnStorageControllerChange();
14158}
14159
14160/**
14161 * @note Locks this object for reading.
14162 */
14163HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167 AutoCaller autoCaller(this);
14168 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14169
14170 ComPtr<IInternalSessionControl> directControl;
14171 {
14172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14173 if (mData->mSession.mLockType == LockType_VM)
14174 directControl = mData->mSession.mDirectControl;
14175 }
14176
14177 /* ignore notifications sent after #OnSessionEnd() is called */
14178 if (!directControl)
14179 return S_OK;
14180
14181 return directControl->OnMediumChange(aAttachment, aForce);
14182}
14183
14184/**
14185 * @note Locks this object for reading.
14186 */
14187HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14188{
14189 LogFlowThisFunc(("\n"));
14190
14191 AutoCaller autoCaller(this);
14192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14193
14194 ComPtr<IInternalSessionControl> directControl;
14195 {
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197 if (mData->mSession.mLockType == LockType_VM)
14198 directControl = mData->mSession.mDirectControl;
14199 }
14200
14201 /* ignore notifications sent after #OnSessionEnd() is called */
14202 if (!directControl)
14203 return S_OK;
14204
14205 return directControl->OnCPUChange(aCPU, aRemove);
14206}
14207
14208HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14209{
14210 LogFlowThisFunc(("\n"));
14211
14212 AutoCaller autoCaller(this);
14213 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14214
14215 ComPtr<IInternalSessionControl> directControl;
14216 {
14217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14218 if (mData->mSession.mLockType == LockType_VM)
14219 directControl = mData->mSession.mDirectControl;
14220 }
14221
14222 /* ignore notifications sent after #OnSessionEnd() is called */
14223 if (!directControl)
14224 return S_OK;
14225
14226 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14227}
14228
14229/**
14230 * @note Locks this object for reading.
14231 */
14232HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14233{
14234 LogFlowThisFunc(("\n"));
14235
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14238
14239 ComPtr<IInternalSessionControl> directControl;
14240 {
14241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14242 if (mData->mSession.mLockType == LockType_VM)
14243 directControl = mData->mSession.mDirectControl;
14244 }
14245
14246 /* ignore notifications sent after #OnSessionEnd() is called */
14247 if (!directControl)
14248 return S_OK;
14249
14250 return directControl->OnVRDEServerChange(aRestart);
14251}
14252
14253/**
14254 * @note Locks this object for reading.
14255 */
14256HRESULT SessionMachine::i_onVideoCaptureChange()
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14262
14263 ComPtr<IInternalSessionControl> directControl;
14264 {
14265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14266 if (mData->mSession.mLockType == LockType_VM)
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 /* ignore notifications sent after #OnSessionEnd() is called */
14271 if (!directControl)
14272 return S_OK;
14273
14274 return directControl->OnVideoCaptureChange();
14275}
14276
14277/**
14278 * @note Locks this object for reading.
14279 */
14280HRESULT SessionMachine::i_onUSBControllerChange()
14281{
14282 LogFlowThisFunc(("\n"));
14283
14284 AutoCaller autoCaller(this);
14285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14286
14287 ComPtr<IInternalSessionControl> directControl;
14288 {
14289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14290 if (mData->mSession.mLockType == LockType_VM)
14291 directControl = mData->mSession.mDirectControl;
14292 }
14293
14294 /* ignore notifications sent after #OnSessionEnd() is called */
14295 if (!directControl)
14296 return S_OK;
14297
14298 return directControl->OnUSBControllerChange();
14299}
14300
14301/**
14302 * @note Locks this object for reading.
14303 */
14304HRESULT SessionMachine::i_onSharedFolderChange()
14305{
14306 LogFlowThisFunc(("\n"));
14307
14308 AutoCaller autoCaller(this);
14309 AssertComRCReturnRC(autoCaller.rc());
14310
14311 ComPtr<IInternalSessionControl> directControl;
14312 {
14313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14314 if (mData->mSession.mLockType == LockType_VM)
14315 directControl = mData->mSession.mDirectControl;
14316 }
14317
14318 /* ignore notifications sent after #OnSessionEnd() is called */
14319 if (!directControl)
14320 return S_OK;
14321
14322 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14323}
14324
14325/**
14326 * @note Locks this object for reading.
14327 */
14328HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14329{
14330 LogFlowThisFunc(("\n"));
14331
14332 AutoCaller autoCaller(this);
14333 AssertComRCReturnRC(autoCaller.rc());
14334
14335 ComPtr<IInternalSessionControl> directControl;
14336 {
14337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14338 if (mData->mSession.mLockType == LockType_VM)
14339 directControl = mData->mSession.mDirectControl;
14340 }
14341
14342 /* ignore notifications sent after #OnSessionEnd() is called */
14343 if (!directControl)
14344 return S_OK;
14345
14346 return directControl->OnClipboardModeChange(aClipboardMode);
14347}
14348
14349/**
14350 * @note Locks this object for reading.
14351 */
14352HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14353{
14354 LogFlowThisFunc(("\n"));
14355
14356 AutoCaller autoCaller(this);
14357 AssertComRCReturnRC(autoCaller.rc());
14358
14359 ComPtr<IInternalSessionControl> directControl;
14360 {
14361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14362 if (mData->mSession.mLockType == LockType_VM)
14363 directControl = mData->mSession.mDirectControl;
14364 }
14365
14366 /* ignore notifications sent after #OnSessionEnd() is called */
14367 if (!directControl)
14368 return S_OK;
14369
14370 return directControl->OnDnDModeChange(aDnDMode);
14371}
14372
14373/**
14374 * @note Locks this object for reading.
14375 */
14376HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14377{
14378 LogFlowThisFunc(("\n"));
14379
14380 AutoCaller autoCaller(this);
14381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14382
14383 ComPtr<IInternalSessionControl> directControl;
14384 {
14385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14386 if (mData->mSession.mLockType == LockType_VM)
14387 directControl = mData->mSession.mDirectControl;
14388 }
14389
14390 /* ignore notifications sent after #OnSessionEnd() is called */
14391 if (!directControl)
14392 return S_OK;
14393
14394 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14395}
14396
14397/**
14398 * @note Locks this object for reading.
14399 */
14400HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14401{
14402 LogFlowThisFunc(("\n"));
14403
14404 AutoCaller autoCaller(this);
14405 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14406
14407 ComPtr<IInternalSessionControl> directControl;
14408 {
14409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14410 if (mData->mSession.mLockType == LockType_VM)
14411 directControl = mData->mSession.mDirectControl;
14412 }
14413
14414 /* ignore notifications sent after #OnSessionEnd() is called */
14415 if (!directControl)
14416 return S_OK;
14417
14418 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14419}
14420
14421/**
14422 * Returns @c true if this machine's USB controller reports it has a matching
14423 * filter for the given USB device and @c false otherwise.
14424 *
14425 * @note locks this object for reading.
14426 */
14427bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14428{
14429 AutoCaller autoCaller(this);
14430 /* silently return if not ready -- this method may be called after the
14431 * direct machine session has been called */
14432 if (!autoCaller.isOk())
14433 return false;
14434
14435#ifdef VBOX_WITH_USB
14436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14437
14438 switch (mData->mMachineState)
14439 {
14440 case MachineState_Starting:
14441 case MachineState_Restoring:
14442 case MachineState_TeleportingIn:
14443 case MachineState_Paused:
14444 case MachineState_Running:
14445 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14446 * elsewhere... */
14447 alock.release();
14448 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14449 default: break;
14450 }
14451#else
14452 NOREF(aDevice);
14453 NOREF(aMaskedIfs);
14454#endif
14455 return false;
14456}
14457
14458/**
14459 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14460 */
14461HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14462 IVirtualBoxErrorInfo *aError,
14463 ULONG aMaskedIfs,
14464 const com::Utf8Str &aCaptureFilename)
14465{
14466 LogFlowThisFunc(("\n"));
14467
14468 AutoCaller autoCaller(this);
14469
14470 /* This notification may happen after the machine object has been
14471 * uninitialized (the session was closed), so don't assert. */
14472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14473
14474 ComPtr<IInternalSessionControl> directControl;
14475 {
14476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14477 if (mData->mSession.mLockType == LockType_VM)
14478 directControl = mData->mSession.mDirectControl;
14479 }
14480
14481 /* fail on notifications sent after #OnSessionEnd() is called, it is
14482 * expected by the caller */
14483 if (!directControl)
14484 return E_FAIL;
14485
14486 /* No locks should be held at this point. */
14487 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14488 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14489
14490 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14491}
14492
14493/**
14494 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14495 */
14496HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14497 IVirtualBoxErrorInfo *aError)
14498{
14499 LogFlowThisFunc(("\n"));
14500
14501 AutoCaller autoCaller(this);
14502
14503 /* This notification may happen after the machine object has been
14504 * uninitialized (the session was closed), so don't assert. */
14505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14506
14507 ComPtr<IInternalSessionControl> directControl;
14508 {
14509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14510 if (mData->mSession.mLockType == LockType_VM)
14511 directControl = mData->mSession.mDirectControl;
14512 }
14513
14514 /* fail on notifications sent after #OnSessionEnd() is called, it is
14515 * expected by the caller */
14516 if (!directControl)
14517 return E_FAIL;
14518
14519 /* No locks should be held at this point. */
14520 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14521 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14522
14523 return directControl->OnUSBDeviceDetach(aId, aError);
14524}
14525
14526// protected methods
14527/////////////////////////////////////////////////////////////////////////////
14528
14529/**
14530 * Deletes the given file if it is no longer in use by either the current machine state
14531 * (if the machine is "saved") or any of the machine's snapshots.
14532 *
14533 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14534 * but is different for each SnapshotMachine. When calling this, the order of calling this
14535 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14536 * is therefore critical. I know, it's all rather messy.
14537 *
14538 * @param strStateFile
14539 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14540 * the test for whether the saved state file is in use.
14541 */
14542void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14543 Snapshot *pSnapshotToIgnore)
14544{
14545 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14546 if ( (strStateFile.isNotEmpty())
14547 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14548 )
14549 // ... and it must also not be shared with other snapshots
14550 if ( !mData->mFirstSnapshot
14551 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14552 // this checks the SnapshotMachine's state file paths
14553 )
14554 RTFileDelete(strStateFile.c_str());
14555}
14556
14557/**
14558 * Locks the attached media.
14559 *
14560 * All attached hard disks are locked for writing and DVD/floppy are locked for
14561 * reading. Parents of attached hard disks (if any) are locked for reading.
14562 *
14563 * This method also performs accessibility check of all media it locks: if some
14564 * media is inaccessible, the method will return a failure and a bunch of
14565 * extended error info objects per each inaccessible medium.
14566 *
14567 * Note that this method is atomic: if it returns a success, all media are
14568 * locked as described above; on failure no media is locked at all (all
14569 * succeeded individual locks will be undone).
14570 *
14571 * The caller is responsible for doing the necessary state sanity checks.
14572 *
14573 * The locks made by this method must be undone by calling #unlockMedia() when
14574 * no more needed.
14575 */
14576HRESULT SessionMachine::i_lockMedia()
14577{
14578 AutoCaller autoCaller(this);
14579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14580
14581 AutoMultiWriteLock2 alock(this->lockHandle(),
14582 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14583
14584 /* bail out if trying to lock things with already set up locking */
14585 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14586
14587 MultiResult mrc(S_OK);
14588
14589 /* Collect locking information for all medium objects attached to the VM. */
14590 for (MediumAttachmentList::const_iterator
14591 it = mMediumAttachments->begin();
14592 it != mMediumAttachments->end();
14593 ++it)
14594 {
14595 MediumAttachment *pAtt = *it;
14596 DeviceType_T devType = pAtt->i_getType();
14597 Medium *pMedium = pAtt->i_getMedium();
14598
14599 MediumLockList *pMediumLockList(new MediumLockList());
14600 // There can be attachments without a medium (floppy/dvd), and thus
14601 // it's impossible to create a medium lock list. It still makes sense
14602 // to have the empty medium lock list in the map in case a medium is
14603 // attached later.
14604 if (pMedium != NULL)
14605 {
14606 MediumType_T mediumType = pMedium->i_getType();
14607 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14608 || mediumType == MediumType_Shareable;
14609 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14610
14611 alock.release();
14612 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14613 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14614 false /* fMediumLockWriteAll */,
14615 NULL,
14616 *pMediumLockList);
14617 alock.acquire();
14618 if (FAILED(mrc))
14619 {
14620 delete pMediumLockList;
14621 mData->mSession.mLockedMedia.Clear();
14622 break;
14623 }
14624 }
14625
14626 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14627 if (FAILED(rc))
14628 {
14629 mData->mSession.mLockedMedia.Clear();
14630 mrc = setError(rc,
14631 tr("Collecting locking information for all attached media failed"));
14632 break;
14633 }
14634 }
14635
14636 if (SUCCEEDED(mrc))
14637 {
14638 /* Now lock all media. If this fails, nothing is locked. */
14639 alock.release();
14640 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14641 alock.acquire();
14642 if (FAILED(rc))
14643 {
14644 mrc = setError(rc,
14645 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14646 }
14647 }
14648
14649 return mrc;
14650}
14651
14652/**
14653 * Undoes the locks made by by #lockMedia().
14654 */
14655HRESULT SessionMachine::i_unlockMedia()
14656{
14657 AutoCaller autoCaller(this);
14658 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14659
14660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14661
14662 /* we may be holding important error info on the current thread;
14663 * preserve it */
14664 ErrorInfoKeeper eik;
14665
14666 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14667 AssertComRC(rc);
14668 return rc;
14669}
14670
14671/**
14672 * Helper to change the machine state (reimplementation).
14673 *
14674 * @note Locks this object for writing.
14675 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14676 * it can cause crashes in random places due to unexpectedly committing
14677 * the current settings. The caller is responsible for that. The call
14678 * to saveStateSettings is fine, because this method does not commit.
14679 */
14680HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14681{
14682 LogFlowThisFuncEnter();
14683 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14684
14685 AutoCaller autoCaller(this);
14686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14687
14688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14689
14690 MachineState_T oldMachineState = mData->mMachineState;
14691
14692 AssertMsgReturn(oldMachineState != aMachineState,
14693 ("oldMachineState=%s, aMachineState=%s\n",
14694 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14695 E_FAIL);
14696
14697 HRESULT rc = S_OK;
14698
14699 int stsFlags = 0;
14700 bool deleteSavedState = false;
14701
14702 /* detect some state transitions */
14703
14704 if ( ( oldMachineState == MachineState_Saved
14705 && aMachineState == MachineState_Restoring)
14706 || ( ( oldMachineState == MachineState_PoweredOff
14707 || oldMachineState == MachineState_Teleported
14708 || oldMachineState == MachineState_Aborted
14709 )
14710 && ( aMachineState == MachineState_TeleportingIn
14711 || aMachineState == MachineState_Starting
14712 )
14713 )
14714 )
14715 {
14716 /* The EMT thread is about to start */
14717
14718 /* Nothing to do here for now... */
14719
14720 /// @todo NEWMEDIA don't let mDVDDrive and other children
14721 /// change anything when in the Starting/Restoring state
14722 }
14723 else if ( ( oldMachineState == MachineState_Running
14724 || oldMachineState == MachineState_Paused
14725 || oldMachineState == MachineState_Teleporting
14726 || oldMachineState == MachineState_OnlineSnapshotting
14727 || oldMachineState == MachineState_LiveSnapshotting
14728 || oldMachineState == MachineState_Stuck
14729 || oldMachineState == MachineState_Starting
14730 || oldMachineState == MachineState_Stopping
14731 || oldMachineState == MachineState_Saving
14732 || oldMachineState == MachineState_Restoring
14733 || oldMachineState == MachineState_TeleportingPausedVM
14734 || oldMachineState == MachineState_TeleportingIn
14735 )
14736 && ( aMachineState == MachineState_PoweredOff
14737 || aMachineState == MachineState_Saved
14738 || aMachineState == MachineState_Teleported
14739 || aMachineState == MachineState_Aborted
14740 )
14741 )
14742 {
14743 /* The EMT thread has just stopped, unlock attached media. Note that as
14744 * opposed to locking that is done from Console, we do unlocking here
14745 * because the VM process may have aborted before having a chance to
14746 * properly unlock all media it locked. */
14747
14748 unlockMedia();
14749 }
14750
14751 if (oldMachineState == MachineState_Restoring)
14752 {
14753 if (aMachineState != MachineState_Saved)
14754 {
14755 /*
14756 * delete the saved state file once the machine has finished
14757 * restoring from it (note that Console sets the state from
14758 * Restoring to Saved if the VM couldn't restore successfully,
14759 * to give the user an ability to fix an error and retry --
14760 * we keep the saved state file in this case)
14761 */
14762 deleteSavedState = true;
14763 }
14764 }
14765 else if ( oldMachineState == MachineState_Saved
14766 && ( aMachineState == MachineState_PoweredOff
14767 || aMachineState == MachineState_Aborted
14768 || aMachineState == MachineState_Teleported
14769 )
14770 )
14771 {
14772 /*
14773 * delete the saved state after SessionMachine::ForgetSavedState() is called
14774 * or if the VM process (owning a direct VM session) crashed while the
14775 * VM was Saved
14776 */
14777
14778 /// @todo (dmik)
14779 // Not sure that deleting the saved state file just because of the
14780 // client death before it attempted to restore the VM is a good
14781 // thing. But when it crashes we need to go to the Aborted state
14782 // which cannot have the saved state file associated... The only
14783 // way to fix this is to make the Aborted condition not a VM state
14784 // but a bool flag: i.e., when a crash occurs, set it to true and
14785 // change the state to PoweredOff or Saved depending on the
14786 // saved state presence.
14787
14788 deleteSavedState = true;
14789 mData->mCurrentStateModified = TRUE;
14790 stsFlags |= SaveSTS_CurStateModified;
14791 }
14792
14793 if ( aMachineState == MachineState_Starting
14794 || aMachineState == MachineState_Restoring
14795 || aMachineState == MachineState_TeleportingIn
14796 )
14797 {
14798 /* set the current state modified flag to indicate that the current
14799 * state is no more identical to the state in the
14800 * current snapshot */
14801 if (!mData->mCurrentSnapshot.isNull())
14802 {
14803 mData->mCurrentStateModified = TRUE;
14804 stsFlags |= SaveSTS_CurStateModified;
14805 }
14806 }
14807
14808 if (deleteSavedState)
14809 {
14810 if (mRemoveSavedState)
14811 {
14812 Assert(!mSSData->strStateFilePath.isEmpty());
14813
14814 // it is safe to delete the saved state file if ...
14815 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14816 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14817 // ... none of the snapshots share the saved state file
14818 )
14819 RTFileDelete(mSSData->strStateFilePath.c_str());
14820 }
14821
14822 mSSData->strStateFilePath.setNull();
14823 stsFlags |= SaveSTS_StateFilePath;
14824 }
14825
14826 /* redirect to the underlying peer machine */
14827 mPeer->i_setMachineState(aMachineState);
14828
14829 if ( oldMachineState != MachineState_RestoringSnapshot
14830 && ( aMachineState == MachineState_PoweredOff
14831 || aMachineState == MachineState_Teleported
14832 || aMachineState == MachineState_Aborted
14833 || aMachineState == MachineState_Saved))
14834 {
14835 /* the machine has stopped execution
14836 * (or the saved state file was adopted) */
14837 stsFlags |= SaveSTS_StateTimeStamp;
14838 }
14839
14840 if ( ( oldMachineState == MachineState_PoweredOff
14841 || oldMachineState == MachineState_Aborted
14842 || oldMachineState == MachineState_Teleported
14843 )
14844 && aMachineState == MachineState_Saved)
14845 {
14846 /* the saved state file was adopted */
14847 Assert(!mSSData->strStateFilePath.isEmpty());
14848 stsFlags |= SaveSTS_StateFilePath;
14849 }
14850
14851#ifdef VBOX_WITH_GUEST_PROPS
14852 if ( aMachineState == MachineState_PoweredOff
14853 || aMachineState == MachineState_Aborted
14854 || aMachineState == MachineState_Teleported)
14855 {
14856 /* Make sure any transient guest properties get removed from the
14857 * property store on shutdown. */
14858 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14859
14860 /* remove it from the settings representation */
14861 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14862 for (settings::GuestPropertiesList::iterator
14863 it = llGuestProperties.begin();
14864 it != llGuestProperties.end();
14865 /*nothing*/)
14866 {
14867 const settings::GuestProperty &prop = *it;
14868 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14869 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14870 {
14871 it = llGuestProperties.erase(it);
14872 fNeedsSaving = true;
14873 }
14874 else
14875 {
14876 ++it;
14877 }
14878 }
14879
14880 /* Additionally remove it from the HWData representation. Required to
14881 * keep everything in sync, as this is what the API keeps using. */
14882 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14883 for (HWData::GuestPropertyMap::iterator
14884 it = llHWGuestProperties.begin();
14885 it != llHWGuestProperties.end();
14886 /*nothing*/)
14887 {
14888 uint32_t fFlags = it->second.mFlags;
14889 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14890 {
14891 /* iterator where we need to continue after the erase call
14892 * (C++03 is a fact still, and it doesn't return the iterator
14893 * which would allow continuing) */
14894 HWData::GuestPropertyMap::iterator it2 = it;
14895 ++it2;
14896 llHWGuestProperties.erase(it);
14897 it = it2;
14898 fNeedsSaving = true;
14899 }
14900 else
14901 {
14902 ++it;
14903 }
14904 }
14905
14906 if (fNeedsSaving)
14907 {
14908 mData->mCurrentStateModified = TRUE;
14909 stsFlags |= SaveSTS_CurStateModified;
14910 }
14911 }
14912#endif /* VBOX_WITH_GUEST_PROPS */
14913
14914 rc = i_saveStateSettings(stsFlags);
14915
14916 if ( ( oldMachineState != MachineState_PoweredOff
14917 && oldMachineState != MachineState_Aborted
14918 && oldMachineState != MachineState_Teleported
14919 )
14920 && ( aMachineState == MachineState_PoweredOff
14921 || aMachineState == MachineState_Aborted
14922 || aMachineState == MachineState_Teleported
14923 )
14924 )
14925 {
14926 /* we've been shut down for any reason */
14927 /* no special action so far */
14928 }
14929
14930 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14931 LogFlowThisFuncLeave();
14932 return rc;
14933}
14934
14935/**
14936 * Sends the current machine state value to the VM process.
14937 *
14938 * @note Locks this object for reading, then calls a client process.
14939 */
14940HRESULT SessionMachine::i_updateMachineStateOnClient()
14941{
14942 AutoCaller autoCaller(this);
14943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14944
14945 ComPtr<IInternalSessionControl> directControl;
14946 {
14947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14948 AssertReturn(!!mData, E_FAIL);
14949 if (mData->mSession.mLockType == LockType_VM)
14950 directControl = mData->mSession.mDirectControl;
14951
14952 /* directControl may be already set to NULL here in #OnSessionEnd()
14953 * called too early by the direct session process while there is still
14954 * some operation (like deleting the snapshot) in progress. The client
14955 * process in this case is waiting inside Session::close() for the
14956 * "end session" process object to complete, while #uninit() called by
14957 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14958 * operation to complete. For now, we accept this inconsistent behavior
14959 * and simply do nothing here. */
14960
14961 if (mData->mSession.mState == SessionState_Unlocking)
14962 return S_OK;
14963 }
14964
14965 /* ignore notifications sent after #OnSessionEnd() is called */
14966 if (!directControl)
14967 return S_OK;
14968
14969 return directControl->UpdateMachineState(mData->mMachineState);
14970}
14971
14972
14973/*static*/
14974HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14975{
14976 va_list args;
14977 va_start(args, pcszMsg);
14978 HRESULT rc = setErrorInternal(aResultCode,
14979 getStaticClassIID(),
14980 getStaticComponentName(),
14981 Utf8Str(pcszMsg, args),
14982 false /* aWarning */,
14983 true /* aLogIt */);
14984 va_end(args);
14985 return rc;
14986}
14987
14988
14989HRESULT Machine::updateState(MachineState_T aState)
14990{
14991 NOREF(aState);
14992 ReturnComNotImplemented();
14993}
14994
14995HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14996{
14997 NOREF(aProgress);
14998 ReturnComNotImplemented();
14999}
15000
15001HRESULT Machine::endPowerUp(LONG aResult)
15002{
15003 NOREF(aResult);
15004 ReturnComNotImplemented();
15005}
15006
15007HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15008{
15009 NOREF(aProgress);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::endPoweringDown(LONG aResult,
15014 const com::Utf8Str &aErrMsg)
15015{
15016 NOREF(aResult);
15017 NOREF(aErrMsg);
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15022 BOOL *aMatched,
15023 ULONG *aMaskedInterfaces)
15024{
15025 NOREF(aDevice);
15026 NOREF(aMatched);
15027 NOREF(aMaskedInterfaces);
15028 ReturnComNotImplemented();
15029
15030}
15031
15032HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15033{
15034 NOREF(aId); NOREF(aCaptureFilename);
15035 ReturnComNotImplemented();
15036}
15037
15038HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15039 BOOL aDone)
15040{
15041 NOREF(aId);
15042 NOREF(aDone);
15043 ReturnComNotImplemented();
15044}
15045
15046HRESULT Machine::autoCaptureUSBDevices()
15047{
15048 ReturnComNotImplemented();
15049}
15050
15051HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15052{
15053 NOREF(aDone);
15054 ReturnComNotImplemented();
15055}
15056
15057HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15058 ComPtr<IProgress> &aProgress)
15059{
15060 NOREF(aSession);
15061 NOREF(aProgress);
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::finishOnlineMergeMedium()
15066{
15067 ReturnComNotImplemented();
15068}
15069
15070HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15071 std::vector<com::Utf8Str> &aValues,
15072 std::vector<LONG64> &aTimestamps,
15073 std::vector<com::Utf8Str> &aFlags)
15074{
15075 NOREF(aNames);
15076 NOREF(aValues);
15077 NOREF(aTimestamps);
15078 NOREF(aFlags);
15079 ReturnComNotImplemented();
15080}
15081
15082HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15083 const com::Utf8Str &aValue,
15084 LONG64 aTimestamp,
15085 const com::Utf8Str &aFlags)
15086{
15087 NOREF(aName);
15088 NOREF(aValue);
15089 NOREF(aTimestamp);
15090 NOREF(aFlags);
15091 ReturnComNotImplemented();
15092}
15093
15094HRESULT Machine::lockMedia()
15095{
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::unlockMedia()
15100{
15101 ReturnComNotImplemented();
15102}
15103
15104HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15105 ComPtr<IMediumAttachment> &aNewAttachment)
15106{
15107 NOREF(aAttachment);
15108 NOREF(aNewAttachment);
15109 ReturnComNotImplemented();
15110}
15111
15112HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15113 ULONG aCpuUser,
15114 ULONG aCpuKernel,
15115 ULONG aCpuIdle,
15116 ULONG aMemTotal,
15117 ULONG aMemFree,
15118 ULONG aMemBalloon,
15119 ULONG aMemShared,
15120 ULONG aMemCache,
15121 ULONG aPagedTotal,
15122 ULONG aMemAllocTotal,
15123 ULONG aMemFreeTotal,
15124 ULONG aMemBalloonTotal,
15125 ULONG aMemSharedTotal,
15126 ULONG aVmNetRx,
15127 ULONG aVmNetTx)
15128{
15129 NOREF(aValidStats);
15130 NOREF(aCpuUser);
15131 NOREF(aCpuKernel);
15132 NOREF(aCpuIdle);
15133 NOREF(aMemTotal);
15134 NOREF(aMemFree);
15135 NOREF(aMemBalloon);
15136 NOREF(aMemShared);
15137 NOREF(aMemCache);
15138 NOREF(aPagedTotal);
15139 NOREF(aMemAllocTotal);
15140 NOREF(aMemFreeTotal);
15141 NOREF(aMemBalloonTotal);
15142 NOREF(aMemSharedTotal);
15143 NOREF(aVmNetRx);
15144 NOREF(aVmNetTx);
15145 ReturnComNotImplemented();
15146}
15147
15148HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15149 com::Utf8Str &aResult)
15150{
15151 NOREF(aAuthParams);
15152 NOREF(aResult);
15153 ReturnComNotImplemented();
15154}
15155
15156HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15157{
15158 NOREF(aFlags);
15159 ReturnComNotImplemented();
15160}
15161
15162/* This isn't handled entirely by the wrapper generator yet. */
15163#ifdef VBOX_WITH_XPCOM
15164NS_DECL_CLASSINFO(SessionMachine)
15165NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15166
15167NS_DECL_CLASSINFO(SnapshotMachine)
15168NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15169#endif
Note: See TracBrowser for help on using the repository browser.

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