VirtualBox

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

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

Main/Machine: also delete VBoxStartup.log when deleting a VM

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 498.1 KB
Line 
1/* $Id: MachineImpl.cpp 52990 2014-10-08 11:42:41Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "Windows81"
1284 || mUserData->s.strOsType == "Windows81_64"
1285 || mUserData->s.strOsType == "Windows8"
1286 || mUserData->s.strOsType == "Windows8_64"
1287 || mUserData->s.strOsType == "Windows7"
1288 || mUserData->s.strOsType == "Windows7_64"
1289 || mUserData->s.strOsType == "WindowsVista"
1290 || mUserData->s.strOsType == "WindowsVista_64"
1291 || mUserData->s.strOsType == "Windows2012"
1292 || mUserData->s.strOsType == "Windows2012_64"
1293 || mUserData->s.strOsType == "Windows2008"
1294 || mUserData->s.strOsType == "Windows2008_64")
1295 {
1296 *aParavirtProvider = ParavirtProvider_HyperV;
1297 }
1298 else
1299 *aParavirtProvider = ParavirtProvider_None;
1300 break;
1301 }
1302 }
1303 break;
1304 }
1305 }
1306
1307 Assert( *aParavirtProvider == ParavirtProvider_None
1308 || *aParavirtProvider == ParavirtProvider_Minimal
1309 || *aParavirtProvider == ParavirtProvider_HyperV);
1310 return S_OK;
1311}
1312
1313HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1314{
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 aHardwareVersion = mHWData->mHWVersion;
1318
1319 return S_OK;
1320}
1321
1322HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHardwareVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 HRESULT rc = i_checkStateDependency(MutableStateDep);
1334 if (FAILED(rc)) return rc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 mHWData->mHWVersion = aHardwareVersion;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 if (!mHWData->mHardwareUUID.isZero())
1348 aHardwareUUID = mHWData->mHardwareUUID;
1349 else
1350 aHardwareUUID = mData->mUuid;
1351
1352 return S_OK;
1353}
1354
1355HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1356{
1357 if (!aHardwareUUID.isValid())
1358 return E_INVALIDARG;
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = i_checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 i_setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (aHardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = aHardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aMemorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setMemorySize(ULONG aMemorySize)
1385{
1386 /* check RAM limits */
1387 if ( aMemorySize < MM_RAM_MIN_IN_MB
1388 || aMemorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mMemorySize = aMemorySize;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 *aCPUCount = mHWData->mCPUCount;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setCPUCount(ULONG aCPUCount)
1416{
1417 /* check CPU limits */
1418 if ( aCPUCount < SchemaDefs::MinCPUCount
1419 || aCPUCount > SchemaDefs::MaxCPUCount
1420 )
1421 return setError(E_INVALIDARG,
1422 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1423 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1424
1425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1426
1427 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1428 if (mHWData->mCPUHotPlugEnabled)
1429 {
1430 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1431 {
1432 if (mHWData->mCPUAttached[idx])
1433 return setError(E_INVALIDARG,
1434 tr("There is still a CPU attached to socket %lu."
1435 "Detach the CPU before removing the socket"),
1436 aCPUCount, idx+1);
1437 }
1438 }
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCPUCount = aCPUCount;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1460{
1461 HRESULT rc = S_OK;
1462
1463 /* check throttle limits */
1464 if ( aCPUExecutionCap < 1
1465 || aCPUExecutionCap > 100
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1469 aCPUExecutionCap, 1, 100);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 alock.release();
1474 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1475 alock.acquire();
1476 if (FAILED(rc)) return rc;
1477
1478 i_setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1481
1482 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1483 if (Global::IsOnline(mData->mMachineState))
1484 i_saveSettings(NULL);
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1499{
1500 HRESULT rc = S_OK;
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 rc = i_checkStateDependency(MutableStateDep);
1505 if (FAILED(rc)) return rc;
1506
1507 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1508 {
1509 if (aCPUHotPlugEnabled)
1510 {
1511 i_setModified(IsModified_MachineData);
1512 mHWData.backup();
1513
1514 /* Add the amount of CPUs currently attached */
1515 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1516 mHWData->mCPUAttached[i] = true;
1517 }
1518 else
1519 {
1520 /*
1521 * We can disable hotplug only if the amount of maximum CPUs is equal
1522 * to the amount of attached CPUs
1523 */
1524 unsigned cCpusAttached = 0;
1525 unsigned iHighestId = 0;
1526
1527 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1528 {
1529 if (mHWData->mCPUAttached[i])
1530 {
1531 cCpusAttached++;
1532 iHighestId = i;
1533 }
1534 }
1535
1536 if ( (cCpusAttached != mHWData->mCPUCount)
1537 || (iHighestId >= mHWData->mCPUCount))
1538 return setError(E_INVALIDARG,
1539 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 }
1544 }
1545
1546 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1547
1548 return rc;
1549}
1550
1551HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1552{
1553#ifdef VBOX_WITH_USB_CARDREADER
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1557
1558 return S_OK;
1559#else
1560 NOREF(aEmulatedUSBCardReaderEnabled);
1561 return E_NOTIMPL;
1562#endif
1563}
1564
1565HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1566{
1567#ifdef VBOX_WITH_USB_CARDREADER
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 HRESULT rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1576
1577 return S_OK;
1578#else
1579 NOREF(aEmulatedUSBCardReaderEnabled);
1580 return E_NOTIMPL;
1581#endif
1582}
1583
1584HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1585{
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *aHPETEnabled = mHWData->mHPETEnabled;
1589
1590 return S_OK;
1591}
1592
1593HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 rc = i_checkStateDependency(MutableStateDep);
1600 if (FAILED(rc)) return rc;
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604
1605 mHWData->mHPETEnabled = aHPETEnabled;
1606
1607 return rc;
1608}
1609
1610HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1611{
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1615 return S_OK;
1616}
1617
1618HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1619{
1620 HRESULT rc = S_OK;
1621
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1627
1628 alock.release();
1629 rc = i_onVideoCaptureChange();
1630 alock.acquire();
1631 if (FAILED(rc))
1632 {
1633 /*
1634 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1635 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1636 * determine if it should start or stop capturing. Therefore we need to manually
1637 * undo change.
1638 */
1639 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1640 return rc;
1641 }
1642
1643 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1644 if (Global::IsOnline(mData->mMachineState))
1645 i_saveSettings(NULL);
1646
1647 return rc;
1648}
1649
1650HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1654 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1655 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1660{
1661 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1662 bool fChanged = false;
1663
1664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1667 {
1668 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1669 {
1670 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1671 fChanged = true;
1672 }
1673 }
1674 if (fChanged)
1675 {
1676 alock.release();
1677 HRESULT rc = i_onVideoCaptureChange();
1678 alock.acquire();
1679 if (FAILED(rc)) return rc;
1680 i_setModified(IsModified_MachineData);
1681
1682 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1683 if (Global::IsOnline(mData->mMachineState))
1684 i_saveSettings(NULL);
1685 }
1686
1687 return S_OK;
1688}
1689
1690HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1691{
1692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1693 if (mHWData->mVideoCaptureFile.isEmpty())
1694 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1695 else
1696 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1697 return S_OK;
1698}
1699
1700HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1701{
1702 Utf8Str strFile(aVideoCaptureFile);
1703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 if ( Global::IsOnline(mData->mMachineState)
1706 && mHWData->mVideoCaptureEnabled)
1707 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1708
1709 if (!RTPathStartsWithRoot(strFile.c_str()))
1710 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1711
1712 if (!strFile.isEmpty())
1713 {
1714 Utf8Str defaultFile;
1715 i_getDefaultVideoCaptureFile(defaultFile);
1716 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1717 strFile.setNull();
1718 }
1719
1720 i_setModified(IsModified_MachineData);
1721 mHWData.backup();
1722 mHWData->mVideoCaptureFile = strFile;
1723
1724 return S_OK;
1725}
1726
1727HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1731 return S_OK;
1732}
1733
1734HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1735{
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 if ( Global::IsOnline(mData->mMachineState)
1739 && mHWData->mVideoCaptureEnabled)
1740 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1741
1742 i_setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1745
1746 return S_OK;
1747}
1748
1749HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1750{
1751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1752 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1753 return S_OK;
1754}
1755
1756HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1757{
1758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1759
1760 if ( Global::IsOnline(mData->mMachineState)
1761 && mHWData->mVideoCaptureEnabled)
1762 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1763
1764 i_setModified(IsModified_MachineData);
1765 mHWData.backup();
1766 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1767
1768 return S_OK;
1769}
1770
1771HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1772{
1773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1774 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1775 return S_OK;
1776}
1777
1778HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1779{
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 if ( Global::IsOnline(mData->mMachineState)
1783 && mHWData->mVideoCaptureEnabled)
1784 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1785
1786 i_setModified(IsModified_MachineData);
1787 mHWData.backup();
1788 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1789
1790 return S_OK;
1791}
1792
1793HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1794{
1795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1796 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1797 return S_OK;
1798}
1799
1800HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1801{
1802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 if ( Global::IsOnline(mData->mMachineState)
1805 && mHWData->mVideoCaptureEnabled)
1806 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1807
1808 i_setModified(IsModified_MachineData);
1809 mHWData.backup();
1810 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1811
1812 return S_OK;
1813}
1814
1815HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1816{
1817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1818 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1819 return S_OK;
1820}
1821
1822HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1823{
1824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1825
1826 if ( Global::IsOnline(mData->mMachineState)
1827 && mHWData->mVideoCaptureEnabled)
1828 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862
1863 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1864 return S_OK;
1865}
1866
1867HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1868{
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 if ( Global::IsOnline(mData->mMachineState)
1872 && mHWData->mVideoCaptureEnabled)
1873 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1874
1875 i_setModified(IsModified_MachineData);
1876 mHWData.backup();
1877 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1892{
1893 switch (aGraphicsControllerType)
1894 {
1895 case GraphicsControllerType_Null:
1896 case GraphicsControllerType_VBoxVGA:
1897#ifdef VBOX_WITH_VMSVGA
1898 case GraphicsControllerType_VMSVGA:
1899#endif
1900 break;
1901 default:
1902 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1903 }
1904
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 HRESULT rc = i_checkStateDependency(MutableStateDep);
1908 if (FAILED(rc)) return rc;
1909
1910 i_setModified(IsModified_MachineData);
1911 mHWData.backup();
1912 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1913
1914 return S_OK;
1915}
1916
1917HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1918{
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 *aVRAMSize = mHWData->mVRAMSize;
1922
1923 return S_OK;
1924}
1925
1926HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1927{
1928 /* check VRAM limits */
1929 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1930 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1931 return setError(E_INVALIDARG,
1932 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1933 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1934
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 HRESULT rc = i_checkStateDependency(MutableStateDep);
1938 if (FAILED(rc)) return rc;
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVRAMSize = aVRAMSize;
1943
1944 return S_OK;
1945}
1946
1947/** @todo this method should not be public */
1948HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1949{
1950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1951
1952 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1953
1954 return S_OK;
1955}
1956
1957/**
1958 * Set the memory balloon size.
1959 *
1960 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1961 * we have to make sure that we never call IGuest from here.
1962 */
1963HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1964{
1965 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1966#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1967 /* check limits */
1968 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1969 return setError(E_INVALIDARG,
1970 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1971 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1972
1973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1978
1979 return S_OK;
1980#else
1981 NOREF(aMemoryBalloonSize);
1982 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1983#endif
1984}
1985
1986HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1987{
1988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1991 return S_OK;
1992}
1993
1994HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1995{
1996#ifdef VBOX_WITH_PAGE_SHARING
1997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2000 i_setModified(IsModified_MachineData);
2001 mHWData.backup();
2002 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2003 return S_OK;
2004#else
2005 NOREF(aPageFusionEnabled);
2006 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2007#endif
2008}
2009
2010HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2011{
2012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2013
2014 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2015
2016 return S_OK;
2017}
2018
2019HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2020{
2021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2022
2023 HRESULT rc = i_checkStateDependency(MutableStateDep);
2024 if (FAILED(rc)) return rc;
2025
2026 /** @todo check validity! */
2027
2028 i_setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2031
2032 return S_OK;
2033}
2034
2035
2036HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2037{
2038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2039
2040 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2041
2042 return S_OK;
2043}
2044
2045HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2046{
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 HRESULT rc = i_checkStateDependency(MutableStateDep);
2050 if (FAILED(rc)) return rc;
2051
2052 /** @todo check validity! */
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2056
2057 return S_OK;
2058}
2059
2060HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2061{
2062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 *aMonitorCount = mHWData->mMonitorCount;
2065
2066 return S_OK;
2067}
2068
2069HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2070{
2071 /* make sure monitor count is a sensible number */
2072 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2073 return setError(E_INVALIDARG,
2074 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2075 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2076
2077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2078
2079 HRESULT rc = i_checkStateDependency(MutableStateDep);
2080 if (FAILED(rc)) return rc;
2081
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mMonitorCount = aMonitorCount;
2085
2086 return S_OK;
2087}
2088
2089HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2090{
2091 /* mBIOSSettings is constant during life time, no need to lock */
2092 aBIOSSettings = mBIOSSettings;
2093
2094 return S_OK;
2095}
2096
2097HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2098{
2099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2100
2101 switch (aProperty)
2102 {
2103 case CPUPropertyType_PAE:
2104 *aValue = mHWData->mPAEEnabled;
2105 break;
2106
2107 case CPUPropertyType_Synthetic:
2108 *aValue = mHWData->mSyntheticCpu;
2109 break;
2110
2111 case CPUPropertyType_LongMode:
2112 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2113 *aValue = TRUE;
2114 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2115 *aValue = FALSE;
2116#if HC_ARCH_BITS == 64
2117 else
2118 *aValue = TRUE;
2119#else
2120 else
2121 {
2122 *aValue = FALSE;
2123
2124 ComPtr<IGuestOSType> ptrGuestOSType;
2125 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2126 if (SUCCEEDED(hrc2))
2127 {
2128 BOOL fIs64Bit = FALSE;
2129 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2130 if (SUCCEEDED(hrc2) && fIs64Bit)
2131 {
2132 ComObjPtr<Host> ptrHost = mParent->i_host();
2133 alock.release();
2134
2135 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2136 if (FAILED(hrc2))
2137 *aValue = FALSE;
2138 }
2139 }
2140 }
2141#endif
2142 break;
2143
2144 case CPUPropertyType_TripleFaultReset:
2145 *aValue = mHWData->mTripleFaultReset;
2146 break;
2147
2148 default:
2149 return E_INVALIDARG;
2150 }
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 switch (aProperty)
2162 {
2163 case CPUPropertyType_PAE:
2164 i_setModified(IsModified_MachineData);
2165 mHWData.backup();
2166 mHWData->mPAEEnabled = !!aValue;
2167 break;
2168
2169 case CPUPropertyType_Synthetic:
2170 i_setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mSyntheticCpu = !!aValue;
2173 break;
2174
2175 case CPUPropertyType_LongMode:
2176 i_setModified(IsModified_MachineData);
2177 mHWData.backup();
2178 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2179 break;
2180
2181 case CPUPropertyType_TripleFaultReset:
2182 i_setModified(IsModified_MachineData);
2183 mHWData.backup();
2184 mHWData->mTripleFaultReset = !!aValue;
2185 break;
2186
2187 default:
2188 return E_INVALIDARG;
2189 }
2190 return S_OK;
2191}
2192
2193HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2194{
2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 switch(aId)
2198 {
2199 case 0x0:
2200 case 0x1:
2201 case 0x2:
2202 case 0x3:
2203 case 0x4:
2204 case 0x5:
2205 case 0x6:
2206 case 0x7:
2207 case 0x8:
2208 case 0x9:
2209 case 0xA:
2210 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2211 return E_INVALIDARG;
2212
2213 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2214 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2215 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2216 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2217 break;
2218
2219 case 0x80000000:
2220 case 0x80000001:
2221 case 0x80000002:
2222 case 0x80000003:
2223 case 0x80000004:
2224 case 0x80000005:
2225 case 0x80000006:
2226 case 0x80000007:
2227 case 0x80000008:
2228 case 0x80000009:
2229 case 0x8000000A:
2230 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2231 return E_INVALIDARG;
2232
2233 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2234 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2235 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2236 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2237 break;
2238
2239 default:
2240 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2241 }
2242 return S_OK;
2243}
2244
2245
2246HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2247{
2248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2249
2250 HRESULT rc = i_checkStateDependency(MutableStateDep);
2251 if (FAILED(rc)) return rc;
2252
2253 switch(aId)
2254 {
2255 case 0x0:
2256 case 0x1:
2257 case 0x2:
2258 case 0x3:
2259 case 0x4:
2260 case 0x5:
2261 case 0x6:
2262 case 0x7:
2263 case 0x8:
2264 case 0x9:
2265 case 0xA:
2266 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2267 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2268 i_setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2271 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2272 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2273 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2274 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2275 break;
2276
2277 case 0x80000000:
2278 case 0x80000001:
2279 case 0x80000002:
2280 case 0x80000003:
2281 case 0x80000004:
2282 case 0x80000005:
2283 case 0x80000006:
2284 case 0x80000007:
2285 case 0x80000008:
2286 case 0x80000009:
2287 case 0x8000000A:
2288 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2289 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2293 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2294 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2295 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2296 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2297 break;
2298
2299 default:
2300 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2301 }
2302 return S_OK;
2303}
2304
2305HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2306{
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 switch(aId)
2313 {
2314 case 0x0:
2315 case 0x1:
2316 case 0x2:
2317 case 0x3:
2318 case 0x4:
2319 case 0x5:
2320 case 0x6:
2321 case 0x7:
2322 case 0x8:
2323 case 0x9:
2324 case 0xA:
2325 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2326 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 /* Invalidate leaf. */
2330 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2331 break;
2332
2333 case 0x80000000:
2334 case 0x80000001:
2335 case 0x80000002:
2336 case 0x80000003:
2337 case 0x80000004:
2338 case 0x80000005:
2339 case 0x80000006:
2340 case 0x80000007:
2341 case 0x80000008:
2342 case 0x80000009:
2343 case 0x8000000A:
2344 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2345 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 /* Invalidate leaf. */
2349 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2350 break;
2351
2352 default:
2353 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2354 }
2355 return S_OK;
2356}
2357
2358HRESULT Machine::removeAllCPUIDLeaves()
2359{
2360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2361
2362 HRESULT rc = i_checkStateDependency(MutableStateDep);
2363 if (FAILED(rc)) return rc;
2364
2365 i_setModified(IsModified_MachineData);
2366 mHWData.backup();
2367
2368 /* Invalidate all standard leafs. */
2369 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2370 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2371
2372 /* Invalidate all extended leafs. */
2373 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2374 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2375
2376 return S_OK;
2377}
2378HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2379{
2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2381
2382 switch(aProperty)
2383 {
2384 case HWVirtExPropertyType_Enabled:
2385 *aValue = mHWData->mHWVirtExEnabled;
2386 break;
2387
2388 case HWVirtExPropertyType_VPID:
2389 *aValue = mHWData->mHWVirtExVPIDEnabled;
2390 break;
2391
2392 case HWVirtExPropertyType_NestedPaging:
2393 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2394 break;
2395
2396 case HWVirtExPropertyType_UnrestrictedExecution:
2397 *aValue = mHWData->mHWVirtExUXEnabled;
2398 break;
2399
2400 case HWVirtExPropertyType_LargePages:
2401 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2402#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2403 *aValue = FALSE;
2404#endif
2405 break;
2406
2407 case HWVirtExPropertyType_Force:
2408 *aValue = mHWData->mHWVirtExForceEnabled;
2409 break;
2410
2411 default:
2412 return E_INVALIDARG;
2413 }
2414 return S_OK;
2415}
2416
2417HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2418{
2419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2420
2421 HRESULT rc = i_checkStateDependency(MutableStateDep);
2422 if (FAILED(rc)) return rc;
2423
2424 switch(aProperty)
2425 {
2426 case HWVirtExPropertyType_Enabled:
2427 i_setModified(IsModified_MachineData);
2428 mHWData.backup();
2429 mHWData->mHWVirtExEnabled = !!aValue;
2430 break;
2431
2432 case HWVirtExPropertyType_VPID:
2433 i_setModified(IsModified_MachineData);
2434 mHWData.backup();
2435 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2436 break;
2437
2438 case HWVirtExPropertyType_NestedPaging:
2439 i_setModified(IsModified_MachineData);
2440 mHWData.backup();
2441 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2442 break;
2443
2444 case HWVirtExPropertyType_UnrestrictedExecution:
2445 i_setModified(IsModified_MachineData);
2446 mHWData.backup();
2447 mHWData->mHWVirtExUXEnabled = !!aValue;
2448 break;
2449
2450 case HWVirtExPropertyType_LargePages:
2451 i_setModified(IsModified_MachineData);
2452 mHWData.backup();
2453 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2454 break;
2455
2456 case HWVirtExPropertyType_Force:
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459 mHWData->mHWVirtExForceEnabled = !!aValue;
2460 break;
2461
2462 default:
2463 return E_INVALIDARG;
2464 }
2465
2466 return S_OK;
2467}
2468
2469HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2470{
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
2473 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2474
2475 return S_OK;
2476}
2477
2478HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2479{
2480 /* @todo (r=dmik):
2481 * 1. Allow to change the name of the snapshot folder containing snapshots
2482 * 2. Rename the folder on disk instead of just changing the property
2483 * value (to be smart and not to leave garbage). Note that it cannot be
2484 * done here because the change may be rolled back. Thus, the right
2485 * place is #saveSettings().
2486 */
2487
2488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 HRESULT rc = i_checkStateDependency(MutableStateDep);
2491 if (FAILED(rc)) return rc;
2492
2493 if (!mData->mCurrentSnapshot.isNull())
2494 return setError(E_FAIL,
2495 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2496
2497 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2498
2499 if (strSnapshotFolder.isEmpty())
2500 strSnapshotFolder = "Snapshots";
2501 int vrc = i_calculateFullPath(strSnapshotFolder,
2502 strSnapshotFolder);
2503 if (RT_FAILURE(vrc))
2504 return setError(E_FAIL,
2505 tr("Invalid snapshot folder '%s' (%Rrc)"),
2506 strSnapshotFolder.c_str(), vrc);
2507
2508 i_setModified(IsModified_MachineData);
2509 mUserData.backup();
2510
2511 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2512
2513 return S_OK;
2514}
2515
2516HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 aMediumAttachments.resize(mMediaData->mAttachments.size());
2521 size_t i = 0;
2522 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2523 it != mMediaData->mAttachments.end(); ++it, ++i)
2524 aMediumAttachments[i] = *it;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 Assert(!!mVRDEServer);
2534
2535 aVRDEServer = mVRDEServer;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 aAudioAdapter = mAudioAdapter;
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2550{
2551#ifdef VBOX_WITH_VUSB
2552 clearError();
2553 MultiResult rc(S_OK);
2554
2555# ifdef VBOX_WITH_USB
2556 rc = mParent->i_host()->i_checkUSBProxyService();
2557 if (FAILED(rc)) return rc;
2558# endif
2559
2560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 USBControllerList data = *mUSBControllers.data();
2563 aUSBControllers.resize(data.size());
2564 size_t i = 0;
2565 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2566 aUSBControllers[i] = *it;
2567
2568 return S_OK;
2569#else
2570 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2571 * extended error info to indicate that USB is simply not available
2572 * (w/o treating it as a failure), for example, as in OSE */
2573 NOREF(aUSBControllers);
2574 ReturnComNotImplemented();
2575#endif /* VBOX_WITH_VUSB */
2576}
2577
2578HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2579{
2580#ifdef VBOX_WITH_VUSB
2581 clearError();
2582 MultiResult rc(S_OK);
2583
2584# ifdef VBOX_WITH_USB
2585 rc = mParent->i_host()->i_checkUSBProxyService();
2586 if (FAILED(rc)) return rc;
2587# endif
2588
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 aUSBDeviceFilters = mUSBDeviceFilters;
2592 return rc;
2593#else
2594 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2595 * extended error info to indicate that USB is simply not available
2596 * (w/o treating it as a failure), for example, as in OSE */
2597 NOREF(aUSBDeviceFilters);
2598 ReturnComNotImplemented();
2599#endif /* VBOX_WITH_VUSB */
2600}
2601
2602HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2603{
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 aSettingsFilePath = mData->m_strConfigFileFull;
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 HRESULT rc = i_checkStateDependency(MutableStateDep);
2616 if (FAILED(rc)) return rc;
2617
2618 if (!mData->pMachineConfigFile->fileExists())
2619 // this is a new machine, and no config file exists yet:
2620 *aSettingsModified = TRUE;
2621 else
2622 *aSettingsModified = (mData->flModifications != 0);
2623
2624 return S_OK;
2625}
2626
2627HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2628{
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aSessionState = mData->mSession.mState;
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 aSessionType = mData->mSession.mType;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 *aSessionPID = mData->mSession.mPID;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getState(MachineState_T *aState)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 *aState = mData->mMachineState;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aStateFilePath = mSSData->strStateFilePath;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 i_getLogFolder(aLogFolder);
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 aCurrentSnapshot = mData->mCurrentSnapshot;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2705 ? 0
2706 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 /* Note: for machines with no snapshots, we always return FALSE
2716 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2717 * reasons :) */
2718
2719 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2720 ? FALSE
2721 : mData->mCurrentStateModified;
2722
2723 return S_OK;
2724}
2725
2726HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2727{
2728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 aSharedFolders.resize(mHWData->mSharedFolders.size());
2731 size_t i = 0;
2732 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2733 it != mHWData->mSharedFolders.end(); ++i, ++it)
2734 aSharedFolders[i] = *it;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 *aClipboardMode = mHWData->mClipboardMode;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2749{
2750 HRESULT rc = S_OK;
2751
2752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 alock.release();
2755 rc = i_onClipboardModeChange(aClipboardMode);
2756 alock.acquire();
2757 if (FAILED(rc)) return rc;
2758
2759 i_setModified(IsModified_MachineData);
2760 mHWData.backup();
2761 mHWData->mClipboardMode = aClipboardMode;
2762
2763 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2764 if (Global::IsOnline(mData->mMachineState))
2765 i_saveSettings(NULL);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 *aDnDMode = mHWData->mDnDMode;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2780{
2781 HRESULT rc = S_OK;
2782
2783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 alock.release();
2786 rc = i_onDnDModeChange(aDnDMode);
2787
2788 alock.acquire();
2789 if (FAILED(rc)) return rc;
2790
2791 i_setModified(IsModified_MachineData);
2792 mHWData.backup();
2793 mHWData->mDnDMode = aDnDMode;
2794
2795 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2796 if (Global::IsOnline(mData->mMachineState))
2797 i_saveSettings(NULL);
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 try
2807 {
2808 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2809 }
2810 catch (...)
2811 {
2812 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2813 }
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2819{
2820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 HRESULT rc = i_checkStateDependency(MutableStateDep);
2823 if (FAILED(rc)) return rc;
2824
2825 i_setModified(IsModified_MachineData);
2826 mHWData.backup();
2827 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2828 return rc;
2829}
2830
2831HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 StorageControllerList data = *mStorageControllers.data();
2835 size_t i = 0;
2836 aStorageControllers.resize(data.size());
2837 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2838 aStorageControllers[i] = *it;
2839 return S_OK;
2840}
2841
2842HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2843{
2844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2845
2846 *aEnabled = mUserData->s.fTeleporterEnabled;
2847
2848 return S_OK;
2849}
2850
2851HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2852{
2853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2854
2855 /* Only allow it to be set to true when PoweredOff or Aborted.
2856 (Clearing it is always permitted.) */
2857 if ( aTeleporterEnabled
2858 && mData->mRegistered
2859 && ( !i_isSessionMachine()
2860 || ( mData->mMachineState != MachineState_PoweredOff
2861 && mData->mMachineState != MachineState_Teleported
2862 && mData->mMachineState != MachineState_Aborted
2863 )
2864 )
2865 )
2866 return setError(VBOX_E_INVALID_VM_STATE,
2867 tr("The machine is not powered off (state is %s)"),
2868 Global::stringifyMachineState(mData->mMachineState));
2869
2870 i_setModified(IsModified_MachineData);
2871 mUserData.backup();
2872 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2878{
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2882
2883 return S_OK;
2884}
2885
2886HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2887{
2888 if (aTeleporterPort >= _64K)
2889 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2890
2891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 HRESULT rc = i_checkStateDependency(MutableStateDep);
2894 if (FAILED(rc)) return rc;
2895
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2913{
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 HRESULT rc = i_checkStateDependency(MutableStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mUserData.backup();
2921 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2927{
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2935{
2936 /*
2937 * Hash the password first.
2938 */
2939 com::Utf8Str aT = aTeleporterPassword;
2940
2941 if (!aT.isEmpty())
2942 {
2943 if (VBoxIsPasswordHashed(&aT))
2944 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2945 VBoxHashPassword(&aT);
2946 }
2947
2948 /*
2949 * Do the update.
2950 */
2951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2952 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2953 if (SUCCEEDED(hrc))
2954 {
2955 i_setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterPassword = aT;
2958 }
2959
2960 return hrc;
2961}
2962
2963HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2964{
2965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2972{
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 /* @todo deal with running state change. */
2976 HRESULT rc = i_checkStateDependency(MutableStateDep);
2977 if (FAILED(rc)) return rc;
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2982 return S_OK;
2983}
2984
2985HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2986{
2987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2988
2989 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2990 return S_OK;
2991}
2992
2993HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2994{
2995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2996
2997 /* @todo deal with running state change. */
2998 HRESULT rc = i_checkStateDependency(MutableStateDep);
2999 if (FAILED(rc)) return rc;
3000
3001 i_setModified(IsModified_MachineData);
3002 mUserData.backup();
3003 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3008{
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3012 return S_OK;
3013}
3014
3015HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3016{
3017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3018
3019 /* @todo deal with running state change. */
3020 HRESULT rc = i_checkStateDependency(MutableStateDep);
3021 if (FAILED(rc)) return rc;
3022
3023 i_setModified(IsModified_MachineData);
3024 mUserData.backup();
3025 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3030{
3031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3034
3035 return S_OK;
3036}
3037
3038HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3039{
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 /* @todo deal with running state change. */
3043 HRESULT rc = i_checkStateDependency(MutableStateDep);
3044 if (FAILED(rc)) return rc;
3045
3046 i_setModified(IsModified_MachineData);
3047 mUserData.backup();
3048 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3054{
3055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3058 return S_OK;
3059}
3060
3061HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3062{
3063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 /* @todo deal with running state change. */
3066 HRESULT rc = i_checkStateDependency(MutableStateDep);
3067 if (FAILED(rc)) return rc;
3068
3069 i_setModified(IsModified_MachineData);
3070 mUserData.backup();
3071 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3072 return S_OK;
3073}
3074
3075HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3076{
3077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3080
3081 return S_OK;
3082}
3083
3084HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3085{
3086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3087
3088 /* Only allow it to be set to true when PoweredOff or Aborted.
3089 (Clearing it is always permitted.) */
3090 if ( aRTCUseUTC
3091 && mData->mRegistered
3092 && ( !i_isSessionMachine()
3093 || ( mData->mMachineState != MachineState_PoweredOff
3094 && mData->mMachineState != MachineState_Teleported
3095 && mData->mMachineState != MachineState_Aborted
3096 )
3097 )
3098 )
3099 return setError(VBOX_E_INVALID_VM_STATE,
3100 tr("The machine is not powered off (state is %s)"),
3101 Global::stringifyMachineState(mData->mMachineState));
3102
3103 i_setModified(IsModified_MachineData);
3104 mUserData.backup();
3105 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3106
3107 return S_OK;
3108}
3109
3110HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3111{
3112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3113
3114 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3115
3116 return S_OK;
3117}
3118
3119HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3120{
3121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 HRESULT rc = i_checkStateDependency(MutableStateDep);
3124 if (FAILED(rc)) return rc;
3125
3126 i_setModified(IsModified_MachineData);
3127 mHWData.backup();
3128 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3129
3130 return S_OK;
3131}
3132
3133HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3134{
3135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3136
3137 *aIOCacheSize = mHWData->mIOCacheSize;
3138
3139 return S_OK;
3140}
3141
3142HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3143{
3144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3145
3146 HRESULT rc = i_checkStateDependency(MutableStateDep);
3147 if (FAILED(rc)) return rc;
3148
3149 i_setModified(IsModified_MachineData);
3150 mHWData.backup();
3151 mHWData->mIOCacheSize = aIOCacheSize;
3152
3153 return S_OK;
3154}
3155
3156
3157/**
3158 * @note Locks objects!
3159 */
3160HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3161 LockType_T aLockType)
3162
3163{
3164 /* check the session state */
3165 SessionState_T state;
3166 HRESULT rc = aSession->COMGETTER(State)(&state);
3167 if (FAILED(rc)) return rc;
3168
3169 if (state != SessionState_Unlocked)
3170 return setError(VBOX_E_INVALID_OBJECT_STATE,
3171 tr("The given session is busy"));
3172
3173 // get the client's IInternalSessionControl interface
3174 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3175 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3176 E_INVALIDARG);
3177
3178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 if (!mData->mRegistered)
3181 return setError(E_UNEXPECTED,
3182 tr("The machine '%s' is not registered"),
3183 mUserData->s.strName.c_str());
3184
3185 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3186
3187 SessionState_T oldState = mData->mSession.mState;
3188 /* Hack: in case the session is closing and there is a progress object
3189 * which allows waiting for the session to be closed, take the opportunity
3190 * and do a limited wait (max. 1 second). This helps a lot when the system
3191 * is busy and thus session closing can take a little while. */
3192 if ( mData->mSession.mState == SessionState_Unlocking
3193 && mData->mSession.mProgress)
3194 {
3195 alock.release();
3196 mData->mSession.mProgress->WaitForCompletion(1000);
3197 alock.acquire();
3198 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3199 }
3200
3201 // try again now
3202 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3203 // (i.e. session machine exists)
3204 && (aLockType == LockType_Shared) // caller wants a shared link to the
3205 // existing session that holds the write lock:
3206 )
3207 {
3208 // OK, share the session... we are now dealing with three processes:
3209 // 1) VBoxSVC (where this code runs);
3210 // 2) process C: the caller's client process (who wants a shared session);
3211 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3212
3213 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3214 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3215 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3216 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3217 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3218
3219 /*
3220 * Release the lock before calling the client process. It's safe here
3221 * since the only thing to do after we get the lock again is to add
3222 * the remote control to the list (which doesn't directly influence
3223 * anything).
3224 */
3225 alock.release();
3226
3227 // get the console of the session holding the write lock (this is a remote call)
3228 ComPtr<IConsole> pConsoleW;
3229 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3230 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3231 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3232 if (FAILED(rc))
3233 // the failure may occur w/o any error info (from RPC), so provide one
3234 return setError(VBOX_E_VM_ERROR,
3235 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3236
3237 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3238
3239 // share the session machine and W's console with the caller's session
3240 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3241 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3242 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3243
3244 if (FAILED(rc))
3245 // the failure may occur w/o any error info (from RPC), so provide one
3246 return setError(VBOX_E_VM_ERROR,
3247 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3248 alock.acquire();
3249
3250 // need to revalidate the state after acquiring the lock again
3251 if (mData->mSession.mState != SessionState_Locked)
3252 {
3253 pSessionControl->Uninitialize();
3254 return setError(VBOX_E_INVALID_SESSION_STATE,
3255 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3256 mUserData->s.strName.c_str());
3257 }
3258
3259 // add the caller's session to the list
3260 mData->mSession.mRemoteControls.push_back(pSessionControl);
3261 }
3262 else if ( mData->mSession.mState == SessionState_Locked
3263 || mData->mSession.mState == SessionState_Unlocking
3264 )
3265 {
3266 // sharing not permitted, or machine still unlocking:
3267 return setError(VBOX_E_INVALID_OBJECT_STATE,
3268 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3269 mUserData->s.strName.c_str());
3270 }
3271 else
3272 {
3273 // machine is not locked: then write-lock the machine (create the session machine)
3274
3275 // must not be busy
3276 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3277
3278 // get the caller's session PID
3279 RTPROCESS pid = NIL_RTPROCESS;
3280 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3281 pSessionControl->GetPID((ULONG*)&pid);
3282 Assert(pid != NIL_RTPROCESS);
3283
3284 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3285
3286 if (fLaunchingVMProcess)
3287 {
3288 if (mData->mSession.mPID == NIL_RTPROCESS)
3289 {
3290 // two or more clients racing for a lock, the one which set the
3291 // session state to Spawning will win, the others will get an
3292 // error as we can't decide here if waiting a little would help
3293 // (only for shared locks this would avoid an error)
3294 return setError(VBOX_E_INVALID_OBJECT_STATE,
3295 tr("The machine '%s' already has a lock request pending"),
3296 mUserData->s.strName.c_str());
3297 }
3298
3299 // this machine is awaiting for a spawning session to be opened:
3300 // then the calling process must be the one that got started by
3301 // LaunchVMProcess()
3302
3303 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3304 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3305
3306#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3307 /* Hardened windows builds spawns three processes when a VM is
3308 launched, the 3rd one is the one that will end up here. */
3309 RTPROCESS ppid;
3310 int rc = RTProcQueryParent(pid, &ppid);
3311 if (RT_SUCCESS(rc))
3312 rc = RTProcQueryParent(ppid, &ppid);
3313 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3314 || rc == VERR_ACCESS_DENIED)
3315 {
3316 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3317 mData->mSession.mPID = pid;
3318 }
3319#endif
3320
3321 if (mData->mSession.mPID != pid)
3322 return setError(E_ACCESSDENIED,
3323 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3324 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3325 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3326 }
3327
3328 // create the mutable SessionMachine from the current machine
3329 ComObjPtr<SessionMachine> sessionMachine;
3330 sessionMachine.createObject();
3331 rc = sessionMachine->init(this);
3332 AssertComRC(rc);
3333
3334 /* NOTE: doing return from this function after this point but
3335 * before the end is forbidden since it may call SessionMachine::uninit()
3336 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3337 * lock while still holding the Machine lock in alock so that a deadlock
3338 * is possible due to the wrong lock order. */
3339
3340 if (SUCCEEDED(rc))
3341 {
3342 /*
3343 * Set the session state to Spawning to protect against subsequent
3344 * attempts to open a session and to unregister the machine after
3345 * we release the lock.
3346 */
3347 SessionState_T origState = mData->mSession.mState;
3348 mData->mSession.mState = SessionState_Spawning;
3349
3350#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3351 /* Get the client token ID to be passed to the client process */
3352 Utf8Str strTokenId;
3353 sessionMachine->i_getTokenId(strTokenId);
3354 Assert(!strTokenId.isEmpty());
3355#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3356 /* Get the client token to be passed to the client process */
3357 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3358 /* The token is now "owned" by pToken, fix refcount */
3359 if (!pToken.isNull())
3360 pToken->Release();
3361#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3362
3363 /*
3364 * Release the lock before calling the client process -- it will call
3365 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3366 * because the state is Spawning, so that LaunchVMProcess() and
3367 * LockMachine() calls will fail. This method, called before we
3368 * acquire the lock again, will fail because of the wrong PID.
3369 *
3370 * Note that mData->mSession.mRemoteControls accessed outside
3371 * the lock may not be modified when state is Spawning, so it's safe.
3372 */
3373 alock.release();
3374
3375 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3376#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3377 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3378#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3379 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3380 /* Now the token is owned by the client process. */
3381 pToken.setNull();
3382#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3383 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3384
3385 /* The failure may occur w/o any error info (from RPC), so provide one */
3386 if (FAILED(rc))
3387 setError(VBOX_E_VM_ERROR,
3388 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3389
3390 if ( SUCCEEDED(rc)
3391 && fLaunchingVMProcess
3392 )
3393 {
3394 /* complete the remote session initialization */
3395
3396 /* get the console from the direct session */
3397 ComPtr<IConsole> console;
3398 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3399 ComAssertComRC(rc);
3400
3401 if (SUCCEEDED(rc) && !console)
3402 {
3403 ComAssert(!!console);
3404 rc = E_FAIL;
3405 }
3406
3407 /* assign machine & console to the remote session */
3408 if (SUCCEEDED(rc))
3409 {
3410 /*
3411 * after LaunchVMProcess(), the first and the only
3412 * entry in remoteControls is that remote session
3413 */
3414 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3415 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3416 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3417
3418 /* The failure may occur w/o any error info (from RPC), so provide one */
3419 if (FAILED(rc))
3420 setError(VBOX_E_VM_ERROR,
3421 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3422 }
3423
3424 if (FAILED(rc))
3425 pSessionControl->Uninitialize();
3426 }
3427
3428 /* acquire the lock again */
3429 alock.acquire();
3430
3431 /* Restore the session state */
3432 mData->mSession.mState = origState;
3433 }
3434
3435 // finalize spawning anyway (this is why we don't return on errors above)
3436 if (fLaunchingVMProcess)
3437 {
3438 /* Note that the progress object is finalized later */
3439 /** @todo Consider checking mData->mSession.mProgress for cancellation
3440 * around here. */
3441
3442 /* We don't reset mSession.mPID here because it is necessary for
3443 * SessionMachine::uninit() to reap the child process later. */
3444
3445 if (FAILED(rc))
3446 {
3447 /* Close the remote session, remove the remote control from the list
3448 * and reset session state to Closed (@note keep the code in sync
3449 * with the relevant part in checkForSpawnFailure()). */
3450
3451 Assert(mData->mSession.mRemoteControls.size() == 1);
3452 if (mData->mSession.mRemoteControls.size() == 1)
3453 {
3454 ErrorInfoKeeper eik;
3455 mData->mSession.mRemoteControls.front()->Uninitialize();
3456 }
3457
3458 mData->mSession.mRemoteControls.clear();
3459 mData->mSession.mState = SessionState_Unlocked;
3460 }
3461 }
3462 else
3463 {
3464 /* memorize PID of the directly opened session */
3465 if (SUCCEEDED(rc))
3466 mData->mSession.mPID = pid;
3467 }
3468
3469 if (SUCCEEDED(rc))
3470 {
3471 /* memorize the direct session control and cache IUnknown for it */
3472 mData->mSession.mDirectControl = pSessionControl;
3473 mData->mSession.mState = SessionState_Locked;
3474 /* associate the SessionMachine with this Machine */
3475 mData->mSession.mMachine = sessionMachine;
3476
3477 /* request an IUnknown pointer early from the remote party for later
3478 * identity checks (it will be internally cached within mDirectControl
3479 * at least on XPCOM) */
3480 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3481 NOREF(unk);
3482 }
3483
3484 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3485 * would break the lock order */
3486 alock.release();
3487
3488 /* uninitialize the created session machine on failure */
3489 if (FAILED(rc))
3490 sessionMachine->uninit();
3491
3492 }
3493
3494 if (SUCCEEDED(rc))
3495 {
3496 /*
3497 * tell the client watcher thread to update the set of
3498 * machines that have open sessions
3499 */
3500 mParent->i_updateClientWatcher();
3501
3502 if (oldState != SessionState_Locked)
3503 /* fire an event */
3504 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3505 }
3506
3507 return rc;
3508}
3509
3510/**
3511 * @note Locks objects!
3512 */
3513HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3514 const com::Utf8Str &aType,
3515 const com::Utf8Str &aEnvironment,
3516 ComPtr<IProgress> &aProgress)
3517{
3518 Utf8Str strFrontend(aType);
3519 /* "emergencystop" doesn't need the session, so skip the checks/interface
3520 * retrieval. This code doesn't quite fit in here, but introducing a
3521 * special API method would be even more effort, and would require explicit
3522 * support by every API client. It's better to hide the feature a bit. */
3523 if (strFrontend != "emergencystop")
3524 CheckComArgNotNull(aSession);
3525
3526 HRESULT rc = S_OK;
3527 if (strFrontend.isEmpty())
3528 {
3529 Bstr bstrFrontend;
3530 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3531 if (FAILED(rc))
3532 return rc;
3533 strFrontend = bstrFrontend;
3534 if (strFrontend.isEmpty())
3535 {
3536 ComPtr<ISystemProperties> systemProperties;
3537 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3538 if (FAILED(rc))
3539 return rc;
3540 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3541 if (FAILED(rc))
3542 return rc;
3543 strFrontend = bstrFrontend;
3544 }
3545 /* paranoia - emergencystop is not a valid default */
3546 if (strFrontend == "emergencystop")
3547 strFrontend = Utf8Str::Empty;
3548 }
3549 /* default frontend: Qt GUI */
3550 if (strFrontend.isEmpty())
3551 strFrontend = "GUI/Qt";
3552
3553 if (strFrontend != "emergencystop")
3554 {
3555 /* check the session state */
3556 SessionState_T state;
3557 rc = aSession->COMGETTER(State)(&state);
3558 if (FAILED(rc))
3559 return rc;
3560
3561 if (state != SessionState_Unlocked)
3562 return setError(VBOX_E_INVALID_OBJECT_STATE,
3563 tr("The given session is busy"));
3564
3565 /* get the IInternalSessionControl interface */
3566 ComPtr<IInternalSessionControl> control(aSession);
3567 ComAssertMsgRet(!control.isNull(),
3568 ("No IInternalSessionControl interface"),
3569 E_INVALIDARG);
3570
3571 /* get the teleporter enable state for the progress object init. */
3572 BOOL fTeleporterEnabled;
3573 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3574 if (FAILED(rc))
3575 return rc;
3576
3577 /* create a progress object */
3578 ComObjPtr<ProgressProxy> progress;
3579 progress.createObject();
3580 rc = progress->init(mParent,
3581 static_cast<IMachine*>(this),
3582 Bstr(tr("Starting VM")).raw(),
3583 TRUE /* aCancelable */,
3584 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3585 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3586 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3587 2 /* uFirstOperationWeight */,
3588 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3589
3590 if (SUCCEEDED(rc))
3591 {
3592 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3593 if (SUCCEEDED(rc))
3594 {
3595 aProgress = progress;
3596
3597 /* signal the client watcher thread */
3598 mParent->i_updateClientWatcher();
3599
3600 /* fire an event */
3601 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3602 }
3603 }
3604 }
3605 else
3606 {
3607 /* no progress object - either instant success or failure */
3608 aProgress = NULL;
3609
3610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3611
3612 if (mData->mSession.mState != SessionState_Locked)
3613 return setError(VBOX_E_INVALID_OBJECT_STATE,
3614 tr("The machine '%s' is not locked by a session"),
3615 mUserData->s.strName.c_str());
3616
3617 /* must have a VM process associated - do not kill normal API clients
3618 * with an open session */
3619 if (!Global::IsOnline(mData->mMachineState))
3620 return setError(VBOX_E_INVALID_OBJECT_STATE,
3621 tr("The machine '%s' does not have a VM process"),
3622 mUserData->s.strName.c_str());
3623
3624 /* forcibly terminate the VM process */
3625 if (mData->mSession.mPID != NIL_RTPROCESS)
3626 RTProcTerminate(mData->mSession.mPID);
3627
3628 /* signal the client watcher thread, as most likely the client has
3629 * been terminated */
3630 mParent->i_updateClientWatcher();
3631 }
3632
3633 return rc;
3634}
3635
3636HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3637{
3638 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3639 return setError(E_INVALIDARG,
3640 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3641 aPosition, SchemaDefs::MaxBootPosition);
3642
3643 if (aDevice == DeviceType_USB)
3644 return setError(E_NOTIMPL,
3645 tr("Booting from USB device is currently not supported"));
3646
3647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3648
3649 HRESULT rc = i_checkStateDependency(MutableStateDep);
3650 if (FAILED(rc)) return rc;
3651
3652 i_setModified(IsModified_MachineData);
3653 mHWData.backup();
3654 mHWData->mBootOrder[aPosition - 1] = aDevice;
3655
3656 return S_OK;
3657}
3658
3659HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3660{
3661 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3662 return setError(E_INVALIDARG,
3663 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3664 aPosition, SchemaDefs::MaxBootPosition);
3665
3666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3667
3668 *aDevice = mHWData->mBootOrder[aPosition - 1];
3669
3670 return S_OK;
3671}
3672
3673HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3674 LONG aControllerPort,
3675 LONG aDevice,
3676 DeviceType_T aType,
3677 const ComPtr<IMedium> &aMedium)
3678{
3679 IMedium *aM = aMedium;
3680 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3681 aName.c_str(), aControllerPort, aDevice, aType, aM));
3682
3683 // request the host lock first, since might be calling Host methods for getting host drives;
3684 // next, protect the media tree all the while we're in here, as well as our member variables
3685 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3686 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3687
3688 HRESULT rc = i_checkStateDependency(MutableStateDep);
3689 if (FAILED(rc)) return rc;
3690
3691 /// @todo NEWMEDIA implicit machine registration
3692 if (!mData->mRegistered)
3693 return setError(VBOX_E_INVALID_OBJECT_STATE,
3694 tr("Cannot attach storage devices to an unregistered machine"));
3695
3696 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3697
3698 /* Check for an existing controller. */
3699 ComObjPtr<StorageController> ctl;
3700 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3701 if (FAILED(rc)) return rc;
3702
3703 StorageControllerType_T ctrlType;
3704 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3705 if (FAILED(rc))
3706 return setError(E_FAIL,
3707 tr("Could not get type of controller '%s'"),
3708 aName.c_str());
3709
3710 bool fSilent = false;
3711 Utf8Str strReconfig;
3712
3713 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3714 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3715 if ( mData->mMachineState == MachineState_Paused
3716 && strReconfig == "1")
3717 fSilent = true;
3718
3719 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3720 bool fHotplug = false;
3721 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3722 fHotplug = true;
3723
3724 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3725 return setError(VBOX_E_INVALID_VM_STATE,
3726 tr("Controller '%s' does not support hotplugging"),
3727 aName.c_str());
3728
3729 // check that the port and device are not out of range
3730 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3731 if (FAILED(rc)) return rc;
3732
3733 /* check if the device slot is already busy */
3734 MediumAttachment *pAttachTemp;
3735 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3736 Bstr(aName).raw(),
3737 aControllerPort,
3738 aDevice)))
3739 {
3740 Medium *pMedium = pAttachTemp->i_getMedium();
3741 if (pMedium)
3742 {
3743 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3744 return setError(VBOX_E_OBJECT_IN_USE,
3745 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3746 pMedium->i_getLocationFull().c_str(),
3747 aControllerPort,
3748 aDevice,
3749 aName.c_str());
3750 }
3751 else
3752 return setError(VBOX_E_OBJECT_IN_USE,
3753 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3754 aControllerPort, aDevice, aName.c_str());
3755 }
3756
3757 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3758 if (aMedium && medium.isNull())
3759 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3760
3761 AutoCaller mediumCaller(medium);
3762 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3763
3764 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3765
3766 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3767 && !medium.isNull()
3768 )
3769 return setError(VBOX_E_OBJECT_IN_USE,
3770 tr("Medium '%s' is already attached to this virtual machine"),
3771 medium->i_getLocationFull().c_str());
3772
3773 if (!medium.isNull())
3774 {
3775 MediumType_T mtype = medium->i_getType();
3776 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3777 // For DVDs it's not written to the config file, so needs no global config
3778 // version bump. For floppies it's a new attribute "type", which is ignored
3779 // by older VirtualBox version, so needs no global config version bump either.
3780 // For hard disks this type is not accepted.
3781 if (mtype == MediumType_MultiAttach)
3782 {
3783 // This type is new with VirtualBox 4.0 and therefore requires settings
3784 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3785 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3786 // two reasons: The medium type is a property of the media registry tree, which
3787 // can reside in the global config file (for pre-4.0 media); we would therefore
3788 // possibly need to bump the global config version. We don't want to do that though
3789 // because that might make downgrading to pre-4.0 impossible.
3790 // As a result, we can only use these two new types if the medium is NOT in the
3791 // global registry:
3792 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3793 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3794 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3795 )
3796 return setError(VBOX_E_INVALID_OBJECT_STATE,
3797 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3798 "to machines that were created with VirtualBox 4.0 or later"),
3799 medium->i_getLocationFull().c_str());
3800 }
3801 }
3802
3803 bool fIndirect = false;
3804 if (!medium.isNull())
3805 fIndirect = medium->i_isReadOnly();
3806 bool associate = true;
3807
3808 do
3809 {
3810 if ( aType == DeviceType_HardDisk
3811 && mMediaData.isBackedUp())
3812 {
3813 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3814
3815 /* check if the medium was attached to the VM before we started
3816 * changing attachments in which case the attachment just needs to
3817 * be restored */
3818 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3819 {
3820 AssertReturn(!fIndirect, E_FAIL);
3821
3822 /* see if it's the same bus/channel/device */
3823 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3824 {
3825 /* the simplest case: restore the whole attachment
3826 * and return, nothing else to do */
3827 mMediaData->mAttachments.push_back(pAttachTemp);
3828
3829 /* Reattach the medium to the VM. */
3830 if (fHotplug || fSilent)
3831 {
3832 mediumLock.release();
3833 treeLock.release();
3834 alock.release();
3835
3836 MediumLockList *pMediumLockList(new MediumLockList());
3837
3838 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3839 true /* fMediumLockWrite */,
3840 NULL,
3841 *pMediumLockList);
3842 alock.acquire();
3843 if (FAILED(rc))
3844 delete pMediumLockList;
3845 else
3846 {
3847 mData->mSession.mLockedMedia.Unlock();
3848 alock.release();
3849 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3850 mData->mSession.mLockedMedia.Lock();
3851 alock.acquire();
3852 }
3853 alock.release();
3854
3855 if (SUCCEEDED(rc))
3856 {
3857 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3858 /* Remove lock list in case of error. */
3859 if (FAILED(rc))
3860 {
3861 mData->mSession.mLockedMedia.Unlock();
3862 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3863 mData->mSession.mLockedMedia.Lock();
3864 }
3865 }
3866 }
3867
3868 return S_OK;
3869 }
3870
3871 /* bus/channel/device differ; we need a new attachment object,
3872 * but don't try to associate it again */
3873 associate = false;
3874 break;
3875 }
3876 }
3877
3878 /* go further only if the attachment is to be indirect */
3879 if (!fIndirect)
3880 break;
3881
3882 /* perform the so called smart attachment logic for indirect
3883 * attachments. Note that smart attachment is only applicable to base
3884 * hard disks. */
3885
3886 if (medium->i_getParent().isNull())
3887 {
3888 /* first, investigate the backup copy of the current hard disk
3889 * attachments to make it possible to re-attach existing diffs to
3890 * another device slot w/o losing their contents */
3891 if (mMediaData.isBackedUp())
3892 {
3893 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3894
3895 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3896 uint32_t foundLevel = 0;
3897
3898 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3899 {
3900 uint32_t level = 0;
3901 MediumAttachment *pAttach = *it;
3902 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3903 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3904 if (pMedium.isNull())
3905 continue;
3906
3907 if (pMedium->i_getBase(&level) == medium)
3908 {
3909 /* skip the hard disk if its currently attached (we
3910 * cannot attach the same hard disk twice) */
3911 if (i_findAttachment(mMediaData->mAttachments,
3912 pMedium))
3913 continue;
3914
3915 /* matched device, channel and bus (i.e. attached to the
3916 * same place) will win and immediately stop the search;
3917 * otherwise the attachment that has the youngest
3918 * descendant of medium will be used
3919 */
3920 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3921 {
3922 /* the simplest case: restore the whole attachment
3923 * and return, nothing else to do */
3924 mMediaData->mAttachments.push_back(*it);
3925
3926 /* Reattach the medium to the VM. */
3927 if (fHotplug || fSilent)
3928 {
3929 mediumLock.release();
3930 treeLock.release();
3931 alock.release();
3932
3933 MediumLockList *pMediumLockList(new MediumLockList());
3934
3935 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3936 true /* fMediumLockWrite */,
3937 NULL,
3938 *pMediumLockList);
3939 alock.acquire();
3940 if (FAILED(rc))
3941 delete pMediumLockList;
3942 else
3943 {
3944 mData->mSession.mLockedMedia.Unlock();
3945 alock.release();
3946 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3947 mData->mSession.mLockedMedia.Lock();
3948 alock.acquire();
3949 }
3950 alock.release();
3951
3952 if (SUCCEEDED(rc))
3953 {
3954 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3955 /* Remove lock list in case of error. */
3956 if (FAILED(rc))
3957 {
3958 mData->mSession.mLockedMedia.Unlock();
3959 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3960 mData->mSession.mLockedMedia.Lock();
3961 }
3962 }
3963 }
3964
3965 return S_OK;
3966 }
3967 else if ( foundIt == oldAtts.end()
3968 || level > foundLevel /* prefer younger */
3969 )
3970 {
3971 foundIt = it;
3972 foundLevel = level;
3973 }
3974 }
3975 }
3976
3977 if (foundIt != oldAtts.end())
3978 {
3979 /* use the previously attached hard disk */
3980 medium = (*foundIt)->i_getMedium();
3981 mediumCaller.attach(medium);
3982 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3983 mediumLock.attach(medium);
3984 /* not implicit, doesn't require association with this VM */
3985 fIndirect = false;
3986 associate = false;
3987 /* go right to the MediumAttachment creation */
3988 break;
3989 }
3990 }
3991
3992 /* must give up the medium lock and medium tree lock as below we
3993 * go over snapshots, which needs a lock with higher lock order. */
3994 mediumLock.release();
3995 treeLock.release();
3996
3997 /* then, search through snapshots for the best diff in the given
3998 * hard disk's chain to base the new diff on */
3999
4000 ComObjPtr<Medium> base;
4001 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4002 while (snap)
4003 {
4004 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4005
4006 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4007
4008 MediumAttachment *pAttachFound = NULL;
4009 uint32_t foundLevel = 0;
4010
4011 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4012 {
4013 MediumAttachment *pAttach = *it;
4014 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4015 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4016 if (pMedium.isNull())
4017 continue;
4018
4019 uint32_t level = 0;
4020 if (pMedium->i_getBase(&level) == medium)
4021 {
4022 /* matched device, channel and bus (i.e. attached to the
4023 * same place) will win and immediately stop the search;
4024 * otherwise the attachment that has the youngest
4025 * descendant of medium will be used
4026 */
4027 if ( pAttach->i_getDevice() == aDevice
4028 && pAttach->i_getPort() == aControllerPort
4029 && pAttach->i_getControllerName() == aName
4030 )
4031 {
4032 pAttachFound = pAttach;
4033 break;
4034 }
4035 else if ( !pAttachFound
4036 || level > foundLevel /* prefer younger */
4037 )
4038 {
4039 pAttachFound = pAttach;
4040 foundLevel = level;
4041 }
4042 }
4043 }
4044
4045 if (pAttachFound)
4046 {
4047 base = pAttachFound->i_getMedium();
4048 break;
4049 }
4050
4051 snap = snap->i_getParent();
4052 }
4053
4054 /* re-lock medium tree and the medium, as we need it below */
4055 treeLock.acquire();
4056 mediumLock.acquire();
4057
4058 /* found a suitable diff, use it as a base */
4059 if (!base.isNull())
4060 {
4061 medium = base;
4062 mediumCaller.attach(medium);
4063 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4064 mediumLock.attach(medium);
4065 }
4066 }
4067
4068 Utf8Str strFullSnapshotFolder;
4069 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4070
4071 ComObjPtr<Medium> diff;
4072 diff.createObject();
4073 // store this diff in the same registry as the parent
4074 Guid uuidRegistryParent;
4075 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4076 {
4077 // parent image has no registry: this can happen if we're attaching a new immutable
4078 // image that has not yet been attached (medium then points to the base and we're
4079 // creating the diff image for the immutable, and the parent is not yet registered);
4080 // put the parent in the machine registry then
4081 mediumLock.release();
4082 treeLock.release();
4083 alock.release();
4084 i_addMediumToRegistry(medium);
4085 alock.acquire();
4086 treeLock.acquire();
4087 mediumLock.acquire();
4088 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4089 }
4090 rc = diff->init(mParent,
4091 medium->i_getPreferredDiffFormat(),
4092 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4093 uuidRegistryParent);
4094 if (FAILED(rc)) return rc;
4095
4096 /* Apply the normal locking logic to the entire chain. */
4097 MediumLockList *pMediumLockList(new MediumLockList());
4098 mediumLock.release();
4099 treeLock.release();
4100 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4101 true /* fMediumLockWrite */,
4102 medium,
4103 *pMediumLockList);
4104 treeLock.acquire();
4105 mediumLock.acquire();
4106 if (SUCCEEDED(rc))
4107 {
4108 mediumLock.release();
4109 treeLock.release();
4110 rc = pMediumLockList->Lock();
4111 treeLock.acquire();
4112 mediumLock.acquire();
4113 if (FAILED(rc))
4114 setError(rc,
4115 tr("Could not lock medium when creating diff '%s'"),
4116 diff->i_getLocationFull().c_str());
4117 else
4118 {
4119 /* will release the lock before the potentially lengthy
4120 * operation, so protect with the special state */
4121 MachineState_T oldState = mData->mMachineState;
4122 i_setMachineState(MachineState_SettingUp);
4123
4124 mediumLock.release();
4125 treeLock.release();
4126 alock.release();
4127
4128 rc = medium->i_createDiffStorage(diff,
4129 MediumVariant_Standard,
4130 pMediumLockList,
4131 NULL /* aProgress */,
4132 true /* aWait */);
4133
4134 alock.acquire();
4135 treeLock.acquire();
4136 mediumLock.acquire();
4137
4138 i_setMachineState(oldState);
4139 }
4140 }
4141
4142 /* Unlock the media and free the associated memory. */
4143 delete pMediumLockList;
4144
4145 if (FAILED(rc)) return rc;
4146
4147 /* use the created diff for the actual attachment */
4148 medium = diff;
4149 mediumCaller.attach(medium);
4150 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4151 mediumLock.attach(medium);
4152 }
4153 while (0);
4154
4155 ComObjPtr<MediumAttachment> attachment;
4156 attachment.createObject();
4157 rc = attachment->init(this,
4158 medium,
4159 aName,
4160 aControllerPort,
4161 aDevice,
4162 aType,
4163 fIndirect,
4164 false /* fPassthrough */,
4165 false /* fTempEject */,
4166 false /* fNonRotational */,
4167 false /* fDiscard */,
4168 fHotplug /* fHotPluggable */,
4169 Utf8Str::Empty);
4170 if (FAILED(rc)) return rc;
4171
4172 if (associate && !medium.isNull())
4173 {
4174 // as the last step, associate the medium to the VM
4175 rc = medium->i_addBackReference(mData->mUuid);
4176 // here we can fail because of Deleting, or being in process of creating a Diff
4177 if (FAILED(rc)) return rc;
4178
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182 i_addMediumToRegistry(medium);
4183 alock.acquire();
4184 treeLock.acquire();
4185 mediumLock.acquire();
4186 }
4187
4188 /* success: finally remember the attachment */
4189 i_setModified(IsModified_Storage);
4190 mMediaData.backup();
4191 mMediaData->mAttachments.push_back(attachment);
4192
4193 mediumLock.release();
4194 treeLock.release();
4195 alock.release();
4196
4197 if (fHotplug || fSilent)
4198 {
4199 if (!medium.isNull())
4200 {
4201 MediumLockList *pMediumLockList(new MediumLockList());
4202
4203 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4204 true /* fMediumLockWrite */,
4205 NULL,
4206 *pMediumLockList);
4207 alock.acquire();
4208 if (FAILED(rc))
4209 delete pMediumLockList;
4210 else
4211 {
4212 mData->mSession.mLockedMedia.Unlock();
4213 alock.release();
4214 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4215 mData->mSession.mLockedMedia.Lock();
4216 alock.acquire();
4217 }
4218 alock.release();
4219 }
4220
4221 if (SUCCEEDED(rc))
4222 {
4223 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4224 /* Remove lock list in case of error. */
4225 if (FAILED(rc))
4226 {
4227 mData->mSession.mLockedMedia.Unlock();
4228 mData->mSession.mLockedMedia.Remove(attachment);
4229 mData->mSession.mLockedMedia.Lock();
4230 }
4231 }
4232 }
4233
4234 mParent->i_saveModifiedRegistries();
4235
4236 return rc;
4237}
4238
4239HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4240 LONG aDevice)
4241{
4242 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4243 aName.c_str(), aControllerPort, aDevice));
4244
4245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4246
4247 HRESULT rc = i_checkStateDependency(MutableStateDep);
4248 if (FAILED(rc)) return rc;
4249
4250 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4251
4252 /* Check for an existing controller. */
4253 ComObjPtr<StorageController> ctl;
4254 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4255 if (FAILED(rc)) return rc;
4256
4257 StorageControllerType_T ctrlType;
4258 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4259 if (FAILED(rc))
4260 return setError(E_FAIL,
4261 tr("Could not get type of controller '%s'"),
4262 aName.c_str());
4263
4264 bool fSilent = false;
4265 Utf8Str strReconfig;
4266
4267 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4268 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4269 if ( mData->mMachineState == MachineState_Paused
4270 && strReconfig == "1")
4271 fSilent = true;
4272
4273 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4274 bool fHotplug = false;
4275 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4276 fHotplug = true;
4277
4278 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4279 return setError(VBOX_E_INVALID_VM_STATE,
4280 tr("Controller '%s' does not support hotplugging"),
4281 aName.c_str());
4282
4283 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4284 Bstr(aName).raw(),
4285 aControllerPort,
4286 aDevice);
4287 if (!pAttach)
4288 return setError(VBOX_E_OBJECT_NOT_FOUND,
4289 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4290 aDevice, aControllerPort, aName.c_str());
4291
4292 if (fHotplug && !pAttach->i_getHotPluggable())
4293 return setError(VBOX_E_NOT_SUPPORTED,
4294 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4295 aDevice, aControllerPort, aName.c_str());
4296
4297 /*
4298 * The VM has to detach the device before we delete any implicit diffs.
4299 * If this fails we can roll back without loosing data.
4300 */
4301 if (fHotplug || fSilent)
4302 {
4303 alock.release();
4304 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4305 alock.acquire();
4306 }
4307 if (FAILED(rc)) return rc;
4308
4309 /* If we are here everything went well and we can delete the implicit now. */
4310 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4311
4312 alock.release();
4313
4314 mParent->i_saveModifiedRegistries();
4315
4316 return rc;
4317}
4318
4319HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4320 LONG aDevice, BOOL aPassthrough)
4321{
4322 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4323 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4324
4325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4326
4327 HRESULT rc = i_checkStateDependency(MutableStateDep);
4328 if (FAILED(rc)) return rc;
4329
4330 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4331
4332 if (Global::IsOnlineOrTransient(mData->mMachineState))
4333 return setError(VBOX_E_INVALID_VM_STATE,
4334 tr("Invalid machine state: %s"),
4335 Global::stringifyMachineState(mData->mMachineState));
4336
4337 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4338 Bstr(aName).raw(),
4339 aControllerPort,
4340 aDevice);
4341 if (!pAttach)
4342 return setError(VBOX_E_OBJECT_NOT_FOUND,
4343 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4344 aDevice, aControllerPort, aName.c_str());
4345
4346
4347 i_setModified(IsModified_Storage);
4348 mMediaData.backup();
4349
4350 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4351
4352 if (pAttach->i_getType() != DeviceType_DVD)
4353 return setError(E_INVALIDARG,
4354 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4355 aDevice, aControllerPort, aName.c_str());
4356 pAttach->i_updatePassthrough(!!aPassthrough);
4357
4358 return S_OK;
4359}
4360
4361HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4362 LONG aDevice, BOOL aTemporaryEject)
4363{
4364
4365 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4366 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4367
4368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4369
4370 HRESULT rc = i_checkStateDependency(MutableStateDep);
4371 if (FAILED(rc)) return rc;
4372
4373 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4374 Bstr(aName).raw(),
4375 aControllerPort,
4376 aDevice);
4377 if (!pAttach)
4378 return setError(VBOX_E_OBJECT_NOT_FOUND,
4379 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4380 aDevice, aControllerPort, aName.c_str());
4381
4382
4383 i_setModified(IsModified_Storage);
4384 mMediaData.backup();
4385
4386 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4387
4388 if (pAttach->i_getType() != DeviceType_DVD)
4389 return setError(E_INVALIDARG,
4390 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4391 aDevice, aControllerPort, aName.c_str());
4392 pAttach->i_updateTempEject(!!aTemporaryEject);
4393
4394 return S_OK;
4395}
4396
4397HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4398 LONG aDevice, BOOL aNonRotational)
4399{
4400
4401 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4402 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4403
4404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4405
4406 HRESULT rc = i_checkStateDependency(MutableStateDep);
4407 if (FAILED(rc)) return rc;
4408
4409 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4410
4411 if (Global::IsOnlineOrTransient(mData->mMachineState))
4412 return setError(VBOX_E_INVALID_VM_STATE,
4413 tr("Invalid machine state: %s"),
4414 Global::stringifyMachineState(mData->mMachineState));
4415
4416 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4417 Bstr(aName).raw(),
4418 aControllerPort,
4419 aDevice);
4420 if (!pAttach)
4421 return setError(VBOX_E_OBJECT_NOT_FOUND,
4422 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4423 aDevice, aControllerPort, aName.c_str());
4424
4425
4426 i_setModified(IsModified_Storage);
4427 mMediaData.backup();
4428
4429 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4430
4431 if (pAttach->i_getType() != DeviceType_HardDisk)
4432 return setError(E_INVALIDARG,
4433 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4434 aDevice, aControllerPort, aName.c_str());
4435 pAttach->i_updateNonRotational(!!aNonRotational);
4436
4437 return S_OK;
4438}
4439
4440HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4441 LONG aDevice, BOOL aDiscard)
4442{
4443
4444 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4445 aName.c_str(), aControllerPort, aDevice, aDiscard));
4446
4447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4448
4449 HRESULT rc = i_checkStateDependency(MutableStateDep);
4450 if (FAILED(rc)) return rc;
4451
4452 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4453
4454 if (Global::IsOnlineOrTransient(mData->mMachineState))
4455 return setError(VBOX_E_INVALID_VM_STATE,
4456 tr("Invalid machine state: %s"),
4457 Global::stringifyMachineState(mData->mMachineState));
4458
4459 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4460 Bstr(aName).raw(),
4461 aControllerPort,
4462 aDevice);
4463 if (!pAttach)
4464 return setError(VBOX_E_OBJECT_NOT_FOUND,
4465 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4466 aDevice, aControllerPort, aName.c_str());
4467
4468
4469 i_setModified(IsModified_Storage);
4470 mMediaData.backup();
4471
4472 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4473
4474 if (pAttach->i_getType() != DeviceType_HardDisk)
4475 return setError(E_INVALIDARG,
4476 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4477 aDevice, aControllerPort, aName.c_str());
4478 pAttach->i_updateDiscard(!!aDiscard);
4479
4480 return S_OK;
4481}
4482
4483HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4484 LONG aDevice, BOOL aHotPluggable)
4485{
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4488
4489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 HRESULT rc = i_checkStateDependency(MutableStateDep);
4492 if (FAILED(rc)) return rc;
4493
4494 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4495
4496 if (Global::IsOnlineOrTransient(mData->mMachineState))
4497 return setError(VBOX_E_INVALID_VM_STATE,
4498 tr("Invalid machine state: %s"),
4499 Global::stringifyMachineState(mData->mMachineState));
4500
4501 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4502 Bstr(aName).raw(),
4503 aControllerPort,
4504 aDevice);
4505 if (!pAttach)
4506 return setError(VBOX_E_OBJECT_NOT_FOUND,
4507 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4508 aDevice, aControllerPort, aName.c_str());
4509
4510 /* Check for an existing controller. */
4511 ComObjPtr<StorageController> ctl;
4512 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4513 if (FAILED(rc)) return rc;
4514
4515 StorageControllerType_T ctrlType;
4516 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4517 if (FAILED(rc))
4518 return setError(E_FAIL,
4519 tr("Could not get type of controller '%s'"),
4520 aName.c_str());
4521
4522 if (!i_isControllerHotplugCapable(ctrlType))
4523 return setError(VBOX_E_NOT_SUPPORTED,
4524 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4525 aName.c_str());
4526
4527 i_setModified(IsModified_Storage);
4528 mMediaData.backup();
4529
4530 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4531
4532 if (pAttach->i_getType() == DeviceType_Floppy)
4533 return setError(E_INVALIDARG,
4534 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4535 aDevice, aControllerPort, aName.c_str());
4536 pAttach->i_updateHotPluggable(!!aHotPluggable);
4537
4538 return S_OK;
4539}
4540
4541HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4542 LONG aDevice)
4543{
4544 int rc = S_OK;
4545 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4546 aName.c_str(), aControllerPort, aDevice));
4547
4548 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4549
4550 return rc;
4551}
4552
4553HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4554 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4555{
4556 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4557 aName.c_str(), aControllerPort, aDevice));
4558
4559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4560
4561 HRESULT rc = i_checkStateDependency(MutableStateDep);
4562 if (FAILED(rc)) return rc;
4563
4564 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4565
4566 if (Global::IsOnlineOrTransient(mData->mMachineState))
4567 return setError(VBOX_E_INVALID_VM_STATE,
4568 tr("Invalid machine state: %s"),
4569 Global::stringifyMachineState(mData->mMachineState));
4570
4571 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4572 Bstr(aName).raw(),
4573 aControllerPort,
4574 aDevice);
4575 if (!pAttach)
4576 return setError(VBOX_E_OBJECT_NOT_FOUND,
4577 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4578 aDevice, aControllerPort, aName.c_str());
4579
4580
4581 i_setModified(IsModified_Storage);
4582 mMediaData.backup();
4583
4584 IBandwidthGroup *iB = aBandwidthGroup;
4585 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4586 if (aBandwidthGroup && group.isNull())
4587 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4588
4589 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4590
4591 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4592 if (strBandwidthGroupOld.isNotEmpty())
4593 {
4594 /* Get the bandwidth group object and release it - this must not fail. */
4595 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4596 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4597 Assert(SUCCEEDED(rc));
4598
4599 pBandwidthGroupOld->i_release();
4600 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4601 }
4602
4603 if (!group.isNull())
4604 {
4605 group->i_reference();
4606 pAttach->i_updateBandwidthGroup(group->i_getName());
4607 }
4608
4609 return S_OK;
4610}
4611
4612HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4613 LONG aControllerPort,
4614 LONG aDevice,
4615 DeviceType_T aType)
4616{
4617 HRESULT rc = S_OK;
4618
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4620 aName.c_str(), aControllerPort, aDevice, aType));
4621
4622 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4623
4624 return rc;
4625}
4626
4627
4628HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4629 LONG aControllerPort,
4630 LONG aDevice,
4631 BOOL aForce)
4632{
4633 int rc = S_OK;
4634 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4635 aName.c_str(), aControllerPort, aForce));
4636
4637 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4638
4639 return rc;
4640}
4641
4642HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4643 LONG aControllerPort,
4644 LONG aDevice,
4645 const ComPtr<IMedium> &aMedium,
4646 BOOL aForce)
4647{
4648 int rc = S_OK;
4649 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4650 aName.c_str(), aControllerPort, aDevice, aForce));
4651
4652 // request the host lock first, since might be calling Host methods for getting host drives;
4653 // next, protect the media tree all the while we're in here, as well as our member variables
4654 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4655 this->lockHandle(),
4656 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4657
4658 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4659 Bstr(aName).raw(),
4660 aControllerPort,
4661 aDevice);
4662 if (pAttach.isNull())
4663 return setError(VBOX_E_OBJECT_NOT_FOUND,
4664 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4665 aDevice, aControllerPort, aName.c_str());
4666
4667 /* Remember previously mounted medium. The medium before taking the
4668 * backup is not necessarily the same thing. */
4669 ComObjPtr<Medium> oldmedium;
4670 oldmedium = pAttach->i_getMedium();
4671
4672 IMedium *iM = aMedium;
4673 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4674 if (aMedium && pMedium.isNull())
4675 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4676
4677 AutoCaller mediumCaller(pMedium);
4678 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4679
4680 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4681 if (pMedium)
4682 {
4683 DeviceType_T mediumType = pAttach->i_getType();
4684 switch (mediumType)
4685 {
4686 case DeviceType_DVD:
4687 case DeviceType_Floppy:
4688 break;
4689
4690 default:
4691 return setError(VBOX_E_INVALID_OBJECT_STATE,
4692 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4693 aControllerPort,
4694 aDevice,
4695 aName.c_str());
4696 }
4697 }
4698
4699 i_setModified(IsModified_Storage);
4700 mMediaData.backup();
4701
4702 {
4703 // The backup operation makes the pAttach reference point to the
4704 // old settings. Re-get the correct reference.
4705 pAttach = i_findAttachment(mMediaData->mAttachments,
4706 Bstr(aName).raw(),
4707 aControllerPort,
4708 aDevice);
4709 if (!oldmedium.isNull())
4710 oldmedium->i_removeBackReference(mData->mUuid);
4711 if (!pMedium.isNull())
4712 {
4713 pMedium->i_addBackReference(mData->mUuid);
4714
4715 mediumLock.release();
4716 multiLock.release();
4717 i_addMediumToRegistry(pMedium);
4718 multiLock.acquire();
4719 mediumLock.acquire();
4720 }
4721
4722 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4723 pAttach->i_updateMedium(pMedium);
4724 }
4725
4726 i_setModified(IsModified_Storage);
4727
4728 mediumLock.release();
4729 multiLock.release();
4730 rc = i_onMediumChange(pAttach, aForce);
4731 multiLock.acquire();
4732 mediumLock.acquire();
4733
4734 /* On error roll back this change only. */
4735 if (FAILED(rc))
4736 {
4737 if (!pMedium.isNull())
4738 pMedium->i_removeBackReference(mData->mUuid);
4739 pAttach = i_findAttachment(mMediaData->mAttachments,
4740 Bstr(aName).raw(),
4741 aControllerPort,
4742 aDevice);
4743 /* If the attachment is gone in the meantime, bail out. */
4744 if (pAttach.isNull())
4745 return rc;
4746 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4747 if (!oldmedium.isNull())
4748 oldmedium->i_addBackReference(mData->mUuid);
4749 pAttach->i_updateMedium(oldmedium);
4750 }
4751
4752 mediumLock.release();
4753 multiLock.release();
4754
4755 mParent->i_saveModifiedRegistries();
4756
4757 return rc;
4758}
4759HRESULT Machine::getMedium(const com::Utf8Str &aName,
4760 LONG aControllerPort,
4761 LONG aDevice,
4762 ComPtr<IMedium> &aMedium)
4763{
4764 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4765 aName.c_str(), aControllerPort, aDevice));
4766
4767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
4769 aMedium = NULL;
4770
4771 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4772 Bstr(aName).raw(),
4773 aControllerPort,
4774 aDevice);
4775 if (pAttach.isNull())
4776 return setError(VBOX_E_OBJECT_NOT_FOUND,
4777 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4778 aDevice, aControllerPort, aName.c_str());
4779
4780 aMedium = pAttach->i_getMedium();
4781
4782 return S_OK;
4783}
4784
4785HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4786{
4787
4788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4789
4790 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4791
4792 return S_OK;
4793}
4794
4795HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4796{
4797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4798
4799 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4800
4801 return S_OK;
4802}
4803
4804HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4805{
4806 /* Do not assert if slot is out of range, just return the advertised
4807 status. testdriver/vbox.py triggers this in logVmInfo. */
4808 if (aSlot >= mNetworkAdapters.size())
4809 return setError(E_INVALIDARG,
4810 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4811 aSlot, mNetworkAdapters.size());
4812
4813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4814
4815 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4816
4817 return S_OK;
4818}
4819
4820HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4821{
4822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4823
4824 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4825 size_t i = 0;
4826 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4827 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4828 ++it, ++i)
4829 aKeys[i] = it->first;
4830
4831 return S_OK;
4832}
4833
4834 /**
4835 * @note Locks this object for reading.
4836 */
4837HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4838 com::Utf8Str &aValue)
4839{
4840 /* start with nothing found */
4841 aValue = "";
4842
4843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4844
4845 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4846 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4847 // found:
4848 aValue = it->second; // source is a Utf8Str
4849
4850 /* return the result to caller (may be empty) */
4851 return S_OK;
4852}
4853
4854 /**
4855 * @note Locks mParent for writing + this object for writing.
4856 */
4857HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4858{
4859 Utf8Str strOldValue; // empty
4860
4861 // locking note: we only hold the read lock briefly to look up the old value,
4862 // then release it and call the onExtraCanChange callbacks. There is a small
4863 // chance of a race insofar as the callback might be called twice if two callers
4864 // change the same key at the same time, but that's a much better solution
4865 // than the deadlock we had here before. The actual changing of the extradata
4866 // is then performed under the write lock and race-free.
4867
4868 // look up the old value first; if nothing has changed then we need not do anything
4869 {
4870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4871 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4872 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4873 strOldValue = it->second;
4874 }
4875
4876 bool fChanged;
4877 if ((fChanged = (strOldValue != aValue)))
4878 {
4879 // ask for permission from all listeners outside the locks;
4880 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4881 // lock to copy the list of callbacks to invoke
4882 Bstr error;
4883 Bstr bstrValue(aValue);
4884
4885 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4886 {
4887 const char *sep = error.isEmpty() ? "" : ": ";
4888 CBSTR err = error.raw();
4889 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4890 sep, err));
4891 return setError(E_ACCESSDENIED,
4892 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4893 aKey.c_str(),
4894 aValue.c_str(),
4895 sep,
4896 err);
4897 }
4898
4899 // data is changing and change not vetoed: then write it out under the lock
4900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4901
4902 if (i_isSnapshotMachine())
4903 {
4904 HRESULT rc = i_checkStateDependency(MutableStateDep);
4905 if (FAILED(rc)) return rc;
4906 }
4907
4908 if (aValue.isEmpty())
4909 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4910 else
4911 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4912 // creates a new key if needed
4913
4914 bool fNeedsGlobalSaveSettings = false;
4915 // This saving of settings is tricky: there is no "old state" for the
4916 // extradata items at all (unlike all other settings), so the old/new
4917 // settings comparison would give a wrong result!
4918 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4919
4920 if (fNeedsGlobalSaveSettings)
4921 {
4922 // save the global settings; for that we should hold only the VirtualBox lock
4923 alock.release();
4924 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4925 mParent->i_saveSettings();
4926 }
4927 }
4928
4929 // fire notification outside the lock
4930 if (fChanged)
4931 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4932
4933 return S_OK;
4934}
4935
4936HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4937{
4938 aProgress = NULL;
4939 NOREF(aSettingsFilePath);
4940 ReturnComNotImplemented();
4941}
4942
4943HRESULT Machine::saveSettings()
4944{
4945 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4946
4947 /* when there was auto-conversion, we want to save the file even if
4948 * the VM is saved */
4949 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4950 if (FAILED(rc)) return rc;
4951
4952 /* the settings file path may never be null */
4953 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4954
4955 /* save all VM data excluding snapshots */
4956 bool fNeedsGlobalSaveSettings = false;
4957 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4958 mlock.release();
4959
4960 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4961 {
4962 // save the global settings; for that we should hold only the VirtualBox lock
4963 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4964 rc = mParent->i_saveSettings();
4965 }
4966
4967 return rc;
4968}
4969
4970
4971HRESULT Machine::discardSettings()
4972{
4973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4974
4975 HRESULT rc = i_checkStateDependency(MutableStateDep);
4976 if (FAILED(rc)) return rc;
4977
4978 /*
4979 * during this rollback, the session will be notified if data has
4980 * been actually changed
4981 */
4982 i_rollback(true /* aNotify */);
4983
4984 return S_OK;
4985}
4986
4987/** @note Locks objects! */
4988HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4989 std::vector<ComPtr<IMedium> > &aMedia)
4990{
4991 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4992 AutoLimitedCaller autoCaller(this);
4993 AssertComRCReturnRC(autoCaller.rc());
4994
4995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4996
4997 Guid id(i_getId());
4998
4999 if (mData->mSession.mState != SessionState_Unlocked)
5000 return setError(VBOX_E_INVALID_OBJECT_STATE,
5001 tr("Cannot unregister the machine '%s' while it is locked"),
5002 mUserData->s.strName.c_str());
5003
5004 // wait for state dependents to drop to zero
5005 i_ensureNoStateDependencies();
5006
5007 if (!mData->mAccessible)
5008 {
5009 // inaccessible maschines can only be unregistered; uninitialize ourselves
5010 // here because currently there may be no unregistered that are inaccessible
5011 // (this state combination is not supported). Note releasing the caller and
5012 // leaving the lock before calling uninit()
5013 alock.release();
5014 autoCaller.release();
5015
5016 uninit();
5017
5018 mParent->i_unregisterMachine(this, id);
5019 // calls VirtualBox::i_saveSettings()
5020
5021 return S_OK;
5022 }
5023
5024 HRESULT rc = S_OK;
5025
5026 // discard saved state
5027 if (mData->mMachineState == MachineState_Saved)
5028 {
5029 // add the saved state file to the list of files the caller should delete
5030 Assert(!mSSData->strStateFilePath.isEmpty());
5031 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5032
5033 mSSData->strStateFilePath.setNull();
5034
5035 // unconditionally set the machine state to powered off, we now
5036 // know no session has locked the machine
5037 mData->mMachineState = MachineState_PoweredOff;
5038 }
5039
5040 size_t cSnapshots = 0;
5041 if (mData->mFirstSnapshot)
5042 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5043 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5044 // fail now before we start detaching media
5045 return setError(VBOX_E_INVALID_OBJECT_STATE,
5046 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5047 mUserData->s.strName.c_str(), cSnapshots);
5048
5049 // This list collects the medium objects from all medium attachments
5050 // which we will detach from the machine and its snapshots, in a specific
5051 // order which allows for closing all media without getting "media in use"
5052 // errors, simply by going through the list from the front to the back:
5053 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5054 // and must be closed before the parent media from the snapshots, or closing the parents
5055 // will fail because they still have children);
5056 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5057 // the root ("first") snapshot of the machine.
5058 MediaList llMedia;
5059
5060 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5061 && mMediaData->mAttachments.size()
5062 )
5063 {
5064 // we have media attachments: detach them all and add the Medium objects to our list
5065 if (aCleanupMode != CleanupMode_UnregisterOnly)
5066 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5067 else
5068 return setError(VBOX_E_INVALID_OBJECT_STATE,
5069 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5070 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5071 }
5072
5073 if (cSnapshots)
5074 {
5075 // autoCleanup must be true here, or we would have failed above
5076
5077 // add the media from the medium attachments of the snapshots to llMedia
5078 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5079 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5080 // into the children first
5081
5082 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5083 MachineState_T oldState = mData->mMachineState;
5084 mData->mMachineState = MachineState_DeletingSnapshot;
5085
5086 // make a copy of the first snapshot so the refcount does not drop to 0
5087 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5088 // because of the AutoCaller voodoo)
5089 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5090
5091 // GO!
5092 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5093
5094 mData->mMachineState = oldState;
5095 }
5096
5097 if (FAILED(rc))
5098 {
5099 i_rollbackMedia();
5100 return rc;
5101 }
5102
5103 // commit all the media changes made above
5104 i_commitMedia();
5105
5106 mData->mRegistered = false;
5107
5108 // machine lock no longer needed
5109 alock.release();
5110
5111 // return media to caller
5112 size_t i = 0;
5113 aMedia.resize(llMedia.size());
5114 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5115 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5116
5117 mParent->i_unregisterMachine(this, id);
5118 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5119
5120 return S_OK;
5121}
5122
5123struct Machine::DeleteTask
5124{
5125 ComObjPtr<Machine> pMachine;
5126 RTCList<ComPtr<IMedium> > llMediums;
5127 StringsList llFilesToDelete;
5128 ComObjPtr<Progress> pProgress;
5129};
5130
5131HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5132{
5133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5134
5135 HRESULT rc = i_checkStateDependency(MutableStateDep);
5136 if (FAILED(rc)) return rc;
5137
5138 if (mData->mRegistered)
5139 return setError(VBOX_E_INVALID_VM_STATE,
5140 tr("Cannot delete settings of a registered machine"));
5141
5142 DeleteTask *pTask = new DeleteTask;
5143 pTask->pMachine = this;
5144
5145 // collect files to delete
5146 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5147
5148 for (size_t i = 0; i < aMedia.size(); ++i)
5149 {
5150 IMedium *pIMedium(aMedia[i]);
5151 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5152 if (pMedium.isNull())
5153 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5154 SafeArray<BSTR> ids;
5155 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5156 if (FAILED(rc)) return rc;
5157 /* At this point the medium should not have any back references
5158 * anymore. If it has it is attached to another VM and *must* not
5159 * deleted. */
5160 if (ids.size() < 1)
5161 pTask->llMediums.append(pMedium);
5162 }
5163 if (mData->pMachineConfigFile->fileExists())
5164 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5165
5166 pTask->pProgress.createObject();
5167 pTask->pProgress->init(i_getVirtualBox(),
5168 static_cast<IMachine*>(this) /* aInitiator */,
5169 Bstr(tr("Deleting files")).raw(),
5170 true /* fCancellable */,
5171 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5172 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5173
5174 int vrc = RTThreadCreate(NULL,
5175 Machine::deleteThread,
5176 (void*)pTask,
5177 0,
5178 RTTHREADTYPE_MAIN_WORKER,
5179 0,
5180 "MachineDelete");
5181
5182 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5183
5184 if (RT_FAILURE(vrc))
5185 {
5186 delete pTask;
5187 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5188 }
5189
5190 LogFlowFuncLeave();
5191
5192 return S_OK;
5193}
5194
5195/**
5196 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5197 * calls Machine::deleteTaskWorker() on the actual machine object.
5198 * @param Thread
5199 * @param pvUser
5200 * @return
5201 */
5202/*static*/
5203DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5204{
5205 LogFlowFuncEnter();
5206
5207 DeleteTask *pTask = (DeleteTask*)pvUser;
5208 Assert(pTask);
5209 Assert(pTask->pMachine);
5210 Assert(pTask->pProgress);
5211
5212 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5213 pTask->pProgress->i_notifyComplete(rc);
5214
5215 delete pTask;
5216
5217 LogFlowFuncLeave();
5218
5219 NOREF(Thread);
5220
5221 return VINF_SUCCESS;
5222}
5223
5224/**
5225 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5226 * @param task
5227 * @return
5228 */
5229HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5230{
5231 AutoCaller autoCaller(this);
5232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5233
5234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5235
5236 HRESULT rc = S_OK;
5237
5238 try
5239 {
5240 ULONG uLogHistoryCount = 3;
5241 ComPtr<ISystemProperties> systemProperties;
5242 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5243 if (FAILED(rc)) throw rc;
5244
5245 if (!systemProperties.isNull())
5246 {
5247 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5248 if (FAILED(rc)) throw rc;
5249 }
5250
5251 MachineState_T oldState = mData->mMachineState;
5252 i_setMachineState(MachineState_SettingUp);
5253 alock.release();
5254 for (size_t i = 0; i < task.llMediums.size(); ++i)
5255 {
5256 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5257 {
5258 AutoCaller mac(pMedium);
5259 if (FAILED(mac.rc())) throw mac.rc();
5260 Utf8Str strLocation = pMedium->i_getLocationFull();
5261 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5262 if (FAILED(rc)) throw rc;
5263 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5264 }
5265 ComPtr<IProgress> pProgress2;
5266 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5267 if (FAILED(rc)) throw rc;
5268 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5269 if (FAILED(rc)) throw rc;
5270 /* Check the result of the asynchronous process. */
5271 LONG iRc;
5272 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5273 if (FAILED(rc)) throw rc;
5274 /* If the thread of the progress object has an error, then
5275 * retrieve the error info from there, or it'll be lost. */
5276 if (FAILED(iRc))
5277 throw setError(ProgressErrorInfo(pProgress2));
5278
5279 /* Close the medium, deliberately without checking the return
5280- * code, and without leaving any trace in the error info, as
5281- * a failure here is a very minor issue, which shouldn't happen
5282- * as above we even managed to delete the medium. */
5283 {
5284 ErrorInfoKeeper eik;
5285 pMedium->Close();
5286 }
5287 }
5288 i_setMachineState(oldState);
5289 alock.acquire();
5290
5291 // delete the files pushed on the task list by Machine::Delete()
5292 // (this includes saved states of the machine and snapshots and
5293 // medium storage files from the IMedium list passed in, and the
5294 // machine XML file)
5295 StringsList::const_iterator it = task.llFilesToDelete.begin();
5296 while (it != task.llFilesToDelete.end())
5297 {
5298 const Utf8Str &strFile = *it;
5299 LogFunc(("Deleting file %s\n", strFile.c_str()));
5300 int vrc = RTFileDelete(strFile.c_str());
5301 if (RT_FAILURE(vrc))
5302 throw setError(VBOX_E_IPRT_ERROR,
5303 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5304
5305 ++it;
5306 if (it == task.llFilesToDelete.end())
5307 {
5308 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5309 if (FAILED(rc)) throw rc;
5310 break;
5311 }
5312
5313 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5314 if (FAILED(rc)) throw rc;
5315 }
5316
5317 /* delete the settings only when the file actually exists */
5318 if (mData->pMachineConfigFile->fileExists())
5319 {
5320 /* Delete any backup or uncommitted XML files. Ignore failures.
5321 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5322 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5323 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5324 RTFileDelete(otherXml.c_str());
5325 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5326 RTFileDelete(otherXml.c_str());
5327
5328 /* delete the Logs folder, nothing important should be left
5329 * there (we don't check for errors because the user might have
5330 * some private files there that we don't want to delete) */
5331 Utf8Str logFolder;
5332 getLogFolder(logFolder);
5333 Assert(logFolder.length());
5334 if (RTDirExists(logFolder.c_str()))
5335 {
5336 /* Delete all VBox.log[.N] files from the Logs folder
5337 * (this must be in sync with the rotation logic in
5338 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5339 * files that may have been created by the GUI. */
5340 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5341 logFolder.c_str(), RTPATH_DELIMITER);
5342 RTFileDelete(log.c_str());
5343 log = Utf8StrFmt("%s%cVBox.png",
5344 logFolder.c_str(), RTPATH_DELIMITER);
5345 RTFileDelete(log.c_str());
5346 for (int i = uLogHistoryCount; i > 0; i--)
5347 {
5348 log = Utf8StrFmt("%s%cVBox.log.%d",
5349 logFolder.c_str(), RTPATH_DELIMITER, i);
5350 RTFileDelete(log.c_str());
5351 log = Utf8StrFmt("%s%cVBox.png.%d",
5352 logFolder.c_str(), RTPATH_DELIMITER, i);
5353 RTFileDelete(log.c_str());
5354 }
5355#if defined(RT_OS_WINDOWS)
5356 log = Utf8StrFmt("%s%cVBoxStartup.log",
5357 logFolder.c_str(), RTPATH_DELIMITER);
5358 RTFileDelete(log.c_str());
5359#endif
5360
5361 RTDirRemove(logFolder.c_str());
5362 }
5363
5364 /* delete the Snapshots folder, nothing important should be left
5365 * there (we don't check for errors because the user might have
5366 * some private files there that we don't want to delete) */
5367 Utf8Str strFullSnapshotFolder;
5368 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5369 Assert(!strFullSnapshotFolder.isEmpty());
5370 if (RTDirExists(strFullSnapshotFolder.c_str()))
5371 RTDirRemove(strFullSnapshotFolder.c_str());
5372
5373 // delete the directory that contains the settings file, but only
5374 // if it matches the VM name
5375 Utf8Str settingsDir;
5376 if (i_isInOwnDir(&settingsDir))
5377 RTDirRemove(settingsDir.c_str());
5378 }
5379
5380 alock.release();
5381
5382 mParent->i_saveModifiedRegistries();
5383 }
5384 catch (HRESULT aRC) { rc = aRC; }
5385
5386 return rc;
5387}
5388
5389HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5390{
5391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5392
5393 ComObjPtr<Snapshot> pSnapshot;
5394 HRESULT rc;
5395
5396 if (aNameOrId.isEmpty())
5397 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5398 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5399 else
5400 {
5401 Guid uuid(aNameOrId);
5402 if (uuid.isValid())
5403 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5404 else
5405 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5406 }
5407 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5408
5409 return rc;
5410}
5411
5412HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5413{
5414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5415
5416 HRESULT rc = i_checkStateDependency(MutableStateDep);
5417 if (FAILED(rc)) return rc;
5418
5419 ComObjPtr<SharedFolder> sharedFolder;
5420 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5421 if (SUCCEEDED(rc))
5422 return setError(VBOX_E_OBJECT_IN_USE,
5423 tr("Shared folder named '%s' already exists"),
5424 aName.c_str());
5425
5426 sharedFolder.createObject();
5427 rc = sharedFolder->init(i_getMachine(),
5428 aName,
5429 aHostPath,
5430 !!aWritable,
5431 !!aAutomount,
5432 true /* fFailOnError */);
5433 if (FAILED(rc)) return rc;
5434
5435 i_setModified(IsModified_SharedFolders);
5436 mHWData.backup();
5437 mHWData->mSharedFolders.push_back(sharedFolder);
5438
5439 /* inform the direct session if any */
5440 alock.release();
5441 i_onSharedFolderChange();
5442
5443 return S_OK;
5444}
5445
5446HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5447{
5448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5449
5450 HRESULT rc = i_checkStateDependency(MutableStateDep);
5451 if (FAILED(rc)) return rc;
5452
5453 ComObjPtr<SharedFolder> sharedFolder;
5454 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5455 if (FAILED(rc)) return rc;
5456
5457 i_setModified(IsModified_SharedFolders);
5458 mHWData.backup();
5459 mHWData->mSharedFolders.remove(sharedFolder);
5460
5461 /* inform the direct session if any */
5462 alock.release();
5463 i_onSharedFolderChange();
5464
5465 return S_OK;
5466}
5467
5468HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5469{
5470 /* start with No */
5471 *aCanShow = FALSE;
5472
5473 ComPtr<IInternalSessionControl> directControl;
5474 {
5475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5476
5477 if (mData->mSession.mState != SessionState_Locked)
5478 return setError(VBOX_E_INVALID_VM_STATE,
5479 tr("Machine is not locked for session (session state: %s)"),
5480 Global::stringifySessionState(mData->mSession.mState));
5481
5482 directControl = mData->mSession.mDirectControl;
5483 }
5484
5485 /* ignore calls made after #OnSessionEnd() is called */
5486 if (!directControl)
5487 return S_OK;
5488
5489 LONG64 dummy;
5490 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5491}
5492
5493HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5494{
5495 ComPtr<IInternalSessionControl> directControl;
5496 {
5497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5498
5499 if (mData->mSession.mState != SessionState_Locked)
5500 return setError(E_FAIL,
5501 tr("Machine is not locked for session (session state: %s)"),
5502 Global::stringifySessionState(mData->mSession.mState));
5503
5504 directControl = mData->mSession.mDirectControl;
5505 }
5506
5507 /* ignore calls made after #OnSessionEnd() is called */
5508 if (!directControl)
5509 return S_OK;
5510
5511 BOOL dummy;
5512 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5513}
5514
5515#ifdef VBOX_WITH_GUEST_PROPS
5516/**
5517 * Look up a guest property in VBoxSVC's internal structures.
5518 */
5519HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5520 com::Utf8Str &aValue,
5521 LONG64 *aTimestamp,
5522 com::Utf8Str &aFlags) const
5523{
5524 using namespace guestProp;
5525
5526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5527 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5528
5529 if (it != mHWData->mGuestProperties.end())
5530 {
5531 char szFlags[MAX_FLAGS_LEN + 1];
5532 aValue = it->second.strValue;
5533 *aTimestamp = it->second.mTimestamp;
5534 writeFlags(it->second.mFlags, szFlags);
5535 aFlags = Utf8Str(szFlags);
5536 }
5537
5538 return S_OK;
5539}
5540
5541/**
5542 * Query the VM that a guest property belongs to for the property.
5543 * @returns E_ACCESSDENIED if the VM process is not available or not
5544 * currently handling queries and the lookup should then be done in
5545 * VBoxSVC.
5546 */
5547HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5548 com::Utf8Str &aValue,
5549 LONG64 *aTimestamp,
5550 com::Utf8Str &aFlags) const
5551{
5552 HRESULT rc = S_OK;
5553 BSTR bValue = NULL;
5554 BSTR bFlags = NULL;
5555
5556 ComPtr<IInternalSessionControl> directControl;
5557 directControl = mData->mSession.mDirectControl;
5558
5559 /* fail if we were called after #OnSessionEnd() is called. This is a
5560 * silly race condition. */
5561
5562 /** @todo This code is bothering API clients (like python script clients) with
5563 * the AccessGuestProperty call, creating unncessary IPC. Need to
5564 * have a way of figuring out which kind of direct session it is... */
5565 if (!directControl)
5566 rc = E_ACCESSDENIED;
5567 else
5568 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5569 0 /* accessMode */,
5570 &bValue, aTimestamp, &bFlags);
5571
5572 aValue = bValue;
5573 aFlags = bFlags;
5574
5575 return rc;
5576}
5577#endif // VBOX_WITH_GUEST_PROPS
5578
5579HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5580 com::Utf8Str &aValue,
5581 LONG64 *aTimestamp,
5582 com::Utf8Str &aFlags)
5583{
5584#ifndef VBOX_WITH_GUEST_PROPS
5585 ReturnComNotImplemented();
5586#else // VBOX_WITH_GUEST_PROPS
5587
5588 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5589
5590 if (rc == E_ACCESSDENIED)
5591 /* The VM is not running or the service is not (yet) accessible */
5592 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5593 return rc;
5594#endif // VBOX_WITH_GUEST_PROPS
5595}
5596
5597HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5598{
5599 LONG64 dummyTimestamp;
5600 com::Utf8Str dummyFlags;
5601 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5602 return rc;
5603
5604}
5605HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5606{
5607 com::Utf8Str dummyFlags;
5608 com::Utf8Str dummyValue;
5609 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5610 return rc;
5611}
5612
5613#ifdef VBOX_WITH_GUEST_PROPS
5614/**
5615 * Set a guest property in VBoxSVC's internal structures.
5616 */
5617HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5618 const com::Utf8Str &aFlags, bool fDelete)
5619{
5620 using namespace guestProp;
5621
5622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5623 HRESULT rc = S_OK;
5624
5625 rc = i_checkStateDependency(MutableStateDep);
5626 if (FAILED(rc)) return rc;
5627
5628 try
5629 {
5630 uint32_t fFlags = NILFLAG;
5631 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5632 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5633
5634 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5635 if (it == mHWData->mGuestProperties.end())
5636 {
5637 if (!fDelete)
5638 {
5639 i_setModified(IsModified_MachineData);
5640 mHWData.backupEx();
5641
5642 RTTIMESPEC time;
5643 HWData::GuestProperty prop;
5644 prop.strValue = Bstr(aValue).raw();
5645 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5646 prop.mFlags = fFlags;
5647 mHWData->mGuestProperties[aName] = prop;
5648 }
5649 }
5650 else
5651 {
5652 if (it->second.mFlags & (RDONLYHOST))
5653 {
5654 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5655 }
5656 else
5657 {
5658 i_setModified(IsModified_MachineData);
5659 mHWData.backupEx();
5660
5661 /* The backupEx() operation invalidates our iterator,
5662 * so get a new one. */
5663 it = mHWData->mGuestProperties.find(aName);
5664 Assert(it != mHWData->mGuestProperties.end());
5665
5666 if (!fDelete)
5667 {
5668 RTTIMESPEC time;
5669 it->second.strValue = aValue;
5670 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5671 it->second.mFlags = fFlags;
5672 }
5673 else
5674 mHWData->mGuestProperties.erase(it);
5675 }
5676 }
5677
5678 if ( SUCCEEDED(rc)
5679 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5680 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5681 RTSTR_MAX,
5682 aName.c_str(),
5683 RTSTR_MAX,
5684 NULL)
5685 )
5686 )
5687 {
5688 alock.release();
5689
5690 mParent->i_onGuestPropertyChange(mData->mUuid,
5691 Bstr(aName).raw(),
5692 Bstr(aValue).raw(),
5693 Bstr(aFlags).raw());
5694 }
5695 }
5696 catch (std::bad_alloc &)
5697 {
5698 rc = E_OUTOFMEMORY;
5699 }
5700
5701 return rc;
5702}
5703
5704/**
5705 * Set a property on the VM that that property belongs to.
5706 * @returns E_ACCESSDENIED if the VM process is not available or not
5707 * currently handling queries and the setting should then be done in
5708 * VBoxSVC.
5709 */
5710HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5711 const com::Utf8Str &aFlags, bool fDelete)
5712{
5713 HRESULT rc;
5714
5715 try
5716 {
5717 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5718
5719 BSTR dummy = NULL; /* will not be changed (setter) */
5720 LONG64 dummy64;
5721 if (!directControl)
5722 rc = E_ACCESSDENIED;
5723 else
5724 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5725 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5726 fDelete? 2: 1 /* accessMode */,
5727 &dummy, &dummy64, &dummy);
5728 }
5729 catch (std::bad_alloc &)
5730 {
5731 rc = E_OUTOFMEMORY;
5732 }
5733
5734 return rc;
5735}
5736#endif // VBOX_WITH_GUEST_PROPS
5737
5738HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5739 const com::Utf8Str &aFlags)
5740{
5741#ifndef VBOX_WITH_GUEST_PROPS
5742 ReturnComNotImplemented();
5743#else // VBOX_WITH_GUEST_PROPS
5744 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5745 if (rc == E_ACCESSDENIED)
5746 /* The VM is not running or the service is not (yet) accessible */
5747 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5748 return rc;
5749#endif // VBOX_WITH_GUEST_PROPS
5750}
5751
5752HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5753{
5754 return setGuestProperty(aProperty, aValue, "");
5755}
5756
5757HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5758{
5759#ifndef VBOX_WITH_GUEST_PROPS
5760 ReturnComNotImplemented();
5761#else // VBOX_WITH_GUEST_PROPS
5762 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5763 if (rc == E_ACCESSDENIED)
5764 /* The VM is not running or the service is not (yet) accessible */
5765 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5766 return rc;
5767#endif // VBOX_WITH_GUEST_PROPS
5768}
5769
5770#ifdef VBOX_WITH_GUEST_PROPS
5771/**
5772 * Enumerate the guest properties in VBoxSVC's internal structures.
5773 */
5774HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5775 std::vector<com::Utf8Str> &aNames,
5776 std::vector<com::Utf8Str> &aValues,
5777 std::vector<LONG64> &aTimestamps,
5778 std::vector<com::Utf8Str> &aFlags)
5779{
5780 using namespace guestProp;
5781
5782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5783 Utf8Str strPatterns(aPatterns);
5784
5785 HWData::GuestPropertyMap propMap;
5786
5787 /*
5788 * Look for matching patterns and build up a list.
5789 */
5790 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5791 while (it != mHWData->mGuestProperties.end())
5792 {
5793 if ( strPatterns.isEmpty()
5794 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5795 RTSTR_MAX,
5796 it->first.c_str(),
5797 RTSTR_MAX,
5798 NULL)
5799 )
5800 propMap.insert(*it);
5801 it++;
5802 }
5803
5804 alock.release();
5805
5806 /*
5807 * And build up the arrays for returning the property information.
5808 */
5809 size_t cEntries = propMap.size();
5810
5811 aNames.resize(cEntries);
5812 aValues.resize(cEntries);
5813 aTimestamps.resize(cEntries);
5814 aFlags.resize(cEntries);
5815
5816 char szFlags[MAX_FLAGS_LEN + 1];
5817 size_t i= 0;
5818 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5819 {
5820 aNames[i] = it->first;
5821 aValues[i] = it->second.strValue;
5822 aTimestamps[i] = it->second.mTimestamp;
5823 writeFlags(it->second.mFlags, szFlags);
5824 aFlags[i] = Utf8Str(szFlags);
5825 }
5826
5827 return S_OK;
5828}
5829
5830/**
5831 * Enumerate the properties managed by a VM.
5832 * @returns E_ACCESSDENIED if the VM process is not available or not
5833 * currently handling queries and the setting should then be done in
5834 * VBoxSVC.
5835 */
5836HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5837 std::vector<com::Utf8Str> &aNames,
5838 std::vector<com::Utf8Str> &aValues,
5839 std::vector<LONG64> &aTimestamps,
5840 std::vector<com::Utf8Str> &aFlags)
5841{
5842 HRESULT rc;
5843 ComPtr<IInternalSessionControl> directControl;
5844 directControl = mData->mSession.mDirectControl;
5845
5846
5847 com::SafeArray<BSTR> bNames;
5848 com::SafeArray<BSTR> bValues;
5849 com::SafeArray<LONG64> bTimestamps;
5850 com::SafeArray<BSTR> bFlags;
5851
5852 if (!directControl)
5853 rc = E_ACCESSDENIED;
5854 else
5855 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5856 ComSafeArrayAsOutParam(bNames),
5857 ComSafeArrayAsOutParam(bValues),
5858 ComSafeArrayAsOutParam(bTimestamps),
5859 ComSafeArrayAsOutParam(bFlags));
5860 size_t i;
5861 aNames.resize(bNames.size());
5862 for (i = 0; i < bNames.size(); ++i)
5863 aNames[i] = Utf8Str(bNames[i]);
5864 aValues.resize(bValues.size());
5865 for (i = 0; i < bValues.size(); ++i)
5866 aValues[i] = Utf8Str(bValues[i]);
5867 aTimestamps.resize(bTimestamps.size());
5868 for (i = 0; i < bTimestamps.size(); ++i)
5869 aTimestamps[i] = bTimestamps[i];
5870 aFlags.resize(bFlags.size());
5871 for (i = 0; i < bFlags.size(); ++i)
5872 aFlags[i] = Utf8Str(bFlags[i]);
5873
5874 return rc;
5875}
5876#endif // VBOX_WITH_GUEST_PROPS
5877HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5878 std::vector<com::Utf8Str> &aNames,
5879 std::vector<com::Utf8Str> &aValues,
5880 std::vector<LONG64> &aTimestamps,
5881 std::vector<com::Utf8Str> &aFlags)
5882{
5883#ifndef VBOX_WITH_GUEST_PROPS
5884 ReturnComNotImplemented();
5885#else // VBOX_WITH_GUEST_PROPS
5886
5887 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5888
5889 if (rc == E_ACCESSDENIED)
5890 /* The VM is not running or the service is not (yet) accessible */
5891 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5892 return rc;
5893#endif // VBOX_WITH_GUEST_PROPS
5894}
5895
5896HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5897 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5898{
5899 MediaData::AttachmentList atts;
5900
5901 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5902 if (FAILED(rc)) return rc;
5903
5904 size_t i = 0;
5905 aMediumAttachments.resize(atts.size());
5906 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5907 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5908
5909 return S_OK;
5910}
5911
5912HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5913 LONG aControllerPort,
5914 LONG aDevice,
5915 ComPtr<IMediumAttachment> &aAttachment)
5916{
5917 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5918 aName.c_str(), aControllerPort, aDevice));
5919
5920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5921
5922 aAttachment = NULL;
5923
5924 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5925 Bstr(aName).raw(),
5926 aControllerPort,
5927 aDevice);
5928 if (pAttach.isNull())
5929 return setError(VBOX_E_OBJECT_NOT_FOUND,
5930 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5931 aDevice, aControllerPort, aName.c_str());
5932
5933 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5934
5935 return S_OK;
5936}
5937
5938
5939HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5940 StorageBus_T aConnectionType,
5941 ComPtr<IStorageController> &aController)
5942{
5943 if ( (aConnectionType <= StorageBus_Null)
5944 || (aConnectionType > StorageBus_USB))
5945 return setError(E_INVALIDARG,
5946 tr("Invalid connection type: %d"),
5947 aConnectionType);
5948
5949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5950
5951 HRESULT rc = i_checkStateDependency(MutableStateDep);
5952 if (FAILED(rc)) return rc;
5953
5954 /* try to find one with the name first. */
5955 ComObjPtr<StorageController> ctrl;
5956
5957 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5958 if (SUCCEEDED(rc))
5959 return setError(VBOX_E_OBJECT_IN_USE,
5960 tr("Storage controller named '%s' already exists"),
5961 aName.c_str());
5962
5963 ctrl.createObject();
5964
5965 /* get a new instance number for the storage controller */
5966 ULONG ulInstance = 0;
5967 bool fBootable = true;
5968 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5969 it != mStorageControllers->end();
5970 ++it)
5971 {
5972 if ((*it)->i_getStorageBus() == aConnectionType)
5973 {
5974 ULONG ulCurInst = (*it)->i_getInstance();
5975
5976 if (ulCurInst >= ulInstance)
5977 ulInstance = ulCurInst + 1;
5978
5979 /* Only one controller of each type can be marked as bootable. */
5980 if ((*it)->i_getBootable())
5981 fBootable = false;
5982 }
5983 }
5984
5985 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5986 if (FAILED(rc)) return rc;
5987
5988 i_setModified(IsModified_Storage);
5989 mStorageControllers.backup();
5990 mStorageControllers->push_back(ctrl);
5991
5992 ctrl.queryInterfaceTo(aController.asOutParam());
5993
5994 /* inform the direct session if any */
5995 alock.release();
5996 i_onStorageControllerChange();
5997
5998 return S_OK;
5999}
6000
6001HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6002 ComPtr<IStorageController> &aStorageController)
6003{
6004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6005
6006 ComObjPtr<StorageController> ctrl;
6007
6008 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6009 if (SUCCEEDED(rc))
6010 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6011
6012 return rc;
6013}
6014
6015HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6016 ComPtr<IStorageController> &aStorageController)
6017{
6018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6019
6020 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6021 it != mStorageControllers->end();
6022 ++it)
6023 {
6024 if ((*it)->i_getInstance() == aInstance)
6025 {
6026 (*it).queryInterfaceTo(aStorageController.asOutParam());
6027 return S_OK;
6028 }
6029 }
6030
6031 return setError(VBOX_E_OBJECT_NOT_FOUND,
6032 tr("Could not find a storage controller with instance number '%lu'"),
6033 aInstance);
6034}
6035
6036HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6037{
6038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6039
6040 HRESULT rc = i_checkStateDependency(MutableStateDep);
6041 if (FAILED(rc)) return rc;
6042
6043 ComObjPtr<StorageController> ctrl;
6044
6045 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6046 if (SUCCEEDED(rc))
6047 {
6048 /* Ensure that only one controller of each type is marked as bootable. */
6049 if (aBootable == TRUE)
6050 {
6051 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6052 it != mStorageControllers->end();
6053 ++it)
6054 {
6055 ComObjPtr<StorageController> aCtrl = (*it);
6056
6057 if ( (aCtrl->i_getName() != aName)
6058 && aCtrl->i_getBootable() == TRUE
6059 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6060 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6061 {
6062 aCtrl->i_setBootable(FALSE);
6063 break;
6064 }
6065 }
6066 }
6067
6068 if (SUCCEEDED(rc))
6069 {
6070 ctrl->i_setBootable(aBootable);
6071 i_setModified(IsModified_Storage);
6072 }
6073 }
6074
6075 if (SUCCEEDED(rc))
6076 {
6077 /* inform the direct session if any */
6078 alock.release();
6079 i_onStorageControllerChange();
6080 }
6081
6082 return rc;
6083}
6084
6085HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6086{
6087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6088
6089 HRESULT rc = i_checkStateDependency(MutableStateDep);
6090 if (FAILED(rc)) return rc;
6091
6092 ComObjPtr<StorageController> ctrl;
6093 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6094 if (FAILED(rc)) return rc;
6095
6096 {
6097 /* find all attached devices to the appropriate storage controller and detach them all */
6098 // make a temporary list because detachDevice invalidates iterators into
6099 // mMediaData->mAttachments
6100 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6101
6102 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6103 it != llAttachments2.end();
6104 ++it)
6105 {
6106 MediumAttachment *pAttachTemp = *it;
6107
6108 AutoCaller localAutoCaller(pAttachTemp);
6109 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6110
6111 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6112
6113 if (pAttachTemp->i_getControllerName() == aName)
6114 {
6115 rc = i_detachDevice(pAttachTemp, alock, NULL);
6116 if (FAILED(rc)) return rc;
6117 }
6118 }
6119 }
6120
6121 /* We can remove it now. */
6122 i_setModified(IsModified_Storage);
6123 mStorageControllers.backup();
6124
6125 ctrl->i_unshare();
6126
6127 mStorageControllers->remove(ctrl);
6128
6129 /* inform the direct session if any */
6130 alock.release();
6131 i_onStorageControllerChange();
6132
6133 return S_OK;
6134}
6135
6136HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6137 ComPtr<IUSBController> &aController)
6138{
6139 if ( (aType <= USBControllerType_Null)
6140 || (aType >= USBControllerType_Last))
6141 return setError(E_INVALIDARG,
6142 tr("Invalid USB controller type: %d"),
6143 aType);
6144
6145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6146
6147 HRESULT rc = i_checkStateDependency(MutableStateDep);
6148 if (FAILED(rc)) return rc;
6149
6150 /* try to find one with the same type first. */
6151 ComObjPtr<USBController> ctrl;
6152
6153 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6154 if (SUCCEEDED(rc))
6155 return setError(VBOX_E_OBJECT_IN_USE,
6156 tr("USB controller named '%s' already exists"),
6157 aName.c_str());
6158
6159 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6160 ULONG maxInstances;
6161 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6162 if (FAILED(rc))
6163 return rc;
6164
6165 ULONG cInstances = i_getUSBControllerCountByType(aType);
6166 if (cInstances >= maxInstances)
6167 return setError(E_INVALIDARG,
6168 tr("Too many USB controllers of this type"));
6169
6170 ctrl.createObject();
6171
6172 rc = ctrl->init(this, aName, aType);
6173 if (FAILED(rc)) return rc;
6174
6175 i_setModified(IsModified_USB);
6176 mUSBControllers.backup();
6177 mUSBControllers->push_back(ctrl);
6178
6179 ctrl.queryInterfaceTo(aController.asOutParam());
6180
6181 /* inform the direct session if any */
6182 alock.release();
6183 i_onUSBControllerChange();
6184
6185 return S_OK;
6186}
6187
6188HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6189{
6190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6191
6192 ComObjPtr<USBController> ctrl;
6193
6194 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6195 if (SUCCEEDED(rc))
6196 ctrl.queryInterfaceTo(aController.asOutParam());
6197
6198 return rc;
6199}
6200
6201HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6202 ULONG *aControllers)
6203{
6204 if ( (aType <= USBControllerType_Null)
6205 || (aType >= USBControllerType_Last))
6206 return setError(E_INVALIDARG,
6207 tr("Invalid USB controller type: %d"),
6208 aType);
6209
6210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6211
6212 ComObjPtr<USBController> ctrl;
6213
6214 *aControllers = i_getUSBControllerCountByType(aType);
6215
6216 return S_OK;
6217}
6218
6219HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6220{
6221
6222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6223
6224 HRESULT rc = i_checkStateDependency(MutableStateDep);
6225 if (FAILED(rc)) return rc;
6226
6227 ComObjPtr<USBController> ctrl;
6228 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6229 if (FAILED(rc)) return rc;
6230
6231 i_setModified(IsModified_USB);
6232 mUSBControllers.backup();
6233
6234 ctrl->i_unshare();
6235
6236 mUSBControllers->remove(ctrl);
6237
6238 /* inform the direct session if any */
6239 alock.release();
6240 i_onUSBControllerChange();
6241
6242 return S_OK;
6243}
6244
6245HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6246 ULONG *aOriginX,
6247 ULONG *aOriginY,
6248 ULONG *aWidth,
6249 ULONG *aHeight,
6250 BOOL *aEnabled)
6251{
6252 uint32_t u32OriginX= 0;
6253 uint32_t u32OriginY= 0;
6254 uint32_t u32Width = 0;
6255 uint32_t u32Height = 0;
6256 uint16_t u16Flags = 0;
6257
6258 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6259 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6260 if (RT_FAILURE(vrc))
6261 {
6262#ifdef RT_OS_WINDOWS
6263 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6264 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6265 * So just assign fEnable to TRUE again.
6266 * The right fix would be to change GUI API wrappers to make sure that parameters
6267 * are changed only if API succeeds.
6268 */
6269 *aEnabled = TRUE;
6270#endif
6271 return setError(VBOX_E_IPRT_ERROR,
6272 tr("Saved guest size is not available (%Rrc)"),
6273 vrc);
6274 }
6275
6276 *aOriginX = u32OriginX;
6277 *aOriginY = u32OriginY;
6278 *aWidth = u32Width;
6279 *aHeight = u32Height;
6280 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6281
6282 return S_OK;
6283}
6284
6285HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6286{
6287 if (aScreenId != 0)
6288 return E_NOTIMPL;
6289
6290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 uint8_t *pu8Data = NULL;
6293 uint32_t cbData = 0;
6294 uint32_t u32Width = 0;
6295 uint32_t u32Height = 0;
6296
6297 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6298
6299 if (RT_FAILURE(vrc))
6300 return setError(VBOX_E_IPRT_ERROR,
6301 tr("Saved screenshot data is not available (%Rrc)"),
6302 vrc);
6303
6304 *aSize = cbData;
6305 *aWidth = u32Width;
6306 *aHeight = u32Height;
6307
6308 freeSavedDisplayScreenshot(pu8Data);
6309
6310 return S_OK;
6311}
6312
6313HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6314 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6315{
6316 if (aScreenId != 0)
6317 return E_NOTIMPL;
6318
6319 if ( aBitmapFormat != BitmapFormat_BGR0
6320 && aBitmapFormat != BitmapFormat_BGRA
6321 && aBitmapFormat != BitmapFormat_RGBA
6322 && aBitmapFormat != BitmapFormat_PNG)
6323 return setError(E_NOTIMPL,
6324 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6325
6326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6327
6328 uint8_t *pu8Data = NULL;
6329 uint32_t cbData = 0;
6330 uint32_t u32Width = 0;
6331 uint32_t u32Height = 0;
6332
6333 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6334
6335 if (RT_FAILURE(vrc))
6336 return setError(VBOX_E_IPRT_ERROR,
6337 tr("Saved thumbnail data is not available (%Rrc)"),
6338 vrc);
6339
6340 HRESULT hr = S_OK;
6341
6342 *aWidth = u32Width;
6343 *aHeight = u32Height;
6344
6345 if (cbData > 0)
6346 {
6347 /* Convert pixels to the format expected by the API caller. */
6348 if (aBitmapFormat == BitmapFormat_BGR0)
6349 {
6350 /* [0] B, [1] G, [2] R, [3] 0. */
6351 aData.resize(cbData);
6352 memcpy(&aData.front(), pu8Data, cbData);
6353 }
6354 else if (aBitmapFormat == BitmapFormat_BGRA)
6355 {
6356 /* [0] B, [1] G, [2] R, [3] A. */
6357 aData.resize(cbData);
6358 for (uint32_t i = 0; i < cbData; i += 4)
6359 {
6360 aData[i] = pu8Data[i];
6361 aData[i + 1] = pu8Data[i + 1];
6362 aData[i + 2] = pu8Data[i + 2];
6363 aData[i + 3] = 0xff;
6364 }
6365 }
6366 else if (aBitmapFormat == BitmapFormat_RGBA)
6367 {
6368 /* [0] R, [1] G, [2] B, [3] A. */
6369 aData.resize(cbData);
6370 for (uint32_t i = 0; i < cbData; i += 4)
6371 {
6372 aData[i] = pu8Data[i + 2];
6373 aData[i + 1] = pu8Data[i + 1];
6374 aData[i + 2] = pu8Data[i];
6375 aData[i + 3] = 0xff;
6376 }
6377 }
6378 else if (aBitmapFormat == BitmapFormat_PNG)
6379 {
6380 uint8_t *pu8PNG = NULL;
6381 uint32_t cbPNG = 0;
6382 uint32_t cxPNG = 0;
6383 uint32_t cyPNG = 0;
6384
6385 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6386
6387 if (RT_SUCCESS(vrc))
6388 {
6389 aData.resize(cbPNG);
6390 if (cbPNG)
6391 memcpy(&aData.front(), pu8PNG, cbPNG);
6392 }
6393 else
6394 hr = setError(VBOX_E_IPRT_ERROR,
6395 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6396 vrc);
6397
6398 RTMemFree(pu8PNG);
6399 }
6400 }
6401
6402 freeSavedDisplayScreenshot(pu8Data);
6403
6404 return hr;
6405}
6406
6407HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6408{
6409 if (aScreenId != 0)
6410 return E_NOTIMPL;
6411
6412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6413
6414 uint8_t *pu8Data = NULL;
6415 uint32_t cbData = 0;
6416 uint32_t u32Width = 0;
6417 uint32_t u32Height = 0;
6418
6419 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6420
6421 if (RT_FAILURE(vrc))
6422 return setError(VBOX_E_IPRT_ERROR,
6423 tr("Saved screenshot data is not available (%Rrc)"),
6424 vrc);
6425
6426 *aSize = cbData;
6427 *aWidth = u32Width;
6428 *aHeight = u32Height;
6429
6430 freeSavedDisplayScreenshot(pu8Data);
6431
6432 return S_OK;
6433}
6434
6435HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6436{
6437 if (aScreenId != 0)
6438 return E_NOTIMPL;
6439
6440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6441
6442 uint8_t *pu8Data = NULL;
6443 uint32_t cbData = 0;
6444 uint32_t u32Width = 0;
6445 uint32_t u32Height = 0;
6446
6447 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6448
6449 if (RT_FAILURE(vrc))
6450 return setError(VBOX_E_IPRT_ERROR,
6451 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6452 vrc);
6453
6454 *aWidth = u32Width;
6455 *aHeight = u32Height;
6456
6457 aData.resize(cbData);
6458 if (cbData)
6459 memcpy(&aData.front(), pu8Data, cbData);
6460
6461 freeSavedDisplayScreenshot(pu8Data);
6462
6463 return S_OK;
6464}
6465
6466HRESULT Machine::hotPlugCPU(ULONG aCpu)
6467{
6468 HRESULT rc = S_OK;
6469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6470
6471 if (!mHWData->mCPUHotPlugEnabled)
6472 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6473
6474 if (aCpu >= mHWData->mCPUCount)
6475 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6476
6477 if (mHWData->mCPUAttached[aCpu])
6478 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6479
6480 alock.release();
6481 rc = i_onCPUChange(aCpu, false);
6482 alock.acquire();
6483 if (FAILED(rc)) return rc;
6484
6485 i_setModified(IsModified_MachineData);
6486 mHWData.backup();
6487 mHWData->mCPUAttached[aCpu] = true;
6488
6489 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6490 if (Global::IsOnline(mData->mMachineState))
6491 i_saveSettings(NULL);
6492
6493 return S_OK;
6494}
6495
6496HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6497{
6498 HRESULT rc = S_OK;
6499
6500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6501
6502 if (!mHWData->mCPUHotPlugEnabled)
6503 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6504
6505 if (aCpu >= SchemaDefs::MaxCPUCount)
6506 return setError(E_INVALIDARG,
6507 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6508 SchemaDefs::MaxCPUCount);
6509
6510 if (!mHWData->mCPUAttached[aCpu])
6511 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6512
6513 /* CPU 0 can't be detached */
6514 if (aCpu == 0)
6515 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6516
6517 alock.release();
6518 rc = i_onCPUChange(aCpu, true);
6519 alock.acquire();
6520 if (FAILED(rc)) return rc;
6521
6522 i_setModified(IsModified_MachineData);
6523 mHWData.backup();
6524 mHWData->mCPUAttached[aCpu] = false;
6525
6526 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6527 if (Global::IsOnline(mData->mMachineState))
6528 i_saveSettings(NULL);
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6534{
6535 *aAttached = false;
6536
6537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 /* If hotplug is enabled the CPU is always enabled. */
6540 if (!mHWData->mCPUHotPlugEnabled)
6541 {
6542 if (aCpu < mHWData->mCPUCount)
6543 *aAttached = true;
6544 }
6545 else
6546 {
6547 if (aCpu < SchemaDefs::MaxCPUCount)
6548 *aAttached = mHWData->mCPUAttached[aCpu];
6549 }
6550
6551 return S_OK;
6552}
6553
6554HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6555{
6556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 Utf8Str log = i_queryLogFilename(aIdx);
6559 if (!RTFileExists(log.c_str()))
6560 log.setNull();
6561 aFilename = log;
6562
6563 return S_OK;
6564}
6565
6566HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6567{
6568 if (aSize < 0)
6569 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6570
6571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6572
6573 HRESULT rc = S_OK;
6574 Utf8Str log = i_queryLogFilename(aIdx);
6575
6576 /* do not unnecessarily hold the lock while doing something which does
6577 * not need the lock and potentially takes a long time. */
6578 alock.release();
6579
6580 /* Limit the chunk size to 32K for now, as that gives better performance
6581 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6582 * One byte expands to approx. 25 bytes of breathtaking XML. */
6583 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6584 aData.resize(cbData);
6585
6586 RTFILE LogFile;
6587 int vrc = RTFileOpen(&LogFile, log.c_str(),
6588 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6589 if (RT_SUCCESS(vrc))
6590 {
6591 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6592 if (RT_SUCCESS(vrc))
6593 aData.resize(cbData);
6594 else
6595 rc = setError(VBOX_E_IPRT_ERROR,
6596 tr("Could not read log file '%s' (%Rrc)"),
6597 log.c_str(), vrc);
6598 RTFileClose(LogFile);
6599 }
6600 else
6601 rc = setError(VBOX_E_IPRT_ERROR,
6602 tr("Could not open log file '%s' (%Rrc)"),
6603 log.c_str(), vrc);
6604
6605 if (FAILED(rc))
6606 aData.resize(0);
6607
6608 return rc;
6609}
6610
6611
6612/**
6613 * Currently this method doesn't attach device to the running VM,
6614 * just makes sure it's plugged on next VM start.
6615 */
6616HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6617{
6618 // lock scope
6619 {
6620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6621
6622 HRESULT rc = i_checkStateDependency(MutableStateDep);
6623 if (FAILED(rc)) return rc;
6624
6625 ChipsetType_T aChipset = ChipsetType_PIIX3;
6626 COMGETTER(ChipsetType)(&aChipset);
6627
6628 if (aChipset != ChipsetType_ICH9)
6629 {
6630 return setError(E_INVALIDARG,
6631 tr("Host PCI attachment only supported with ICH9 chipset"));
6632 }
6633
6634 // check if device with this host PCI address already attached
6635 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6636 it != mHWData->mPCIDeviceAssignments.end();
6637 ++it)
6638 {
6639 LONG iHostAddress = -1;
6640 ComPtr<PCIDeviceAttachment> pAttach;
6641 pAttach = *it;
6642 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6643 if (iHostAddress == aHostAddress)
6644 return setError(E_INVALIDARG,
6645 tr("Device with host PCI address already attached to this VM"));
6646 }
6647
6648 ComObjPtr<PCIDeviceAttachment> pda;
6649 char name[32];
6650
6651 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6652 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6653 Bstr bname(name);
6654 pda.createObject();
6655 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6656 i_setModified(IsModified_MachineData);
6657 mHWData.backup();
6658 mHWData->mPCIDeviceAssignments.push_back(pda);
6659 }
6660
6661 return S_OK;
6662}
6663
6664/**
6665 * Currently this method doesn't detach device from the running VM,
6666 * just makes sure it's not plugged on next VM start.
6667 */
6668HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6669{
6670 ComObjPtr<PCIDeviceAttachment> pAttach;
6671 bool fRemoved = false;
6672 HRESULT rc;
6673
6674 // lock scope
6675 {
6676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6677
6678 rc = i_checkStateDependency(MutableStateDep);
6679 if (FAILED(rc)) return rc;
6680
6681 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6682 it != mHWData->mPCIDeviceAssignments.end();
6683 ++it)
6684 {
6685 LONG iHostAddress = -1;
6686 pAttach = *it;
6687 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6688 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6689 {
6690 i_setModified(IsModified_MachineData);
6691 mHWData.backup();
6692 mHWData->mPCIDeviceAssignments.remove(pAttach);
6693 fRemoved = true;
6694 break;
6695 }
6696 }
6697 }
6698
6699
6700 /* Fire event outside of the lock */
6701 if (fRemoved)
6702 {
6703 Assert(!pAttach.isNull());
6704 ComPtr<IEventSource> es;
6705 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6706 Assert(SUCCEEDED(rc));
6707 Bstr mid;
6708 rc = this->COMGETTER(Id)(mid.asOutParam());
6709 Assert(SUCCEEDED(rc));
6710 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6711 }
6712
6713 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6714 tr("No host PCI device %08x attached"),
6715 aHostAddress
6716 );
6717}
6718
6719HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6720{
6721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6722
6723 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6724
6725 size_t i = 0;
6726 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6727 it != mHWData->mPCIDeviceAssignments.end();
6728 ++i, ++it)
6729 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6730
6731 return S_OK;
6732}
6733
6734HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6735{
6736 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6737
6738 return S_OK;
6739}
6740
6741HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6742{
6743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6744
6745 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6746
6747 return S_OK;
6748}
6749
6750HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6751{
6752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6753 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6754 if (SUCCEEDED(hrc))
6755 {
6756 hrc = mHWData.backupEx();
6757 if (SUCCEEDED(hrc))
6758 {
6759 i_setModified(IsModified_MachineData);
6760 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6761 }
6762 }
6763 return hrc;
6764}
6765
6766HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6767{
6768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6769 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6770 return S_OK;
6771}
6772
6773HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6774{
6775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6776 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6777 if (SUCCEEDED(hrc))
6778 {
6779 hrc = mHWData.backupEx();
6780 if (SUCCEEDED(hrc))
6781 {
6782 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6783 if (SUCCEEDED(hrc))
6784 i_setModified(IsModified_MachineData);
6785 }
6786 }
6787 return hrc;
6788}
6789
6790HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6791{
6792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6793
6794 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6795
6796 return S_OK;
6797}
6798
6799HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6800{
6801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6802 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6803 if (SUCCEEDED(hrc))
6804 {
6805 hrc = mHWData.backupEx();
6806 if (SUCCEEDED(hrc))
6807 {
6808 i_setModified(IsModified_MachineData);
6809 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6810 }
6811 }
6812 return hrc;
6813}
6814
6815HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6816{
6817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6818
6819 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6820
6821 return S_OK;
6822}
6823
6824HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6825{
6826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6829 if ( SUCCEEDED(hrc)
6830 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6831 {
6832 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6833 int vrc;
6834
6835 if (aAutostartEnabled)
6836 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6837 else
6838 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6839
6840 if (RT_SUCCESS(vrc))
6841 {
6842 hrc = mHWData.backupEx();
6843 if (SUCCEEDED(hrc))
6844 {
6845 i_setModified(IsModified_MachineData);
6846 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6847 }
6848 }
6849 else if (vrc == VERR_NOT_SUPPORTED)
6850 hrc = setError(VBOX_E_NOT_SUPPORTED,
6851 tr("The VM autostart feature is not supported on this platform"));
6852 else if (vrc == VERR_PATH_NOT_FOUND)
6853 hrc = setError(E_FAIL,
6854 tr("The path to the autostart database is not set"));
6855 else
6856 hrc = setError(E_UNEXPECTED,
6857 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6858 aAutostartEnabled ? "Adding" : "Removing",
6859 mUserData->s.strName.c_str(), vrc);
6860 }
6861 return hrc;
6862}
6863
6864HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6865{
6866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6867
6868 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6869
6870 return S_OK;
6871}
6872
6873HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6874{
6875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6876 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6877 if (SUCCEEDED(hrc))
6878 {
6879 hrc = mHWData.backupEx();
6880 if (SUCCEEDED(hrc))
6881 {
6882 i_setModified(IsModified_MachineData);
6883 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6884 }
6885 }
6886 return hrc;
6887}
6888
6889HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6890{
6891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6892
6893 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6894
6895 return S_OK;
6896}
6897
6898HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6899{
6900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6901 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6902 if ( SUCCEEDED(hrc)
6903 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6904 {
6905 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6906 int vrc;
6907
6908 if (aAutostopType != AutostopType_Disabled)
6909 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6910 else
6911 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6912
6913 if (RT_SUCCESS(vrc))
6914 {
6915 hrc = mHWData.backupEx();
6916 if (SUCCEEDED(hrc))
6917 {
6918 i_setModified(IsModified_MachineData);
6919 mHWData->mAutostart.enmAutostopType = aAutostopType;
6920 }
6921 }
6922 else if (vrc == VERR_NOT_SUPPORTED)
6923 hrc = setError(VBOX_E_NOT_SUPPORTED,
6924 tr("The VM autostop feature is not supported on this platform"));
6925 else if (vrc == VERR_PATH_NOT_FOUND)
6926 hrc = setError(E_FAIL,
6927 tr("The path to the autostart database is not set"));
6928 else
6929 hrc = setError(E_UNEXPECTED,
6930 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6931 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6932 mUserData->s.strName.c_str(), vrc);
6933 }
6934 return hrc;
6935}
6936
6937HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6938{
6939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6940
6941 aDefaultFrontend = mHWData->mDefaultFrontend;
6942
6943 return S_OK;
6944}
6945
6946HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6947{
6948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6949 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6950 if (SUCCEEDED(hrc))
6951 {
6952 hrc = mHWData.backupEx();
6953 if (SUCCEEDED(hrc))
6954 {
6955 i_setModified(IsModified_MachineData);
6956 mHWData->mDefaultFrontend = aDefaultFrontend;
6957 }
6958 }
6959 return hrc;
6960}
6961
6962HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6963{
6964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6965 size_t cbIcon = mUserData->mIcon.size();
6966 aIcon.resize(cbIcon);
6967 if (cbIcon)
6968 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6969 return S_OK;
6970}
6971
6972HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6973{
6974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6975 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6976 if (SUCCEEDED(hrc))
6977 {
6978 i_setModified(IsModified_MachineData);
6979 mUserData.backup();
6980 size_t cbIcon = aIcon.size();
6981 mUserData->mIcon.resize(cbIcon);
6982 if (cbIcon)
6983 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6984 }
6985 return hrc;
6986}
6987
6988HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6989{
6990#ifdef VBOX_WITH_USB
6991 *aUSBProxyAvailable = true;
6992#else
6993 *aUSBProxyAvailable = false;
6994#endif
6995 return S_OK;
6996}
6997
6998HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6999 ComPtr<IProgress> &aProgress)
7000{
7001 ComObjPtr<Progress> pP;
7002 Progress *ppP = pP;
7003 IProgress *iP = static_cast<IProgress *>(ppP);
7004 IProgress **pProgress = &iP;
7005
7006 IMachine *pTarget = aTarget;
7007
7008 /* Convert the options. */
7009 RTCList<CloneOptions_T> optList;
7010 if (aOptions.size())
7011 for (size_t i = 0; i < aOptions.size(); ++i)
7012 optList.append(aOptions[i]);
7013
7014 if (optList.contains(CloneOptions_Link))
7015 {
7016 if (!i_isSnapshotMachine())
7017 return setError(E_INVALIDARG,
7018 tr("Linked clone can only be created from a snapshot"));
7019 if (aMode != CloneMode_MachineState)
7020 return setError(E_INVALIDARG,
7021 tr("Linked clone can only be created for a single machine state"));
7022 }
7023 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7024
7025 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7026
7027 HRESULT rc = pWorker->start(pProgress);
7028
7029 pP = static_cast<Progress *>(*pProgress);
7030 pP.queryInterfaceTo(aProgress.asOutParam());
7031
7032 return rc;
7033
7034}
7035
7036// public methods for internal purposes
7037/////////////////////////////////////////////////////////////////////////////
7038
7039/**
7040 * Adds the given IsModified_* flag to the dirty flags of the machine.
7041 * This must be called either during i_loadSettings or under the machine write lock.
7042 * @param fl
7043 */
7044void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7045{
7046 mData->flModifications |= fl;
7047 if (fAllowStateModification && i_isStateModificationAllowed())
7048 mData->mCurrentStateModified = true;
7049}
7050
7051/**
7052 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7053 * care of the write locking.
7054 *
7055 * @param fModifications The flag to add.
7056 */
7057void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7058{
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 i_setModified(fModification, fAllowStateModification);
7061}
7062
7063/**
7064 * Saves the registry entry of this machine to the given configuration node.
7065 *
7066 * @param aEntryNode Node to save the registry entry to.
7067 *
7068 * @note locks this object for reading.
7069 */
7070HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7071{
7072 AutoLimitedCaller autoCaller(this);
7073 AssertComRCReturnRC(autoCaller.rc());
7074
7075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7076
7077 data.uuid = mData->mUuid;
7078 data.strSettingsFile = mData->m_strConfigFile;
7079
7080 return S_OK;
7081}
7082
7083/**
7084 * Calculates the absolute path of the given path taking the directory of the
7085 * machine settings file as the current directory.
7086 *
7087 * @param aPath Path to calculate the absolute path for.
7088 * @param aResult Where to put the result (used only on success, can be the
7089 * same Utf8Str instance as passed in @a aPath).
7090 * @return IPRT result.
7091 *
7092 * @note Locks this object for reading.
7093 */
7094int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7095{
7096 AutoCaller autoCaller(this);
7097 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7098
7099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7100
7101 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7102
7103 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7104
7105 strSettingsDir.stripFilename();
7106 char folder[RTPATH_MAX];
7107 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7108 if (RT_SUCCESS(vrc))
7109 aResult = folder;
7110
7111 return vrc;
7112}
7113
7114/**
7115 * Copies strSource to strTarget, making it relative to the machine folder
7116 * if it is a subdirectory thereof, or simply copying it otherwise.
7117 *
7118 * @param strSource Path to evaluate and copy.
7119 * @param strTarget Buffer to receive target path.
7120 *
7121 * @note Locks this object for reading.
7122 */
7123void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7124 Utf8Str &strTarget)
7125{
7126 AutoCaller autoCaller(this);
7127 AssertComRCReturn(autoCaller.rc(), (void)0);
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7132 // use strTarget as a temporary buffer to hold the machine settings dir
7133 strTarget = mData->m_strConfigFileFull;
7134 strTarget.stripFilename();
7135 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7136 {
7137 // is relative: then append what's left
7138 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7139 // for empty paths (only possible for subdirs) use "." to avoid
7140 // triggering default settings for not present config attributes.
7141 if (strTarget.isEmpty())
7142 strTarget = ".";
7143 }
7144 else
7145 // is not relative: then overwrite
7146 strTarget = strSource;
7147}
7148
7149/**
7150 * Returns the full path to the machine's log folder in the
7151 * \a aLogFolder argument.
7152 */
7153void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7154{
7155 AutoCaller autoCaller(this);
7156 AssertComRCReturnVoid(autoCaller.rc());
7157
7158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7159
7160 char szTmp[RTPATH_MAX];
7161 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7162 if (RT_SUCCESS(vrc))
7163 {
7164 if (szTmp[0] && !mUserData.isNull())
7165 {
7166 char szTmp2[RTPATH_MAX];
7167 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7168 if (RT_SUCCESS(vrc))
7169 aLogFolder = BstrFmt("%s%c%s",
7170 szTmp2,
7171 RTPATH_DELIMITER,
7172 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7173 }
7174 else
7175 vrc = VERR_PATH_IS_RELATIVE;
7176 }
7177
7178 if (RT_FAILURE(vrc))
7179 {
7180 // fallback if VBOX_USER_LOGHOME is not set or invalid
7181 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7182 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7183 aLogFolder.append(RTPATH_DELIMITER);
7184 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7185 }
7186}
7187
7188/**
7189 * Returns the full path to the machine's log file for an given index.
7190 */
7191Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7192 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7193{
7194 Utf8Str logFolder;
7195 getLogFolder(logFolder);
7196 Assert(logFolder.length());
7197 Utf8Str log;
7198 if (idx == 0)
7199 log = Utf8StrFmt("%s%cVBox.log",
7200 logFolder.c_str(), RTPATH_DELIMITER);
7201 else
7202 log = Utf8StrFmt("%s%cVBox.log.%d",
7203 logFolder.c_str(), RTPATH_DELIMITER, idx);
7204 return log;
7205}
7206
7207/**
7208 * Returns the full path to the machine's (hardened) startup log file.
7209 */
7210Utf8Str Machine::i_getStartupLogFilename(void)
7211{
7212 Utf8Str strFilename;
7213 getLogFolder(strFilename);
7214 Assert(strFilename.length());
7215 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7216 return strFilename;
7217}
7218
7219
7220/**
7221 * Composes a unique saved state filename based on the current system time. The filename is
7222 * granular to the second so this will work so long as no more than one snapshot is taken on
7223 * a machine per second.
7224 *
7225 * Before version 4.1, we used this formula for saved state files:
7226 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7227 * which no longer works because saved state files can now be shared between the saved state of the
7228 * "saved" machine and an online snapshot, and the following would cause problems:
7229 * 1) save machine
7230 * 2) create online snapshot from that machine state --> reusing saved state file
7231 * 3) save machine again --> filename would be reused, breaking the online snapshot
7232 *
7233 * So instead we now use a timestamp.
7234 *
7235 * @param str
7236 */
7237
7238void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7239{
7240 AutoCaller autoCaller(this);
7241 AssertComRCReturnVoid(autoCaller.rc());
7242
7243 {
7244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7245 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7246 }
7247
7248 RTTIMESPEC ts;
7249 RTTimeNow(&ts);
7250 RTTIME time;
7251 RTTimeExplode(&time, &ts);
7252
7253 strStateFilePath += RTPATH_DELIMITER;
7254 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7255 time.i32Year, time.u8Month, time.u8MonthDay,
7256 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7257}
7258
7259/**
7260 * Returns the full path to the default video capture file.
7261 */
7262void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7263{
7264 AutoCaller autoCaller(this);
7265 AssertComRCReturnVoid(autoCaller.rc());
7266
7267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7268
7269 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7270 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7271 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7272}
7273
7274/**
7275 * Returns whether at least one USB controller is present for the VM.
7276 */
7277bool Machine::i_isUSBControllerPresent()
7278{
7279 AutoCaller autoCaller(this);
7280 AssertComRCReturn(autoCaller.rc(), false);
7281
7282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7283
7284 return (mUSBControllers->size() > 0);
7285}
7286
7287/**
7288 * @note Locks this object for writing, calls the client process
7289 * (inside the lock).
7290 */
7291HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7292 const Utf8Str &strFrontend,
7293 const Utf8Str &strEnvironment,
7294 ProgressProxy *aProgress)
7295{
7296 LogFlowThisFuncEnter();
7297
7298 AssertReturn(aControl, E_FAIL);
7299 AssertReturn(aProgress, E_FAIL);
7300 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7301
7302 AutoCaller autoCaller(this);
7303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7304
7305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7306
7307 if (!mData->mRegistered)
7308 return setError(E_UNEXPECTED,
7309 tr("The machine '%s' is not registered"),
7310 mUserData->s.strName.c_str());
7311
7312 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7313
7314 if ( mData->mSession.mState == SessionState_Locked
7315 || mData->mSession.mState == SessionState_Spawning
7316 || mData->mSession.mState == SessionState_Unlocking)
7317 return setError(VBOX_E_INVALID_OBJECT_STATE,
7318 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7319 mUserData->s.strName.c_str());
7320
7321 /* may not be busy */
7322 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7323
7324 /* get the path to the executable */
7325 char szPath[RTPATH_MAX];
7326 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7327 size_t cchBufLeft = strlen(szPath);
7328 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7329 szPath[cchBufLeft] = 0;
7330 char *pszNamePart = szPath + cchBufLeft;
7331 cchBufLeft = sizeof(szPath) - cchBufLeft;
7332
7333 int vrc = VINF_SUCCESS;
7334 RTPROCESS pid = NIL_RTPROCESS;
7335
7336 RTENV env = RTENV_DEFAULT;
7337
7338 if (!strEnvironment.isEmpty())
7339 {
7340 char *newEnvStr = NULL;
7341
7342 do
7343 {
7344 /* clone the current environment */
7345 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7346 AssertRCBreakStmt(vrc2, vrc = vrc2);
7347
7348 newEnvStr = RTStrDup(strEnvironment.c_str());
7349 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7350
7351 /* put new variables to the environment
7352 * (ignore empty variable names here since RTEnv API
7353 * intentionally doesn't do that) */
7354 char *var = newEnvStr;
7355 for (char *p = newEnvStr; *p; ++p)
7356 {
7357 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7358 {
7359 *p = '\0';
7360 if (*var)
7361 {
7362 char *val = strchr(var, '=');
7363 if (val)
7364 {
7365 *val++ = '\0';
7366 vrc2 = RTEnvSetEx(env, var, val);
7367 }
7368 else
7369 vrc2 = RTEnvUnsetEx(env, var);
7370 if (RT_FAILURE(vrc2))
7371 break;
7372 }
7373 var = p + 1;
7374 }
7375 }
7376 if (RT_SUCCESS(vrc2) && *var)
7377 vrc2 = RTEnvPutEx(env, var);
7378
7379 AssertRCBreakStmt(vrc2, vrc = vrc2);
7380 }
7381 while (0);
7382
7383 if (newEnvStr != NULL)
7384 RTStrFree(newEnvStr);
7385 }
7386
7387 /* Hardened startup logging */
7388#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7389 Utf8Str strSupStartLogArg("--sup-startup-log=");
7390 {
7391 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7392 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7393 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7394 {
7395 Utf8Str strStartupLogDir = strStartupLogFile;
7396 strStartupLogDir.stripFilename();
7397 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7398 file without stripping the file. */
7399 }
7400 strSupStartLogArg.append(strStartupLogFile);
7401 }
7402 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7403#else
7404 const char *pszSupStartupLogArg = NULL;
7405#endif
7406
7407
7408#ifdef VBOX_WITH_QTGUI
7409 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7410 {
7411# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7412 /* Modify the base path so that we don't need to use ".." below. */
7413 RTPathStripTrailingSlash(szPath);
7414 RTPathStripFilename(szPath);
7415 cchBufLeft = strlen(szPath);
7416 pszNamePart = szPath + cchBufLeft;
7417 cchBufLeft = sizeof(szPath) - cchBufLeft;
7418
7419# define OSX_APP_NAME "VirtualBoxVM"
7420# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7421
7422 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7423 if ( strAppOverride.contains(".")
7424 || strAppOverride.contains("/")
7425 || strAppOverride.contains("\\")
7426 || strAppOverride.contains(":"))
7427 strAppOverride.setNull();
7428 Utf8Str strAppPath;
7429 if (!strAppOverride.isEmpty())
7430 {
7431 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7432 Utf8Str strFullPath(szPath);
7433 strFullPath.append(strAppPath);
7434 /* there is a race, but people using this deserve the failure */
7435 if (!RTFileExists(strFullPath.c_str()))
7436 strAppOverride.setNull();
7437 }
7438 if (strAppOverride.isEmpty())
7439 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7440 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7441 strcpy(pszNamePart, strAppPath.c_str());
7442# else
7443 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7444 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7445 strcpy(pszNamePart, s_szVirtualBox_exe);
7446# endif
7447
7448 Utf8Str idStr = mData->mUuid.toString();
7449 const char *apszArgs[] =
7450 {
7451 szPath,
7452 "--comment", mUserData->s.strName.c_str(),
7453 "--startvm", idStr.c_str(),
7454 "--no-startvm-errormsgbox",
7455 pszSupStartupLogArg,
7456 NULL
7457 };
7458 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7459 }
7460#else /* !VBOX_WITH_QTGUI */
7461 if (0)
7462 ;
7463#endif /* VBOX_WITH_QTGUI */
7464
7465 else
7466
7467#ifdef VBOX_WITH_VBOXSDL
7468 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7469 {
7470 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7471 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7472 strcpy(pszNamePart, s_szVBoxSDL_exe);
7473
7474 Utf8Str idStr = mData->mUuid.toString();
7475 const char *apszArgs[] =
7476 {
7477 szPath,
7478 "--comment", mUserData->s.strName.c_str(),
7479 "--startvm", idStr.c_str(),
7480 pszSupStartupLogArg,
7481 NULL
7482 };
7483 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7484 }
7485#else /* !VBOX_WITH_VBOXSDL */
7486 if (0)
7487 ;
7488#endif /* !VBOX_WITH_VBOXSDL */
7489
7490 else
7491
7492#ifdef VBOX_WITH_HEADLESS
7493 if ( strFrontend == "headless"
7494 || strFrontend == "capture"
7495 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7496 )
7497 {
7498 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7499 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7500 * and a VM works even if the server has not been installed.
7501 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7502 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7503 * differently in 4.0 and 3.x.
7504 */
7505 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7506 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7507 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7508
7509 Utf8Str idStr = mData->mUuid.toString();
7510 const char *apszArgs[] =
7511 {
7512 szPath,
7513 "--comment", mUserData->s.strName.c_str(),
7514 "--startvm", idStr.c_str(),
7515 "--vrde", "config",
7516 0, /* For "--capture". */
7517 0, /* For "--sup-startup-log". */
7518 0
7519 };
7520 unsigned iArg = 7;
7521 if (strFrontend == "capture")
7522 apszArgs[iArg++] = "--capture";
7523 apszArgs[iArg++] = pszSupStartupLogArg;
7524
7525# ifdef RT_OS_WINDOWS
7526 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7527# else
7528 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7529# endif
7530 }
7531#else /* !VBOX_WITH_HEADLESS */
7532 if (0)
7533 ;
7534#endif /* !VBOX_WITH_HEADLESS */
7535 else
7536 {
7537 RTEnvDestroy(env);
7538 return setError(E_INVALIDARG,
7539 tr("Invalid frontend name: '%s'"),
7540 strFrontend.c_str());
7541 }
7542
7543 RTEnvDestroy(env);
7544
7545 if (RT_FAILURE(vrc))
7546 return setError(VBOX_E_IPRT_ERROR,
7547 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7548 mUserData->s.strName.c_str(), vrc);
7549
7550 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7551
7552 /*
7553 * Note that we don't release the lock here before calling the client,
7554 * because it doesn't need to call us back if called with a NULL argument.
7555 * Releasing the lock here is dangerous because we didn't prepare the
7556 * launch data yet, but the client we've just started may happen to be
7557 * too fast and call LockMachine() that will fail (because of PID, etc.),
7558 * so that the Machine will never get out of the Spawning session state.
7559 */
7560
7561 /* inform the session that it will be a remote one */
7562 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7563#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7564 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7565#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7566 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7567#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7568 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7569
7570 if (FAILED(rc))
7571 {
7572 /* restore the session state */
7573 mData->mSession.mState = SessionState_Unlocked;
7574 alock.release();
7575 mParent->i_addProcessToReap(pid);
7576 /* The failure may occur w/o any error info (from RPC), so provide one */
7577 return setError(VBOX_E_VM_ERROR,
7578 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7579 }
7580
7581 /* attach launch data to the machine */
7582 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7583 mData->mSession.mRemoteControls.push_back(aControl);
7584 mData->mSession.mProgress = aProgress;
7585 mData->mSession.mPID = pid;
7586 mData->mSession.mState = SessionState_Spawning;
7587 mData->mSession.mType = strFrontend;
7588
7589 alock.release();
7590 mParent->i_addProcessToReap(pid);
7591
7592 LogFlowThisFuncLeave();
7593 return S_OK;
7594}
7595
7596/**
7597 * Returns @c true if the given session machine instance has an open direct
7598 * session (and optionally also for direct sessions which are closing) and
7599 * returns the session control machine instance if so.
7600 *
7601 * Note that when the method returns @c false, the arguments remain unchanged.
7602 *
7603 * @param aMachine Session machine object.
7604 * @param aControl Direct session control object (optional).
7605 * @param aAllowClosing If true then additionally a session which is currently
7606 * being closed will also be allowed.
7607 *
7608 * @note locks this object for reading.
7609 */
7610bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7611 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7612 bool aAllowClosing /*= false*/)
7613{
7614 AutoLimitedCaller autoCaller(this);
7615 AssertComRCReturn(autoCaller.rc(), false);
7616
7617 /* just return false for inaccessible machines */
7618 if (getObjectState().getState() != ObjectState::Ready)
7619 return false;
7620
7621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7622
7623 if ( mData->mSession.mState == SessionState_Locked
7624 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7625 )
7626 {
7627 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7628
7629 aMachine = mData->mSession.mMachine;
7630
7631 if (aControl != NULL)
7632 *aControl = mData->mSession.mDirectControl;
7633
7634 return true;
7635 }
7636
7637 return false;
7638}
7639
7640/**
7641 * Returns @c true if the given machine has an spawning direct session.
7642 *
7643 * @note locks this object for reading.
7644 */
7645bool Machine::i_isSessionSpawning()
7646{
7647 AutoLimitedCaller autoCaller(this);
7648 AssertComRCReturn(autoCaller.rc(), false);
7649
7650 /* just return false for inaccessible machines */
7651 if (getObjectState().getState() != ObjectState::Ready)
7652 return false;
7653
7654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7655
7656 if (mData->mSession.mState == SessionState_Spawning)
7657 return true;
7658
7659 return false;
7660}
7661
7662/**
7663 * Called from the client watcher thread to check for unexpected client process
7664 * death during Session_Spawning state (e.g. before it successfully opened a
7665 * direct session).
7666 *
7667 * On Win32 and on OS/2, this method is called only when we've got the
7668 * direct client's process termination notification, so it always returns @c
7669 * true.
7670 *
7671 * On other platforms, this method returns @c true if the client process is
7672 * terminated and @c false if it's still alive.
7673 *
7674 * @note Locks this object for writing.
7675 */
7676bool Machine::i_checkForSpawnFailure()
7677{
7678 AutoCaller autoCaller(this);
7679 if (!autoCaller.isOk())
7680 {
7681 /* nothing to do */
7682 LogFlowThisFunc(("Already uninitialized!\n"));
7683 return true;
7684 }
7685
7686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7687
7688 if (mData->mSession.mState != SessionState_Spawning)
7689 {
7690 /* nothing to do */
7691 LogFlowThisFunc(("Not spawning any more!\n"));
7692 return true;
7693 }
7694
7695 HRESULT rc = S_OK;
7696
7697 /* PID not yet initialized, skip check. */
7698 if (mData->mSession.mPID == NIL_RTPROCESS)
7699 return false;
7700
7701 RTPROCSTATUS status;
7702 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7703
7704 if (vrc != VERR_PROCESS_RUNNING)
7705 {
7706 Utf8Str strExtraInfo;
7707
7708#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7709 /* If the startup logfile exists and is of non-zero length, tell the
7710 user to look there for more details to encourage them to attach it
7711 when reporting startup issues. */
7712 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7713 uint64_t cbStartupLogFile = 0;
7714 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7715 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7716 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7717#endif
7718
7719 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7720 rc = setError(E_FAIL,
7721 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7722 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7723 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7724 rc = setError(E_FAIL,
7725 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7726 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7727 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7728 rc = setError(E_FAIL,
7729 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7730 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7731 else
7732 rc = setError(E_FAIL,
7733 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7734 i_getName().c_str(), vrc, strExtraInfo.c_str());
7735 }
7736
7737 if (FAILED(rc))
7738 {
7739 /* Close the remote session, remove the remote control from the list
7740 * and reset session state to Closed (@note keep the code in sync with
7741 * the relevant part in LockMachine()). */
7742
7743 Assert(mData->mSession.mRemoteControls.size() == 1);
7744 if (mData->mSession.mRemoteControls.size() == 1)
7745 {
7746 ErrorInfoKeeper eik;
7747 mData->mSession.mRemoteControls.front()->Uninitialize();
7748 }
7749
7750 mData->mSession.mRemoteControls.clear();
7751 mData->mSession.mState = SessionState_Unlocked;
7752
7753 /* finalize the progress after setting the state */
7754 if (!mData->mSession.mProgress.isNull())
7755 {
7756 mData->mSession.mProgress->notifyComplete(rc);
7757 mData->mSession.mProgress.setNull();
7758 }
7759
7760 mData->mSession.mPID = NIL_RTPROCESS;
7761
7762 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7763 return true;
7764 }
7765
7766 return false;
7767}
7768
7769/**
7770 * Checks whether the machine can be registered. If so, commits and saves
7771 * all settings.
7772 *
7773 * @note Must be called from mParent's write lock. Locks this object and
7774 * children for writing.
7775 */
7776HRESULT Machine::i_prepareRegister()
7777{
7778 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7779
7780 AutoLimitedCaller autoCaller(this);
7781 AssertComRCReturnRC(autoCaller.rc());
7782
7783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 /* wait for state dependents to drop to zero */
7786 i_ensureNoStateDependencies();
7787
7788 if (!mData->mAccessible)
7789 return setError(VBOX_E_INVALID_OBJECT_STATE,
7790 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7791 mUserData->s.strName.c_str(),
7792 mData->mUuid.toString().c_str());
7793
7794 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7795
7796 if (mData->mRegistered)
7797 return setError(VBOX_E_INVALID_OBJECT_STATE,
7798 tr("The machine '%s' with UUID {%s} is already registered"),
7799 mUserData->s.strName.c_str(),
7800 mData->mUuid.toString().c_str());
7801
7802 HRESULT rc = S_OK;
7803
7804 // Ensure the settings are saved. If we are going to be registered and
7805 // no config file exists yet, create it by calling i_saveSettings() too.
7806 if ( (mData->flModifications)
7807 || (!mData->pMachineConfigFile->fileExists())
7808 )
7809 {
7810 rc = i_saveSettings(NULL);
7811 // no need to check whether VirtualBox.xml needs saving too since
7812 // we can't have a machine XML file rename pending
7813 if (FAILED(rc)) return rc;
7814 }
7815
7816 /* more config checking goes here */
7817
7818 if (SUCCEEDED(rc))
7819 {
7820 /* we may have had implicit modifications we want to fix on success */
7821 i_commit();
7822
7823 mData->mRegistered = true;
7824 }
7825 else
7826 {
7827 /* we may have had implicit modifications we want to cancel on failure*/
7828 i_rollback(false /* aNotify */);
7829 }
7830
7831 return rc;
7832}
7833
7834/**
7835 * Increases the number of objects dependent on the machine state or on the
7836 * registered state. Guarantees that these two states will not change at least
7837 * until #releaseStateDependency() is called.
7838 *
7839 * Depending on the @a aDepType value, additional state checks may be made.
7840 * These checks will set extended error info on failure. See
7841 * #checkStateDependency() for more info.
7842 *
7843 * If this method returns a failure, the dependency is not added and the caller
7844 * is not allowed to rely on any particular machine state or registration state
7845 * value and may return the failed result code to the upper level.
7846 *
7847 * @param aDepType Dependency type to add.
7848 * @param aState Current machine state (NULL if not interested).
7849 * @param aRegistered Current registered state (NULL if not interested).
7850 *
7851 * @note Locks this object for writing.
7852 */
7853HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7854 MachineState_T *aState /* = NULL */,
7855 BOOL *aRegistered /* = NULL */)
7856{
7857 AutoCaller autoCaller(this);
7858 AssertComRCReturnRC(autoCaller.rc());
7859
7860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7861
7862 HRESULT rc = i_checkStateDependency(aDepType);
7863 if (FAILED(rc)) return rc;
7864
7865 {
7866 if (mData->mMachineStateChangePending != 0)
7867 {
7868 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7869 * drop to zero so don't add more. It may make sense to wait a bit
7870 * and retry before reporting an error (since the pending state
7871 * transition should be really quick) but let's just assert for
7872 * now to see if it ever happens on practice. */
7873
7874 AssertFailed();
7875
7876 return setError(E_ACCESSDENIED,
7877 tr("Machine state change is in progress. Please retry the operation later."));
7878 }
7879
7880 ++mData->mMachineStateDeps;
7881 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7882 }
7883
7884 if (aState)
7885 *aState = mData->mMachineState;
7886 if (aRegistered)
7887 *aRegistered = mData->mRegistered;
7888
7889 return S_OK;
7890}
7891
7892/**
7893 * Decreases the number of objects dependent on the machine state.
7894 * Must always complete the #addStateDependency() call after the state
7895 * dependency is no more necessary.
7896 */
7897void Machine::i_releaseStateDependency()
7898{
7899 AutoCaller autoCaller(this);
7900 AssertComRCReturnVoid(autoCaller.rc());
7901
7902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7903
7904 /* releaseStateDependency() w/o addStateDependency()? */
7905 AssertReturnVoid(mData->mMachineStateDeps != 0);
7906 -- mData->mMachineStateDeps;
7907
7908 if (mData->mMachineStateDeps == 0)
7909 {
7910 /* inform i_ensureNoStateDependencies() that there are no more deps */
7911 if (mData->mMachineStateChangePending != 0)
7912 {
7913 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7914 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7915 }
7916 }
7917}
7918
7919Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7920{
7921 /* start with nothing found */
7922 Utf8Str strResult("");
7923
7924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7925
7926 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7927 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7928 // found:
7929 strResult = it->second; // source is a Utf8Str
7930
7931 return strResult;
7932}
7933
7934// protected methods
7935/////////////////////////////////////////////////////////////////////////////
7936
7937/**
7938 * Performs machine state checks based on the @a aDepType value. If a check
7939 * fails, this method will set extended error info, otherwise it will return
7940 * S_OK. It is supposed, that on failure, the caller will immediately return
7941 * the return value of this method to the upper level.
7942 *
7943 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7944 *
7945 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7946 * current state of this machine object allows to change settings of the
7947 * machine (i.e. the machine is not registered, or registered but not running
7948 * and not saved). It is useful to call this method from Machine setters
7949 * before performing any change.
7950 *
7951 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7952 * as for MutableStateDep except that if the machine is saved, S_OK is also
7953 * returned. This is useful in setters which allow changing machine
7954 * properties when it is in the saved state.
7955 *
7956 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7957 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7958 * Aborted).
7959 *
7960 * @param aDepType Dependency type to check.
7961 *
7962 * @note Non Machine based classes should use #addStateDependency() and
7963 * #releaseStateDependency() methods or the smart AutoStateDependency
7964 * template.
7965 *
7966 * @note This method must be called from under this object's read or write
7967 * lock.
7968 */
7969HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7970{
7971 switch (aDepType)
7972 {
7973 case AnyStateDep:
7974 {
7975 break;
7976 }
7977 case MutableStateDep:
7978 {
7979 if ( mData->mRegistered
7980 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7981 Paused should actually be included here... (Live Migration) */
7982 || ( mData->mMachineState != MachineState_Paused
7983 && mData->mMachineState != MachineState_Running
7984 && mData->mMachineState != MachineState_Aborted
7985 && mData->mMachineState != MachineState_Teleported
7986 && mData->mMachineState != MachineState_PoweredOff
7987 )
7988 )
7989 )
7990 return setError(VBOX_E_INVALID_VM_STATE,
7991 tr("The machine is not mutable (state is %s)"),
7992 Global::stringifyMachineState(mData->mMachineState));
7993 break;
7994 }
7995 case MutableOrSavedStateDep:
7996 {
7997 if ( mData->mRegistered
7998 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7999 Paused should actually be included here... (Live Migration) */
8000 || ( mData->mMachineState != MachineState_Paused
8001 && mData->mMachineState != MachineState_Running
8002 && mData->mMachineState != MachineState_Aborted
8003 && mData->mMachineState != MachineState_Teleported
8004 && mData->mMachineState != MachineState_Saved
8005 && mData->mMachineState != MachineState_PoweredOff
8006 )
8007 )
8008 )
8009 return setError(VBOX_E_INVALID_VM_STATE,
8010 tr("The machine is not mutable (state is %s)"),
8011 Global::stringifyMachineState(mData->mMachineState));
8012 break;
8013 }
8014 case OfflineStateDep:
8015 {
8016 if ( mData->mRegistered
8017 && ( !i_isSessionMachine()
8018 || ( mData->mMachineState != MachineState_PoweredOff
8019 && mData->mMachineState != MachineState_Saved
8020 && mData->mMachineState != MachineState_Aborted
8021 && mData->mMachineState != MachineState_Teleported
8022 )
8023 )
8024 )
8025 return setError(VBOX_E_INVALID_VM_STATE,
8026 tr("The machine is not offline (state is %s)"),
8027 Global::stringifyMachineState(mData->mMachineState));
8028 break;
8029 }
8030 }
8031
8032 return S_OK;
8033}
8034
8035/**
8036 * Helper to initialize all associated child objects and allocate data
8037 * structures.
8038 *
8039 * This method must be called as a part of the object's initialization procedure
8040 * (usually done in the #init() method).
8041 *
8042 * @note Must be called only from #init() or from #registeredInit().
8043 */
8044HRESULT Machine::initDataAndChildObjects()
8045{
8046 AutoCaller autoCaller(this);
8047 AssertComRCReturnRC(autoCaller.rc());
8048 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8049 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8050
8051 AssertReturn(!mData->mAccessible, E_FAIL);
8052
8053 /* allocate data structures */
8054 mSSData.allocate();
8055 mUserData.allocate();
8056 mHWData.allocate();
8057 mMediaData.allocate();
8058 mStorageControllers.allocate();
8059 mUSBControllers.allocate();
8060
8061 /* initialize mOSTypeId */
8062 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8063
8064 /* create associated BIOS settings object */
8065 unconst(mBIOSSettings).createObject();
8066 mBIOSSettings->init(this);
8067
8068 /* create an associated VRDE object (default is disabled) */
8069 unconst(mVRDEServer).createObject();
8070 mVRDEServer->init(this);
8071
8072 /* create associated serial port objects */
8073 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8074 {
8075 unconst(mSerialPorts[slot]).createObject();
8076 mSerialPorts[slot]->init(this, slot);
8077 }
8078
8079 /* create associated parallel port objects */
8080 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8081 {
8082 unconst(mParallelPorts[slot]).createObject();
8083 mParallelPorts[slot]->init(this, slot);
8084 }
8085
8086 /* create the audio adapter object (always present, default is disabled) */
8087 unconst(mAudioAdapter).createObject();
8088 mAudioAdapter->init(this);
8089
8090 /* create the USB device filters object (always present) */
8091 unconst(mUSBDeviceFilters).createObject();
8092 mUSBDeviceFilters->init(this);
8093
8094 /* create associated network adapter objects */
8095 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8096 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8097 {
8098 unconst(mNetworkAdapters[slot]).createObject();
8099 mNetworkAdapters[slot]->init(this, slot);
8100 }
8101
8102 /* create the bandwidth control */
8103 unconst(mBandwidthControl).createObject();
8104 mBandwidthControl->init(this);
8105
8106 return S_OK;
8107}
8108
8109/**
8110 * Helper to uninitialize all associated child objects and to free all data
8111 * structures.
8112 *
8113 * This method must be called as a part of the object's uninitialization
8114 * procedure (usually done in the #uninit() method).
8115 *
8116 * @note Must be called only from #uninit() or from #registeredInit().
8117 */
8118void Machine::uninitDataAndChildObjects()
8119{
8120 AutoCaller autoCaller(this);
8121 AssertComRCReturnVoid(autoCaller.rc());
8122 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8123 || getObjectState().getState() == ObjectState::Limited);
8124
8125 /* tell all our other child objects we've been uninitialized */
8126 if (mBandwidthControl)
8127 {
8128 mBandwidthControl->uninit();
8129 unconst(mBandwidthControl).setNull();
8130 }
8131
8132 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8133 {
8134 if (mNetworkAdapters[slot])
8135 {
8136 mNetworkAdapters[slot]->uninit();
8137 unconst(mNetworkAdapters[slot]).setNull();
8138 }
8139 }
8140
8141 if (mUSBDeviceFilters)
8142 {
8143 mUSBDeviceFilters->uninit();
8144 unconst(mUSBDeviceFilters).setNull();
8145 }
8146
8147 if (mAudioAdapter)
8148 {
8149 mAudioAdapter->uninit();
8150 unconst(mAudioAdapter).setNull();
8151 }
8152
8153 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8154 {
8155 if (mParallelPorts[slot])
8156 {
8157 mParallelPorts[slot]->uninit();
8158 unconst(mParallelPorts[slot]).setNull();
8159 }
8160 }
8161
8162 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8163 {
8164 if (mSerialPorts[slot])
8165 {
8166 mSerialPorts[slot]->uninit();
8167 unconst(mSerialPorts[slot]).setNull();
8168 }
8169 }
8170
8171 if (mVRDEServer)
8172 {
8173 mVRDEServer->uninit();
8174 unconst(mVRDEServer).setNull();
8175 }
8176
8177 if (mBIOSSettings)
8178 {
8179 mBIOSSettings->uninit();
8180 unconst(mBIOSSettings).setNull();
8181 }
8182
8183 /* Deassociate media (only when a real Machine or a SnapshotMachine
8184 * instance is uninitialized; SessionMachine instances refer to real
8185 * Machine media). This is necessary for a clean re-initialization of
8186 * the VM after successfully re-checking the accessibility state. Note
8187 * that in case of normal Machine or SnapshotMachine uninitialization (as
8188 * a result of unregistering or deleting the snapshot), outdated media
8189 * attachments will already be uninitialized and deleted, so this
8190 * code will not affect them. */
8191 if ( !!mMediaData
8192 && (!i_isSessionMachine())
8193 )
8194 {
8195 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8196 it != mMediaData->mAttachments.end();
8197 ++it)
8198 {
8199 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8200 if (pMedium.isNull())
8201 continue;
8202 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8203 AssertComRC(rc);
8204 }
8205 }
8206
8207 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8208 {
8209 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8210 if (mData->mFirstSnapshot)
8211 {
8212 // snapshots tree is protected by machine write lock; strictly
8213 // this isn't necessary here since we're deleting the entire
8214 // machine, but otherwise we assert in Snapshot::uninit()
8215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8216 mData->mFirstSnapshot->uninit();
8217 mData->mFirstSnapshot.setNull();
8218 }
8219
8220 mData->mCurrentSnapshot.setNull();
8221 }
8222
8223 /* free data structures (the essential mData structure is not freed here
8224 * since it may be still in use) */
8225 mMediaData.free();
8226 mStorageControllers.free();
8227 mUSBControllers.free();
8228 mHWData.free();
8229 mUserData.free();
8230 mSSData.free();
8231}
8232
8233/**
8234 * Returns a pointer to the Machine object for this machine that acts like a
8235 * parent for complex machine data objects such as shared folders, etc.
8236 *
8237 * For primary Machine objects and for SnapshotMachine objects, returns this
8238 * object's pointer itself. For SessionMachine objects, returns the peer
8239 * (primary) machine pointer.
8240 */
8241Machine* Machine::i_getMachine()
8242{
8243 if (i_isSessionMachine())
8244 return (Machine*)mPeer;
8245 return this;
8246}
8247
8248/**
8249 * Makes sure that there are no machine state dependents. If necessary, waits
8250 * for the number of dependents to drop to zero.
8251 *
8252 * Make sure this method is called from under this object's write lock to
8253 * guarantee that no new dependents may be added when this method returns
8254 * control to the caller.
8255 *
8256 * @note Locks this object for writing. The lock will be released while waiting
8257 * (if necessary).
8258 *
8259 * @warning To be used only in methods that change the machine state!
8260 */
8261void Machine::i_ensureNoStateDependencies()
8262{
8263 AssertReturnVoid(isWriteLockOnCurrentThread());
8264
8265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8266
8267 /* Wait for all state dependents if necessary */
8268 if (mData->mMachineStateDeps != 0)
8269 {
8270 /* lazy semaphore creation */
8271 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8272 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8273
8274 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8275 mData->mMachineStateDeps));
8276
8277 ++mData->mMachineStateChangePending;
8278
8279 /* reset the semaphore before waiting, the last dependent will signal
8280 * it */
8281 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8282
8283 alock.release();
8284
8285 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8286
8287 alock.acquire();
8288
8289 -- mData->mMachineStateChangePending;
8290 }
8291}
8292
8293/**
8294 * Changes the machine state and informs callbacks.
8295 *
8296 * This method is not intended to fail so it either returns S_OK or asserts (and
8297 * returns a failure).
8298 *
8299 * @note Locks this object for writing.
8300 */
8301HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8302{
8303 LogFlowThisFuncEnter();
8304 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8305
8306 AutoCaller autoCaller(this);
8307 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8308
8309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8310
8311 /* wait for state dependents to drop to zero */
8312 i_ensureNoStateDependencies();
8313
8314 if (mData->mMachineState != aMachineState)
8315 {
8316 mData->mMachineState = aMachineState;
8317
8318 RTTimeNow(&mData->mLastStateChange);
8319
8320 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8321 }
8322
8323 LogFlowThisFuncLeave();
8324 return S_OK;
8325}
8326
8327/**
8328 * Searches for a shared folder with the given logical name
8329 * in the collection of shared folders.
8330 *
8331 * @param aName logical name of the shared folder
8332 * @param aSharedFolder where to return the found object
8333 * @param aSetError whether to set the error info if the folder is
8334 * not found
8335 * @return
8336 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8337 *
8338 * @note
8339 * must be called from under the object's lock!
8340 */
8341HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8342 ComObjPtr<SharedFolder> &aSharedFolder,
8343 bool aSetError /* = false */)
8344{
8345 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8346 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8347 it != mHWData->mSharedFolders.end();
8348 ++it)
8349 {
8350 SharedFolder *pSF = *it;
8351 AutoCaller autoCaller(pSF);
8352 if (pSF->i_getName() == aName)
8353 {
8354 aSharedFolder = pSF;
8355 rc = S_OK;
8356 break;
8357 }
8358 }
8359
8360 if (aSetError && FAILED(rc))
8361 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8362
8363 return rc;
8364}
8365
8366/**
8367 * Initializes all machine instance data from the given settings structures
8368 * from XML. The exception is the machine UUID which needs special handling
8369 * depending on the caller's use case, so the caller needs to set that herself.
8370 *
8371 * This gets called in several contexts during machine initialization:
8372 *
8373 * -- When machine XML exists on disk already and needs to be loaded into memory,
8374 * for example, from registeredInit() to load all registered machines on
8375 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8376 * attached to the machine should be part of some media registry already.
8377 *
8378 * -- During OVF import, when a machine config has been constructed from an
8379 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8380 * ensure that the media listed as attachments in the config (which have
8381 * been imported from the OVF) receive the correct registry ID.
8382 *
8383 * -- During VM cloning.
8384 *
8385 * @param config Machine settings from XML.
8386 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8387 * for each attached medium in the config.
8388 * @return
8389 */
8390HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8391 const Guid *puuidRegistry)
8392{
8393 // copy name, description, OS type, teleporter, UTC etc.
8394 mUserData->s = config.machineUserData;
8395
8396 // Decode the Icon overide data from config userdata and set onto Machine.
8397 #define DECODE_STR_MAX _1M
8398 const char* pszStr = config.machineUserData.ovIcon.c_str();
8399 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8400 if (cbOut > DECODE_STR_MAX)
8401 return setError(E_FAIL,
8402 tr("Icon Data too long.'%d' > '%d'"),
8403 cbOut,
8404 DECODE_STR_MAX);
8405 mUserData->mIcon.resize(cbOut);
8406 int vrc = VINF_SUCCESS;
8407 if (cbOut)
8408 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8409 if (RT_FAILURE(vrc))
8410 {
8411 mUserData->mIcon.resize(0);
8412 return setError(E_FAIL,
8413 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8414 pszStr,
8415 vrc);
8416 }
8417
8418 // look up the object by Id to check it is valid
8419 ComPtr<IGuestOSType> guestOSType;
8420 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8421 guestOSType.asOutParam());
8422 if (FAILED(rc)) return rc;
8423
8424 // stateFile (optional)
8425 if (config.strStateFile.isEmpty())
8426 mSSData->strStateFilePath.setNull();
8427 else
8428 {
8429 Utf8Str stateFilePathFull(config.strStateFile);
8430 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8431 if (RT_FAILURE(vrc))
8432 return setError(E_FAIL,
8433 tr("Invalid saved state file path '%s' (%Rrc)"),
8434 config.strStateFile.c_str(),
8435 vrc);
8436 mSSData->strStateFilePath = stateFilePathFull;
8437 }
8438
8439 // snapshot folder needs special processing so set it again
8440 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8441 if (FAILED(rc)) return rc;
8442
8443 /* Copy the extra data items (Not in any case config is already the same as
8444 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8445 * make sure the extra data map is copied). */
8446 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8447
8448 /* currentStateModified (optional, default is true) */
8449 mData->mCurrentStateModified = config.fCurrentStateModified;
8450
8451 mData->mLastStateChange = config.timeLastStateChange;
8452
8453 /*
8454 * note: all mUserData members must be assigned prior this point because
8455 * we need to commit changes in order to let mUserData be shared by all
8456 * snapshot machine instances.
8457 */
8458 mUserData.commitCopy();
8459
8460 // machine registry, if present (must be loaded before snapshots)
8461 if (config.canHaveOwnMediaRegistry())
8462 {
8463 // determine machine folder
8464 Utf8Str strMachineFolder = i_getSettingsFileFull();
8465 strMachineFolder.stripFilename();
8466 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8467 config.mediaRegistry,
8468 strMachineFolder);
8469 if (FAILED(rc)) return rc;
8470 }
8471
8472 /* Snapshot node (optional) */
8473 size_t cRootSnapshots;
8474 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8475 {
8476 // there must be only one root snapshot
8477 Assert(cRootSnapshots == 1);
8478
8479 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8480
8481 rc = i_loadSnapshot(snap,
8482 config.uuidCurrentSnapshot,
8483 NULL); // no parent == first snapshot
8484 if (FAILED(rc)) return rc;
8485 }
8486
8487 // hardware data
8488 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8489 if (FAILED(rc)) return rc;
8490
8491 // load storage controllers
8492 rc = i_loadStorageControllers(config.storageMachine,
8493 puuidRegistry,
8494 NULL /* puuidSnapshot */);
8495 if (FAILED(rc)) return rc;
8496
8497 /*
8498 * NOTE: the assignment below must be the last thing to do,
8499 * otherwise it will be not possible to change the settings
8500 * somewhere in the code above because all setters will be
8501 * blocked by i_checkStateDependency(MutableStateDep).
8502 */
8503
8504 /* set the machine state to Aborted or Saved when appropriate */
8505 if (config.fAborted)
8506 {
8507 mSSData->strStateFilePath.setNull();
8508
8509 /* no need to use i_setMachineState() during init() */
8510 mData->mMachineState = MachineState_Aborted;
8511 }
8512 else if (!mSSData->strStateFilePath.isEmpty())
8513 {
8514 /* no need to use i_setMachineState() during init() */
8515 mData->mMachineState = MachineState_Saved;
8516 }
8517
8518 // after loading settings, we are no longer different from the XML on disk
8519 mData->flModifications = 0;
8520
8521 return S_OK;
8522}
8523
8524/**
8525 * Recursively loads all snapshots starting from the given.
8526 *
8527 * @param aNode <Snapshot> node.
8528 * @param aCurSnapshotId Current snapshot ID from the settings file.
8529 * @param aParentSnapshot Parent snapshot.
8530 */
8531HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8532 const Guid &aCurSnapshotId,
8533 Snapshot *aParentSnapshot)
8534{
8535 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8536 AssertReturn(!i_isSessionMachine(), E_FAIL);
8537
8538 HRESULT rc = S_OK;
8539
8540 Utf8Str strStateFile;
8541 if (!data.strStateFile.isEmpty())
8542 {
8543 /* optional */
8544 strStateFile = data.strStateFile;
8545 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8546 if (RT_FAILURE(vrc))
8547 return setError(E_FAIL,
8548 tr("Invalid saved state file path '%s' (%Rrc)"),
8549 strStateFile.c_str(),
8550 vrc);
8551 }
8552
8553 /* create a snapshot machine object */
8554 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8555 pSnapshotMachine.createObject();
8556 rc = pSnapshotMachine->initFromSettings(this,
8557 data.hardware,
8558 &data.debugging,
8559 &data.autostart,
8560 data.storage,
8561 data.uuid.ref(),
8562 strStateFile);
8563 if (FAILED(rc)) return rc;
8564
8565 /* create a snapshot object */
8566 ComObjPtr<Snapshot> pSnapshot;
8567 pSnapshot.createObject();
8568 /* initialize the snapshot */
8569 rc = pSnapshot->init(mParent, // VirtualBox object
8570 data.uuid,
8571 data.strName,
8572 data.strDescription,
8573 data.timestamp,
8574 pSnapshotMachine,
8575 aParentSnapshot);
8576 if (FAILED(rc)) return rc;
8577
8578 /* memorize the first snapshot if necessary */
8579 if (!mData->mFirstSnapshot)
8580 mData->mFirstSnapshot = pSnapshot;
8581
8582 /* memorize the current snapshot when appropriate */
8583 if ( !mData->mCurrentSnapshot
8584 && pSnapshot->i_getId() == aCurSnapshotId
8585 )
8586 mData->mCurrentSnapshot = pSnapshot;
8587
8588 // now create the children
8589 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8590 it != data.llChildSnapshots.end();
8591 ++it)
8592 {
8593 const settings::Snapshot &childData = *it;
8594 // recurse
8595 rc = i_loadSnapshot(childData,
8596 aCurSnapshotId,
8597 pSnapshot); // parent = the one we created above
8598 if (FAILED(rc)) return rc;
8599 }
8600
8601 return rc;
8602}
8603
8604/**
8605 * Loads settings into mHWData.
8606 *
8607 * @param data Reference to the hardware settings.
8608 * @param pDbg Pointer to the debugging settings.
8609 * @param pAutostart Pointer to the autostart settings.
8610 */
8611HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8612 const settings::Autostart *pAutostart)
8613{
8614 AssertReturn(!i_isSessionMachine(), E_FAIL);
8615
8616 HRESULT rc = S_OK;
8617
8618 try
8619 {
8620 /* The hardware version attribute (optional). */
8621 mHWData->mHWVersion = data.strVersion;
8622 mHWData->mHardwareUUID = data.uuid;
8623
8624 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8625 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8626 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8627 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8628 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8629 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8630 mHWData->mPAEEnabled = data.fPAE;
8631 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8632 mHWData->mLongMode = data.enmLongMode;
8633 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8634 mHWData->mCPUCount = data.cCPUs;
8635 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8636 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8637
8638 // cpu
8639 if (mHWData->mCPUHotPlugEnabled)
8640 {
8641 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8642 it != data.llCpus.end();
8643 ++it)
8644 {
8645 const settings::Cpu &cpu = *it;
8646
8647 mHWData->mCPUAttached[cpu.ulId] = true;
8648 }
8649 }
8650
8651 // cpuid leafs
8652 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8653 it != data.llCpuIdLeafs.end();
8654 ++it)
8655 {
8656 const settings::CpuIdLeaf &leaf = *it;
8657
8658 switch (leaf.ulId)
8659 {
8660 case 0x0:
8661 case 0x1:
8662 case 0x2:
8663 case 0x3:
8664 case 0x4:
8665 case 0x5:
8666 case 0x6:
8667 case 0x7:
8668 case 0x8:
8669 case 0x9:
8670 case 0xA:
8671 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8672 break;
8673
8674 case 0x80000000:
8675 case 0x80000001:
8676 case 0x80000002:
8677 case 0x80000003:
8678 case 0x80000004:
8679 case 0x80000005:
8680 case 0x80000006:
8681 case 0x80000007:
8682 case 0x80000008:
8683 case 0x80000009:
8684 case 0x8000000A:
8685 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8686 break;
8687
8688 default:
8689 /* just ignore */
8690 break;
8691 }
8692 }
8693
8694 mHWData->mMemorySize = data.ulMemorySizeMB;
8695 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8696
8697 // boot order
8698 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8699 {
8700 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8701 if (it == data.mapBootOrder.end())
8702 mHWData->mBootOrder[i] = DeviceType_Null;
8703 else
8704 mHWData->mBootOrder[i] = it->second;
8705 }
8706
8707 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8708 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8709 mHWData->mMonitorCount = data.cMonitors;
8710 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8711 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8712 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8713 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8714 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8715 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8716 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8717 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8718 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8719 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8720 if (!data.strVideoCaptureFile.isEmpty())
8721 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8722 else
8723 mHWData->mVideoCaptureFile.setNull();
8724 mHWData->mFirmwareType = data.firmwareType;
8725 mHWData->mPointingHIDType = data.pointingHIDType;
8726 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8727 mHWData->mChipsetType = data.chipsetType;
8728 mHWData->mParavirtProvider = data.paravirtProvider;
8729 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8730 mHWData->mHPETEnabled = data.fHPETEnabled;
8731
8732 /* VRDEServer */
8733 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8734 if (FAILED(rc)) return rc;
8735
8736 /* BIOS */
8737 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8738 if (FAILED(rc)) return rc;
8739
8740 // Bandwidth control (must come before network adapters)
8741 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8742 if (FAILED(rc)) return rc;
8743
8744 /* Shared folders */
8745 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8746 it != data.usbSettings.llUSBControllers.end();
8747 ++it)
8748 {
8749 const settings::USBController &settingsCtrl = *it;
8750 ComObjPtr<USBController> newCtrl;
8751
8752 newCtrl.createObject();
8753 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8754 mUSBControllers->push_back(newCtrl);
8755 }
8756
8757 /* USB device filters */
8758 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8759 if (FAILED(rc)) return rc;
8760
8761 // network adapters
8762 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8763 size_t oldCount = mNetworkAdapters.size();
8764 if (newCount > oldCount)
8765 {
8766 mNetworkAdapters.resize(newCount);
8767 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8768 {
8769 unconst(mNetworkAdapters[slot]).createObject();
8770 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8771 }
8772 }
8773 else if (newCount < oldCount)
8774 mNetworkAdapters.resize(newCount);
8775 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8776 it != data.llNetworkAdapters.end();
8777 ++it)
8778 {
8779 const settings::NetworkAdapter &nic = *it;
8780
8781 /* slot unicity is guaranteed by XML Schema */
8782 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8783 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8784 if (FAILED(rc)) return rc;
8785 }
8786
8787 // serial ports
8788 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8789 it != data.llSerialPorts.end();
8790 ++it)
8791 {
8792 const settings::SerialPort &s = *it;
8793
8794 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8795 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8796 if (FAILED(rc)) return rc;
8797 }
8798
8799 // parallel ports (optional)
8800 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8801 it != data.llParallelPorts.end();
8802 ++it)
8803 {
8804 const settings::ParallelPort &p = *it;
8805
8806 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8807 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8808 if (FAILED(rc)) return rc;
8809 }
8810
8811 /* AudioAdapter */
8812 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8813 if (FAILED(rc)) return rc;
8814
8815 /* Shared folders */
8816 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8817 it != data.llSharedFolders.end();
8818 ++it)
8819 {
8820 const settings::SharedFolder &sf = *it;
8821
8822 ComObjPtr<SharedFolder> sharedFolder;
8823 /* Check for double entries. Not allowed! */
8824 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8825 if (SUCCEEDED(rc))
8826 return setError(VBOX_E_OBJECT_IN_USE,
8827 tr("Shared folder named '%s' already exists"),
8828 sf.strName.c_str());
8829
8830 /* Create the new shared folder. Don't break on error. This will be
8831 * reported when the machine starts. */
8832 sharedFolder.createObject();
8833 rc = sharedFolder->init(i_getMachine(),
8834 sf.strName,
8835 sf.strHostPath,
8836 RT_BOOL(sf.fWritable),
8837 RT_BOOL(sf.fAutoMount),
8838 false /* fFailOnError */);
8839 if (FAILED(rc)) return rc;
8840 mHWData->mSharedFolders.push_back(sharedFolder);
8841 }
8842
8843 // Clipboard
8844 mHWData->mClipboardMode = data.clipboardMode;
8845
8846 // drag'n'drop
8847 mHWData->mDnDMode = data.dndMode;
8848
8849 // guest settings
8850 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8851
8852 // IO settings
8853 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8854 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8855
8856 // Host PCI devices
8857 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8858 it != data.pciAttachments.end();
8859 ++it)
8860 {
8861 const settings::HostPCIDeviceAttachment &hpda = *it;
8862 ComObjPtr<PCIDeviceAttachment> pda;
8863
8864 pda.createObject();
8865 pda->i_loadSettings(this, hpda);
8866 mHWData->mPCIDeviceAssignments.push_back(pda);
8867 }
8868
8869 /*
8870 * (The following isn't really real hardware, but it lives in HWData
8871 * for reasons of convenience.)
8872 */
8873
8874#ifdef VBOX_WITH_GUEST_PROPS
8875 /* Guest properties (optional) */
8876 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8877 it != data.llGuestProperties.end();
8878 ++it)
8879 {
8880 const settings::GuestProperty &prop = *it;
8881 uint32_t fFlags = guestProp::NILFLAG;
8882 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8883 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8884 mHWData->mGuestProperties[prop.strName] = property;
8885 }
8886
8887 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8888#endif /* VBOX_WITH_GUEST_PROPS defined */
8889
8890 rc = i_loadDebugging(pDbg);
8891 if (FAILED(rc))
8892 return rc;
8893
8894 mHWData->mAutostart = *pAutostart;
8895
8896 /* default frontend */
8897 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8898 }
8899 catch(std::bad_alloc &)
8900 {
8901 return E_OUTOFMEMORY;
8902 }
8903
8904 AssertComRC(rc);
8905 return rc;
8906}
8907
8908/**
8909 * Called from Machine::loadHardware() to load the debugging settings of the
8910 * machine.
8911 *
8912 * @param pDbg Pointer to the settings.
8913 */
8914HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8915{
8916 mHWData->mDebugging = *pDbg;
8917 /* no more processing currently required, this will probably change. */
8918 return S_OK;
8919}
8920
8921/**
8922 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8923 *
8924 * @param data
8925 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8926 * @param puuidSnapshot
8927 * @return
8928 */
8929HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8930 const Guid *puuidRegistry,
8931 const Guid *puuidSnapshot)
8932{
8933 AssertReturn(!i_isSessionMachine(), E_FAIL);
8934
8935 HRESULT rc = S_OK;
8936
8937 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8938 it != data.llStorageControllers.end();
8939 ++it)
8940 {
8941 const settings::StorageController &ctlData = *it;
8942
8943 ComObjPtr<StorageController> pCtl;
8944 /* Try to find one with the name first. */
8945 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8946 if (SUCCEEDED(rc))
8947 return setError(VBOX_E_OBJECT_IN_USE,
8948 tr("Storage controller named '%s' already exists"),
8949 ctlData.strName.c_str());
8950
8951 pCtl.createObject();
8952 rc = pCtl->init(this,
8953 ctlData.strName,
8954 ctlData.storageBus,
8955 ctlData.ulInstance,
8956 ctlData.fBootable);
8957 if (FAILED(rc)) return rc;
8958
8959 mStorageControllers->push_back(pCtl);
8960
8961 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8962 if (FAILED(rc)) return rc;
8963
8964 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8965 if (FAILED(rc)) return rc;
8966
8967 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8968 if (FAILED(rc)) return rc;
8969
8970 /* Set IDE emulation settings (only for AHCI controller). */
8971 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8972 {
8973 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8974 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8975 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8976 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8977 )
8978 return rc;
8979 }
8980
8981 /* Load the attached devices now. */
8982 rc = i_loadStorageDevices(pCtl,
8983 ctlData,
8984 puuidRegistry,
8985 puuidSnapshot);
8986 if (FAILED(rc)) return rc;
8987 }
8988
8989 return S_OK;
8990}
8991
8992/**
8993 * Called from i_loadStorageControllers for a controller's devices.
8994 *
8995 * @param aStorageController
8996 * @param data
8997 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8998 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8999 * @return
9000 */
9001HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9002 const settings::StorageController &data,
9003 const Guid *puuidRegistry,
9004 const Guid *puuidSnapshot)
9005{
9006 HRESULT rc = S_OK;
9007
9008 /* paranoia: detect duplicate attachments */
9009 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9010 it != data.llAttachedDevices.end();
9011 ++it)
9012 {
9013 const settings::AttachedDevice &ad = *it;
9014
9015 for (settings::AttachedDevicesList::const_iterator it2 = it;
9016 it2 != data.llAttachedDevices.end();
9017 ++it2)
9018 {
9019 if (it == it2)
9020 continue;
9021
9022 const settings::AttachedDevice &ad2 = *it2;
9023
9024 if ( ad.lPort == ad2.lPort
9025 && ad.lDevice == ad2.lDevice)
9026 {
9027 return setError(E_FAIL,
9028 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9029 aStorageController->i_getName().c_str(),
9030 ad.lPort,
9031 ad.lDevice,
9032 mUserData->s.strName.c_str());
9033 }
9034 }
9035 }
9036
9037 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9038 it != data.llAttachedDevices.end();
9039 ++it)
9040 {
9041 const settings::AttachedDevice &dev = *it;
9042 ComObjPtr<Medium> medium;
9043
9044 switch (dev.deviceType)
9045 {
9046 case DeviceType_Floppy:
9047 case DeviceType_DVD:
9048 if (dev.strHostDriveSrc.isNotEmpty())
9049 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9050 false /* fRefresh */, medium);
9051 else
9052 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9053 dev.uuid,
9054 false /* fRefresh */,
9055 false /* aSetError */,
9056 medium);
9057 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9058 // This is not an error. The host drive or UUID might have vanished, so just go
9059 // ahead without this removeable medium attachment
9060 rc = S_OK;
9061 break;
9062
9063 case DeviceType_HardDisk:
9064 {
9065 /* find a hard disk by UUID */
9066 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9067 if (FAILED(rc))
9068 {
9069 if (i_isSnapshotMachine())
9070 {
9071 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9072 // so the user knows that the bad disk is in a snapshot somewhere
9073 com::ErrorInfo info;
9074 return setError(E_FAIL,
9075 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9076 puuidSnapshot->raw(),
9077 info.getText().raw());
9078 }
9079 else
9080 return rc;
9081 }
9082
9083 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9084
9085 if (medium->i_getType() == MediumType_Immutable)
9086 {
9087 if (i_isSnapshotMachine())
9088 return setError(E_FAIL,
9089 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9090 "of the virtual machine '%s' ('%s')"),
9091 medium->i_getLocationFull().c_str(),
9092 dev.uuid.raw(),
9093 puuidSnapshot->raw(),
9094 mUserData->s.strName.c_str(),
9095 mData->m_strConfigFileFull.c_str());
9096
9097 return setError(E_FAIL,
9098 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9099 medium->i_getLocationFull().c_str(),
9100 dev.uuid.raw(),
9101 mUserData->s.strName.c_str(),
9102 mData->m_strConfigFileFull.c_str());
9103 }
9104
9105 if (medium->i_getType() == MediumType_MultiAttach)
9106 {
9107 if (i_isSnapshotMachine())
9108 return setError(E_FAIL,
9109 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9110 "of the virtual machine '%s' ('%s')"),
9111 medium->i_getLocationFull().c_str(),
9112 dev.uuid.raw(),
9113 puuidSnapshot->raw(),
9114 mUserData->s.strName.c_str(),
9115 mData->m_strConfigFileFull.c_str());
9116
9117 return setError(E_FAIL,
9118 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9119 medium->i_getLocationFull().c_str(),
9120 dev.uuid.raw(),
9121 mUserData->s.strName.c_str(),
9122 mData->m_strConfigFileFull.c_str());
9123 }
9124
9125 if ( !i_isSnapshotMachine()
9126 && medium->i_getChildren().size() != 0
9127 )
9128 return setError(E_FAIL,
9129 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9130 "because it has %d differencing child hard disks"),
9131 medium->i_getLocationFull().c_str(),
9132 dev.uuid.raw(),
9133 mUserData->s.strName.c_str(),
9134 mData->m_strConfigFileFull.c_str(),
9135 medium->i_getChildren().size());
9136
9137 if (i_findAttachment(mMediaData->mAttachments,
9138 medium))
9139 return setError(E_FAIL,
9140 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9141 medium->i_getLocationFull().c_str(),
9142 dev.uuid.raw(),
9143 mUserData->s.strName.c_str(),
9144 mData->m_strConfigFileFull.c_str());
9145
9146 break;
9147 }
9148
9149 default:
9150 return setError(E_FAIL,
9151 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9152 medium->i_getLocationFull().c_str(),
9153 mUserData->s.strName.c_str(),
9154 mData->m_strConfigFileFull.c_str());
9155 }
9156
9157 if (FAILED(rc))
9158 break;
9159
9160 /* Bandwidth groups are loaded at this point. */
9161 ComObjPtr<BandwidthGroup> pBwGroup;
9162
9163 if (!dev.strBwGroup.isEmpty())
9164 {
9165 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9166 if (FAILED(rc))
9167 return setError(E_FAIL,
9168 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9169 medium->i_getLocationFull().c_str(),
9170 dev.strBwGroup.c_str(),
9171 mUserData->s.strName.c_str(),
9172 mData->m_strConfigFileFull.c_str());
9173 pBwGroup->i_reference();
9174 }
9175
9176 const Bstr controllerName = aStorageController->i_getName();
9177 ComObjPtr<MediumAttachment> pAttachment;
9178 pAttachment.createObject();
9179 rc = pAttachment->init(this,
9180 medium,
9181 controllerName,
9182 dev.lPort,
9183 dev.lDevice,
9184 dev.deviceType,
9185 false,
9186 dev.fPassThrough,
9187 dev.fTempEject,
9188 dev.fNonRotational,
9189 dev.fDiscard,
9190 dev.fHotPluggable,
9191 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9192 if (FAILED(rc)) break;
9193
9194 /* associate the medium with this machine and snapshot */
9195 if (!medium.isNull())
9196 {
9197 AutoCaller medCaller(medium);
9198 if (FAILED(medCaller.rc())) return medCaller.rc();
9199 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9200
9201 if (i_isSnapshotMachine())
9202 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9203 else
9204 rc = medium->i_addBackReference(mData->mUuid);
9205 /* If the medium->addBackReference fails it sets an appropriate
9206 * error message, so no need to do any guesswork here. */
9207
9208 if (puuidRegistry)
9209 // caller wants registry ID to be set on all attached media (OVF import case)
9210 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9211 }
9212
9213 if (FAILED(rc))
9214 break;
9215
9216 /* back up mMediaData to let registeredInit() properly rollback on failure
9217 * (= limited accessibility) */
9218 i_setModified(IsModified_Storage);
9219 mMediaData.backup();
9220 mMediaData->mAttachments.push_back(pAttachment);
9221 }
9222
9223 return rc;
9224}
9225
9226/**
9227 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9228 *
9229 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9230 * @param aSnapshot where to return the found snapshot
9231 * @param aSetError true to set extended error info on failure
9232 */
9233HRESULT Machine::i_findSnapshotById(const Guid &aId,
9234 ComObjPtr<Snapshot> &aSnapshot,
9235 bool aSetError /* = false */)
9236{
9237 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9238
9239 if (!mData->mFirstSnapshot)
9240 {
9241 if (aSetError)
9242 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9243 return E_FAIL;
9244 }
9245
9246 if (aId.isZero())
9247 aSnapshot = mData->mFirstSnapshot;
9248 else
9249 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9250
9251 if (!aSnapshot)
9252 {
9253 if (aSetError)
9254 return setError(E_FAIL,
9255 tr("Could not find a snapshot with UUID {%s}"),
9256 aId.toString().c_str());
9257 return E_FAIL;
9258 }
9259
9260 return S_OK;
9261}
9262
9263/**
9264 * Returns the snapshot with the given name or fails of no such snapshot.
9265 *
9266 * @param aName snapshot name to find
9267 * @param aSnapshot where to return the found snapshot
9268 * @param aSetError true to set extended error info on failure
9269 */
9270HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9271 ComObjPtr<Snapshot> &aSnapshot,
9272 bool aSetError /* = false */)
9273{
9274 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9275
9276 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9277
9278 if (!mData->mFirstSnapshot)
9279 {
9280 if (aSetError)
9281 return setError(VBOX_E_OBJECT_NOT_FOUND,
9282 tr("This machine does not have any snapshots"));
9283 return VBOX_E_OBJECT_NOT_FOUND;
9284 }
9285
9286 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9287
9288 if (!aSnapshot)
9289 {
9290 if (aSetError)
9291 return setError(VBOX_E_OBJECT_NOT_FOUND,
9292 tr("Could not find a snapshot named '%s'"), strName.c_str());
9293 return VBOX_E_OBJECT_NOT_FOUND;
9294 }
9295
9296 return S_OK;
9297}
9298
9299/**
9300 * Returns a storage controller object with the given name.
9301 *
9302 * @param aName storage controller name to find
9303 * @param aStorageController where to return the found storage controller
9304 * @param aSetError true to set extended error info on failure
9305 */
9306HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9307 ComObjPtr<StorageController> &aStorageController,
9308 bool aSetError /* = false */)
9309{
9310 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9311
9312 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9313 it != mStorageControllers->end();
9314 ++it)
9315 {
9316 if ((*it)->i_getName() == aName)
9317 {
9318 aStorageController = (*it);
9319 return S_OK;
9320 }
9321 }
9322
9323 if (aSetError)
9324 return setError(VBOX_E_OBJECT_NOT_FOUND,
9325 tr("Could not find a storage controller named '%s'"),
9326 aName.c_str());
9327 return VBOX_E_OBJECT_NOT_FOUND;
9328}
9329
9330/**
9331 * Returns a USB controller object with the given name.
9332 *
9333 * @param aName USB controller name to find
9334 * @param aUSBController where to return the found USB controller
9335 * @param aSetError true to set extended error info on failure
9336 */
9337HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9338 ComObjPtr<USBController> &aUSBController,
9339 bool aSetError /* = false */)
9340{
9341 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9342
9343 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9344 it != mUSBControllers->end();
9345 ++it)
9346 {
9347 if ((*it)->i_getName() == aName)
9348 {
9349 aUSBController = (*it);
9350 return S_OK;
9351 }
9352 }
9353
9354 if (aSetError)
9355 return setError(VBOX_E_OBJECT_NOT_FOUND,
9356 tr("Could not find a storage controller named '%s'"),
9357 aName.c_str());
9358 return VBOX_E_OBJECT_NOT_FOUND;
9359}
9360
9361/**
9362 * Returns the number of USB controller instance of the given type.
9363 *
9364 * @param enmType USB controller type.
9365 */
9366ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9367{
9368 ULONG cCtrls = 0;
9369
9370 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9371 it != mUSBControllers->end();
9372 ++it)
9373 {
9374 if ((*it)->i_getControllerType() == enmType)
9375 cCtrls++;
9376 }
9377
9378 return cCtrls;
9379}
9380
9381HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9382 MediaData::AttachmentList &atts)
9383{
9384 AutoCaller autoCaller(this);
9385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9386
9387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9388
9389 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9390 it != mMediaData->mAttachments.end();
9391 ++it)
9392 {
9393 const ComObjPtr<MediumAttachment> &pAtt = *it;
9394 // should never happen, but deal with NULL pointers in the list.
9395 AssertStmt(!pAtt.isNull(), continue);
9396
9397 // getControllerName() needs caller+read lock
9398 AutoCaller autoAttCaller(pAtt);
9399 if (FAILED(autoAttCaller.rc()))
9400 {
9401 atts.clear();
9402 return autoAttCaller.rc();
9403 }
9404 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9405
9406 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9407 atts.push_back(pAtt);
9408 }
9409
9410 return S_OK;
9411}
9412
9413
9414/**
9415 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9416 * file if the machine name was changed and about creating a new settings file
9417 * if this is a new machine.
9418 *
9419 * @note Must be never called directly but only from #saveSettings().
9420 */
9421HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9422{
9423 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9424
9425 HRESULT rc = S_OK;
9426
9427 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9428
9429 /// @todo need to handle primary group change, too
9430
9431 /* attempt to rename the settings file if machine name is changed */
9432 if ( mUserData->s.fNameSync
9433 && mUserData.isBackedUp()
9434 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9435 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9436 )
9437 {
9438 bool dirRenamed = false;
9439 bool fileRenamed = false;
9440
9441 Utf8Str configFile, newConfigFile;
9442 Utf8Str configFilePrev, newConfigFilePrev;
9443 Utf8Str configDir, newConfigDir;
9444
9445 do
9446 {
9447 int vrc = VINF_SUCCESS;
9448
9449 Utf8Str name = mUserData.backedUpData()->s.strName;
9450 Utf8Str newName = mUserData->s.strName;
9451 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9452 if (group == "/")
9453 group.setNull();
9454 Utf8Str newGroup = mUserData->s.llGroups.front();
9455 if (newGroup == "/")
9456 newGroup.setNull();
9457
9458 configFile = mData->m_strConfigFileFull;
9459
9460 /* first, rename the directory if it matches the group and machine name */
9461 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9462 group.c_str(), RTPATH_DELIMITER, name.c_str());
9463 /** @todo hack, make somehow use of ComposeMachineFilename */
9464 if (mUserData->s.fDirectoryIncludesUUID)
9465 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9466 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9467 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9468 /** @todo hack, make somehow use of ComposeMachineFilename */
9469 if (mUserData->s.fDirectoryIncludesUUID)
9470 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9471 configDir = configFile;
9472 configDir.stripFilename();
9473 newConfigDir = configDir;
9474 if ( configDir.length() >= groupPlusName.length()
9475 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9476 groupPlusName.c_str()))
9477 {
9478 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9479 Utf8Str newConfigBaseDir(newConfigDir);
9480 newConfigDir.append(newGroupPlusName);
9481 /* consistency: use \ if appropriate on the platform */
9482 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9483 /* new dir and old dir cannot be equal here because of 'if'
9484 * above and because name != newName */
9485 Assert(configDir != newConfigDir);
9486 if (!fSettingsFileIsNew)
9487 {
9488 /* perform real rename only if the machine is not new */
9489 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9490 if ( vrc == VERR_FILE_NOT_FOUND
9491 || vrc == VERR_PATH_NOT_FOUND)
9492 {
9493 /* create the parent directory, then retry renaming */
9494 Utf8Str parent(newConfigDir);
9495 parent.stripFilename();
9496 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9497 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9498 }
9499 if (RT_FAILURE(vrc))
9500 {
9501 rc = setError(E_FAIL,
9502 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9503 configDir.c_str(),
9504 newConfigDir.c_str(),
9505 vrc);
9506 break;
9507 }
9508 /* delete subdirectories which are no longer needed */
9509 Utf8Str dir(configDir);
9510 dir.stripFilename();
9511 while (dir != newConfigBaseDir && dir != ".")
9512 {
9513 vrc = RTDirRemove(dir.c_str());
9514 if (RT_FAILURE(vrc))
9515 break;
9516 dir.stripFilename();
9517 }
9518 dirRenamed = true;
9519 }
9520 }
9521
9522 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9523 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9524
9525 /* then try to rename the settings file itself */
9526 if (newConfigFile != configFile)
9527 {
9528 /* get the path to old settings file in renamed directory */
9529 configFile = Utf8StrFmt("%s%c%s",
9530 newConfigDir.c_str(),
9531 RTPATH_DELIMITER,
9532 RTPathFilename(configFile.c_str()));
9533 if (!fSettingsFileIsNew)
9534 {
9535 /* perform real rename only if the machine is not new */
9536 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9537 if (RT_FAILURE(vrc))
9538 {
9539 rc = setError(E_FAIL,
9540 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9541 configFile.c_str(),
9542 newConfigFile.c_str(),
9543 vrc);
9544 break;
9545 }
9546 fileRenamed = true;
9547 configFilePrev = configFile;
9548 configFilePrev += "-prev";
9549 newConfigFilePrev = newConfigFile;
9550 newConfigFilePrev += "-prev";
9551 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9552 }
9553 }
9554
9555 // update m_strConfigFileFull amd mConfigFile
9556 mData->m_strConfigFileFull = newConfigFile;
9557 // compute the relative path too
9558 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9559
9560 // store the old and new so that VirtualBox::i_saveSettings() can update
9561 // the media registry
9562 if ( mData->mRegistered
9563 && (configDir != newConfigDir || configFile != newConfigFile))
9564 {
9565 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9566
9567 if (pfNeedsGlobalSaveSettings)
9568 *pfNeedsGlobalSaveSettings = true;
9569 }
9570
9571 // in the saved state file path, replace the old directory with the new directory
9572 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9573 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9574
9575 // and do the same thing for the saved state file paths of all the online snapshots
9576 if (mData->mFirstSnapshot)
9577 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9578 newConfigDir.c_str());
9579 }
9580 while (0);
9581
9582 if (FAILED(rc))
9583 {
9584 /* silently try to rename everything back */
9585 if (fileRenamed)
9586 {
9587 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9588 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9589 }
9590 if (dirRenamed)
9591 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9592 }
9593
9594 if (FAILED(rc)) return rc;
9595 }
9596
9597 if (fSettingsFileIsNew)
9598 {
9599 /* create a virgin config file */
9600 int vrc = VINF_SUCCESS;
9601
9602 /* ensure the settings directory exists */
9603 Utf8Str path(mData->m_strConfigFileFull);
9604 path.stripFilename();
9605 if (!RTDirExists(path.c_str()))
9606 {
9607 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9608 if (RT_FAILURE(vrc))
9609 {
9610 return setError(E_FAIL,
9611 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9612 path.c_str(),
9613 vrc);
9614 }
9615 }
9616
9617 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9618 path = Utf8Str(mData->m_strConfigFileFull);
9619 RTFILE f = NIL_RTFILE;
9620 vrc = RTFileOpen(&f, path.c_str(),
9621 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9622 if (RT_FAILURE(vrc))
9623 return setError(E_FAIL,
9624 tr("Could not create the settings file '%s' (%Rrc)"),
9625 path.c_str(),
9626 vrc);
9627 RTFileClose(f);
9628 }
9629
9630 return rc;
9631}
9632
9633/**
9634 * Saves and commits machine data, user data and hardware data.
9635 *
9636 * Note that on failure, the data remains uncommitted.
9637 *
9638 * @a aFlags may combine the following flags:
9639 *
9640 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9641 * Used when saving settings after an operation that makes them 100%
9642 * correspond to the settings from the current snapshot.
9643 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9644 * #isReallyModified() returns false. This is necessary for cases when we
9645 * change machine data directly, not through the backup()/commit() mechanism.
9646 * - SaveS_Force: settings will be saved without doing a deep compare of the
9647 * settings structures. This is used when this is called because snapshots
9648 * have changed to avoid the overhead of the deep compare.
9649 *
9650 * @note Must be called from under this object's write lock. Locks children for
9651 * writing.
9652 *
9653 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9654 * initialized to false and that will be set to true by this function if
9655 * the caller must invoke VirtualBox::i_saveSettings() because the global
9656 * settings have changed. This will happen if a machine rename has been
9657 * saved and the global machine and media registries will therefore need
9658 * updating.
9659 */
9660HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9661 int aFlags /*= 0*/)
9662{
9663 LogFlowThisFuncEnter();
9664
9665 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9666
9667 /* make sure child objects are unable to modify the settings while we are
9668 * saving them */
9669 i_ensureNoStateDependencies();
9670
9671 AssertReturn(!i_isSnapshotMachine(),
9672 E_FAIL);
9673
9674 HRESULT rc = S_OK;
9675 bool fNeedsWrite = false;
9676
9677 /* First, prepare to save settings. It will care about renaming the
9678 * settings directory and file if the machine name was changed and about
9679 * creating a new settings file if this is a new machine. */
9680 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9681 if (FAILED(rc)) return rc;
9682
9683 // keep a pointer to the current settings structures
9684 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9685 settings::MachineConfigFile *pNewConfig = NULL;
9686
9687 try
9688 {
9689 // make a fresh one to have everyone write stuff into
9690 pNewConfig = new settings::MachineConfigFile(NULL);
9691 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9692
9693 // now go and copy all the settings data from COM to the settings structures
9694 // (this calles i_saveSettings() on all the COM objects in the machine)
9695 i_copyMachineDataToSettings(*pNewConfig);
9696
9697 if (aFlags & SaveS_ResetCurStateModified)
9698 {
9699 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9700 mData->mCurrentStateModified = FALSE;
9701 fNeedsWrite = true; // always, no need to compare
9702 }
9703 else if (aFlags & SaveS_Force)
9704 {
9705 fNeedsWrite = true; // always, no need to compare
9706 }
9707 else
9708 {
9709 if (!mData->mCurrentStateModified)
9710 {
9711 // do a deep compare of the settings that we just saved with the settings
9712 // previously stored in the config file; this invokes MachineConfigFile::operator==
9713 // which does a deep compare of all the settings, which is expensive but less expensive
9714 // than writing out XML in vain
9715 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9716
9717 // could still be modified if any settings changed
9718 mData->mCurrentStateModified = fAnySettingsChanged;
9719
9720 fNeedsWrite = fAnySettingsChanged;
9721 }
9722 else
9723 fNeedsWrite = true;
9724 }
9725
9726 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9727
9728 if (fNeedsWrite)
9729 // now spit it all out!
9730 pNewConfig->write(mData->m_strConfigFileFull);
9731
9732 mData->pMachineConfigFile = pNewConfig;
9733 delete pOldConfig;
9734 i_commit();
9735
9736 // after saving settings, we are no longer different from the XML on disk
9737 mData->flModifications = 0;
9738 }
9739 catch (HRESULT err)
9740 {
9741 // we assume that error info is set by the thrower
9742 rc = err;
9743
9744 // restore old config
9745 delete pNewConfig;
9746 mData->pMachineConfigFile = pOldConfig;
9747 }
9748 catch (...)
9749 {
9750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9751 }
9752
9753 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9754 {
9755 /* Fire the data change event, even on failure (since we've already
9756 * committed all data). This is done only for SessionMachines because
9757 * mutable Machine instances are always not registered (i.e. private
9758 * to the client process that creates them) and thus don't need to
9759 * inform callbacks. */
9760 if (i_isSessionMachine())
9761 mParent->i_onMachineDataChange(mData->mUuid);
9762 }
9763
9764 LogFlowThisFunc(("rc=%08X\n", rc));
9765 LogFlowThisFuncLeave();
9766 return rc;
9767}
9768
9769/**
9770 * Implementation for saving the machine settings into the given
9771 * settings::MachineConfigFile instance. This copies machine extradata
9772 * from the previous machine config file in the instance data, if any.
9773 *
9774 * This gets called from two locations:
9775 *
9776 * -- Machine::i_saveSettings(), during the regular XML writing;
9777 *
9778 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9779 * exported to OVF and we write the VirtualBox proprietary XML
9780 * into a <vbox:Machine> tag.
9781 *
9782 * This routine fills all the fields in there, including snapshots, *except*
9783 * for the following:
9784 *
9785 * -- fCurrentStateModified. There is some special logic associated with that.
9786 *
9787 * The caller can then call MachineConfigFile::write() or do something else
9788 * with it.
9789 *
9790 * Caller must hold the machine lock!
9791 *
9792 * This throws XML errors and HRESULT, so the caller must have a catch block!
9793 */
9794void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9795{
9796 // deep copy extradata
9797 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9798
9799 config.uuid = mData->mUuid;
9800
9801 // copy name, description, OS type, teleport, UTC etc.
9802 config.machineUserData = mUserData->s;
9803
9804 // Encode the Icon Override data from Machine and store on config userdata.
9805 std::vector<BYTE> iconByte;
9806 getIcon(iconByte);
9807 ssize_t cbData = iconByte.size();
9808 if (cbData > 0)
9809 {
9810 ssize_t cchOut = RTBase64EncodedLength(cbData);
9811 Utf8Str strIconData;
9812 strIconData.reserve(cchOut+1);
9813 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9814 strIconData.mutableRaw(), strIconData.capacity(),
9815 NULL);
9816 if (RT_FAILURE(vrc))
9817 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9818 strIconData.jolt();
9819 config.machineUserData.ovIcon = strIconData;
9820 }
9821 else
9822 config.machineUserData.ovIcon.setNull();
9823
9824 if ( mData->mMachineState == MachineState_Saved
9825 || mData->mMachineState == MachineState_Restoring
9826 // when deleting a snapshot we may or may not have a saved state in the current state,
9827 // so let's not assert here please
9828 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9829 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9830 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9831 && (!mSSData->strStateFilePath.isEmpty())
9832 )
9833 )
9834 {
9835 Assert(!mSSData->strStateFilePath.isEmpty());
9836 /* try to make the file name relative to the settings file dir */
9837 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9838 }
9839 else
9840 {
9841 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9842 config.strStateFile.setNull();
9843 }
9844
9845 if (mData->mCurrentSnapshot)
9846 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9847 else
9848 config.uuidCurrentSnapshot.clear();
9849
9850 config.timeLastStateChange = mData->mLastStateChange;
9851 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9852 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9853
9854 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9855 if (FAILED(rc)) throw rc;
9856
9857 rc = i_saveStorageControllers(config.storageMachine);
9858 if (FAILED(rc)) throw rc;
9859
9860 // save machine's media registry if this is VirtualBox 4.0 or later
9861 if (config.canHaveOwnMediaRegistry())
9862 {
9863 // determine machine folder
9864 Utf8Str strMachineFolder = i_getSettingsFileFull();
9865 strMachineFolder.stripFilename();
9866 mParent->i_saveMediaRegistry(config.mediaRegistry,
9867 i_getId(), // only media with registry ID == machine UUID
9868 strMachineFolder);
9869 // this throws HRESULT
9870 }
9871
9872 // save snapshots
9873 rc = i_saveAllSnapshots(config);
9874 if (FAILED(rc)) throw rc;
9875}
9876
9877/**
9878 * Saves all snapshots of the machine into the given machine config file. Called
9879 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9880 * @param config
9881 * @return
9882 */
9883HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9884{
9885 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9886
9887 HRESULT rc = S_OK;
9888
9889 try
9890 {
9891 config.llFirstSnapshot.clear();
9892
9893 if (mData->mFirstSnapshot)
9894 {
9895 settings::Snapshot snapNew;
9896 config.llFirstSnapshot.push_back(snapNew);
9897
9898 // get reference to the fresh copy of the snapshot on the list and
9899 // work on that copy directly to avoid excessive copying later
9900 settings::Snapshot &snap = config.llFirstSnapshot.front();
9901
9902 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9903 if (FAILED(rc)) throw rc;
9904 }
9905
9906// if (mType == IsSessionMachine)
9907// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9908
9909 }
9910 catch (HRESULT err)
9911 {
9912 /* we assume that error info is set by the thrower */
9913 rc = err;
9914 }
9915 catch (...)
9916 {
9917 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9918 }
9919
9920 return rc;
9921}
9922
9923/**
9924 * Saves the VM hardware configuration. It is assumed that the
9925 * given node is empty.
9926 *
9927 * @param data Reference to the settings object for the hardware config.
9928 * @param pDbg Pointer to the settings object for the debugging config
9929 * which happens to live in mHWData.
9930 * @param pAutostart Pointer to the settings object for the autostart config
9931 * which happens to live in mHWData.
9932 */
9933HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9934 settings::Autostart *pAutostart)
9935{
9936 HRESULT rc = S_OK;
9937
9938 try
9939 {
9940 /* The hardware version attribute (optional).
9941 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9942 if ( mHWData->mHWVersion == "1"
9943 && mSSData->strStateFilePath.isEmpty()
9944 )
9945 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9946 other point needs to be found where this can be done. */
9947
9948 data.strVersion = mHWData->mHWVersion;
9949 data.uuid = mHWData->mHardwareUUID;
9950
9951 // CPU
9952 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9953 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9954 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9955 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9956 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9957 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9958 data.fPAE = !!mHWData->mPAEEnabled;
9959 data.enmLongMode = mHWData->mLongMode;
9960 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9961 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9962
9963 /* Standard and Extended CPUID leafs. */
9964 data.llCpuIdLeafs.clear();
9965 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9966 {
9967 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9968 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9969 }
9970 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9971 {
9972 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9973 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9974 }
9975
9976 data.cCPUs = mHWData->mCPUCount;
9977 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9978 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9979
9980 data.llCpus.clear();
9981 if (data.fCpuHotPlug)
9982 {
9983 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9984 {
9985 if (mHWData->mCPUAttached[idx])
9986 {
9987 settings::Cpu cpu;
9988 cpu.ulId = idx;
9989 data.llCpus.push_back(cpu);
9990 }
9991 }
9992 }
9993
9994 // memory
9995 data.ulMemorySizeMB = mHWData->mMemorySize;
9996 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9997
9998 // firmware
9999 data.firmwareType = mHWData->mFirmwareType;
10000
10001 // HID
10002 data.pointingHIDType = mHWData->mPointingHIDType;
10003 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10004
10005 // chipset
10006 data.chipsetType = mHWData->mChipsetType;
10007
10008 // paravirt
10009 data.paravirtProvider = mHWData->mParavirtProvider;
10010
10011
10012 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10013
10014 // HPET
10015 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10016
10017 // boot order
10018 data.mapBootOrder.clear();
10019 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10020 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10021
10022 // display
10023 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10024 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10025 data.cMonitors = mHWData->mMonitorCount;
10026 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10027 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10028 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10029 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10030 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10031 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10032 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10033 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10034 {
10035 if (mHWData->maVideoCaptureScreens[i])
10036 ASMBitSet(&data.u64VideoCaptureScreens, i);
10037 else
10038 ASMBitClear(&data.u64VideoCaptureScreens, i);
10039 }
10040 /* store relative video capture file if possible */
10041 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10042
10043 /* VRDEServer settings (optional) */
10044 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10045 if (FAILED(rc)) throw rc;
10046
10047 /* BIOS (required) */
10048 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10049 if (FAILED(rc)) throw rc;
10050
10051 /* USB Controller (required) */
10052 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10053 {
10054 ComObjPtr<USBController> ctrl = *it;
10055 settings::USBController settingsCtrl;
10056
10057 settingsCtrl.strName = ctrl->i_getName();
10058 settingsCtrl.enmType = ctrl->i_getControllerType();
10059
10060 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10061 }
10062
10063 /* USB device filters (required) */
10064 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10065 if (FAILED(rc)) throw rc;
10066
10067 /* Network adapters (required) */
10068 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10069 data.llNetworkAdapters.clear();
10070 /* Write out only the nominal number of network adapters for this
10071 * chipset type. Since Machine::commit() hasn't been called there
10072 * may be extra NIC settings in the vector. */
10073 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10074 {
10075 settings::NetworkAdapter nic;
10076 nic.ulSlot = (uint32_t)slot;
10077 /* paranoia check... must not be NULL, but must not crash either. */
10078 if (mNetworkAdapters[slot])
10079 {
10080 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10081 if (FAILED(rc)) throw rc;
10082
10083 data.llNetworkAdapters.push_back(nic);
10084 }
10085 }
10086
10087 /* Serial ports */
10088 data.llSerialPorts.clear();
10089 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10090 {
10091 settings::SerialPort s;
10092 s.ulSlot = slot;
10093 rc = mSerialPorts[slot]->i_saveSettings(s);
10094 if (FAILED(rc)) return rc;
10095
10096 data.llSerialPorts.push_back(s);
10097 }
10098
10099 /* Parallel ports */
10100 data.llParallelPorts.clear();
10101 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10102 {
10103 settings::ParallelPort p;
10104 p.ulSlot = slot;
10105 rc = mParallelPorts[slot]->i_saveSettings(p);
10106 if (FAILED(rc)) return rc;
10107
10108 data.llParallelPorts.push_back(p);
10109 }
10110
10111 /* Audio adapter */
10112 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10113 if (FAILED(rc)) return rc;
10114
10115 /* Shared folders */
10116 data.llSharedFolders.clear();
10117 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10118 it != mHWData->mSharedFolders.end();
10119 ++it)
10120 {
10121 SharedFolder *pSF = *it;
10122 AutoCaller sfCaller(pSF);
10123 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10124 settings::SharedFolder sf;
10125 sf.strName = pSF->i_getName();
10126 sf.strHostPath = pSF->i_getHostPath();
10127 sf.fWritable = !!pSF->i_isWritable();
10128 sf.fAutoMount = !!pSF->i_isAutoMounted();
10129
10130 data.llSharedFolders.push_back(sf);
10131 }
10132
10133 // clipboard
10134 data.clipboardMode = mHWData->mClipboardMode;
10135
10136 // drag'n'drop
10137 data.dndMode = mHWData->mDnDMode;
10138
10139 /* Guest */
10140 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10141
10142 // IO settings
10143 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10144 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10145
10146 /* BandwidthControl (required) */
10147 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10148 if (FAILED(rc)) throw rc;
10149
10150 /* Host PCI devices */
10151 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10152 it != mHWData->mPCIDeviceAssignments.end();
10153 ++it)
10154 {
10155 ComObjPtr<PCIDeviceAttachment> pda = *it;
10156 settings::HostPCIDeviceAttachment hpda;
10157
10158 rc = pda->i_saveSettings(hpda);
10159 if (FAILED(rc)) throw rc;
10160
10161 data.pciAttachments.push_back(hpda);
10162 }
10163
10164
10165 // guest properties
10166 data.llGuestProperties.clear();
10167#ifdef VBOX_WITH_GUEST_PROPS
10168 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10169 it != mHWData->mGuestProperties.end();
10170 ++it)
10171 {
10172 HWData::GuestProperty property = it->second;
10173
10174 /* Remove transient guest properties at shutdown unless we
10175 * are saving state */
10176 if ( ( mData->mMachineState == MachineState_PoweredOff
10177 || mData->mMachineState == MachineState_Aborted
10178 || mData->mMachineState == MachineState_Teleported)
10179 && ( property.mFlags & guestProp::TRANSIENT
10180 || property.mFlags & guestProp::TRANSRESET))
10181 continue;
10182 settings::GuestProperty prop;
10183 prop.strName = it->first;
10184 prop.strValue = property.strValue;
10185 prop.timestamp = property.mTimestamp;
10186 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10187 guestProp::writeFlags(property.mFlags, szFlags);
10188 prop.strFlags = szFlags;
10189
10190 data.llGuestProperties.push_back(prop);
10191 }
10192
10193 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10194 /* I presume this doesn't require a backup(). */
10195 mData->mGuestPropertiesModified = FALSE;
10196#endif /* VBOX_WITH_GUEST_PROPS defined */
10197
10198 *pDbg = mHWData->mDebugging;
10199 *pAutostart = mHWData->mAutostart;
10200
10201 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10202 }
10203 catch(std::bad_alloc &)
10204 {
10205 return E_OUTOFMEMORY;
10206 }
10207
10208 AssertComRC(rc);
10209 return rc;
10210}
10211
10212/**
10213 * Saves the storage controller configuration.
10214 *
10215 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10216 */
10217HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10218{
10219 data.llStorageControllers.clear();
10220
10221 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10222 it != mStorageControllers->end();
10223 ++it)
10224 {
10225 HRESULT rc;
10226 ComObjPtr<StorageController> pCtl = *it;
10227
10228 settings::StorageController ctl;
10229 ctl.strName = pCtl->i_getName();
10230 ctl.controllerType = pCtl->i_getControllerType();
10231 ctl.storageBus = pCtl->i_getStorageBus();
10232 ctl.ulInstance = pCtl->i_getInstance();
10233 ctl.fBootable = pCtl->i_getBootable();
10234
10235 /* Save the port count. */
10236 ULONG portCount;
10237 rc = pCtl->COMGETTER(PortCount)(&portCount);
10238 ComAssertComRCRet(rc, rc);
10239 ctl.ulPortCount = portCount;
10240
10241 /* Save fUseHostIOCache */
10242 BOOL fUseHostIOCache;
10243 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10244 ComAssertComRCRet(rc, rc);
10245 ctl.fUseHostIOCache = !!fUseHostIOCache;
10246
10247 /* Save IDE emulation settings. */
10248 if (ctl.controllerType == StorageControllerType_IntelAhci)
10249 {
10250 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10251 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10252 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10253 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10254 )
10255 ComAssertComRCRet(rc, rc);
10256 }
10257
10258 /* save the devices now. */
10259 rc = i_saveStorageDevices(pCtl, ctl);
10260 ComAssertComRCRet(rc, rc);
10261
10262 data.llStorageControllers.push_back(ctl);
10263 }
10264
10265 return S_OK;
10266}
10267
10268/**
10269 * Saves the hard disk configuration.
10270 */
10271HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10272 settings::StorageController &data)
10273{
10274 MediaData::AttachmentList atts;
10275
10276 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10277 if (FAILED(rc)) return rc;
10278
10279 data.llAttachedDevices.clear();
10280 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10281 it != atts.end();
10282 ++it)
10283 {
10284 settings::AttachedDevice dev;
10285 IMediumAttachment *iA = *it;
10286 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10287 Medium *pMedium = pAttach->i_getMedium();
10288
10289 dev.deviceType = pAttach->i_getType();
10290 dev.lPort = pAttach->i_getPort();
10291 dev.lDevice = pAttach->i_getDevice();
10292 dev.fPassThrough = pAttach->i_getPassthrough();
10293 dev.fHotPluggable = pAttach->i_getHotPluggable();
10294 if (pMedium)
10295 {
10296 if (pMedium->i_isHostDrive())
10297 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10298 else
10299 dev.uuid = pMedium->i_getId();
10300 dev.fTempEject = pAttach->i_getTempEject();
10301 dev.fNonRotational = pAttach->i_getNonRotational();
10302 dev.fDiscard = pAttach->i_getDiscard();
10303 }
10304
10305 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10306
10307 data.llAttachedDevices.push_back(dev);
10308 }
10309
10310 return S_OK;
10311}
10312
10313/**
10314 * Saves machine state settings as defined by aFlags
10315 * (SaveSTS_* values).
10316 *
10317 * @param aFlags Combination of SaveSTS_* flags.
10318 *
10319 * @note Locks objects for writing.
10320 */
10321HRESULT Machine::i_saveStateSettings(int aFlags)
10322{
10323 if (aFlags == 0)
10324 return S_OK;
10325
10326 AutoCaller autoCaller(this);
10327 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10328
10329 /* This object's write lock is also necessary to serialize file access
10330 * (prevent concurrent reads and writes) */
10331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10332
10333 HRESULT rc = S_OK;
10334
10335 Assert(mData->pMachineConfigFile);
10336
10337 try
10338 {
10339 if (aFlags & SaveSTS_CurStateModified)
10340 mData->pMachineConfigFile->fCurrentStateModified = true;
10341
10342 if (aFlags & SaveSTS_StateFilePath)
10343 {
10344 if (!mSSData->strStateFilePath.isEmpty())
10345 /* try to make the file name relative to the settings file dir */
10346 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10347 else
10348 mData->pMachineConfigFile->strStateFile.setNull();
10349 }
10350
10351 if (aFlags & SaveSTS_StateTimeStamp)
10352 {
10353 Assert( mData->mMachineState != MachineState_Aborted
10354 || mSSData->strStateFilePath.isEmpty());
10355
10356 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10357
10358 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10359//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10360 }
10361
10362 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10363 }
10364 catch (...)
10365 {
10366 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10367 }
10368
10369 return rc;
10370}
10371
10372/**
10373 * Ensures that the given medium is added to a media registry. If this machine
10374 * was created with 4.0 or later, then the machine registry is used. Otherwise
10375 * the global VirtualBox media registry is used.
10376 *
10377 * Caller must NOT hold machine lock, media tree or any medium locks!
10378 *
10379 * @param pMedium
10380 */
10381void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10382{
10383 /* Paranoia checks: do not hold machine or media tree locks. */
10384 AssertReturnVoid(!isWriteLockOnCurrentThread());
10385 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10386
10387 ComObjPtr<Medium> pBase;
10388 {
10389 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10390 pBase = pMedium->i_getBase();
10391 }
10392
10393 /* Paranoia checks: do not hold medium locks. */
10394 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10395 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10396
10397 // decide which medium registry to use now that the medium is attached:
10398 Guid uuid;
10399 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10400 // machine XML is VirtualBox 4.0 or higher:
10401 uuid = i_getId(); // machine UUID
10402 else
10403 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10404
10405 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10406 mParent->i_markRegistryModified(uuid);
10407
10408 /* For more complex hard disk structures it can happen that the base
10409 * medium isn't yet associated with any medium registry. Do that now. */
10410 if (pMedium != pBase)
10411 {
10412 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10413 mParent->i_markRegistryModified(uuid);
10414 }
10415}
10416
10417/**
10418 * Creates differencing hard disks for all normal hard disks attached to this
10419 * machine and a new set of attachments to refer to created disks.
10420 *
10421 * Used when taking a snapshot or when deleting the current state. Gets called
10422 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10423 *
10424 * This method assumes that mMediaData contains the original hard disk attachments
10425 * it needs to create diffs for. On success, these attachments will be replaced
10426 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10427 * called to delete created diffs which will also rollback mMediaData and restore
10428 * whatever was backed up before calling this method.
10429 *
10430 * Attachments with non-normal hard disks are left as is.
10431 *
10432 * If @a aOnline is @c false then the original hard disks that require implicit
10433 * diffs will be locked for reading. Otherwise it is assumed that they are
10434 * already locked for writing (when the VM was started). Note that in the latter
10435 * case it is responsibility of the caller to lock the newly created diffs for
10436 * writing if this method succeeds.
10437 *
10438 * @param aProgress Progress object to run (must contain at least as
10439 * many operations left as the number of hard disks
10440 * attached).
10441 * @param aOnline Whether the VM was online prior to this operation.
10442 *
10443 * @note The progress object is not marked as completed, neither on success nor
10444 * on failure. This is a responsibility of the caller.
10445 *
10446 * @note Locks this object and the media tree for writing.
10447 */
10448HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10449 ULONG aWeight,
10450 bool aOnline)
10451{
10452 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10453
10454 AutoCaller autoCaller(this);
10455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10456
10457 AutoMultiWriteLock2 alock(this->lockHandle(),
10458 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10459
10460 /* must be in a protective state because we release the lock below */
10461 AssertReturn( mData->mMachineState == MachineState_Saving
10462 || mData->mMachineState == MachineState_LiveSnapshotting
10463 || mData->mMachineState == MachineState_RestoringSnapshot
10464 || mData->mMachineState == MachineState_DeletingSnapshot
10465 , E_FAIL);
10466
10467 HRESULT rc = S_OK;
10468
10469 // use appropriate locked media map (online or offline)
10470 MediumLockListMap lockedMediaOffline;
10471 MediumLockListMap *lockedMediaMap;
10472 if (aOnline)
10473 lockedMediaMap = &mData->mSession.mLockedMedia;
10474 else
10475 lockedMediaMap = &lockedMediaOffline;
10476
10477 try
10478 {
10479 if (!aOnline)
10480 {
10481 /* lock all attached hard disks early to detect "in use"
10482 * situations before creating actual diffs */
10483 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10484 it != mMediaData->mAttachments.end();
10485 ++it)
10486 {
10487 MediumAttachment* pAtt = *it;
10488 if (pAtt->i_getType() == DeviceType_HardDisk)
10489 {
10490 Medium* pMedium = pAtt->i_getMedium();
10491 Assert(pMedium);
10492
10493 MediumLockList *pMediumLockList(new MediumLockList());
10494 alock.release();
10495 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10496 false /* fMediumLockWrite */,
10497 NULL,
10498 *pMediumLockList);
10499 alock.acquire();
10500 if (FAILED(rc))
10501 {
10502 delete pMediumLockList;
10503 throw rc;
10504 }
10505 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10506 if (FAILED(rc))
10507 {
10508 throw setError(rc,
10509 tr("Collecting locking information for all attached media failed"));
10510 }
10511 }
10512 }
10513
10514 /* Now lock all media. If this fails, nothing is locked. */
10515 alock.release();
10516 rc = lockedMediaMap->Lock();
10517 alock.acquire();
10518 if (FAILED(rc))
10519 {
10520 throw setError(rc,
10521 tr("Locking of attached media failed"));
10522 }
10523 }
10524
10525 /* remember the current list (note that we don't use backup() since
10526 * mMediaData may be already backed up) */
10527 MediaData::AttachmentList atts = mMediaData->mAttachments;
10528
10529 /* start from scratch */
10530 mMediaData->mAttachments.clear();
10531
10532 /* go through remembered attachments and create diffs for normal hard
10533 * disks and attach them */
10534 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10535 it != atts.end();
10536 ++it)
10537 {
10538 MediumAttachment* pAtt = *it;
10539
10540 DeviceType_T devType = pAtt->i_getType();
10541 Medium* pMedium = pAtt->i_getMedium();
10542
10543 if ( devType != DeviceType_HardDisk
10544 || pMedium == NULL
10545 || pMedium->i_getType() != MediumType_Normal)
10546 {
10547 /* copy the attachment as is */
10548
10549 /** @todo the progress object created in Console::TakeSnaphot
10550 * only expects operations for hard disks. Later other
10551 * device types need to show up in the progress as well. */
10552 if (devType == DeviceType_HardDisk)
10553 {
10554 if (pMedium == NULL)
10555 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10556 aWeight); // weight
10557 else
10558 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10559 pMedium->i_getBase()->i_getName().c_str()).raw(),
10560 aWeight); // weight
10561 }
10562
10563 mMediaData->mAttachments.push_back(pAtt);
10564 continue;
10565 }
10566
10567 /* need a diff */
10568 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10569 pMedium->i_getBase()->i_getName().c_str()).raw(),
10570 aWeight); // weight
10571
10572 Utf8Str strFullSnapshotFolder;
10573 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10574
10575 ComObjPtr<Medium> diff;
10576 diff.createObject();
10577 // store the diff in the same registry as the parent
10578 // (this cannot fail here because we can't create implicit diffs for
10579 // unregistered images)
10580 Guid uuidRegistryParent;
10581 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10582 Assert(fInRegistry); NOREF(fInRegistry);
10583 rc = diff->init(mParent,
10584 pMedium->i_getPreferredDiffFormat(),
10585 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10586 uuidRegistryParent);
10587 if (FAILED(rc)) throw rc;
10588
10589 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10590 * the push_back? Looks like we're going to release medium with the
10591 * wrong kind of lock (general issue with if we fail anywhere at all)
10592 * and an orphaned VDI in the snapshots folder. */
10593
10594 /* update the appropriate lock list */
10595 MediumLockList *pMediumLockList;
10596 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10597 AssertComRCThrowRC(rc);
10598 if (aOnline)
10599 {
10600 alock.release();
10601 /* The currently attached medium will be read-only, change
10602 * the lock type to read. */
10603 rc = pMediumLockList->Update(pMedium, false);
10604 alock.acquire();
10605 AssertComRCThrowRC(rc);
10606 }
10607
10608 /* release the locks before the potentially lengthy operation */
10609 alock.release();
10610 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10611 pMediumLockList,
10612 NULL /* aProgress */,
10613 true /* aWait */);
10614 alock.acquire();
10615 if (FAILED(rc)) throw rc;
10616
10617 /* actual lock list update is done in Medium::commitMedia */
10618
10619 rc = diff->i_addBackReference(mData->mUuid);
10620 AssertComRCThrowRC(rc);
10621
10622 /* add a new attachment */
10623 ComObjPtr<MediumAttachment> attachment;
10624 attachment.createObject();
10625 rc = attachment->init(this,
10626 diff,
10627 pAtt->i_getControllerName(),
10628 pAtt->i_getPort(),
10629 pAtt->i_getDevice(),
10630 DeviceType_HardDisk,
10631 true /* aImplicit */,
10632 false /* aPassthrough */,
10633 false /* aTempEject */,
10634 pAtt->i_getNonRotational(),
10635 pAtt->i_getDiscard(),
10636 pAtt->i_getHotPluggable(),
10637 pAtt->i_getBandwidthGroup());
10638 if (FAILED(rc)) throw rc;
10639
10640 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10641 AssertComRCThrowRC(rc);
10642 mMediaData->mAttachments.push_back(attachment);
10643 }
10644 }
10645 catch (HRESULT aRC) { rc = aRC; }
10646
10647 /* unlock all hard disks we locked when there is no VM */
10648 if (!aOnline)
10649 {
10650 ErrorInfoKeeper eik;
10651
10652 HRESULT rc1 = lockedMediaMap->Clear();
10653 AssertComRC(rc1);
10654 }
10655
10656 return rc;
10657}
10658
10659/**
10660 * Deletes implicit differencing hard disks created either by
10661 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10662 *
10663 * Note that to delete hard disks created by #AttachDevice() this method is
10664 * called from #fixupMedia() when the changes are rolled back.
10665 *
10666 * @note Locks this object and the media tree for writing.
10667 */
10668HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10669{
10670 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10671
10672 AutoCaller autoCaller(this);
10673 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10674
10675 AutoMultiWriteLock2 alock(this->lockHandle(),
10676 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10677
10678 /* We absolutely must have backed up state. */
10679 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10680
10681 /* Check if there are any implicitly created diff images. */
10682 bool fImplicitDiffs = false;
10683 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10684 it != mMediaData->mAttachments.end();
10685 ++it)
10686 {
10687 const ComObjPtr<MediumAttachment> &pAtt = *it;
10688 if (pAtt->i_isImplicit())
10689 {
10690 fImplicitDiffs = true;
10691 break;
10692 }
10693 }
10694 /* If there is nothing to do, leave early. This saves lots of image locking
10695 * effort. It also avoids a MachineStateChanged event without real reason.
10696 * This is important e.g. when loading a VM config, because there should be
10697 * no events. Otherwise API clients can become thoroughly confused for
10698 * inaccessible VMs (the code for loading VM configs uses this method for
10699 * cleanup if the config makes no sense), as they take such events as an
10700 * indication that the VM is alive, and they would force the VM config to
10701 * be reread, leading to an endless loop. */
10702 if (!fImplicitDiffs)
10703 return S_OK;
10704
10705 HRESULT rc = S_OK;
10706 MachineState_T oldState = mData->mMachineState;
10707
10708 /* will release the lock before the potentially lengthy operation,
10709 * so protect with the special state (unless already protected) */
10710 if ( oldState != MachineState_Saving
10711 && oldState != MachineState_LiveSnapshotting
10712 && oldState != MachineState_RestoringSnapshot
10713 && oldState != MachineState_DeletingSnapshot
10714 && oldState != MachineState_DeletingSnapshotOnline
10715 && oldState != MachineState_DeletingSnapshotPaused
10716 )
10717 i_setMachineState(MachineState_SettingUp);
10718
10719 // use appropriate locked media map (online or offline)
10720 MediumLockListMap lockedMediaOffline;
10721 MediumLockListMap *lockedMediaMap;
10722 if (aOnline)
10723 lockedMediaMap = &mData->mSession.mLockedMedia;
10724 else
10725 lockedMediaMap = &lockedMediaOffline;
10726
10727 try
10728 {
10729 if (!aOnline)
10730 {
10731 /* lock all attached hard disks early to detect "in use"
10732 * situations before deleting actual diffs */
10733 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10734 it != mMediaData->mAttachments.end();
10735 ++it)
10736 {
10737 MediumAttachment* pAtt = *it;
10738 if (pAtt->i_getType() == DeviceType_HardDisk)
10739 {
10740 Medium* pMedium = pAtt->i_getMedium();
10741 Assert(pMedium);
10742
10743 MediumLockList *pMediumLockList(new MediumLockList());
10744 alock.release();
10745 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10746 false /* fMediumLockWrite */,
10747 NULL,
10748 *pMediumLockList);
10749 alock.acquire();
10750
10751 if (FAILED(rc))
10752 {
10753 delete pMediumLockList;
10754 throw rc;
10755 }
10756
10757 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10758 if (FAILED(rc))
10759 throw rc;
10760 }
10761 }
10762
10763 if (FAILED(rc))
10764 throw rc;
10765 } // end of offline
10766
10767 /* Lock lists are now up to date and include implicitly created media */
10768
10769 /* Go through remembered attachments and delete all implicitly created
10770 * diffs and fix up the attachment information */
10771 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10772 MediaData::AttachmentList implicitAtts;
10773 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10774 it != mMediaData->mAttachments.end();
10775 ++it)
10776 {
10777 ComObjPtr<MediumAttachment> pAtt = *it;
10778 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10779 if (pMedium.isNull())
10780 continue;
10781
10782 // Implicit attachments go on the list for deletion and back references are removed.
10783 if (pAtt->i_isImplicit())
10784 {
10785 /* Deassociate and mark for deletion */
10786 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10787 rc = pMedium->i_removeBackReference(mData->mUuid);
10788 if (FAILED(rc))
10789 throw rc;
10790 implicitAtts.push_back(pAtt);
10791 continue;
10792 }
10793
10794 /* Was this medium attached before? */
10795 if (!i_findAttachment(oldAtts, pMedium))
10796 {
10797 /* no: de-associate */
10798 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10799 rc = pMedium->i_removeBackReference(mData->mUuid);
10800 if (FAILED(rc))
10801 throw rc;
10802 continue;
10803 }
10804 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10805 }
10806
10807 /* If there are implicit attachments to delete, throw away the lock
10808 * map contents (which will unlock all media) since the medium
10809 * attachments will be rolled back. Below we need to completely
10810 * recreate the lock map anyway since it is infinitely complex to
10811 * do this incrementally (would need reconstructing each attachment
10812 * change, which would be extremely hairy). */
10813 if (implicitAtts.size() != 0)
10814 {
10815 ErrorInfoKeeper eik;
10816
10817 HRESULT rc1 = lockedMediaMap->Clear();
10818 AssertComRC(rc1);
10819 }
10820
10821 /* rollback hard disk changes */
10822 mMediaData.rollback();
10823
10824 MultiResult mrc(S_OK);
10825
10826 // Delete unused implicit diffs.
10827 if (implicitAtts.size() != 0)
10828 {
10829 alock.release();
10830
10831 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10832 {
10833 // Remove medium associated with this attachment.
10834 ComObjPtr<MediumAttachment> pAtt = *it;
10835 Assert(pAtt);
10836 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10837 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10838 Assert(pMedium);
10839
10840 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10841 // continue on delete failure, just collect error messages
10842 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10843 pMedium->i_getLocationFull().c_str() ));
10844 mrc = rc;
10845 }
10846
10847 alock.acquire();
10848
10849 /* if there is a VM recreate media lock map as mentioned above,
10850 * otherwise it is a waste of time and we leave things unlocked */
10851 if (aOnline)
10852 {
10853 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10854 /* must never be NULL, but better safe than sorry */
10855 if (!pMachine.isNull())
10856 {
10857 alock.release();
10858 rc = mData->mSession.mMachine->i_lockMedia();
10859 alock.acquire();
10860 if (FAILED(rc))
10861 throw rc;
10862 }
10863 }
10864 }
10865 }
10866 catch (HRESULT aRC) {rc = aRC;}
10867
10868 if (mData->mMachineState == MachineState_SettingUp)
10869 i_setMachineState(oldState);
10870
10871 /* unlock all hard disks we locked when there is no VM */
10872 if (!aOnline)
10873 {
10874 ErrorInfoKeeper eik;
10875
10876 HRESULT rc1 = lockedMediaMap->Clear();
10877 AssertComRC(rc1);
10878 }
10879
10880 return rc;
10881}
10882
10883
10884/**
10885 * Looks through the given list of media attachments for one with the given parameters
10886 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10887 * can be searched as well if needed.
10888 *
10889 * @param list
10890 * @param aControllerName
10891 * @param aControllerPort
10892 * @param aDevice
10893 * @return
10894 */
10895MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10896 IN_BSTR aControllerName,
10897 LONG aControllerPort,
10898 LONG aDevice)
10899{
10900 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10901 {
10902 MediumAttachment *pAttach = *it;
10903 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10904 return pAttach;
10905 }
10906
10907 return NULL;
10908}
10909
10910/**
10911 * Looks through the given list of media attachments for one with the given parameters
10912 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10913 * can be searched as well if needed.
10914 *
10915 * @param list
10916 * @param aControllerName
10917 * @param aControllerPort
10918 * @param aDevice
10919 * @return
10920 */
10921MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10922 ComObjPtr<Medium> pMedium)
10923{
10924 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10925 {
10926 MediumAttachment *pAttach = *it;
10927 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10928 if (pMediumThis == pMedium)
10929 return pAttach;
10930 }
10931
10932 return NULL;
10933}
10934
10935/**
10936 * Looks through the given list of media attachments for one with the given parameters
10937 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10938 * can be searched as well if needed.
10939 *
10940 * @param list
10941 * @param aControllerName
10942 * @param aControllerPort
10943 * @param aDevice
10944 * @return
10945 */
10946MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10947 Guid &id)
10948{
10949 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10950 {
10951 MediumAttachment *pAttach = *it;
10952 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10953 if (pMediumThis->i_getId() == id)
10954 return pAttach;
10955 }
10956
10957 return NULL;
10958}
10959
10960/**
10961 * Main implementation for Machine::DetachDevice. This also gets called
10962 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10963 *
10964 * @param pAttach Medium attachment to detach.
10965 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10966 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10967 * SnapshotMachine, and this must be its snapshot.
10968 * @return
10969 */
10970HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10971 AutoWriteLock &writeLock,
10972 Snapshot *pSnapshot)
10973{
10974 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10975 DeviceType_T mediumType = pAttach->i_getType();
10976
10977 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10978
10979 if (pAttach->i_isImplicit())
10980 {
10981 /* attempt to implicitly delete the implicitly created diff */
10982
10983 /// @todo move the implicit flag from MediumAttachment to Medium
10984 /// and forbid any hard disk operation when it is implicit. Or maybe
10985 /// a special media state for it to make it even more simple.
10986
10987 Assert(mMediaData.isBackedUp());
10988
10989 /* will release the lock before the potentially lengthy operation, so
10990 * protect with the special state */
10991 MachineState_T oldState = mData->mMachineState;
10992 i_setMachineState(MachineState_SettingUp);
10993
10994 writeLock.release();
10995
10996 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10997 true /*aWait*/);
10998
10999 writeLock.acquire();
11000
11001 i_setMachineState(oldState);
11002
11003 if (FAILED(rc)) return rc;
11004 }
11005
11006 i_setModified(IsModified_Storage);
11007 mMediaData.backup();
11008 mMediaData->mAttachments.remove(pAttach);
11009
11010 if (!oldmedium.isNull())
11011 {
11012 // if this is from a snapshot, do not defer detachment to commitMedia()
11013 if (pSnapshot)
11014 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11015 // else if non-hard disk media, do not defer detachment to commitMedia() either
11016 else if (mediumType != DeviceType_HardDisk)
11017 oldmedium->i_removeBackReference(mData->mUuid);
11018 }
11019
11020 return S_OK;
11021}
11022
11023/**
11024 * Goes thru all media of the given list and
11025 *
11026 * 1) calls i_detachDevice() on each of them for this machine and
11027 * 2) adds all Medium objects found in the process to the given list,
11028 * depending on cleanupMode.
11029 *
11030 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11031 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11032 * media to the list.
11033 *
11034 * This gets called from Machine::Unregister, both for the actual Machine and
11035 * the SnapshotMachine objects that might be found in the snapshots.
11036 *
11037 * Requires caller and locking. The machine lock must be passed in because it
11038 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11039 *
11040 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11041 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11042 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11043 * Full, then all media get added;
11044 * otherwise no media get added.
11045 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11046 * @return
11047 */
11048HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11049 Snapshot *pSnapshot,
11050 CleanupMode_T cleanupMode,
11051 MediaList &llMedia)
11052{
11053 Assert(isWriteLockOnCurrentThread());
11054
11055 HRESULT rc;
11056
11057 // make a temporary list because i_detachDevice invalidates iterators into
11058 // mMediaData->mAttachments
11059 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11060
11061 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11062 {
11063 ComObjPtr<MediumAttachment> &pAttach = *it;
11064 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11065
11066 if (!pMedium.isNull())
11067 {
11068 AutoCaller mac(pMedium);
11069 if (FAILED(mac.rc())) return mac.rc();
11070 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11071 DeviceType_T devType = pMedium->i_getDeviceType();
11072 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11073 && devType == DeviceType_HardDisk)
11074 || (cleanupMode == CleanupMode_Full)
11075 )
11076 {
11077 llMedia.push_back(pMedium);
11078 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11079 /*
11080 * Search for medias which are not attached to any machine, but
11081 * in the chain to an attached disk. Mediums are only consided
11082 * if they are:
11083 * - have only one child
11084 * - no references to any machines
11085 * - are of normal medium type
11086 */
11087 while (!pParent.isNull())
11088 {
11089 AutoCaller mac1(pParent);
11090 if (FAILED(mac1.rc())) return mac1.rc();
11091 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11092 if (pParent->i_getChildren().size() == 1)
11093 {
11094 if ( pParent->i_getMachineBackRefCount() == 0
11095 && pParent->i_getType() == MediumType_Normal
11096 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11097 llMedia.push_back(pParent);
11098 }
11099 else
11100 break;
11101 pParent = pParent->i_getParent();
11102 }
11103 }
11104 }
11105
11106 // real machine: then we need to use the proper method
11107 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11108
11109 if (FAILED(rc))
11110 return rc;
11111 }
11112
11113 return S_OK;
11114}
11115
11116/**
11117 * Perform deferred hard disk detachments.
11118 *
11119 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11120 * backed up).
11121 *
11122 * If @a aOnline is @c true then this method will also unlock the old hard disks
11123 * for which the new implicit diffs were created and will lock these new diffs for
11124 * writing.
11125 *
11126 * @param aOnline Whether the VM was online prior to this operation.
11127 *
11128 * @note Locks this object for writing!
11129 */
11130void Machine::i_commitMedia(bool aOnline /*= false*/)
11131{
11132 AutoCaller autoCaller(this);
11133 AssertComRCReturnVoid(autoCaller.rc());
11134
11135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11136
11137 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11138
11139 HRESULT rc = S_OK;
11140
11141 /* no attach/detach operations -- nothing to do */
11142 if (!mMediaData.isBackedUp())
11143 return;
11144
11145 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11146 bool fMediaNeedsLocking = false;
11147
11148 /* enumerate new attachments */
11149 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11150 it != mMediaData->mAttachments.end();
11151 ++it)
11152 {
11153 MediumAttachment *pAttach = *it;
11154
11155 pAttach->i_commit();
11156
11157 Medium* pMedium = pAttach->i_getMedium();
11158 bool fImplicit = pAttach->i_isImplicit();
11159
11160 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11161 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11162 fImplicit));
11163
11164 /** @todo convert all this Machine-based voodoo to MediumAttachment
11165 * based commit logic. */
11166 if (fImplicit)
11167 {
11168 /* convert implicit attachment to normal */
11169 pAttach->i_setImplicit(false);
11170
11171 if ( aOnline
11172 && pMedium
11173 && pAttach->i_getType() == DeviceType_HardDisk
11174 )
11175 {
11176 ComObjPtr<Medium> parent = pMedium->i_getParent();
11177 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11178
11179 /* update the appropriate lock list */
11180 MediumLockList *pMediumLockList;
11181 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11182 AssertComRC(rc);
11183 if (pMediumLockList)
11184 {
11185 /* unlock if there's a need to change the locking */
11186 if (!fMediaNeedsLocking)
11187 {
11188 rc = mData->mSession.mLockedMedia.Unlock();
11189 AssertComRC(rc);
11190 fMediaNeedsLocking = true;
11191 }
11192 rc = pMediumLockList->Update(parent, false);
11193 AssertComRC(rc);
11194 rc = pMediumLockList->Append(pMedium, true);
11195 AssertComRC(rc);
11196 }
11197 }
11198
11199 continue;
11200 }
11201
11202 if (pMedium)
11203 {
11204 /* was this medium attached before? */
11205 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11206 {
11207 MediumAttachment *pOldAttach = *oldIt;
11208 if (pOldAttach->i_getMedium() == pMedium)
11209 {
11210 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11211
11212 /* yes: remove from old to avoid de-association */
11213 oldAtts.erase(oldIt);
11214 break;
11215 }
11216 }
11217 }
11218 }
11219
11220 /* enumerate remaining old attachments and de-associate from the
11221 * current machine state */
11222 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11223 {
11224 MediumAttachment *pAttach = *it;
11225 Medium* pMedium = pAttach->i_getMedium();
11226
11227 /* Detach only hard disks, since DVD/floppy media is detached
11228 * instantly in MountMedium. */
11229 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11230 {
11231 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11232
11233 /* now de-associate from the current machine state */
11234 rc = pMedium->i_removeBackReference(mData->mUuid);
11235 AssertComRC(rc);
11236
11237 if (aOnline)
11238 {
11239 /* unlock since medium is not used anymore */
11240 MediumLockList *pMediumLockList;
11241 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11242 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11243 {
11244 /* this happens for online snapshots, there the attachment
11245 * is changing, but only to a diff image created under
11246 * the old one, so there is no separate lock list */
11247 Assert(!pMediumLockList);
11248 }
11249 else
11250 {
11251 AssertComRC(rc);
11252 if (pMediumLockList)
11253 {
11254 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11255 AssertComRC(rc);
11256 }
11257 }
11258 }
11259 }
11260 }
11261
11262 /* take media locks again so that the locking state is consistent */
11263 if (fMediaNeedsLocking)
11264 {
11265 Assert(aOnline);
11266 rc = mData->mSession.mLockedMedia.Lock();
11267 AssertComRC(rc);
11268 }
11269
11270 /* commit the hard disk changes */
11271 mMediaData.commit();
11272
11273 if (i_isSessionMachine())
11274 {
11275 /*
11276 * Update the parent machine to point to the new owner.
11277 * This is necessary because the stored parent will point to the
11278 * session machine otherwise and cause crashes or errors later
11279 * when the session machine gets invalid.
11280 */
11281 /** @todo Change the MediumAttachment class to behave like any other
11282 * class in this regard by creating peer MediumAttachment
11283 * objects for session machines and share the data with the peer
11284 * machine.
11285 */
11286 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11287 it != mMediaData->mAttachments.end();
11288 ++it)
11289 (*it)->i_updateParentMachine(mPeer);
11290
11291 /* attach new data to the primary machine and reshare it */
11292 mPeer->mMediaData.attach(mMediaData);
11293 }
11294
11295 return;
11296}
11297
11298/**
11299 * Perform deferred deletion of implicitly created diffs.
11300 *
11301 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11302 * backed up).
11303 *
11304 * @note Locks this object for writing!
11305 */
11306void Machine::i_rollbackMedia()
11307{
11308 AutoCaller autoCaller(this);
11309 AssertComRCReturnVoid(autoCaller.rc());
11310
11311 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11312 LogFlowThisFunc(("Entering rollbackMedia\n"));
11313
11314 HRESULT rc = S_OK;
11315
11316 /* no attach/detach operations -- nothing to do */
11317 if (!mMediaData.isBackedUp())
11318 return;
11319
11320 /* enumerate new attachments */
11321 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11322 it != mMediaData->mAttachments.end();
11323 ++it)
11324 {
11325 MediumAttachment *pAttach = *it;
11326 /* Fix up the backrefs for DVD/floppy media. */
11327 if (pAttach->i_getType() != DeviceType_HardDisk)
11328 {
11329 Medium* pMedium = pAttach->i_getMedium();
11330 if (pMedium)
11331 {
11332 rc = pMedium->i_removeBackReference(mData->mUuid);
11333 AssertComRC(rc);
11334 }
11335 }
11336
11337 (*it)->i_rollback();
11338
11339 pAttach = *it;
11340 /* Fix up the backrefs for DVD/floppy media. */
11341 if (pAttach->i_getType() != DeviceType_HardDisk)
11342 {
11343 Medium* pMedium = pAttach->i_getMedium();
11344 if (pMedium)
11345 {
11346 rc = pMedium->i_addBackReference(mData->mUuid);
11347 AssertComRC(rc);
11348 }
11349 }
11350 }
11351
11352 /** @todo convert all this Machine-based voodoo to MediumAttachment
11353 * based rollback logic. */
11354 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11355
11356 return;
11357}
11358
11359/**
11360 * Returns true if the settings file is located in the directory named exactly
11361 * as the machine; this means, among other things, that the machine directory
11362 * should be auto-renamed.
11363 *
11364 * @param aSettingsDir if not NULL, the full machine settings file directory
11365 * name will be assigned there.
11366 *
11367 * @note Doesn't lock anything.
11368 * @note Not thread safe (must be called from this object's lock).
11369 */
11370bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11371{
11372 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11373 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11374 if (aSettingsDir)
11375 *aSettingsDir = strMachineDirName;
11376 strMachineDirName.stripPath(); // vmname
11377 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11378 strConfigFileOnly.stripPath() // vmname.vbox
11379 .stripSuffix(); // vmname
11380 /** @todo hack, make somehow use of ComposeMachineFilename */
11381 if (mUserData->s.fDirectoryIncludesUUID)
11382 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11383
11384 AssertReturn(!strMachineDirName.isEmpty(), false);
11385 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11386
11387 return strMachineDirName == strConfigFileOnly;
11388}
11389
11390/**
11391 * Discards all changes to machine settings.
11392 *
11393 * @param aNotify Whether to notify the direct session about changes or not.
11394 *
11395 * @note Locks objects for writing!
11396 */
11397void Machine::i_rollback(bool aNotify)
11398{
11399 AutoCaller autoCaller(this);
11400 AssertComRCReturn(autoCaller.rc(), (void)0);
11401
11402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11403
11404 if (!mStorageControllers.isNull())
11405 {
11406 if (mStorageControllers.isBackedUp())
11407 {
11408 /* unitialize all new devices (absent in the backed up list). */
11409 StorageControllerList::const_iterator it = mStorageControllers->begin();
11410 StorageControllerList *backedList = mStorageControllers.backedUpData();
11411 while (it != mStorageControllers->end())
11412 {
11413 if ( std::find(backedList->begin(), backedList->end(), *it)
11414 == backedList->end()
11415 )
11416 {
11417 (*it)->uninit();
11418 }
11419 ++it;
11420 }
11421
11422 /* restore the list */
11423 mStorageControllers.rollback();
11424 }
11425
11426 /* rollback any changes to devices after restoring the list */
11427 if (mData->flModifications & IsModified_Storage)
11428 {
11429 StorageControllerList::const_iterator it = mStorageControllers->begin();
11430 while (it != mStorageControllers->end())
11431 {
11432 (*it)->i_rollback();
11433 ++it;
11434 }
11435 }
11436 }
11437
11438 if (!mUSBControllers.isNull())
11439 {
11440 if (mUSBControllers.isBackedUp())
11441 {
11442 /* unitialize all new devices (absent in the backed up list). */
11443 USBControllerList::const_iterator it = mUSBControllers->begin();
11444 USBControllerList *backedList = mUSBControllers.backedUpData();
11445 while (it != mUSBControllers->end())
11446 {
11447 if ( std::find(backedList->begin(), backedList->end(), *it)
11448 == backedList->end()
11449 )
11450 {
11451 (*it)->uninit();
11452 }
11453 ++it;
11454 }
11455
11456 /* restore the list */
11457 mUSBControllers.rollback();
11458 }
11459
11460 /* rollback any changes to devices after restoring the list */
11461 if (mData->flModifications & IsModified_USB)
11462 {
11463 USBControllerList::const_iterator it = mUSBControllers->begin();
11464 while (it != mUSBControllers->end())
11465 {
11466 (*it)->i_rollback();
11467 ++it;
11468 }
11469 }
11470 }
11471
11472 mUserData.rollback();
11473
11474 mHWData.rollback();
11475
11476 if (mData->flModifications & IsModified_Storage)
11477 i_rollbackMedia();
11478
11479 if (mBIOSSettings)
11480 mBIOSSettings->i_rollback();
11481
11482 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11483 mVRDEServer->i_rollback();
11484
11485 if (mAudioAdapter)
11486 mAudioAdapter->i_rollback();
11487
11488 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11489 mUSBDeviceFilters->i_rollback();
11490
11491 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11492 mBandwidthControl->i_rollback();
11493
11494 if (!mHWData.isNull())
11495 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11496 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11497 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11498 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11499
11500 if (mData->flModifications & IsModified_NetworkAdapters)
11501 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11502 if ( mNetworkAdapters[slot]
11503 && mNetworkAdapters[slot]->i_isModified())
11504 {
11505 mNetworkAdapters[slot]->i_rollback();
11506 networkAdapters[slot] = mNetworkAdapters[slot];
11507 }
11508
11509 if (mData->flModifications & IsModified_SerialPorts)
11510 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11511 if ( mSerialPorts[slot]
11512 && mSerialPorts[slot]->i_isModified())
11513 {
11514 mSerialPorts[slot]->i_rollback();
11515 serialPorts[slot] = mSerialPorts[slot];
11516 }
11517
11518 if (mData->flModifications & IsModified_ParallelPorts)
11519 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11520 if ( mParallelPorts[slot]
11521 && mParallelPorts[slot]->i_isModified())
11522 {
11523 mParallelPorts[slot]->i_rollback();
11524 parallelPorts[slot] = mParallelPorts[slot];
11525 }
11526
11527 if (aNotify)
11528 {
11529 /* inform the direct session about changes */
11530
11531 ComObjPtr<Machine> that = this;
11532 uint32_t flModifications = mData->flModifications;
11533 alock.release();
11534
11535 if (flModifications & IsModified_SharedFolders)
11536 that->i_onSharedFolderChange();
11537
11538 if (flModifications & IsModified_VRDEServer)
11539 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11540 if (flModifications & IsModified_USB)
11541 that->i_onUSBControllerChange();
11542
11543 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11544 if (networkAdapters[slot])
11545 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11546 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11547 if (serialPorts[slot])
11548 that->i_onSerialPortChange(serialPorts[slot]);
11549 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11550 if (parallelPorts[slot])
11551 that->i_onParallelPortChange(parallelPorts[slot]);
11552
11553 if (flModifications & IsModified_Storage)
11554 that->i_onStorageControllerChange();
11555
11556#if 0
11557 if (flModifications & IsModified_BandwidthControl)
11558 that->onBandwidthControlChange();
11559#endif
11560 }
11561}
11562
11563/**
11564 * Commits all the changes to machine settings.
11565 *
11566 * Note that this operation is supposed to never fail.
11567 *
11568 * @note Locks this object and children for writing.
11569 */
11570void Machine::i_commit()
11571{
11572 AutoCaller autoCaller(this);
11573 AssertComRCReturnVoid(autoCaller.rc());
11574
11575 AutoCaller peerCaller(mPeer);
11576 AssertComRCReturnVoid(peerCaller.rc());
11577
11578 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11579
11580 /*
11581 * use safe commit to ensure Snapshot machines (that share mUserData)
11582 * will still refer to a valid memory location
11583 */
11584 mUserData.commitCopy();
11585
11586 mHWData.commit();
11587
11588 if (mMediaData.isBackedUp())
11589 i_commitMedia(Global::IsOnline(mData->mMachineState));
11590
11591 mBIOSSettings->i_commit();
11592 mVRDEServer->i_commit();
11593 mAudioAdapter->i_commit();
11594 mUSBDeviceFilters->i_commit();
11595 mBandwidthControl->i_commit();
11596
11597 /* Since mNetworkAdapters is a list which might have been changed (resized)
11598 * without using the Backupable<> template we need to handle the copying
11599 * of the list entries manually, including the creation of peers for the
11600 * new objects. */
11601 bool commitNetworkAdapters = false;
11602 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11603 if (mPeer)
11604 {
11605 /* commit everything, even the ones which will go away */
11606 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11607 mNetworkAdapters[slot]->i_commit();
11608 /* copy over the new entries, creating a peer and uninit the original */
11609 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11610 for (size_t slot = 0; slot < newSize; slot++)
11611 {
11612 /* look if this adapter has a peer device */
11613 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11614 if (!peer)
11615 {
11616 /* no peer means the adapter is a newly created one;
11617 * create a peer owning data this data share it with */
11618 peer.createObject();
11619 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11620 }
11621 mPeer->mNetworkAdapters[slot] = peer;
11622 }
11623 /* uninit any no longer needed network adapters */
11624 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11625 mNetworkAdapters[slot]->uninit();
11626 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11627 {
11628 if (mPeer->mNetworkAdapters[slot])
11629 mPeer->mNetworkAdapters[slot]->uninit();
11630 }
11631 /* Keep the original network adapter count until this point, so that
11632 * discarding a chipset type change will not lose settings. */
11633 mNetworkAdapters.resize(newSize);
11634 mPeer->mNetworkAdapters.resize(newSize);
11635 }
11636 else
11637 {
11638 /* we have no peer (our parent is the newly created machine);
11639 * just commit changes to the network adapters */
11640 commitNetworkAdapters = true;
11641 }
11642 if (commitNetworkAdapters)
11643 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11644 mNetworkAdapters[slot]->i_commit();
11645
11646 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11647 mSerialPorts[slot]->i_commit();
11648 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11649 mParallelPorts[slot]->i_commit();
11650
11651 bool commitStorageControllers = false;
11652
11653 if (mStorageControllers.isBackedUp())
11654 {
11655 mStorageControllers.commit();
11656
11657 if (mPeer)
11658 {
11659 /* Commit all changes to new controllers (this will reshare data with
11660 * peers for those who have peers) */
11661 StorageControllerList *newList = new StorageControllerList();
11662 StorageControllerList::const_iterator it = mStorageControllers->begin();
11663 while (it != mStorageControllers->end())
11664 {
11665 (*it)->i_commit();
11666
11667 /* look if this controller has a peer device */
11668 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11669 if (!peer)
11670 {
11671 /* no peer means the device is a newly created one;
11672 * create a peer owning data this device share it with */
11673 peer.createObject();
11674 peer->init(mPeer, *it, true /* aReshare */);
11675 }
11676 else
11677 {
11678 /* remove peer from the old list */
11679 mPeer->mStorageControllers->remove(peer);
11680 }
11681 /* and add it to the new list */
11682 newList->push_back(peer);
11683
11684 ++it;
11685 }
11686
11687 /* uninit old peer's controllers that are left */
11688 it = mPeer->mStorageControllers->begin();
11689 while (it != mPeer->mStorageControllers->end())
11690 {
11691 (*it)->uninit();
11692 ++it;
11693 }
11694
11695 /* attach new list of controllers to our peer */
11696 mPeer->mStorageControllers.attach(newList);
11697 }
11698 else
11699 {
11700 /* we have no peer (our parent is the newly created machine);
11701 * just commit changes to devices */
11702 commitStorageControllers = true;
11703 }
11704 }
11705 else
11706 {
11707 /* the list of controllers itself is not changed,
11708 * just commit changes to controllers themselves */
11709 commitStorageControllers = true;
11710 }
11711
11712 if (commitStorageControllers)
11713 {
11714 StorageControllerList::const_iterator it = mStorageControllers->begin();
11715 while (it != mStorageControllers->end())
11716 {
11717 (*it)->i_commit();
11718 ++it;
11719 }
11720 }
11721
11722 bool commitUSBControllers = false;
11723
11724 if (mUSBControllers.isBackedUp())
11725 {
11726 mUSBControllers.commit();
11727
11728 if (mPeer)
11729 {
11730 /* Commit all changes to new controllers (this will reshare data with
11731 * peers for those who have peers) */
11732 USBControllerList *newList = new USBControllerList();
11733 USBControllerList::const_iterator it = mUSBControllers->begin();
11734 while (it != mUSBControllers->end())
11735 {
11736 (*it)->i_commit();
11737
11738 /* look if this controller has a peer device */
11739 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11740 if (!peer)
11741 {
11742 /* no peer means the device is a newly created one;
11743 * create a peer owning data this device share it with */
11744 peer.createObject();
11745 peer->init(mPeer, *it, true /* aReshare */);
11746 }
11747 else
11748 {
11749 /* remove peer from the old list */
11750 mPeer->mUSBControllers->remove(peer);
11751 }
11752 /* and add it to the new list */
11753 newList->push_back(peer);
11754
11755 ++it;
11756 }
11757
11758 /* uninit old peer's controllers that are left */
11759 it = mPeer->mUSBControllers->begin();
11760 while (it != mPeer->mUSBControllers->end())
11761 {
11762 (*it)->uninit();
11763 ++it;
11764 }
11765
11766 /* attach new list of controllers to our peer */
11767 mPeer->mUSBControllers.attach(newList);
11768 }
11769 else
11770 {
11771 /* we have no peer (our parent is the newly created machine);
11772 * just commit changes to devices */
11773 commitUSBControllers = true;
11774 }
11775 }
11776 else
11777 {
11778 /* the list of controllers itself is not changed,
11779 * just commit changes to controllers themselves */
11780 commitUSBControllers = true;
11781 }
11782
11783 if (commitUSBControllers)
11784 {
11785 USBControllerList::const_iterator it = mUSBControllers->begin();
11786 while (it != mUSBControllers->end())
11787 {
11788 (*it)->i_commit();
11789 ++it;
11790 }
11791 }
11792
11793 if (i_isSessionMachine())
11794 {
11795 /* attach new data to the primary machine and reshare it */
11796 mPeer->mUserData.attach(mUserData);
11797 mPeer->mHWData.attach(mHWData);
11798 /* mMediaData is reshared by fixupMedia */
11799 // mPeer->mMediaData.attach(mMediaData);
11800 Assert(mPeer->mMediaData.data() == mMediaData.data());
11801 }
11802}
11803
11804/**
11805 * Copies all the hardware data from the given machine.
11806 *
11807 * Currently, only called when the VM is being restored from a snapshot. In
11808 * particular, this implies that the VM is not running during this method's
11809 * call.
11810 *
11811 * @note This method must be called from under this object's lock.
11812 *
11813 * @note This method doesn't call #commit(), so all data remains backed up and
11814 * unsaved.
11815 */
11816void Machine::i_copyFrom(Machine *aThat)
11817{
11818 AssertReturnVoid(!i_isSnapshotMachine());
11819 AssertReturnVoid(aThat->i_isSnapshotMachine());
11820
11821 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11822
11823 mHWData.assignCopy(aThat->mHWData);
11824
11825 // create copies of all shared folders (mHWData after attaching a copy
11826 // contains just references to original objects)
11827 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11828 it != mHWData->mSharedFolders.end();
11829 ++it)
11830 {
11831 ComObjPtr<SharedFolder> folder;
11832 folder.createObject();
11833 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11834 AssertComRC(rc);
11835 *it = folder;
11836 }
11837
11838 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11839 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11840 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11841 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11842 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11843
11844 /* create private copies of all controllers */
11845 mStorageControllers.backup();
11846 mStorageControllers->clear();
11847 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11848 it != aThat->mStorageControllers->end();
11849 ++it)
11850 {
11851 ComObjPtr<StorageController> ctrl;
11852 ctrl.createObject();
11853 ctrl->initCopy(this, *it);
11854 mStorageControllers->push_back(ctrl);
11855 }
11856
11857 /* create private copies of all USB controllers */
11858 mUSBControllers.backup();
11859 mUSBControllers->clear();
11860 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11861 it != aThat->mUSBControllers->end();
11862 ++it)
11863 {
11864 ComObjPtr<USBController> ctrl;
11865 ctrl.createObject();
11866 ctrl->initCopy(this, *it);
11867 mUSBControllers->push_back(ctrl);
11868 }
11869
11870 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11871 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11872 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11873 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11874 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11875 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11876 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11877}
11878
11879/**
11880 * Returns whether the given storage controller is hotplug capable.
11881 *
11882 * @returns true if the controller supports hotplugging
11883 * false otherwise.
11884 * @param enmCtrlType The controller type to check for.
11885 */
11886bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11887{
11888 ComPtr<ISystemProperties> systemProperties;
11889 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11890 if (FAILED(rc))
11891 return false;
11892
11893 BOOL aHotplugCapable = FALSE;
11894 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11895
11896 return RT_BOOL(aHotplugCapable);
11897}
11898
11899#ifdef VBOX_WITH_RESOURCE_USAGE_API
11900
11901void Machine::i_getDiskList(MediaList &list)
11902{
11903 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11904 it != mMediaData->mAttachments.end();
11905 ++it)
11906 {
11907 MediumAttachment* pAttach = *it;
11908 /* just in case */
11909 AssertStmt(pAttach, continue);
11910
11911 AutoCaller localAutoCallerA(pAttach);
11912 if (FAILED(localAutoCallerA.rc())) continue;
11913
11914 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11915
11916 if (pAttach->i_getType() == DeviceType_HardDisk)
11917 list.push_back(pAttach->i_getMedium());
11918 }
11919}
11920
11921void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11922{
11923 AssertReturnVoid(isWriteLockOnCurrentThread());
11924 AssertPtrReturnVoid(aCollector);
11925
11926 pm::CollectorHAL *hal = aCollector->getHAL();
11927 /* Create sub metrics */
11928 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11929 "Percentage of processor time spent in user mode by the VM process.");
11930 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11931 "Percentage of processor time spent in kernel mode by the VM process.");
11932 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11933 "Size of resident portion of VM process in memory.");
11934 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11935 "Actual size of all VM disks combined.");
11936 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11937 "Network receive rate.");
11938 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11939 "Network transmit rate.");
11940 /* Create and register base metrics */
11941 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11942 cpuLoadUser, cpuLoadKernel);
11943 aCollector->registerBaseMetric(cpuLoad);
11944 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11945 ramUsageUsed);
11946 aCollector->registerBaseMetric(ramUsage);
11947 MediaList disks;
11948 i_getDiskList(disks);
11949 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11950 diskUsageUsed);
11951 aCollector->registerBaseMetric(diskUsage);
11952
11953 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11954 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11955 new pm::AggregateAvg()));
11956 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11957 new pm::AggregateMin()));
11958 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11959 new pm::AggregateMax()));
11960 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11961 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11962 new pm::AggregateAvg()));
11963 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11964 new pm::AggregateMin()));
11965 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11966 new pm::AggregateMax()));
11967
11968 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11969 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11970 new pm::AggregateAvg()));
11971 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11972 new pm::AggregateMin()));
11973 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11974 new pm::AggregateMax()));
11975
11976 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11977 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11978 new pm::AggregateAvg()));
11979 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11980 new pm::AggregateMin()));
11981 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11982 new pm::AggregateMax()));
11983
11984
11985 /* Guest metrics collector */
11986 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11987 aCollector->registerGuest(mCollectorGuest);
11988 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11989 this, __PRETTY_FUNCTION__, mCollectorGuest));
11990
11991 /* Create sub metrics */
11992 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11993 "Percentage of processor time spent in user mode as seen by the guest.");
11994 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11995 "Percentage of processor time spent in kernel mode as seen by the guest.");
11996 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11997 "Percentage of processor time spent idling as seen by the guest.");
11998
11999 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12000 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12001 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12002 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12003 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12004 pm::SubMetric *guestMemCache = new pm::SubMetric(
12005 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12006
12007 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12008 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12009
12010 /* Create and register base metrics */
12011 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12012 machineNetRx, machineNetTx);
12013 aCollector->registerBaseMetric(machineNetRate);
12014
12015 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12016 guestLoadUser, guestLoadKernel, guestLoadIdle);
12017 aCollector->registerBaseMetric(guestCpuLoad);
12018
12019 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12020 guestMemTotal, guestMemFree,
12021 guestMemBalloon, guestMemShared,
12022 guestMemCache, guestPagedTotal);
12023 aCollector->registerBaseMetric(guestCpuMem);
12024
12025 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12026 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12027 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12028 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12029
12030 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12031 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12032 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12033 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12034
12035 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12036 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12037 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12038 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12039
12040 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12041 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12042 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12043 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12044
12045 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12046 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12047 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12048 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12049
12050 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12051 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12052 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12053 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12054
12055 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12056 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12058 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12059
12060 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12061 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12063 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12064
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12066 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12069
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12074
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12078 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12079}
12080
12081void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12082{
12083 AssertReturnVoid(isWriteLockOnCurrentThread());
12084
12085 if (aCollector)
12086 {
12087 aCollector->unregisterMetricsFor(aMachine);
12088 aCollector->unregisterBaseMetricsFor(aMachine);
12089 }
12090}
12091
12092#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12093
12094
12095////////////////////////////////////////////////////////////////////////////////
12096
12097DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12098
12099HRESULT SessionMachine::FinalConstruct()
12100{
12101 LogFlowThisFunc(("\n"));
12102
12103 mClientToken = NULL;
12104
12105 return BaseFinalConstruct();
12106}
12107
12108void SessionMachine::FinalRelease()
12109{
12110 LogFlowThisFunc(("\n"));
12111
12112 Assert(!mClientToken);
12113 /* paranoia, should not hang around any more */
12114 if (mClientToken)
12115 {
12116 delete mClientToken;
12117 mClientToken = NULL;
12118 }
12119
12120 uninit(Uninit::Unexpected);
12121
12122 BaseFinalRelease();
12123}
12124
12125/**
12126 * @note Must be called only by Machine::LockMachine() from its own write lock.
12127 */
12128HRESULT SessionMachine::init(Machine *aMachine)
12129{
12130 LogFlowThisFuncEnter();
12131 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12132
12133 AssertReturn(aMachine, E_INVALIDARG);
12134
12135 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12136
12137 /* Enclose the state transition NotReady->InInit->Ready */
12138 AutoInitSpan autoInitSpan(this);
12139 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12140
12141 HRESULT rc = S_OK;
12142
12143 /* create the machine client token */
12144 try
12145 {
12146 mClientToken = new ClientToken(aMachine, this);
12147 if (!mClientToken->isReady())
12148 {
12149 delete mClientToken;
12150 mClientToken = NULL;
12151 rc = E_FAIL;
12152 }
12153 }
12154 catch (std::bad_alloc &)
12155 {
12156 rc = E_OUTOFMEMORY;
12157 }
12158 if (FAILED(rc))
12159 return rc;
12160
12161 /* memorize the peer Machine */
12162 unconst(mPeer) = aMachine;
12163 /* share the parent pointer */
12164 unconst(mParent) = aMachine->mParent;
12165
12166 /* take the pointers to data to share */
12167 mData.share(aMachine->mData);
12168 mSSData.share(aMachine->mSSData);
12169
12170 mUserData.share(aMachine->mUserData);
12171 mHWData.share(aMachine->mHWData);
12172 mMediaData.share(aMachine->mMediaData);
12173
12174 mStorageControllers.allocate();
12175 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12176 it != aMachine->mStorageControllers->end();
12177 ++it)
12178 {
12179 ComObjPtr<StorageController> ctl;
12180 ctl.createObject();
12181 ctl->init(this, *it);
12182 mStorageControllers->push_back(ctl);
12183 }
12184
12185 mUSBControllers.allocate();
12186 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12187 it != aMachine->mUSBControllers->end();
12188 ++it)
12189 {
12190 ComObjPtr<USBController> ctl;
12191 ctl.createObject();
12192 ctl->init(this, *it);
12193 mUSBControllers->push_back(ctl);
12194 }
12195
12196 unconst(mBIOSSettings).createObject();
12197 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12198 /* create another VRDEServer object that will be mutable */
12199 unconst(mVRDEServer).createObject();
12200 mVRDEServer->init(this, aMachine->mVRDEServer);
12201 /* create another audio adapter object that will be mutable */
12202 unconst(mAudioAdapter).createObject();
12203 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12204 /* create a list of serial ports that will be mutable */
12205 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12206 {
12207 unconst(mSerialPorts[slot]).createObject();
12208 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12209 }
12210 /* create a list of parallel ports that will be mutable */
12211 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12212 {
12213 unconst(mParallelPorts[slot]).createObject();
12214 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12215 }
12216
12217 /* create another USB device filters object that will be mutable */
12218 unconst(mUSBDeviceFilters).createObject();
12219 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12220
12221 /* create a list of network adapters that will be mutable */
12222 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12223 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12224 {
12225 unconst(mNetworkAdapters[slot]).createObject();
12226 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12227 }
12228
12229 /* create another bandwidth control object that will be mutable */
12230 unconst(mBandwidthControl).createObject();
12231 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12232
12233 /* default is to delete saved state on Saved -> PoweredOff transition */
12234 mRemoveSavedState = true;
12235
12236 /* Confirm a successful initialization when it's the case */
12237 autoInitSpan.setSucceeded();
12238
12239 miNATNetworksStarted = 0;
12240
12241 LogFlowThisFuncLeave();
12242 return rc;
12243}
12244
12245/**
12246 * Uninitializes this session object. If the reason is other than
12247 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12248 * or the client watcher code.
12249 *
12250 * @param aReason uninitialization reason
12251 *
12252 * @note Locks mParent + this object for writing.
12253 */
12254void SessionMachine::uninit(Uninit::Reason aReason)
12255{
12256 LogFlowThisFuncEnter();
12257 LogFlowThisFunc(("reason=%d\n", aReason));
12258
12259 /*
12260 * Strongly reference ourselves to prevent this object deletion after
12261 * mData->mSession.mMachine.setNull() below (which can release the last
12262 * reference and call the destructor). Important: this must be done before
12263 * accessing any members (and before AutoUninitSpan that does it as well).
12264 * This self reference will be released as the very last step on return.
12265 */
12266 ComObjPtr<SessionMachine> selfRef = this;
12267
12268 /* Enclose the state transition Ready->InUninit->NotReady */
12269 AutoUninitSpan autoUninitSpan(this);
12270 if (autoUninitSpan.uninitDone())
12271 {
12272 LogFlowThisFunc(("Already uninitialized\n"));
12273 LogFlowThisFuncLeave();
12274 return;
12275 }
12276
12277 if (autoUninitSpan.initFailed())
12278 {
12279 /* We've been called by init() because it's failed. It's not really
12280 * necessary (nor it's safe) to perform the regular uninit sequence
12281 * below, the following is enough.
12282 */
12283 LogFlowThisFunc(("Initialization failed.\n"));
12284 /* destroy the machine client token */
12285 if (mClientToken)
12286 {
12287 delete mClientToken;
12288 mClientToken = NULL;
12289 }
12290 uninitDataAndChildObjects();
12291 mData.free();
12292 unconst(mParent) = NULL;
12293 unconst(mPeer) = NULL;
12294 LogFlowThisFuncLeave();
12295 return;
12296 }
12297
12298 MachineState_T lastState;
12299 {
12300 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12301 lastState = mData->mMachineState;
12302 }
12303 NOREF(lastState);
12304
12305#ifdef VBOX_WITH_USB
12306 // release all captured USB devices, but do this before requesting the locks below
12307 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12308 {
12309 /* Console::captureUSBDevices() is called in the VM process only after
12310 * setting the machine state to Starting or Restoring.
12311 * Console::detachAllUSBDevices() will be called upon successful
12312 * termination. So, we need to release USB devices only if there was
12313 * an abnormal termination of a running VM.
12314 *
12315 * This is identical to SessionMachine::DetachAllUSBDevices except
12316 * for the aAbnormal argument. */
12317 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12318 AssertComRC(rc);
12319 NOREF(rc);
12320
12321 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12322 if (service)
12323 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12324 }
12325#endif /* VBOX_WITH_USB */
12326
12327 // we need to lock this object in uninit() because the lock is shared
12328 // with mPeer (as well as data we modify below). mParent lock is needed
12329 // by several calls to it, and USB needs host lock.
12330 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12331
12332#ifdef VBOX_WITH_RESOURCE_USAGE_API
12333 /*
12334 * It is safe to call Machine::i_unregisterMetrics() here because
12335 * PerformanceCollector::samplerCallback no longer accesses guest methods
12336 * holding the lock.
12337 */
12338 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12339 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12340 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12341 this, __PRETTY_FUNCTION__, mCollectorGuest));
12342 if (mCollectorGuest)
12343 {
12344 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12345 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12346 mCollectorGuest = NULL;
12347 }
12348#endif
12349
12350 if (aReason == Uninit::Abnormal)
12351 {
12352 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12353 Global::IsOnlineOrTransient(lastState)));
12354
12355 /* reset the state to Aborted */
12356 if (mData->mMachineState != MachineState_Aborted)
12357 i_setMachineState(MachineState_Aborted);
12358 }
12359
12360 // any machine settings modified?
12361 if (mData->flModifications)
12362 {
12363 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12364 i_rollback(false /* aNotify */);
12365 }
12366
12367 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12368 || !mConsoleTaskData.mSnapshot);
12369 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12370 {
12371 LogWarningThisFunc(("canceling failed save state request!\n"));
12372 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12373 }
12374 else if (!mConsoleTaskData.mSnapshot.isNull())
12375 {
12376 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12377
12378 /* delete all differencing hard disks created (this will also attach
12379 * their parents back by rolling back mMediaData) */
12380 i_rollbackMedia();
12381
12382 // delete the saved state file (it might have been already created)
12383 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12384 // think it's still in use
12385 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12386 mConsoleTaskData.mSnapshot->uninit();
12387 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12388 }
12389
12390 mData->mSession.mPID = NIL_RTPROCESS;
12391
12392 if (aReason == Uninit::Unexpected)
12393 {
12394 /* Uninitialization didn't come from #checkForDeath(), so tell the
12395 * client watcher thread to update the set of machines that have open
12396 * sessions. */
12397 mParent->i_updateClientWatcher();
12398 }
12399
12400 /* uninitialize all remote controls */
12401 if (mData->mSession.mRemoteControls.size())
12402 {
12403 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12404 mData->mSession.mRemoteControls.size()));
12405
12406 Data::Session::RemoteControlList::iterator it =
12407 mData->mSession.mRemoteControls.begin();
12408 while (it != mData->mSession.mRemoteControls.end())
12409 {
12410 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12411 HRESULT rc = (*it)->Uninitialize();
12412 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12413 if (FAILED(rc))
12414 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12415 ++it;
12416 }
12417 mData->mSession.mRemoteControls.clear();
12418 }
12419
12420 /* Remove all references to the NAT network service. The service will stop
12421 * if all references (also from other VMs) are removed. */
12422 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12423 {
12424 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12425 {
12426 NetworkAttachmentType_T type;
12427 HRESULT hrc;
12428
12429 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12430 if ( SUCCEEDED(hrc)
12431 && type == NetworkAttachmentType_NATNetwork)
12432 {
12433 Bstr name;
12434 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12435 if (SUCCEEDED(hrc))
12436 {
12437 multilock.release();
12438 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12439 mUserData->s.strName.c_str(), name.raw()));
12440 mParent->i_natNetworkRefDec(name.raw());
12441 multilock.acquire();
12442 }
12443 }
12444 }
12445 }
12446
12447 /*
12448 * An expected uninitialization can come only from #checkForDeath().
12449 * Otherwise it means that something's gone really wrong (for example,
12450 * the Session implementation has released the VirtualBox reference
12451 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12452 * etc). However, it's also possible, that the client releases the IPC
12453 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12454 * but the VirtualBox release event comes first to the server process.
12455 * This case is practically possible, so we should not assert on an
12456 * unexpected uninit, just log a warning.
12457 */
12458
12459 if ((aReason == Uninit::Unexpected))
12460 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12461
12462 if (aReason != Uninit::Normal)
12463 {
12464 mData->mSession.mDirectControl.setNull();
12465 }
12466 else
12467 {
12468 /* this must be null here (see #OnSessionEnd()) */
12469 Assert(mData->mSession.mDirectControl.isNull());
12470 Assert(mData->mSession.mState == SessionState_Unlocking);
12471 Assert(!mData->mSession.mProgress.isNull());
12472 }
12473 if (mData->mSession.mProgress)
12474 {
12475 if (aReason == Uninit::Normal)
12476 mData->mSession.mProgress->i_notifyComplete(S_OK);
12477 else
12478 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12479 COM_IIDOF(ISession),
12480 getComponentName(),
12481 tr("The VM session was aborted"));
12482 mData->mSession.mProgress.setNull();
12483 }
12484
12485 /* remove the association between the peer machine and this session machine */
12486 Assert( (SessionMachine*)mData->mSession.mMachine == this
12487 || aReason == Uninit::Unexpected);
12488
12489 /* reset the rest of session data */
12490 mData->mSession.mMachine.setNull();
12491 mData->mSession.mState = SessionState_Unlocked;
12492 mData->mSession.mType.setNull();
12493
12494 /* destroy the machine client token before leaving the exclusive lock */
12495 if (mClientToken)
12496 {
12497 delete mClientToken;
12498 mClientToken = NULL;
12499 }
12500
12501 /* fire an event */
12502 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12503
12504 uninitDataAndChildObjects();
12505
12506 /* free the essential data structure last */
12507 mData.free();
12508
12509 /* release the exclusive lock before setting the below two to NULL */
12510 multilock.release();
12511
12512 unconst(mParent) = NULL;
12513 unconst(mPeer) = NULL;
12514
12515 LogFlowThisFuncLeave();
12516}
12517
12518// util::Lockable interface
12519////////////////////////////////////////////////////////////////////////////////
12520
12521/**
12522 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12523 * with the primary Machine instance (mPeer).
12524 */
12525RWLockHandle *SessionMachine::lockHandle() const
12526{
12527 AssertReturn(mPeer != NULL, NULL);
12528 return mPeer->lockHandle();
12529}
12530
12531// IInternalMachineControl methods
12532////////////////////////////////////////////////////////////////////////////////
12533
12534/**
12535 * Passes collected guest statistics to performance collector object
12536 */
12537HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12538 ULONG aCpuKernel, ULONG aCpuIdle,
12539 ULONG aMemTotal, ULONG aMemFree,
12540 ULONG aMemBalloon, ULONG aMemShared,
12541 ULONG aMemCache, ULONG aPageTotal,
12542 ULONG aAllocVMM, ULONG aFreeVMM,
12543 ULONG aBalloonedVMM, ULONG aSharedVMM,
12544 ULONG aVmNetRx, ULONG aVmNetTx)
12545{
12546#ifdef VBOX_WITH_RESOURCE_USAGE_API
12547 if (mCollectorGuest)
12548 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12549 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12550 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12551 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12552
12553 return S_OK;
12554#else
12555 NOREF(aValidStats);
12556 NOREF(aCpuUser);
12557 NOREF(aCpuKernel);
12558 NOREF(aCpuIdle);
12559 NOREF(aMemTotal);
12560 NOREF(aMemFree);
12561 NOREF(aMemBalloon);
12562 NOREF(aMemShared);
12563 NOREF(aMemCache);
12564 NOREF(aPageTotal);
12565 NOREF(aAllocVMM);
12566 NOREF(aFreeVMM);
12567 NOREF(aBalloonedVMM);
12568 NOREF(aSharedVMM);
12569 NOREF(aVmNetRx);
12570 NOREF(aVmNetTx);
12571 return E_NOTIMPL;
12572#endif
12573}
12574
12575/**
12576 * @note Locks this object for writing.
12577 */
12578HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12579{
12580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12581
12582 mRemoveSavedState = RT_BOOL(aRemove);
12583
12584 return S_OK;
12585}
12586
12587/**
12588 * @note Locks the same as #i_setMachineState() does.
12589 */
12590HRESULT SessionMachine::updateState(MachineState_T aState)
12591{
12592 return i_setMachineState(aState);
12593}
12594
12595/**
12596 * @note Locks this object for writing.
12597 */
12598HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12599{
12600 IProgress* pProgress(aProgress);
12601
12602 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12603
12604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12605
12606 if (mData->mSession.mState != SessionState_Locked)
12607 return VBOX_E_INVALID_OBJECT_STATE;
12608
12609 if (!mData->mSession.mProgress.isNull())
12610 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12611
12612 /* If we didn't reference the NAT network service yet, add a reference to
12613 * force a start */
12614 if (miNATNetworksStarted < 1)
12615 {
12616 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12617 {
12618 NetworkAttachmentType_T type;
12619 HRESULT hrc;
12620 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12621 if ( SUCCEEDED(hrc)
12622 && type == NetworkAttachmentType_NATNetwork)
12623 {
12624 Bstr name;
12625 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12626 if (SUCCEEDED(hrc))
12627 {
12628 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12629 mUserData->s.strName.c_str(), name.raw()));
12630 mPeer->lockHandle()->unlockWrite();
12631 mParent->i_natNetworkRefInc(name.raw());
12632#ifdef RT_LOCK_STRICT
12633 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12634#else
12635 mPeer->lockHandle()->lockWrite();
12636#endif
12637 }
12638 }
12639 }
12640 miNATNetworksStarted++;
12641 }
12642
12643 LogFlowThisFunc(("returns S_OK.\n"));
12644 return S_OK;
12645}
12646
12647/**
12648 * @note Locks this object for writing.
12649 */
12650HRESULT SessionMachine::endPowerUp(LONG aResult)
12651{
12652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12653
12654 if (mData->mSession.mState != SessionState_Locked)
12655 return VBOX_E_INVALID_OBJECT_STATE;
12656
12657 /* Finalize the LaunchVMProcess progress object. */
12658 if (mData->mSession.mProgress)
12659 {
12660 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12661 mData->mSession.mProgress.setNull();
12662 }
12663
12664 if (SUCCEEDED((HRESULT)aResult))
12665 {
12666#ifdef VBOX_WITH_RESOURCE_USAGE_API
12667 /* The VM has been powered up successfully, so it makes sense
12668 * now to offer the performance metrics for a running machine
12669 * object. Doing it earlier wouldn't be safe. */
12670 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12671 mData->mSession.mPID);
12672#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12673 }
12674
12675 return S_OK;
12676}
12677
12678/**
12679 * @note Locks this object for writing.
12680 */
12681HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12682{
12683 LogFlowThisFuncEnter();
12684
12685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12686
12687 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12688 E_FAIL);
12689
12690 /* create a progress object to track operation completion */
12691 ComObjPtr<Progress> pProgress;
12692 pProgress.createObject();
12693 pProgress->init(i_getVirtualBox(),
12694 static_cast<IMachine *>(this) /* aInitiator */,
12695 Bstr(tr("Stopping the virtual machine")).raw(),
12696 FALSE /* aCancelable */);
12697
12698 /* fill in the console task data */
12699 mConsoleTaskData.mLastState = mData->mMachineState;
12700 mConsoleTaskData.mProgress = pProgress;
12701
12702 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12703 i_setMachineState(MachineState_Stopping);
12704
12705 pProgress.queryInterfaceTo(aProgress.asOutParam());
12706
12707 return S_OK;
12708}
12709
12710/**
12711 * @note Locks this object for writing.
12712 */
12713HRESULT SessionMachine::endPoweringDown(LONG aResult,
12714 const com::Utf8Str &aErrMsg)
12715{
12716 LogFlowThisFuncEnter();
12717
12718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12719
12720 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12721 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12722 && mConsoleTaskData.mLastState != MachineState_Null,
12723 E_FAIL);
12724
12725 /*
12726 * On failure, set the state to the state we had when BeginPoweringDown()
12727 * was called (this is expected by Console::PowerDown() and the associated
12728 * task). On success the VM process already changed the state to
12729 * MachineState_PoweredOff, so no need to do anything.
12730 */
12731 if (FAILED(aResult))
12732 i_setMachineState(mConsoleTaskData.mLastState);
12733
12734 /* notify the progress object about operation completion */
12735 Assert(mConsoleTaskData.mProgress);
12736 if (SUCCEEDED(aResult))
12737 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12738 else
12739 {
12740 if (aErrMsg.length())
12741 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12742 COM_IIDOF(ISession),
12743 getComponentName(),
12744 aErrMsg.c_str());
12745 else
12746 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12747 }
12748
12749 /* clear out the temporary saved state data */
12750 mConsoleTaskData.mLastState = MachineState_Null;
12751 mConsoleTaskData.mProgress.setNull();
12752
12753 LogFlowThisFuncLeave();
12754 return S_OK;
12755}
12756
12757
12758/**
12759 * Goes through the USB filters of the given machine to see if the given
12760 * device matches any filter or not.
12761 *
12762 * @note Locks the same as USBController::hasMatchingFilter() does.
12763 */
12764HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12765 BOOL *aMatched,
12766 ULONG *aMaskedInterfaces)
12767{
12768 LogFlowThisFunc(("\n"));
12769
12770#ifdef VBOX_WITH_USB
12771 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12772#else
12773 NOREF(aDevice);
12774 NOREF(aMaskedInterfaces);
12775 *aMatched = FALSE;
12776#endif
12777
12778 return S_OK;
12779}
12780
12781/**
12782 * @note Locks the same as Host::captureUSBDevice() does.
12783 */
12784HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId)
12785{
12786 LogFlowThisFunc(("\n"));
12787
12788#ifdef VBOX_WITH_USB
12789 /* if captureDeviceForVM() fails, it must have set extended error info */
12790 clearError();
12791 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12792 if (FAILED(rc)) return rc;
12793
12794 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12795 AssertReturn(service, E_FAIL);
12796 return service->captureDeviceForVM(this, aId.ref());
12797#else
12798 NOREF(aId);
12799 return E_NOTIMPL;
12800#endif
12801}
12802
12803/**
12804 * @note Locks the same as Host::detachUSBDevice() does.
12805 */
12806HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12807 BOOL aDone)
12808{
12809 LogFlowThisFunc(("\n"));
12810
12811#ifdef VBOX_WITH_USB
12812 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12813 AssertReturn(service, E_FAIL);
12814 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12815#else
12816 NOREF(aId);
12817 NOREF(aDone);
12818 return E_NOTIMPL;
12819#endif
12820}
12821
12822/**
12823 * Inserts all machine filters to the USB proxy service and then calls
12824 * Host::autoCaptureUSBDevices().
12825 *
12826 * Called by Console from the VM process upon VM startup.
12827 *
12828 * @note Locks what called methods lock.
12829 */
12830HRESULT SessionMachine::autoCaptureUSBDevices()
12831{
12832 LogFlowThisFunc(("\n"));
12833
12834#ifdef VBOX_WITH_USB
12835 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12836 AssertComRC(rc);
12837 NOREF(rc);
12838
12839 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12840 AssertReturn(service, E_FAIL);
12841 return service->autoCaptureDevicesForVM(this);
12842#else
12843 return S_OK;
12844#endif
12845}
12846
12847/**
12848 * Removes all machine filters from the USB proxy service and then calls
12849 * Host::detachAllUSBDevices().
12850 *
12851 * Called by Console from the VM process upon normal VM termination or by
12852 * SessionMachine::uninit() upon abnormal VM termination (from under the
12853 * Machine/SessionMachine lock).
12854 *
12855 * @note Locks what called methods lock.
12856 */
12857HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12858{
12859 LogFlowThisFunc(("\n"));
12860
12861#ifdef VBOX_WITH_USB
12862 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12863 AssertComRC(rc);
12864 NOREF(rc);
12865
12866 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12867 AssertReturn(service, E_FAIL);
12868 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12869#else
12870 NOREF(aDone);
12871 return S_OK;
12872#endif
12873}
12874
12875/**
12876 * @note Locks this object for writing.
12877 */
12878HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12879 ComPtr<IProgress> &aProgress)
12880{
12881 LogFlowThisFuncEnter();
12882
12883 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12884 /*
12885 * We don't assert below because it might happen that a non-direct session
12886 * informs us it is closed right after we've been uninitialized -- it's ok.
12887 */
12888
12889 /* get IInternalSessionControl interface */
12890 ComPtr<IInternalSessionControl> control(aSession);
12891
12892 ComAssertRet(!control.isNull(), E_INVALIDARG);
12893
12894 /* Creating a Progress object requires the VirtualBox lock, and
12895 * thus locking it here is required by the lock order rules. */
12896 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12897
12898 if (control == mData->mSession.mDirectControl)
12899 {
12900 /* The direct session is being normally closed by the client process
12901 * ----------------------------------------------------------------- */
12902
12903 /* go to the closing state (essential for all open*Session() calls and
12904 * for #checkForDeath()) */
12905 Assert(mData->mSession.mState == SessionState_Locked);
12906 mData->mSession.mState = SessionState_Unlocking;
12907
12908 /* set direct control to NULL to release the remote instance */
12909 mData->mSession.mDirectControl.setNull();
12910 LogFlowThisFunc(("Direct control is set to NULL\n"));
12911
12912 if (mData->mSession.mProgress)
12913 {
12914 /* finalize the progress, someone might wait if a frontend
12915 * closes the session before powering on the VM. */
12916 mData->mSession.mProgress->notifyComplete(E_FAIL,
12917 COM_IIDOF(ISession),
12918 getComponentName(),
12919 tr("The VM session was closed before any attempt to power it on"));
12920 mData->mSession.mProgress.setNull();
12921 }
12922
12923 /* Create the progress object the client will use to wait until
12924 * #checkForDeath() is called to uninitialize this session object after
12925 * it releases the IPC semaphore.
12926 * Note! Because we're "reusing" mProgress here, this must be a proxy
12927 * object just like for LaunchVMProcess. */
12928 Assert(mData->mSession.mProgress.isNull());
12929 ComObjPtr<ProgressProxy> progress;
12930 progress.createObject();
12931 ComPtr<IUnknown> pPeer(mPeer);
12932 progress->init(mParent, pPeer,
12933 Bstr(tr("Closing session")).raw(),
12934 FALSE /* aCancelable */);
12935 progress.queryInterfaceTo(aProgress.asOutParam());
12936 mData->mSession.mProgress = progress;
12937 }
12938 else
12939 {
12940 /* the remote session is being normally closed */
12941 Data::Session::RemoteControlList::iterator it =
12942 mData->mSession.mRemoteControls.begin();
12943 while (it != mData->mSession.mRemoteControls.end())
12944 {
12945 if (control == *it)
12946 break;
12947 ++it;
12948 }
12949 BOOL found = it != mData->mSession.mRemoteControls.end();
12950 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12951 E_INVALIDARG);
12952 // This MUST be erase(it), not remove(*it) as the latter triggers a
12953 // very nasty use after free due to the place where the value "lives".
12954 mData->mSession.mRemoteControls.erase(it);
12955 }
12956
12957 /* signal the client watcher thread, because the client is going away */
12958 mParent->i_updateClientWatcher();
12959
12960 LogFlowThisFuncLeave();
12961 return S_OK;
12962}
12963
12964/**
12965 * @note Locks this object for writing.
12966 */
12967HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12968 com::Utf8Str &aStateFilePath)
12969{
12970 LogFlowThisFuncEnter();
12971
12972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12973
12974 AssertReturn( mData->mMachineState == MachineState_Paused
12975 && mConsoleTaskData.mLastState == MachineState_Null
12976 && mConsoleTaskData.strStateFilePath.isEmpty(),
12977 E_FAIL);
12978
12979 /* create a progress object to track operation completion */
12980 ComObjPtr<Progress> pProgress;
12981 pProgress.createObject();
12982 pProgress->init(i_getVirtualBox(),
12983 static_cast<IMachine *>(this) /* aInitiator */,
12984 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12985 FALSE /* aCancelable */);
12986
12987 /* stateFilePath is null when the machine is not running */
12988 if (mData->mMachineState == MachineState_Paused)
12989 i_composeSavedStateFilename(aStateFilePath);
12990
12991 /* fill in the console task data */
12992 mConsoleTaskData.mLastState = mData->mMachineState;
12993 mConsoleTaskData.strStateFilePath = aStateFilePath;
12994 mConsoleTaskData.mProgress = pProgress;
12995
12996 /* set the state to Saving (this is expected by Console::SaveState()) */
12997 i_setMachineState(MachineState_Saving);
12998
12999 pProgress.queryInterfaceTo(aProgress.asOutParam());
13000
13001 return S_OK;
13002}
13003
13004/**
13005 * @note Locks mParent + this object for writing.
13006 */
13007HRESULT SessionMachine::endSavingState(LONG aResult,
13008 const com::Utf8Str &aErrMsg)
13009{
13010 LogFlowThisFunc(("\n"));
13011
13012 /* endSavingState() need mParent lock */
13013 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13014
13015 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13016 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13017 && mConsoleTaskData.mLastState != MachineState_Null
13018 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13019 E_FAIL);
13020
13021 /*
13022 * On failure, set the state to the state we had when BeginSavingState()
13023 * was called (this is expected by Console::SaveState() and the associated
13024 * task). On success the VM process already changed the state to
13025 * MachineState_Saved, so no need to do anything.
13026 */
13027 if (FAILED(aResult))
13028 i_setMachineState(mConsoleTaskData.mLastState);
13029
13030 return i_endSavingState(aResult, aErrMsg);
13031}
13032
13033/**
13034 * @note Locks this object for writing.
13035 */
13036HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13037{
13038 LogFlowThisFunc(("\n"));
13039
13040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13041
13042 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13043 || mData->mMachineState == MachineState_Teleported
13044 || mData->mMachineState == MachineState_Aborted
13045 , E_FAIL); /** @todo setError. */
13046
13047 com::Utf8Str stateFilePathFull;
13048 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13049 if (RT_FAILURE(vrc))
13050 return setError(VBOX_E_FILE_ERROR,
13051 tr("Invalid saved state file path '%s' (%Rrc)"),
13052 aSavedStateFile.c_str(),
13053 vrc);
13054
13055 mSSData->strStateFilePath = stateFilePathFull;
13056
13057 /* The below i_setMachineState() will detect the state transition and will
13058 * update the settings file */
13059
13060 return i_setMachineState(MachineState_Saved);
13061}
13062
13063HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13064 std::vector<com::Utf8Str> &aValues,
13065 std::vector<LONG64> &aTimestamps,
13066 std::vector<com::Utf8Str> &aFlags)
13067{
13068 LogFlowThisFunc(("\n"));
13069
13070#ifdef VBOX_WITH_GUEST_PROPS
13071 using namespace guestProp;
13072
13073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13074
13075 size_t cEntries = mHWData->mGuestProperties.size();
13076 aNames.resize(cEntries);
13077 aValues.resize(cEntries);
13078 aTimestamps.resize(cEntries);
13079 aFlags.resize(cEntries);
13080
13081 size_t i = 0;
13082 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13083 it != mHWData->mGuestProperties.end();
13084 ++it, ++i)
13085 {
13086 char szFlags[MAX_FLAGS_LEN + 1];
13087 aNames[i] = it->first;
13088 aValues[i] = it->second.strValue;
13089 aTimestamps[i] = it->second.mTimestamp;
13090
13091 /* If it is NULL, keep it NULL. */
13092 if (it->second.mFlags)
13093 {
13094 writeFlags(it->second.mFlags, szFlags);
13095 aFlags[i] = szFlags;
13096 }
13097 else
13098 aFlags[i] = "";
13099 }
13100 return S_OK;
13101#else
13102 ReturnComNotImplemented();
13103#endif
13104}
13105
13106HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13107 const com::Utf8Str &aValue,
13108 LONG64 aTimestamp,
13109 const com::Utf8Str &aFlags)
13110{
13111 LogFlowThisFunc(("\n"));
13112
13113#ifdef VBOX_WITH_GUEST_PROPS
13114 using namespace guestProp;
13115
13116 try
13117 {
13118 /*
13119 * Convert input up front.
13120 */
13121 uint32_t fFlags = NILFLAG;
13122 if (aFlags.length())
13123 {
13124 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13125 AssertRCReturn(vrc, E_INVALIDARG);
13126 }
13127
13128 /*
13129 * Now grab the object lock, validate the state and do the update.
13130 */
13131
13132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13133
13134 switch (mData->mMachineState)
13135 {
13136 case MachineState_Paused:
13137 case MachineState_Running:
13138 case MachineState_Teleporting:
13139 case MachineState_TeleportingPausedVM:
13140 case MachineState_LiveSnapshotting:
13141 case MachineState_DeletingSnapshotOnline:
13142 case MachineState_DeletingSnapshotPaused:
13143 case MachineState_Saving:
13144 case MachineState_Stopping:
13145 break;
13146
13147 default:
13148 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13149 VBOX_E_INVALID_VM_STATE);
13150 }
13151
13152 i_setModified(IsModified_MachineData);
13153 mHWData.backup();
13154
13155 bool fDelete = !aValue.length();
13156 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13157 if (it != mHWData->mGuestProperties.end())
13158 {
13159 if (!fDelete)
13160 {
13161 it->second.strValue = aValue;
13162 it->second.mTimestamp = aTimestamp;
13163 it->second.mFlags = fFlags;
13164 }
13165 else
13166 mHWData->mGuestProperties.erase(it);
13167
13168 mData->mGuestPropertiesModified = TRUE;
13169 }
13170 else if (!fDelete)
13171 {
13172 HWData::GuestProperty prop;
13173 prop.strValue = aValue;
13174 prop.mTimestamp = aTimestamp;
13175 prop.mFlags = fFlags;
13176
13177 mHWData->mGuestProperties[aName] = prop;
13178 mData->mGuestPropertiesModified = TRUE;
13179 }
13180
13181 /*
13182 * Send a callback notification if appropriate
13183 */
13184 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13185 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13186 RTSTR_MAX,
13187 aName.c_str(),
13188 RTSTR_MAX, NULL)
13189 )
13190 {
13191 alock.release();
13192
13193 mParent->i_onGuestPropertyChange(mData->mUuid,
13194 Bstr(aName).raw(),
13195 Bstr(aValue).raw(),
13196 Bstr(aFlags).raw());
13197 }
13198 }
13199 catch (...)
13200 {
13201 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13202 }
13203 return S_OK;
13204#else
13205 ReturnComNotImplemented();
13206#endif
13207}
13208
13209
13210HRESULT SessionMachine::lockMedia()
13211{
13212 AutoMultiWriteLock2 alock(this->lockHandle(),
13213 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13214
13215 AssertReturn( mData->mMachineState == MachineState_Starting
13216 || mData->mMachineState == MachineState_Restoring
13217 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13218
13219 clearError();
13220 alock.release();
13221 return i_lockMedia();
13222}
13223
13224HRESULT SessionMachine::unlockMedia()
13225{
13226 HRESULT hrc = i_unlockMedia();
13227 return hrc;
13228}
13229
13230HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13231 ComPtr<IMediumAttachment> &aNewAttachment)
13232{
13233 // request the host lock first, since might be calling Host methods for getting host drives;
13234 // next, protect the media tree all the while we're in here, as well as our member variables
13235 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13236 this->lockHandle(),
13237 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13238
13239 IMediumAttachment *iAttach = aAttachment;
13240 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13241
13242 Bstr ctrlName;
13243 LONG lPort;
13244 LONG lDevice;
13245 bool fTempEject;
13246 {
13247 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13248
13249 /* Need to query the details first, as the IMediumAttachment reference
13250 * might be to the original settings, which we are going to change. */
13251 ctrlName = pAttach->i_getControllerName();
13252 lPort = pAttach->i_getPort();
13253 lDevice = pAttach->i_getDevice();
13254 fTempEject = pAttach->i_getTempEject();
13255 }
13256
13257 if (!fTempEject)
13258 {
13259 /* Remember previously mounted medium. The medium before taking the
13260 * backup is not necessarily the same thing. */
13261 ComObjPtr<Medium> oldmedium;
13262 oldmedium = pAttach->i_getMedium();
13263
13264 i_setModified(IsModified_Storage);
13265 mMediaData.backup();
13266
13267 // The backup operation makes the pAttach reference point to the
13268 // old settings. Re-get the correct reference.
13269 pAttach = i_findAttachment(mMediaData->mAttachments,
13270 ctrlName.raw(),
13271 lPort,
13272 lDevice);
13273
13274 {
13275 AutoCaller autoAttachCaller(this);
13276 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13277
13278 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13279 if (!oldmedium.isNull())
13280 oldmedium->i_removeBackReference(mData->mUuid);
13281
13282 pAttach->i_updateMedium(NULL);
13283 pAttach->i_updateEjected();
13284 }
13285
13286 i_setModified(IsModified_Storage);
13287 }
13288 else
13289 {
13290 {
13291 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13292 pAttach->i_updateEjected();
13293 }
13294 }
13295
13296 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13297
13298 return S_OK;
13299}
13300
13301// public methods only for internal purposes
13302/////////////////////////////////////////////////////////////////////////////
13303
13304#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13305/**
13306 * Called from the client watcher thread to check for expected or unexpected
13307 * death of the client process that has a direct session to this machine.
13308 *
13309 * On Win32 and on OS/2, this method is called only when we've got the
13310 * mutex (i.e. the client has either died or terminated normally) so it always
13311 * returns @c true (the client is terminated, the session machine is
13312 * uninitialized).
13313 *
13314 * On other platforms, the method returns @c true if the client process has
13315 * terminated normally or abnormally and the session machine was uninitialized,
13316 * and @c false if the client process is still alive.
13317 *
13318 * @note Locks this object for writing.
13319 */
13320bool SessionMachine::i_checkForDeath()
13321{
13322 Uninit::Reason reason;
13323 bool terminated = false;
13324
13325 /* Enclose autoCaller with a block because calling uninit() from under it
13326 * will deadlock. */
13327 {
13328 AutoCaller autoCaller(this);
13329 if (!autoCaller.isOk())
13330 {
13331 /* return true if not ready, to cause the client watcher to exclude
13332 * the corresponding session from watching */
13333 LogFlowThisFunc(("Already uninitialized!\n"));
13334 return true;
13335 }
13336
13337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13338
13339 /* Determine the reason of death: if the session state is Closing here,
13340 * everything is fine. Otherwise it means that the client did not call
13341 * OnSessionEnd() before it released the IPC semaphore. This may happen
13342 * either because the client process has abnormally terminated, or
13343 * because it simply forgot to call ISession::Close() before exiting. We
13344 * threat the latter also as an abnormal termination (see
13345 * Session::uninit() for details). */
13346 reason = mData->mSession.mState == SessionState_Unlocking ?
13347 Uninit::Normal :
13348 Uninit::Abnormal;
13349
13350 if (mClientToken)
13351 terminated = mClientToken->release();
13352 } /* AutoCaller block */
13353
13354 if (terminated)
13355 uninit(reason);
13356
13357 return terminated;
13358}
13359
13360void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13361{
13362 LogFlowThisFunc(("\n"));
13363
13364 strTokenId.setNull();
13365
13366 AutoCaller autoCaller(this);
13367 AssertComRCReturnVoid(autoCaller.rc());
13368
13369 Assert(mClientToken);
13370 if (mClientToken)
13371 mClientToken->getId(strTokenId);
13372}
13373#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13374IToken *SessionMachine::i_getToken()
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378 AutoCaller autoCaller(this);
13379 AssertComRCReturn(autoCaller.rc(), NULL);
13380
13381 Assert(mClientToken);
13382 if (mClientToken)
13383 return mClientToken->getToken();
13384 else
13385 return NULL;
13386}
13387#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13388
13389Machine::ClientToken *SessionMachine::i_getClientToken()
13390{
13391 LogFlowThisFunc(("\n"));
13392
13393 AutoCaller autoCaller(this);
13394 AssertComRCReturn(autoCaller.rc(), NULL);
13395
13396 return mClientToken;
13397}
13398
13399
13400/**
13401 * @note Locks this object for reading.
13402 */
13403HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13404{
13405 LogFlowThisFunc(("\n"));
13406
13407 AutoCaller autoCaller(this);
13408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13409
13410 ComPtr<IInternalSessionControl> directControl;
13411 {
13412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13413 directControl = mData->mSession.mDirectControl;
13414 }
13415
13416 /* ignore notifications sent after #OnSessionEnd() is called */
13417 if (!directControl)
13418 return S_OK;
13419
13420 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13421}
13422
13423/**
13424 * @note Locks this object for reading.
13425 */
13426HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13427 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13428 IN_BSTR aGuestIp, LONG aGuestPort)
13429{
13430 LogFlowThisFunc(("\n"));
13431
13432 AutoCaller autoCaller(this);
13433 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13434
13435 ComPtr<IInternalSessionControl> directControl;
13436 {
13437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13438 directControl = mData->mSession.mDirectControl;
13439 }
13440
13441 /* ignore notifications sent after #OnSessionEnd() is called */
13442 if (!directControl)
13443 return S_OK;
13444 /*
13445 * instead acting like callback we ask IVirtualBox deliver corresponding event
13446 */
13447
13448 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13449 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13450 return S_OK;
13451}
13452
13453/**
13454 * @note Locks this object for reading.
13455 */
13456HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13457{
13458 LogFlowThisFunc(("\n"));
13459
13460 AutoCaller autoCaller(this);
13461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13462
13463 ComPtr<IInternalSessionControl> directControl;
13464 {
13465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13466 directControl = mData->mSession.mDirectControl;
13467 }
13468
13469 /* ignore notifications sent after #OnSessionEnd() is called */
13470 if (!directControl)
13471 return S_OK;
13472
13473 return directControl->OnSerialPortChange(serialPort);
13474}
13475
13476/**
13477 * @note Locks this object for reading.
13478 */
13479HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13480{
13481 LogFlowThisFunc(("\n"));
13482
13483 AutoCaller autoCaller(this);
13484 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13485
13486 ComPtr<IInternalSessionControl> directControl;
13487 {
13488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13489 directControl = mData->mSession.mDirectControl;
13490 }
13491
13492 /* ignore notifications sent after #OnSessionEnd() is called */
13493 if (!directControl)
13494 return S_OK;
13495
13496 return directControl->OnParallelPortChange(parallelPort);
13497}
13498
13499/**
13500 * @note Locks this object for reading.
13501 */
13502HRESULT SessionMachine::i_onStorageControllerChange()
13503{
13504 LogFlowThisFunc(("\n"));
13505
13506 AutoCaller autoCaller(this);
13507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13508
13509 ComPtr<IInternalSessionControl> directControl;
13510 {
13511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13512 directControl = mData->mSession.mDirectControl;
13513 }
13514
13515 /* ignore notifications sent after #OnSessionEnd() is called */
13516 if (!directControl)
13517 return S_OK;
13518
13519 return directControl->OnStorageControllerChange();
13520}
13521
13522/**
13523 * @note Locks this object for reading.
13524 */
13525HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13526{
13527 LogFlowThisFunc(("\n"));
13528
13529 AutoCaller autoCaller(this);
13530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13531
13532 ComPtr<IInternalSessionControl> directControl;
13533 {
13534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13535 directControl = mData->mSession.mDirectControl;
13536 }
13537
13538 /* ignore notifications sent after #OnSessionEnd() is called */
13539 if (!directControl)
13540 return S_OK;
13541
13542 return directControl->OnMediumChange(aAttachment, aForce);
13543}
13544
13545/**
13546 * @note Locks this object for reading.
13547 */
13548HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552 AutoCaller autoCaller(this);
13553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13554
13555 ComPtr<IInternalSessionControl> directControl;
13556 {
13557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13558 directControl = mData->mSession.mDirectControl;
13559 }
13560
13561 /* ignore notifications sent after #OnSessionEnd() is called */
13562 if (!directControl)
13563 return S_OK;
13564
13565 return directControl->OnCPUChange(aCPU, aRemove);
13566}
13567
13568HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13569{
13570 LogFlowThisFunc(("\n"));
13571
13572 AutoCaller autoCaller(this);
13573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13574
13575 ComPtr<IInternalSessionControl> directControl;
13576 {
13577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13578 directControl = mData->mSession.mDirectControl;
13579 }
13580
13581 /* ignore notifications sent after #OnSessionEnd() is called */
13582 if (!directControl)
13583 return S_OK;
13584
13585 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13586}
13587
13588/**
13589 * @note Locks this object for reading.
13590 */
13591HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13592{
13593 LogFlowThisFunc(("\n"));
13594
13595 AutoCaller autoCaller(this);
13596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13597
13598 ComPtr<IInternalSessionControl> directControl;
13599 {
13600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13601 directControl = mData->mSession.mDirectControl;
13602 }
13603
13604 /* ignore notifications sent after #OnSessionEnd() is called */
13605 if (!directControl)
13606 return S_OK;
13607
13608 return directControl->OnVRDEServerChange(aRestart);
13609}
13610
13611/**
13612 * @note Locks this object for reading.
13613 */
13614HRESULT SessionMachine::i_onVideoCaptureChange()
13615{
13616 LogFlowThisFunc(("\n"));
13617
13618 AutoCaller autoCaller(this);
13619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13620
13621 ComPtr<IInternalSessionControl> directControl;
13622 {
13623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13624 directControl = mData->mSession.mDirectControl;
13625 }
13626
13627 /* ignore notifications sent after #OnSessionEnd() is called */
13628 if (!directControl)
13629 return S_OK;
13630
13631 return directControl->OnVideoCaptureChange();
13632}
13633
13634/**
13635 * @note Locks this object for reading.
13636 */
13637HRESULT SessionMachine::i_onUSBControllerChange()
13638{
13639 LogFlowThisFunc(("\n"));
13640
13641 AutoCaller autoCaller(this);
13642 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13643
13644 ComPtr<IInternalSessionControl> directControl;
13645 {
13646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13647 directControl = mData->mSession.mDirectControl;
13648 }
13649
13650 /* ignore notifications sent after #OnSessionEnd() is called */
13651 if (!directControl)
13652 return S_OK;
13653
13654 return directControl->OnUSBControllerChange();
13655}
13656
13657/**
13658 * @note Locks this object for reading.
13659 */
13660HRESULT SessionMachine::i_onSharedFolderChange()
13661{
13662 LogFlowThisFunc(("\n"));
13663
13664 AutoCaller autoCaller(this);
13665 AssertComRCReturnRC(autoCaller.rc());
13666
13667 ComPtr<IInternalSessionControl> directControl;
13668 {
13669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13670 directControl = mData->mSession.mDirectControl;
13671 }
13672
13673 /* ignore notifications sent after #OnSessionEnd() is called */
13674 if (!directControl)
13675 return S_OK;
13676
13677 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13678}
13679
13680/**
13681 * @note Locks this object for reading.
13682 */
13683HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13684{
13685 LogFlowThisFunc(("\n"));
13686
13687 AutoCaller autoCaller(this);
13688 AssertComRCReturnRC(autoCaller.rc());
13689
13690 ComPtr<IInternalSessionControl> directControl;
13691 {
13692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13693 directControl = mData->mSession.mDirectControl;
13694 }
13695
13696 /* ignore notifications sent after #OnSessionEnd() is called */
13697 if (!directControl)
13698 return S_OK;
13699
13700 return directControl->OnClipboardModeChange(aClipboardMode);
13701}
13702
13703/**
13704 * @note Locks this object for reading.
13705 */
13706HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13707{
13708 LogFlowThisFunc(("\n"));
13709
13710 AutoCaller autoCaller(this);
13711 AssertComRCReturnRC(autoCaller.rc());
13712
13713 ComPtr<IInternalSessionControl> directControl;
13714 {
13715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13716 directControl = mData->mSession.mDirectControl;
13717 }
13718
13719 /* ignore notifications sent after #OnSessionEnd() is called */
13720 if (!directControl)
13721 return S_OK;
13722
13723 return directControl->OnDnDModeChange(aDnDMode);
13724}
13725
13726/**
13727 * @note Locks this object for reading.
13728 */
13729HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13730{
13731 LogFlowThisFunc(("\n"));
13732
13733 AutoCaller autoCaller(this);
13734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13735
13736 ComPtr<IInternalSessionControl> directControl;
13737 {
13738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13739 directControl = mData->mSession.mDirectControl;
13740 }
13741
13742 /* ignore notifications sent after #OnSessionEnd() is called */
13743 if (!directControl)
13744 return S_OK;
13745
13746 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13747}
13748
13749/**
13750 * @note Locks this object for reading.
13751 */
13752HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13753{
13754 LogFlowThisFunc(("\n"));
13755
13756 AutoCaller autoCaller(this);
13757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13758
13759 ComPtr<IInternalSessionControl> directControl;
13760 {
13761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13762 directControl = mData->mSession.mDirectControl;
13763 }
13764
13765 /* ignore notifications sent after #OnSessionEnd() is called */
13766 if (!directControl)
13767 return S_OK;
13768
13769 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13770}
13771
13772/**
13773 * Returns @c true if this machine's USB controller reports it has a matching
13774 * filter for the given USB device and @c false otherwise.
13775 *
13776 * @note locks this object for reading.
13777 */
13778bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13779{
13780 AutoCaller autoCaller(this);
13781 /* silently return if not ready -- this method may be called after the
13782 * direct machine session has been called */
13783 if (!autoCaller.isOk())
13784 return false;
13785
13786#ifdef VBOX_WITH_USB
13787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13788
13789 switch (mData->mMachineState)
13790 {
13791 case MachineState_Starting:
13792 case MachineState_Restoring:
13793 case MachineState_TeleportingIn:
13794 case MachineState_Paused:
13795 case MachineState_Running:
13796 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13797 * elsewhere... */
13798 alock.release();
13799 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13800 default: break;
13801 }
13802#else
13803 NOREF(aDevice);
13804 NOREF(aMaskedIfs);
13805#endif
13806 return false;
13807}
13808
13809/**
13810 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13811 */
13812HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13813 IVirtualBoxErrorInfo *aError,
13814 ULONG aMaskedIfs)
13815{
13816 LogFlowThisFunc(("\n"));
13817
13818 AutoCaller autoCaller(this);
13819
13820 /* This notification may happen after the machine object has been
13821 * uninitialized (the session was closed), so don't assert. */
13822 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13823
13824 ComPtr<IInternalSessionControl> directControl;
13825 {
13826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13827 directControl = mData->mSession.mDirectControl;
13828 }
13829
13830 /* fail on notifications sent after #OnSessionEnd() is called, it is
13831 * expected by the caller */
13832 if (!directControl)
13833 return E_FAIL;
13834
13835 /* No locks should be held at this point. */
13836 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13837 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13838
13839 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13840}
13841
13842/**
13843 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13844 */
13845HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13846 IVirtualBoxErrorInfo *aError)
13847{
13848 LogFlowThisFunc(("\n"));
13849
13850 AutoCaller autoCaller(this);
13851
13852 /* This notification may happen after the machine object has been
13853 * uninitialized (the session was closed), so don't assert. */
13854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13855
13856 ComPtr<IInternalSessionControl> directControl;
13857 {
13858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13859 directControl = mData->mSession.mDirectControl;
13860 }
13861
13862 /* fail on notifications sent after #OnSessionEnd() is called, it is
13863 * expected by the caller */
13864 if (!directControl)
13865 return E_FAIL;
13866
13867 /* No locks should be held at this point. */
13868 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13869 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13870
13871 return directControl->OnUSBDeviceDetach(aId, aError);
13872}
13873
13874// protected methods
13875/////////////////////////////////////////////////////////////////////////////
13876
13877/**
13878 * Helper method to finalize saving the state.
13879 *
13880 * @note Must be called from under this object's lock.
13881 *
13882 * @param aRc S_OK if the snapshot has been taken successfully
13883 * @param aErrMsg human readable error message for failure
13884 *
13885 * @note Locks mParent + this objects for writing.
13886 */
13887HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13888{
13889 LogFlowThisFuncEnter();
13890
13891 AutoCaller autoCaller(this);
13892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13893
13894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13895
13896 HRESULT rc = S_OK;
13897
13898 if (SUCCEEDED(aRc))
13899 {
13900 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13901
13902 /* save all VM settings */
13903 rc = i_saveSettings(NULL);
13904 // no need to check whether VirtualBox.xml needs saving also since
13905 // we can't have a name change pending at this point
13906 }
13907 else
13908 {
13909 // delete the saved state file (it might have been already created);
13910 // we need not check whether this is shared with a snapshot here because
13911 // we certainly created this saved state file here anew
13912 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13913 }
13914
13915 /* notify the progress object about operation completion */
13916 Assert(mConsoleTaskData.mProgress);
13917 if (SUCCEEDED(aRc))
13918 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13919 else
13920 {
13921 if (aErrMsg.length())
13922 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13923 COM_IIDOF(ISession),
13924 getComponentName(),
13925 aErrMsg.c_str());
13926 else
13927 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13928 }
13929
13930 /* clear out the temporary saved state data */
13931 mConsoleTaskData.mLastState = MachineState_Null;
13932 mConsoleTaskData.strStateFilePath.setNull();
13933 mConsoleTaskData.mProgress.setNull();
13934
13935 LogFlowThisFuncLeave();
13936 return rc;
13937}
13938
13939/**
13940 * Deletes the given file if it is no longer in use by either the current machine state
13941 * (if the machine is "saved") or any of the machine's snapshots.
13942 *
13943 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13944 * but is different for each SnapshotMachine. When calling this, the order of calling this
13945 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13946 * is therefore critical. I know, it's all rather messy.
13947 *
13948 * @param strStateFile
13949 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13950 * the test for whether the saved state file is in use.
13951 */
13952void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13953 Snapshot *pSnapshotToIgnore)
13954{
13955 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13956 if ( (strStateFile.isNotEmpty())
13957 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13958 )
13959 // ... and it must also not be shared with other snapshots
13960 if ( !mData->mFirstSnapshot
13961 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13962 // this checks the SnapshotMachine's state file paths
13963 )
13964 RTFileDelete(strStateFile.c_str());
13965}
13966
13967/**
13968 * Locks the attached media.
13969 *
13970 * All attached hard disks are locked for writing and DVD/floppy are locked for
13971 * reading. Parents of attached hard disks (if any) are locked for reading.
13972 *
13973 * This method also performs accessibility check of all media it locks: if some
13974 * media is inaccessible, the method will return a failure and a bunch of
13975 * extended error info objects per each inaccessible medium.
13976 *
13977 * Note that this method is atomic: if it returns a success, all media are
13978 * locked as described above; on failure no media is locked at all (all
13979 * succeeded individual locks will be undone).
13980 *
13981 * The caller is responsible for doing the necessary state sanity checks.
13982 *
13983 * The locks made by this method must be undone by calling #unlockMedia() when
13984 * no more needed.
13985 */
13986HRESULT SessionMachine::i_lockMedia()
13987{
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13990
13991 AutoMultiWriteLock2 alock(this->lockHandle(),
13992 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13993
13994 /* bail out if trying to lock things with already set up locking */
13995 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13996
13997 MultiResult mrc(S_OK);
13998
13999 /* Collect locking information for all medium objects attached to the VM. */
14000 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14001 it != mMediaData->mAttachments.end();
14002 ++it)
14003 {
14004 MediumAttachment* pAtt = *it;
14005 DeviceType_T devType = pAtt->i_getType();
14006 Medium *pMedium = pAtt->i_getMedium();
14007
14008 MediumLockList *pMediumLockList(new MediumLockList());
14009 // There can be attachments without a medium (floppy/dvd), and thus
14010 // it's impossible to create a medium lock list. It still makes sense
14011 // to have the empty medium lock list in the map in case a medium is
14012 // attached later.
14013 if (pMedium != NULL)
14014 {
14015 MediumType_T mediumType = pMedium->i_getType();
14016 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14017 || mediumType == MediumType_Shareable;
14018 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14019
14020 alock.release();
14021 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14022 !fIsReadOnlyLock /* fMediumLockWrite */,
14023 NULL,
14024 *pMediumLockList);
14025 alock.acquire();
14026 if (FAILED(mrc))
14027 {
14028 delete pMediumLockList;
14029 mData->mSession.mLockedMedia.Clear();
14030 break;
14031 }
14032 }
14033
14034 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14035 if (FAILED(rc))
14036 {
14037 mData->mSession.mLockedMedia.Clear();
14038 mrc = setError(rc,
14039 tr("Collecting locking information for all attached media failed"));
14040 break;
14041 }
14042 }
14043
14044 if (SUCCEEDED(mrc))
14045 {
14046 /* Now lock all media. If this fails, nothing is locked. */
14047 alock.release();
14048 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14049 alock.acquire();
14050 if (FAILED(rc))
14051 {
14052 mrc = setError(rc,
14053 tr("Locking of attached media failed"));
14054 }
14055 }
14056
14057 return mrc;
14058}
14059
14060/**
14061 * Undoes the locks made by by #lockMedia().
14062 */
14063HRESULT SessionMachine::i_unlockMedia()
14064{
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14067
14068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14069
14070 /* we may be holding important error info on the current thread;
14071 * preserve it */
14072 ErrorInfoKeeper eik;
14073
14074 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14075 AssertComRC(rc);
14076 return rc;
14077}
14078
14079/**
14080 * Helper to change the machine state (reimplementation).
14081 *
14082 * @note Locks this object for writing.
14083 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14084 * it can cause crashes in random places due to unexpectedly committing
14085 * the current settings. The caller is responsible for that. The call
14086 * to saveStateSettings is fine, because this method does not commit.
14087 */
14088HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14089{
14090 LogFlowThisFuncEnter();
14091 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14095
14096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14097
14098 MachineState_T oldMachineState = mData->mMachineState;
14099
14100 AssertMsgReturn(oldMachineState != aMachineState,
14101 ("oldMachineState=%s, aMachineState=%s\n",
14102 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14103 E_FAIL);
14104
14105 HRESULT rc = S_OK;
14106
14107 int stsFlags = 0;
14108 bool deleteSavedState = false;
14109
14110 /* detect some state transitions */
14111
14112 if ( ( oldMachineState == MachineState_Saved
14113 && aMachineState == MachineState_Restoring)
14114 || ( ( oldMachineState == MachineState_PoweredOff
14115 || oldMachineState == MachineState_Teleported
14116 || oldMachineState == MachineState_Aborted
14117 )
14118 && ( aMachineState == MachineState_TeleportingIn
14119 || aMachineState == MachineState_Starting
14120 )
14121 )
14122 )
14123 {
14124 /* The EMT thread is about to start */
14125
14126 /* Nothing to do here for now... */
14127
14128 /// @todo NEWMEDIA don't let mDVDDrive and other children
14129 /// change anything when in the Starting/Restoring state
14130 }
14131 else if ( ( oldMachineState == MachineState_Running
14132 || oldMachineState == MachineState_Paused
14133 || oldMachineState == MachineState_Teleporting
14134 || oldMachineState == MachineState_LiveSnapshotting
14135 || oldMachineState == MachineState_Stuck
14136 || oldMachineState == MachineState_Starting
14137 || oldMachineState == MachineState_Stopping
14138 || oldMachineState == MachineState_Saving
14139 || oldMachineState == MachineState_Restoring
14140 || oldMachineState == MachineState_TeleportingPausedVM
14141 || oldMachineState == MachineState_TeleportingIn
14142 )
14143 && ( aMachineState == MachineState_PoweredOff
14144 || aMachineState == MachineState_Saved
14145 || aMachineState == MachineState_Teleported
14146 || aMachineState == MachineState_Aborted
14147 )
14148 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14149 * snapshot */
14150 && ( mConsoleTaskData.mSnapshot.isNull()
14151 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14152 )
14153 )
14154 {
14155 /* The EMT thread has just stopped, unlock attached media. Note that as
14156 * opposed to locking that is done from Console, we do unlocking here
14157 * because the VM process may have aborted before having a chance to
14158 * properly unlock all media it locked. */
14159
14160 unlockMedia();
14161 }
14162
14163 if (oldMachineState == MachineState_Restoring)
14164 {
14165 if (aMachineState != MachineState_Saved)
14166 {
14167 /*
14168 * delete the saved state file once the machine has finished
14169 * restoring from it (note that Console sets the state from
14170 * Restoring to Saved if the VM couldn't restore successfully,
14171 * to give the user an ability to fix an error and retry --
14172 * we keep the saved state file in this case)
14173 */
14174 deleteSavedState = true;
14175 }
14176 }
14177 else if ( oldMachineState == MachineState_Saved
14178 && ( aMachineState == MachineState_PoweredOff
14179 || aMachineState == MachineState_Aborted
14180 || aMachineState == MachineState_Teleported
14181 )
14182 )
14183 {
14184 /*
14185 * delete the saved state after Console::ForgetSavedState() is called
14186 * or if the VM process (owning a direct VM session) crashed while the
14187 * VM was Saved
14188 */
14189
14190 /// @todo (dmik)
14191 // Not sure that deleting the saved state file just because of the
14192 // client death before it attempted to restore the VM is a good
14193 // thing. But when it crashes we need to go to the Aborted state
14194 // which cannot have the saved state file associated... The only
14195 // way to fix this is to make the Aborted condition not a VM state
14196 // but a bool flag: i.e., when a crash occurs, set it to true and
14197 // change the state to PoweredOff or Saved depending on the
14198 // saved state presence.
14199
14200 deleteSavedState = true;
14201 mData->mCurrentStateModified = TRUE;
14202 stsFlags |= SaveSTS_CurStateModified;
14203 }
14204
14205 if ( aMachineState == MachineState_Starting
14206 || aMachineState == MachineState_Restoring
14207 || aMachineState == MachineState_TeleportingIn
14208 )
14209 {
14210 /* set the current state modified flag to indicate that the current
14211 * state is no more identical to the state in the
14212 * current snapshot */
14213 if (!mData->mCurrentSnapshot.isNull())
14214 {
14215 mData->mCurrentStateModified = TRUE;
14216 stsFlags |= SaveSTS_CurStateModified;
14217 }
14218 }
14219
14220 if (deleteSavedState)
14221 {
14222 if (mRemoveSavedState)
14223 {
14224 Assert(!mSSData->strStateFilePath.isEmpty());
14225
14226 // it is safe to delete the saved state file if ...
14227 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14228 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14229 // ... none of the snapshots share the saved state file
14230 )
14231 RTFileDelete(mSSData->strStateFilePath.c_str());
14232 }
14233
14234 mSSData->strStateFilePath.setNull();
14235 stsFlags |= SaveSTS_StateFilePath;
14236 }
14237
14238 /* redirect to the underlying peer machine */
14239 mPeer->i_setMachineState(aMachineState);
14240
14241 if ( aMachineState == MachineState_PoweredOff
14242 || aMachineState == MachineState_Teleported
14243 || aMachineState == MachineState_Aborted
14244 || aMachineState == MachineState_Saved)
14245 {
14246 /* the machine has stopped execution
14247 * (or the saved state file was adopted) */
14248 stsFlags |= SaveSTS_StateTimeStamp;
14249 }
14250
14251 if ( ( oldMachineState == MachineState_PoweredOff
14252 || oldMachineState == MachineState_Aborted
14253 || oldMachineState == MachineState_Teleported
14254 )
14255 && aMachineState == MachineState_Saved)
14256 {
14257 /* the saved state file was adopted */
14258 Assert(!mSSData->strStateFilePath.isEmpty());
14259 stsFlags |= SaveSTS_StateFilePath;
14260 }
14261
14262#ifdef VBOX_WITH_GUEST_PROPS
14263 if ( aMachineState == MachineState_PoweredOff
14264 || aMachineState == MachineState_Aborted
14265 || aMachineState == MachineState_Teleported)
14266 {
14267 /* Make sure any transient guest properties get removed from the
14268 * property store on shutdown. */
14269
14270 HWData::GuestPropertyMap::const_iterator it;
14271 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14272 if (!fNeedsSaving)
14273 for (it = mHWData->mGuestProperties.begin();
14274 it != mHWData->mGuestProperties.end(); ++it)
14275 if ( (it->second.mFlags & guestProp::TRANSIENT)
14276 || (it->second.mFlags & guestProp::TRANSRESET))
14277 {
14278 fNeedsSaving = true;
14279 break;
14280 }
14281 if (fNeedsSaving)
14282 {
14283 mData->mCurrentStateModified = TRUE;
14284 stsFlags |= SaveSTS_CurStateModified;
14285 }
14286 }
14287#endif
14288
14289 rc = i_saveStateSettings(stsFlags);
14290
14291 if ( ( oldMachineState != MachineState_PoweredOff
14292 && oldMachineState != MachineState_Aborted
14293 && oldMachineState != MachineState_Teleported
14294 )
14295 && ( aMachineState == MachineState_PoweredOff
14296 || aMachineState == MachineState_Aborted
14297 || aMachineState == MachineState_Teleported
14298 )
14299 )
14300 {
14301 /* we've been shut down for any reason */
14302 /* no special action so far */
14303 }
14304
14305 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14306 LogFlowThisFuncLeave();
14307 return rc;
14308}
14309
14310/**
14311 * Sends the current machine state value to the VM process.
14312 *
14313 * @note Locks this object for reading, then calls a client process.
14314 */
14315HRESULT SessionMachine::i_updateMachineStateOnClient()
14316{
14317 AutoCaller autoCaller(this);
14318 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14319
14320 ComPtr<IInternalSessionControl> directControl;
14321 {
14322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14323 AssertReturn(!!mData, E_FAIL);
14324 directControl = mData->mSession.mDirectControl;
14325
14326 /* directControl may be already set to NULL here in #OnSessionEnd()
14327 * called too early by the direct session process while there is still
14328 * some operation (like deleting the snapshot) in progress. The client
14329 * process in this case is waiting inside Session::close() for the
14330 * "end session" process object to complete, while #uninit() called by
14331 * #checkForDeath() on the Watcher thread is waiting for the pending
14332 * operation to complete. For now, we accept this inconsistent behavior
14333 * and simply do nothing here. */
14334
14335 if (mData->mSession.mState == SessionState_Unlocking)
14336 return S_OK;
14337
14338 AssertReturn(!directControl.isNull(), E_FAIL);
14339 }
14340
14341 return directControl->UpdateMachineState(mData->mMachineState);
14342}
14343
14344HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14345{
14346 NOREF(aRemove);
14347 ReturnComNotImplemented();
14348}
14349
14350HRESULT Machine::updateState(MachineState_T aState)
14351{
14352 NOREF(aState);
14353 ReturnComNotImplemented();
14354}
14355
14356HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14357{
14358 NOREF(aProgress);
14359 ReturnComNotImplemented();
14360}
14361
14362HRESULT Machine::endPowerUp(LONG aResult)
14363{
14364 NOREF(aResult);
14365 ReturnComNotImplemented();
14366}
14367
14368HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14369{
14370 NOREF(aProgress);
14371 ReturnComNotImplemented();
14372}
14373
14374HRESULT Machine::endPoweringDown(LONG aResult,
14375 const com::Utf8Str &aErrMsg)
14376{
14377 NOREF(aResult);
14378 NOREF(aErrMsg);
14379 ReturnComNotImplemented();
14380}
14381
14382HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14383 BOOL *aMatched,
14384 ULONG *aMaskedInterfaces)
14385{
14386 NOREF(aDevice);
14387 NOREF(aMatched);
14388 NOREF(aMaskedInterfaces);
14389 ReturnComNotImplemented();
14390
14391}
14392
14393HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14394{
14395 NOREF(aId);
14396 ReturnComNotImplemented();
14397}
14398
14399HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14400 BOOL aDone)
14401{
14402 NOREF(aId);
14403 NOREF(aDone);
14404 ReturnComNotImplemented();
14405}
14406
14407HRESULT Machine::autoCaptureUSBDevices()
14408{
14409 ReturnComNotImplemented();
14410}
14411
14412HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14413{
14414 NOREF(aDone);
14415 ReturnComNotImplemented();
14416}
14417
14418HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14419 ComPtr<IProgress> &aProgress)
14420{
14421 NOREF(aSession);
14422 NOREF(aProgress);
14423 ReturnComNotImplemented();
14424}
14425
14426HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14427 com::Utf8Str &aStateFilePath)
14428{
14429 NOREF(aProgress);
14430 NOREF(aStateFilePath);
14431 ReturnComNotImplemented();
14432}
14433
14434HRESULT Machine::endSavingState(LONG aResult,
14435 const com::Utf8Str &aErrMsg)
14436{
14437 NOREF(aResult);
14438 NOREF(aErrMsg);
14439 ReturnComNotImplemented();
14440}
14441
14442HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14443{
14444 NOREF(aSavedStateFile);
14445 ReturnComNotImplemented();
14446}
14447
14448HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14449 const com::Utf8Str &aName,
14450 const com::Utf8Str &aDescription,
14451 const ComPtr<IProgress> &aConsoleProgress,
14452 BOOL aFTakingSnapshotOnline,
14453 com::Utf8Str &aStateFilePath)
14454{
14455 NOREF(aInitiator);
14456 NOREF(aName);
14457 NOREF(aDescription);
14458 NOREF(aConsoleProgress);
14459 NOREF(aFTakingSnapshotOnline);
14460 NOREF(aStateFilePath);
14461 ReturnComNotImplemented();
14462}
14463
14464HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14465{
14466 NOREF(aSuccess);
14467 ReturnComNotImplemented();
14468}
14469
14470HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14471 const com::Guid &aStartId,
14472 const com::Guid &aEndId,
14473 BOOL aDeleteAllChildren,
14474 MachineState_T *aMachineState,
14475 ComPtr<IProgress> &aProgress)
14476{
14477 NOREF(aInitiator);
14478 NOREF(aStartId);
14479 NOREF(aEndId);
14480 NOREF(aDeleteAllChildren);
14481 NOREF(aMachineState);
14482 NOREF(aProgress);
14483 ReturnComNotImplemented();
14484}
14485
14486HRESULT Machine::finishOnlineMergeMedium()
14487{
14488 ReturnComNotImplemented();
14489}
14490
14491HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14492 const ComPtr<ISnapshot> &aSnapshot,
14493 MachineState_T *aMachineState,
14494 ComPtr<IProgress> &aProgress)
14495{
14496 NOREF(aInitiator);
14497 NOREF(aSnapshot);
14498 NOREF(aMachineState);
14499 NOREF(aProgress);
14500 ReturnComNotImplemented();
14501}
14502
14503HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14504 std::vector<com::Utf8Str> &aValues,
14505 std::vector<LONG64> &aTimestamps,
14506 std::vector<com::Utf8Str> &aFlags)
14507{
14508 NOREF(aNames);
14509 NOREF(aValues);
14510 NOREF(aTimestamps);
14511 NOREF(aFlags);
14512 ReturnComNotImplemented();
14513}
14514
14515HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14516 const com::Utf8Str &aValue,
14517 LONG64 aTimestamp,
14518 const com::Utf8Str &aFlags)
14519{
14520 NOREF(aName);
14521 NOREF(aValue);
14522 NOREF(aTimestamp);
14523 NOREF(aFlags);
14524 ReturnComNotImplemented();
14525}
14526
14527HRESULT Machine::lockMedia()
14528{
14529 ReturnComNotImplemented();
14530}
14531
14532HRESULT Machine::unlockMedia()
14533{
14534 ReturnComNotImplemented();
14535}
14536
14537HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14538 ComPtr<IMediumAttachment> &aNewAttachment)
14539{
14540 NOREF(aAttachment);
14541 NOREF(aNewAttachment);
14542 ReturnComNotImplemented();
14543}
14544
14545HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14546 ULONG aCpuUser,
14547 ULONG aCpuKernel,
14548 ULONG aCpuIdle,
14549 ULONG aMemTotal,
14550 ULONG aMemFree,
14551 ULONG aMemBalloon,
14552 ULONG aMemShared,
14553 ULONG aMemCache,
14554 ULONG aPagedTotal,
14555 ULONG aMemAllocTotal,
14556 ULONG aMemFreeTotal,
14557 ULONG aMemBalloonTotal,
14558 ULONG aMemSharedTotal,
14559 ULONG aVmNetRx,
14560 ULONG aVmNetTx)
14561{
14562 NOREF(aValidStats);
14563 NOREF(aCpuUser);
14564 NOREF(aCpuKernel);
14565 NOREF(aCpuIdle);
14566 NOREF(aMemTotal);
14567 NOREF(aMemFree);
14568 NOREF(aMemBalloon);
14569 NOREF(aMemShared);
14570 NOREF(aMemCache);
14571 NOREF(aPagedTotal);
14572 NOREF(aMemAllocTotal);
14573 NOREF(aMemFreeTotal);
14574 NOREF(aMemBalloonTotal);
14575 NOREF(aMemSharedTotal);
14576 NOREF(aVmNetRx);
14577 NOREF(aVmNetTx);
14578 ReturnComNotImplemented();
14579}
Note: See TracBrowser for help on using the repository browser.

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