VirtualBox

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

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

Main: safearray cleanup, removed unnecessary SafeArray<->vector conversions

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 497.8 KB
Line 
1/* $Id: MachineImpl.cpp 52934 2014-10-02 13:53:30Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "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
5356 RTDirRemove(logFolder.c_str());
5357 }
5358
5359 /* delete the Snapshots folder, nothing important should be left
5360 * there (we don't check for errors because the user might have
5361 * some private files there that we don't want to delete) */
5362 Utf8Str strFullSnapshotFolder;
5363 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5364 Assert(!strFullSnapshotFolder.isEmpty());
5365 if (RTDirExists(strFullSnapshotFolder.c_str()))
5366 RTDirRemove(strFullSnapshotFolder.c_str());
5367
5368 // delete the directory that contains the settings file, but only
5369 // if it matches the VM name
5370 Utf8Str settingsDir;
5371 if (i_isInOwnDir(&settingsDir))
5372 RTDirRemove(settingsDir.c_str());
5373 }
5374
5375 alock.release();
5376
5377 mParent->i_saveModifiedRegistries();
5378 }
5379 catch (HRESULT aRC) { rc = aRC; }
5380
5381 return rc;
5382}
5383
5384HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5385{
5386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 ComObjPtr<Snapshot> pSnapshot;
5389 HRESULT rc;
5390
5391 if (aNameOrId.isEmpty())
5392 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5393 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5394 else
5395 {
5396 Guid uuid(aNameOrId);
5397 if (uuid.isValid())
5398 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5399 else
5400 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5401 }
5402 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5403
5404 return rc;
5405}
5406
5407HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5408{
5409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5410
5411 HRESULT rc = i_checkStateDependency(MutableStateDep);
5412 if (FAILED(rc)) return rc;
5413
5414 ComObjPtr<SharedFolder> sharedFolder;
5415 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5416 if (SUCCEEDED(rc))
5417 return setError(VBOX_E_OBJECT_IN_USE,
5418 tr("Shared folder named '%s' already exists"),
5419 aName.c_str());
5420
5421 sharedFolder.createObject();
5422 rc = sharedFolder->init(i_getMachine(),
5423 aName,
5424 aHostPath,
5425 !!aWritable,
5426 !!aAutomount,
5427 true /* fFailOnError */);
5428 if (FAILED(rc)) return rc;
5429
5430 i_setModified(IsModified_SharedFolders);
5431 mHWData.backup();
5432 mHWData->mSharedFolders.push_back(sharedFolder);
5433
5434 /* inform the direct session if any */
5435 alock.release();
5436 i_onSharedFolderChange();
5437
5438 return S_OK;
5439}
5440
5441HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5442{
5443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5444
5445 HRESULT rc = i_checkStateDependency(MutableStateDep);
5446 if (FAILED(rc)) return rc;
5447
5448 ComObjPtr<SharedFolder> sharedFolder;
5449 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5450 if (FAILED(rc)) return rc;
5451
5452 i_setModified(IsModified_SharedFolders);
5453 mHWData.backup();
5454 mHWData->mSharedFolders.remove(sharedFolder);
5455
5456 /* inform the direct session if any */
5457 alock.release();
5458 i_onSharedFolderChange();
5459
5460 return S_OK;
5461}
5462
5463HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5464{
5465 /* start with No */
5466 *aCanShow = FALSE;
5467
5468 ComPtr<IInternalSessionControl> directControl;
5469 {
5470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5471
5472 if (mData->mSession.mState != SessionState_Locked)
5473 return setError(VBOX_E_INVALID_VM_STATE,
5474 tr("Machine is not locked for session (session state: %s)"),
5475 Global::stringifySessionState(mData->mSession.mState));
5476
5477 directControl = mData->mSession.mDirectControl;
5478 }
5479
5480 /* ignore calls made after #OnSessionEnd() is called */
5481 if (!directControl)
5482 return S_OK;
5483
5484 LONG64 dummy;
5485 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5486}
5487
5488HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5489{
5490 ComPtr<IInternalSessionControl> directControl;
5491 {
5492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 if (mData->mSession.mState != SessionState_Locked)
5495 return setError(E_FAIL,
5496 tr("Machine is not locked for session (session state: %s)"),
5497 Global::stringifySessionState(mData->mSession.mState));
5498
5499 directControl = mData->mSession.mDirectControl;
5500 }
5501
5502 /* ignore calls made after #OnSessionEnd() is called */
5503 if (!directControl)
5504 return S_OK;
5505
5506 BOOL dummy;
5507 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5508}
5509
5510#ifdef VBOX_WITH_GUEST_PROPS
5511/**
5512 * Look up a guest property in VBoxSVC's internal structures.
5513 */
5514HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5515 com::Utf8Str &aValue,
5516 LONG64 *aTimestamp,
5517 com::Utf8Str &aFlags) const
5518{
5519 using namespace guestProp;
5520
5521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5522 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5523
5524 if (it != mHWData->mGuestProperties.end())
5525 {
5526 char szFlags[MAX_FLAGS_LEN + 1];
5527 aValue = it->second.strValue;
5528 *aTimestamp = it->second.mTimestamp;
5529 writeFlags(it->second.mFlags, szFlags);
5530 aFlags = Utf8Str(szFlags);
5531 }
5532
5533 return S_OK;
5534}
5535
5536/**
5537 * Query the VM that a guest property belongs to for the property.
5538 * @returns E_ACCESSDENIED if the VM process is not available or not
5539 * currently handling queries and the lookup should then be done in
5540 * VBoxSVC.
5541 */
5542HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5543 com::Utf8Str &aValue,
5544 LONG64 *aTimestamp,
5545 com::Utf8Str &aFlags) const
5546{
5547 HRESULT rc = S_OK;
5548 BSTR bValue = NULL;
5549 BSTR bFlags = NULL;
5550
5551 ComPtr<IInternalSessionControl> directControl;
5552 directControl = mData->mSession.mDirectControl;
5553
5554 /* fail if we were called after #OnSessionEnd() is called. This is a
5555 * silly race condition. */
5556
5557 /** @todo This code is bothering API clients (like python script clients) with
5558 * the AccessGuestProperty call, creating unncessary IPC. Need to
5559 * have a way of figuring out which kind of direct session it is... */
5560 if (!directControl)
5561 rc = E_ACCESSDENIED;
5562 else
5563 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5564 0 /* accessMode */,
5565 &bValue, aTimestamp, &bFlags);
5566
5567 aValue = bValue;
5568 aFlags = bFlags;
5569
5570 return rc;
5571}
5572#endif // VBOX_WITH_GUEST_PROPS
5573
5574HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5575 com::Utf8Str &aValue,
5576 LONG64 *aTimestamp,
5577 com::Utf8Str &aFlags)
5578{
5579#ifndef VBOX_WITH_GUEST_PROPS
5580 ReturnComNotImplemented();
5581#else // VBOX_WITH_GUEST_PROPS
5582
5583 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5584
5585 if (rc == E_ACCESSDENIED)
5586 /* The VM is not running or the service is not (yet) accessible */
5587 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5588 return rc;
5589#endif // VBOX_WITH_GUEST_PROPS
5590}
5591
5592HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5593{
5594 LONG64 dummyTimestamp;
5595 com::Utf8Str dummyFlags;
5596 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5597 return rc;
5598
5599}
5600HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5601{
5602 com::Utf8Str dummyFlags;
5603 com::Utf8Str dummyValue;
5604 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5605 return rc;
5606}
5607
5608#ifdef VBOX_WITH_GUEST_PROPS
5609/**
5610 * Set a guest property in VBoxSVC's internal structures.
5611 */
5612HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5613 const com::Utf8Str &aFlags, bool fDelete)
5614{
5615 using namespace guestProp;
5616
5617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5618 HRESULT rc = S_OK;
5619
5620 rc = i_checkStateDependency(MutableStateDep);
5621 if (FAILED(rc)) return rc;
5622
5623 try
5624 {
5625 uint32_t fFlags = NILFLAG;
5626 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5627 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5628
5629 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5630 if (it == mHWData->mGuestProperties.end())
5631 {
5632 if (!fDelete)
5633 {
5634 i_setModified(IsModified_MachineData);
5635 mHWData.backupEx();
5636
5637 RTTIMESPEC time;
5638 HWData::GuestProperty prop;
5639 prop.strValue = Bstr(aValue).raw();
5640 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5641 prop.mFlags = fFlags;
5642 mHWData->mGuestProperties[aName] = prop;
5643 }
5644 }
5645 else
5646 {
5647 if (it->second.mFlags & (RDONLYHOST))
5648 {
5649 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5650 }
5651 else
5652 {
5653 i_setModified(IsModified_MachineData);
5654 mHWData.backupEx();
5655
5656 /* The backupEx() operation invalidates our iterator,
5657 * so get a new one. */
5658 it = mHWData->mGuestProperties.find(aName);
5659 Assert(it != mHWData->mGuestProperties.end());
5660
5661 if (!fDelete)
5662 {
5663 RTTIMESPEC time;
5664 it->second.strValue = aValue;
5665 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5666 it->second.mFlags = fFlags;
5667 }
5668 else
5669 mHWData->mGuestProperties.erase(it);
5670 }
5671 }
5672
5673 if ( SUCCEEDED(rc)
5674 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5675 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5676 RTSTR_MAX,
5677 aName.c_str(),
5678 RTSTR_MAX,
5679 NULL)
5680 )
5681 )
5682 {
5683 alock.release();
5684
5685 mParent->i_onGuestPropertyChange(mData->mUuid,
5686 Bstr(aName).raw(),
5687 Bstr(aValue).raw(),
5688 Bstr(aFlags).raw());
5689 }
5690 }
5691 catch (std::bad_alloc &)
5692 {
5693 rc = E_OUTOFMEMORY;
5694 }
5695
5696 return rc;
5697}
5698
5699/**
5700 * Set a property on the VM that that property belongs to.
5701 * @returns E_ACCESSDENIED if the VM process is not available or not
5702 * currently handling queries and the setting should then be done in
5703 * VBoxSVC.
5704 */
5705HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5706 const com::Utf8Str &aFlags, bool fDelete)
5707{
5708 HRESULT rc;
5709
5710 try
5711 {
5712 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5713
5714 BSTR dummy = NULL; /* will not be changed (setter) */
5715 LONG64 dummy64;
5716 if (!directControl)
5717 rc = E_ACCESSDENIED;
5718 else
5719 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5720 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5721 fDelete? 2: 1 /* accessMode */,
5722 &dummy, &dummy64, &dummy);
5723 }
5724 catch (std::bad_alloc &)
5725 {
5726 rc = E_OUTOFMEMORY;
5727 }
5728
5729 return rc;
5730}
5731#endif // VBOX_WITH_GUEST_PROPS
5732
5733HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5734 const com::Utf8Str &aFlags)
5735{
5736#ifndef VBOX_WITH_GUEST_PROPS
5737 ReturnComNotImplemented();
5738#else // VBOX_WITH_GUEST_PROPS
5739 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5740 if (rc == E_ACCESSDENIED)
5741 /* The VM is not running or the service is not (yet) accessible */
5742 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5743 return rc;
5744#endif // VBOX_WITH_GUEST_PROPS
5745}
5746
5747HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5748{
5749 return setGuestProperty(aProperty, aValue, "");
5750}
5751
5752HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5753{
5754#ifndef VBOX_WITH_GUEST_PROPS
5755 ReturnComNotImplemented();
5756#else // VBOX_WITH_GUEST_PROPS
5757 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5758 if (rc == E_ACCESSDENIED)
5759 /* The VM is not running or the service is not (yet) accessible */
5760 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5761 return rc;
5762#endif // VBOX_WITH_GUEST_PROPS
5763}
5764
5765#ifdef VBOX_WITH_GUEST_PROPS
5766/**
5767 * Enumerate the guest properties in VBoxSVC's internal structures.
5768 */
5769HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5770 std::vector<com::Utf8Str> &aNames,
5771 std::vector<com::Utf8Str> &aValues,
5772 std::vector<LONG64> &aTimestamps,
5773 std::vector<com::Utf8Str> &aFlags)
5774{
5775 using namespace guestProp;
5776
5777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5778 Utf8Str strPatterns(aPatterns);
5779
5780 HWData::GuestPropertyMap propMap;
5781
5782 /*
5783 * Look for matching patterns and build up a list.
5784 */
5785 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5786 while (it != mHWData->mGuestProperties.end())
5787 {
5788 if ( strPatterns.isEmpty()
5789 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5790 RTSTR_MAX,
5791 it->first.c_str(),
5792 RTSTR_MAX,
5793 NULL)
5794 )
5795 propMap.insert(*it);
5796 it++;
5797 }
5798
5799 alock.release();
5800
5801 /*
5802 * And build up the arrays for returning the property information.
5803 */
5804 size_t cEntries = propMap.size();
5805
5806 aNames.resize(cEntries);
5807 aValues.resize(cEntries);
5808 aTimestamps.resize(cEntries);
5809 aFlags.resize(cEntries);
5810
5811 char szFlags[MAX_FLAGS_LEN + 1];
5812 size_t i= 0;
5813 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5814 {
5815 aNames[i] = it->first;
5816 aValues[i] = it->second.strValue;
5817 aTimestamps[i] = it->second.mTimestamp;
5818 writeFlags(it->second.mFlags, szFlags);
5819 aFlags[i] = Utf8Str(szFlags);
5820 }
5821
5822 return S_OK;
5823}
5824
5825/**
5826 * Enumerate the properties managed by a VM.
5827 * @returns E_ACCESSDENIED if the VM process is not available or not
5828 * currently handling queries and the setting should then be done in
5829 * VBoxSVC.
5830 */
5831HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5832 std::vector<com::Utf8Str> &aNames,
5833 std::vector<com::Utf8Str> &aValues,
5834 std::vector<LONG64> &aTimestamps,
5835 std::vector<com::Utf8Str> &aFlags)
5836{
5837 HRESULT rc;
5838 ComPtr<IInternalSessionControl> directControl;
5839 directControl = mData->mSession.mDirectControl;
5840
5841
5842 com::SafeArray<BSTR> bNames;
5843 com::SafeArray<BSTR> bValues;
5844 com::SafeArray<LONG64> bTimestamps;
5845 com::SafeArray<BSTR> bFlags;
5846
5847 if (!directControl)
5848 rc = E_ACCESSDENIED;
5849 else
5850 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5851 ComSafeArrayAsOutParam(bNames),
5852 ComSafeArrayAsOutParam(bValues),
5853 ComSafeArrayAsOutParam(bTimestamps),
5854 ComSafeArrayAsOutParam(bFlags));
5855 size_t i;
5856 aNames.resize(bNames.size());
5857 for (i = 0; i < bNames.size(); ++i)
5858 aNames[i] = Utf8Str(bNames[i]);
5859 aValues.resize(bValues.size());
5860 for (i = 0; i < bValues.size(); ++i)
5861 aValues[i] = Utf8Str(bValues[i]);
5862 aTimestamps.resize(bTimestamps.size());
5863 for (i = 0; i < bTimestamps.size(); ++i)
5864 aTimestamps[i] = bTimestamps[i];
5865 aFlags.resize(bFlags.size());
5866 for (i = 0; i < bFlags.size(); ++i)
5867 aFlags[i] = Utf8Str(bFlags[i]);
5868
5869 return rc;
5870}
5871#endif // VBOX_WITH_GUEST_PROPS
5872HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5873 std::vector<com::Utf8Str> &aNames,
5874 std::vector<com::Utf8Str> &aValues,
5875 std::vector<LONG64> &aTimestamps,
5876 std::vector<com::Utf8Str> &aFlags)
5877{
5878#ifndef VBOX_WITH_GUEST_PROPS
5879 ReturnComNotImplemented();
5880#else // VBOX_WITH_GUEST_PROPS
5881
5882 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5883
5884 if (rc == E_ACCESSDENIED)
5885 /* The VM is not running or the service is not (yet) accessible */
5886 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5887 return rc;
5888#endif // VBOX_WITH_GUEST_PROPS
5889}
5890
5891HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5892 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5893{
5894 MediaData::AttachmentList atts;
5895
5896 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5897 if (FAILED(rc)) return rc;
5898
5899 size_t i = 0;
5900 aMediumAttachments.resize(atts.size());
5901 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5902 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5903
5904 return S_OK;
5905}
5906
5907HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5908 LONG aControllerPort,
5909 LONG aDevice,
5910 ComPtr<IMediumAttachment> &aAttachment)
5911{
5912 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5913 aName.c_str(), aControllerPort, aDevice));
5914
5915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5916
5917 aAttachment = NULL;
5918
5919 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5920 Bstr(aName).raw(),
5921 aControllerPort,
5922 aDevice);
5923 if (pAttach.isNull())
5924 return setError(VBOX_E_OBJECT_NOT_FOUND,
5925 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5926 aDevice, aControllerPort, aName.c_str());
5927
5928 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5929
5930 return S_OK;
5931}
5932
5933
5934HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5935 StorageBus_T aConnectionType,
5936 ComPtr<IStorageController> &aController)
5937{
5938 if ( (aConnectionType <= StorageBus_Null)
5939 || (aConnectionType > StorageBus_USB))
5940 return setError(E_INVALIDARG,
5941 tr("Invalid connection type: %d"),
5942 aConnectionType);
5943
5944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5945
5946 HRESULT rc = i_checkStateDependency(MutableStateDep);
5947 if (FAILED(rc)) return rc;
5948
5949 /* try to find one with the name first. */
5950 ComObjPtr<StorageController> ctrl;
5951
5952 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5953 if (SUCCEEDED(rc))
5954 return setError(VBOX_E_OBJECT_IN_USE,
5955 tr("Storage controller named '%s' already exists"),
5956 aName.c_str());
5957
5958 ctrl.createObject();
5959
5960 /* get a new instance number for the storage controller */
5961 ULONG ulInstance = 0;
5962 bool fBootable = true;
5963 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5964 it != mStorageControllers->end();
5965 ++it)
5966 {
5967 if ((*it)->i_getStorageBus() == aConnectionType)
5968 {
5969 ULONG ulCurInst = (*it)->i_getInstance();
5970
5971 if (ulCurInst >= ulInstance)
5972 ulInstance = ulCurInst + 1;
5973
5974 /* Only one controller of each type can be marked as bootable. */
5975 if ((*it)->i_getBootable())
5976 fBootable = false;
5977 }
5978 }
5979
5980 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5981 if (FAILED(rc)) return rc;
5982
5983 i_setModified(IsModified_Storage);
5984 mStorageControllers.backup();
5985 mStorageControllers->push_back(ctrl);
5986
5987 ctrl.queryInterfaceTo(aController.asOutParam());
5988
5989 /* inform the direct session if any */
5990 alock.release();
5991 i_onStorageControllerChange();
5992
5993 return S_OK;
5994}
5995
5996HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5997 ComPtr<IStorageController> &aStorageController)
5998{
5999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6000
6001 ComObjPtr<StorageController> ctrl;
6002
6003 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6004 if (SUCCEEDED(rc))
6005 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6006
6007 return rc;
6008}
6009
6010HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6011 ComPtr<IStorageController> &aStorageController)
6012{
6013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6014
6015 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6016 it != mStorageControllers->end();
6017 ++it)
6018 {
6019 if ((*it)->i_getInstance() == aInstance)
6020 {
6021 (*it).queryInterfaceTo(aStorageController.asOutParam());
6022 return S_OK;
6023 }
6024 }
6025
6026 return setError(VBOX_E_OBJECT_NOT_FOUND,
6027 tr("Could not find a storage controller with instance number '%lu'"),
6028 aInstance);
6029}
6030
6031HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6032{
6033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 HRESULT rc = i_checkStateDependency(MutableStateDep);
6036 if (FAILED(rc)) return rc;
6037
6038 ComObjPtr<StorageController> ctrl;
6039
6040 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6041 if (SUCCEEDED(rc))
6042 {
6043 /* Ensure that only one controller of each type is marked as bootable. */
6044 if (aBootable == TRUE)
6045 {
6046 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6047 it != mStorageControllers->end();
6048 ++it)
6049 {
6050 ComObjPtr<StorageController> aCtrl = (*it);
6051
6052 if ( (aCtrl->i_getName() != aName)
6053 && aCtrl->i_getBootable() == TRUE
6054 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6055 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6056 {
6057 aCtrl->i_setBootable(FALSE);
6058 break;
6059 }
6060 }
6061 }
6062
6063 if (SUCCEEDED(rc))
6064 {
6065 ctrl->i_setBootable(aBootable);
6066 i_setModified(IsModified_Storage);
6067 }
6068 }
6069
6070 if (SUCCEEDED(rc))
6071 {
6072 /* inform the direct session if any */
6073 alock.release();
6074 i_onStorageControllerChange();
6075 }
6076
6077 return rc;
6078}
6079
6080HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6081{
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 HRESULT rc = i_checkStateDependency(MutableStateDep);
6085 if (FAILED(rc)) return rc;
6086
6087 ComObjPtr<StorageController> ctrl;
6088 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6089 if (FAILED(rc)) return rc;
6090
6091 {
6092 /* find all attached devices to the appropriate storage controller and detach them all */
6093 // make a temporary list because detachDevice invalidates iterators into
6094 // mMediaData->mAttachments
6095 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6096
6097 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6098 it != llAttachments2.end();
6099 ++it)
6100 {
6101 MediumAttachment *pAttachTemp = *it;
6102
6103 AutoCaller localAutoCaller(pAttachTemp);
6104 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6105
6106 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6107
6108 if (pAttachTemp->i_getControllerName() == aName)
6109 {
6110 rc = i_detachDevice(pAttachTemp, alock, NULL);
6111 if (FAILED(rc)) return rc;
6112 }
6113 }
6114 }
6115
6116 /* We can remove it now. */
6117 i_setModified(IsModified_Storage);
6118 mStorageControllers.backup();
6119
6120 ctrl->i_unshare();
6121
6122 mStorageControllers->remove(ctrl);
6123
6124 /* inform the direct session if any */
6125 alock.release();
6126 i_onStorageControllerChange();
6127
6128 return S_OK;
6129}
6130
6131HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6132 ComPtr<IUSBController> &aController)
6133{
6134 if ( (aType <= USBControllerType_Null)
6135 || (aType >= USBControllerType_Last))
6136 return setError(E_INVALIDARG,
6137 tr("Invalid USB controller type: %d"),
6138 aType);
6139
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 /* try to find one with the same type first. */
6146 ComObjPtr<USBController> ctrl;
6147
6148 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6149 if (SUCCEEDED(rc))
6150 return setError(VBOX_E_OBJECT_IN_USE,
6151 tr("USB controller named '%s' already exists"),
6152 aName.c_str());
6153
6154 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6155 ULONG maxInstances;
6156 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6157 if (FAILED(rc))
6158 return rc;
6159
6160 ULONG cInstances = i_getUSBControllerCountByType(aType);
6161 if (cInstances >= maxInstances)
6162 return setError(E_INVALIDARG,
6163 tr("Too many USB controllers of this type"));
6164
6165 ctrl.createObject();
6166
6167 rc = ctrl->init(this, aName, aType);
6168 if (FAILED(rc)) return rc;
6169
6170 i_setModified(IsModified_USB);
6171 mUSBControllers.backup();
6172 mUSBControllers->push_back(ctrl);
6173
6174 ctrl.queryInterfaceTo(aController.asOutParam());
6175
6176 /* inform the direct session if any */
6177 alock.release();
6178 i_onUSBControllerChange();
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6184{
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 ComObjPtr<USBController> ctrl;
6188
6189 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6190 if (SUCCEEDED(rc))
6191 ctrl.queryInterfaceTo(aController.asOutParam());
6192
6193 return rc;
6194}
6195
6196HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6197 ULONG *aControllers)
6198{
6199 if ( (aType <= USBControllerType_Null)
6200 || (aType >= USBControllerType_Last))
6201 return setError(E_INVALIDARG,
6202 tr("Invalid USB controller type: %d"),
6203 aType);
6204
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 ComObjPtr<USBController> ctrl;
6208
6209 *aControllers = i_getUSBControllerCountByType(aType);
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6215{
6216
6217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6218
6219 HRESULT rc = i_checkStateDependency(MutableStateDep);
6220 if (FAILED(rc)) return rc;
6221
6222 ComObjPtr<USBController> ctrl;
6223 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6224 if (FAILED(rc)) return rc;
6225
6226 i_setModified(IsModified_USB);
6227 mUSBControllers.backup();
6228
6229 ctrl->i_unshare();
6230
6231 mUSBControllers->remove(ctrl);
6232
6233 /* inform the direct session if any */
6234 alock.release();
6235 i_onUSBControllerChange();
6236
6237 return S_OK;
6238}
6239
6240HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6241 ULONG *aOriginX,
6242 ULONG *aOriginY,
6243 ULONG *aWidth,
6244 ULONG *aHeight,
6245 BOOL *aEnabled)
6246{
6247 uint32_t u32OriginX= 0;
6248 uint32_t u32OriginY= 0;
6249 uint32_t u32Width = 0;
6250 uint32_t u32Height = 0;
6251 uint16_t u16Flags = 0;
6252
6253 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6254 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6255 if (RT_FAILURE(vrc))
6256 {
6257#ifdef RT_OS_WINDOWS
6258 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6259 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6260 * So just assign fEnable to TRUE again.
6261 * The right fix would be to change GUI API wrappers to make sure that parameters
6262 * are changed only if API succeeds.
6263 */
6264 *aEnabled = TRUE;
6265#endif
6266 return setError(VBOX_E_IPRT_ERROR,
6267 tr("Saved guest size is not available (%Rrc)"),
6268 vrc);
6269 }
6270
6271 *aOriginX = u32OriginX;
6272 *aOriginY = u32OriginY;
6273 *aWidth = u32Width;
6274 *aHeight = u32Height;
6275 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6276
6277 return S_OK;
6278}
6279
6280HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6281{
6282 if (aScreenId != 0)
6283 return E_NOTIMPL;
6284
6285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6286
6287 uint8_t *pu8Data = NULL;
6288 uint32_t cbData = 0;
6289 uint32_t u32Width = 0;
6290 uint32_t u32Height = 0;
6291
6292 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6293
6294 if (RT_FAILURE(vrc))
6295 return setError(VBOX_E_IPRT_ERROR,
6296 tr("Saved screenshot data is not available (%Rrc)"),
6297 vrc);
6298
6299 *aSize = cbData;
6300 *aWidth = u32Width;
6301 *aHeight = u32Height;
6302
6303 freeSavedDisplayScreenshot(pu8Data);
6304
6305 return S_OK;
6306}
6307
6308
6309HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6310{
6311 if (aScreenId != 0)
6312 return E_NOTIMPL;
6313
6314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6315
6316 uint8_t *pu8Data = NULL;
6317 uint32_t cbData = 0;
6318 uint32_t u32Width = 0;
6319 uint32_t u32Height = 0;
6320
6321 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6322
6323 if (RT_FAILURE(vrc))
6324 return setError(VBOX_E_IPRT_ERROR,
6325 tr("Saved screenshot data is not available (%Rrc)"),
6326 vrc);
6327
6328 *aWidth = u32Width;
6329 *aHeight = u32Height;
6330
6331 aData.resize(cbData);
6332 /* Convert pixels to format expected by the API caller. */
6333 if (aBGR)
6334 {
6335 /* [0] B, [1] G, [2] R, [3] A. */
6336 for (unsigned i = 0; i < cbData; i += 4)
6337 {
6338 aData[i] = pu8Data[i];
6339 aData[i + 1] = pu8Data[i + 1];
6340 aData[i + 2] = pu8Data[i + 2];
6341 aData[i + 3] = 0xff;
6342 }
6343 }
6344 else
6345 {
6346 /* [0] R, [1] G, [2] B, [3] A. */
6347 for (unsigned i = 0; i < cbData; i += 4)
6348 {
6349 aData[i] = pu8Data[i + 2];
6350 aData[i + 1] = pu8Data[i + 1];
6351 aData[i + 2] = pu8Data[i];
6352 aData[i + 3] = 0xff;
6353 }
6354 }
6355
6356 freeSavedDisplayScreenshot(pu8Data);
6357
6358 return S_OK;
6359}
6360
6361HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6362{
6363 if (aScreenId != 0)
6364 return E_NOTIMPL;
6365
6366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6367
6368 uint8_t *pu8Data = NULL;
6369 uint32_t cbData = 0;
6370 uint32_t u32Width = 0;
6371 uint32_t u32Height = 0;
6372
6373 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6374
6375 if (RT_FAILURE(vrc))
6376 return setError(VBOX_E_IPRT_ERROR,
6377 tr("Saved screenshot data is not available (%Rrc)"),
6378 vrc);
6379
6380 *aWidth = u32Width;
6381 *aHeight = u32Height;
6382
6383 HRESULT rc = S_OK;
6384 uint8_t *pu8PNG = NULL;
6385 uint32_t cbPNG = 0;
6386 uint32_t cxPNG = 0;
6387 uint32_t cyPNG = 0;
6388
6389 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6390
6391 if (RT_SUCCESS(vrc))
6392 {
6393 aData.resize(cbPNG);
6394 if (cbPNG)
6395 memcpy(&aData.front(), pu8PNG, cbPNG);
6396 if (pu8PNG)
6397 RTMemFree(pu8PNG);
6398 }
6399 else
6400 {
6401 if (pu8PNG)
6402 RTMemFree(pu8PNG);
6403 return setError(VBOX_E_IPRT_ERROR,
6404 tr("Could not convert screenshot to PNG (%Rrc)"),
6405 vrc);
6406 }
6407
6408 freeSavedDisplayScreenshot(pu8Data);
6409
6410 return rc;
6411}
6412
6413HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6414{
6415 if (aScreenId != 0)
6416 return E_NOTIMPL;
6417
6418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6419
6420 uint8_t *pu8Data = NULL;
6421 uint32_t cbData = 0;
6422 uint32_t u32Width = 0;
6423 uint32_t u32Height = 0;
6424
6425 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6426
6427 if (RT_FAILURE(vrc))
6428 return setError(VBOX_E_IPRT_ERROR,
6429 tr("Saved screenshot data is not available (%Rrc)"),
6430 vrc);
6431
6432 *aSize = cbData;
6433 *aWidth = u32Width;
6434 *aHeight = u32Height;
6435
6436 freeSavedDisplayScreenshot(pu8Data);
6437
6438 return S_OK;
6439}
6440
6441HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6442{
6443 if (aScreenId != 0)
6444 return E_NOTIMPL;
6445
6446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6447
6448 uint8_t *pu8Data = NULL;
6449 uint32_t cbData = 0;
6450 uint32_t u32Width = 0;
6451 uint32_t u32Height = 0;
6452
6453 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6454
6455 if (RT_FAILURE(vrc))
6456 return setError(VBOX_E_IPRT_ERROR,
6457 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6458 vrc);
6459
6460 *aWidth = u32Width;
6461 *aHeight = u32Height;
6462
6463 aData.resize(cbData);
6464 if (cbData)
6465 memcpy(&aData.front(), pu8Data, cbData);
6466
6467 freeSavedDisplayScreenshot(pu8Data);
6468
6469 return S_OK;
6470}
6471
6472HRESULT Machine::hotPlugCPU(ULONG aCpu)
6473{
6474 HRESULT rc = S_OK;
6475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6476
6477 if (!mHWData->mCPUHotPlugEnabled)
6478 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6479
6480 if (aCpu >= mHWData->mCPUCount)
6481 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6482
6483 if (mHWData->mCPUAttached[aCpu])
6484 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6485
6486 alock.release();
6487 rc = i_onCPUChange(aCpu, false);
6488 alock.acquire();
6489 if (FAILED(rc)) return rc;
6490
6491 i_setModified(IsModified_MachineData);
6492 mHWData.backup();
6493 mHWData->mCPUAttached[aCpu] = true;
6494
6495 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6496 if (Global::IsOnline(mData->mMachineState))
6497 i_saveSettings(NULL);
6498
6499 return S_OK;
6500}
6501
6502HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6503{
6504 HRESULT rc = S_OK;
6505
6506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6507
6508 if (!mHWData->mCPUHotPlugEnabled)
6509 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6510
6511 if (aCpu >= SchemaDefs::MaxCPUCount)
6512 return setError(E_INVALIDARG,
6513 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6514 SchemaDefs::MaxCPUCount);
6515
6516 if (!mHWData->mCPUAttached[aCpu])
6517 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6518
6519 /* CPU 0 can't be detached */
6520 if (aCpu == 0)
6521 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6522
6523 alock.release();
6524 rc = i_onCPUChange(aCpu, true);
6525 alock.acquire();
6526 if (FAILED(rc)) return rc;
6527
6528 i_setModified(IsModified_MachineData);
6529 mHWData.backup();
6530 mHWData->mCPUAttached[aCpu] = false;
6531
6532 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6533 if (Global::IsOnline(mData->mMachineState))
6534 i_saveSettings(NULL);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6540{
6541 *aAttached = false;
6542
6543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6544
6545 /* If hotplug is enabled the CPU is always enabled. */
6546 if (!mHWData->mCPUHotPlugEnabled)
6547 {
6548 if (aCpu < mHWData->mCPUCount)
6549 *aAttached = true;
6550 }
6551 else
6552 {
6553 if (aCpu < SchemaDefs::MaxCPUCount)
6554 *aAttached = mHWData->mCPUAttached[aCpu];
6555 }
6556
6557 return S_OK;
6558}
6559
6560HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6561{
6562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6563
6564 Utf8Str log = i_queryLogFilename(aIdx);
6565 if (!RTFileExists(log.c_str()))
6566 log.setNull();
6567 aFilename = log;
6568
6569 return S_OK;
6570}
6571
6572HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6573{
6574 if (aSize < 0)
6575 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6576
6577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 HRESULT rc = S_OK;
6580 Utf8Str log = i_queryLogFilename(aIdx);
6581
6582 /* do not unnecessarily hold the lock while doing something which does
6583 * not need the lock and potentially takes a long time. */
6584 alock.release();
6585
6586 /* Limit the chunk size to 32K for now, as that gives better performance
6587 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6588 * One byte expands to approx. 25 bytes of breathtaking XML. */
6589 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6590 aData.resize(cbData);
6591
6592 RTFILE LogFile;
6593 int vrc = RTFileOpen(&LogFile, log.c_str(),
6594 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6595 if (RT_SUCCESS(vrc))
6596 {
6597 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6598 if (RT_SUCCESS(vrc))
6599 aData.resize(cbData);
6600 else
6601 rc = setError(VBOX_E_IPRT_ERROR,
6602 tr("Could not read log file '%s' (%Rrc)"),
6603 log.c_str(), vrc);
6604 RTFileClose(LogFile);
6605 }
6606 else
6607 rc = setError(VBOX_E_IPRT_ERROR,
6608 tr("Could not open log file '%s' (%Rrc)"),
6609 log.c_str(), vrc);
6610
6611 if (FAILED(rc))
6612 aData.resize(0);
6613
6614 return rc;
6615}
6616
6617
6618/**
6619 * Currently this method doesn't attach device to the running VM,
6620 * just makes sure it's plugged on next VM start.
6621 */
6622HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6623{
6624 // lock scope
6625 {
6626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6627
6628 HRESULT rc = i_checkStateDependency(MutableStateDep);
6629 if (FAILED(rc)) return rc;
6630
6631 ChipsetType_T aChipset = ChipsetType_PIIX3;
6632 COMGETTER(ChipsetType)(&aChipset);
6633
6634 if (aChipset != ChipsetType_ICH9)
6635 {
6636 return setError(E_INVALIDARG,
6637 tr("Host PCI attachment only supported with ICH9 chipset"));
6638 }
6639
6640 // check if device with this host PCI address already attached
6641 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6642 it != mHWData->mPCIDeviceAssignments.end();
6643 ++it)
6644 {
6645 LONG iHostAddress = -1;
6646 ComPtr<PCIDeviceAttachment> pAttach;
6647 pAttach = *it;
6648 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6649 if (iHostAddress == aHostAddress)
6650 return setError(E_INVALIDARG,
6651 tr("Device with host PCI address already attached to this VM"));
6652 }
6653
6654 ComObjPtr<PCIDeviceAttachment> pda;
6655 char name[32];
6656
6657 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6658 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6659 Bstr bname(name);
6660 pda.createObject();
6661 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6662 i_setModified(IsModified_MachineData);
6663 mHWData.backup();
6664 mHWData->mPCIDeviceAssignments.push_back(pda);
6665 }
6666
6667 return S_OK;
6668}
6669
6670/**
6671 * Currently this method doesn't detach device from the running VM,
6672 * just makes sure it's not plugged on next VM start.
6673 */
6674HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6675{
6676 ComObjPtr<PCIDeviceAttachment> pAttach;
6677 bool fRemoved = false;
6678 HRESULT rc;
6679
6680 // lock scope
6681 {
6682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 rc = i_checkStateDependency(MutableStateDep);
6685 if (FAILED(rc)) return rc;
6686
6687 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6688 it != mHWData->mPCIDeviceAssignments.end();
6689 ++it)
6690 {
6691 LONG iHostAddress = -1;
6692 pAttach = *it;
6693 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6694 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6695 {
6696 i_setModified(IsModified_MachineData);
6697 mHWData.backup();
6698 mHWData->mPCIDeviceAssignments.remove(pAttach);
6699 fRemoved = true;
6700 break;
6701 }
6702 }
6703 }
6704
6705
6706 /* Fire event outside of the lock */
6707 if (fRemoved)
6708 {
6709 Assert(!pAttach.isNull());
6710 ComPtr<IEventSource> es;
6711 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6712 Assert(SUCCEEDED(rc));
6713 Bstr mid;
6714 rc = this->COMGETTER(Id)(mid.asOutParam());
6715 Assert(SUCCEEDED(rc));
6716 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6717 }
6718
6719 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6720 tr("No host PCI device %08x attached"),
6721 aHostAddress
6722 );
6723}
6724
6725HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6726{
6727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6728
6729 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6730
6731 size_t i = 0;
6732 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6733 it != mHWData->mPCIDeviceAssignments.end();
6734 ++i, ++it)
6735 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6736
6737 return S_OK;
6738}
6739
6740HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6741{
6742 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6743
6744 return S_OK;
6745}
6746
6747HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6748{
6749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6750
6751 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6752
6753 return S_OK;
6754}
6755
6756HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6757{
6758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6759 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6760 if (SUCCEEDED(hrc))
6761 {
6762 hrc = mHWData.backupEx();
6763 if (SUCCEEDED(hrc))
6764 {
6765 i_setModified(IsModified_MachineData);
6766 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6767 }
6768 }
6769 return hrc;
6770}
6771
6772HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6773{
6774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6775 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6776 return S_OK;
6777}
6778
6779HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6780{
6781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6782 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6783 if (SUCCEEDED(hrc))
6784 {
6785 hrc = mHWData.backupEx();
6786 if (SUCCEEDED(hrc))
6787 {
6788 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6789 if (SUCCEEDED(hrc))
6790 i_setModified(IsModified_MachineData);
6791 }
6792 }
6793 return hrc;
6794}
6795
6796HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6797{
6798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6799
6800 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6801
6802 return S_OK;
6803}
6804
6805HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6806{
6807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6809 if (SUCCEEDED(hrc))
6810 {
6811 hrc = mHWData.backupEx();
6812 if (SUCCEEDED(hrc))
6813 {
6814 i_setModified(IsModified_MachineData);
6815 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6816 }
6817 }
6818 return hrc;
6819}
6820
6821HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6822{
6823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6824
6825 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6826
6827 return S_OK;
6828}
6829
6830HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6831{
6832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6833
6834 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6835 if ( SUCCEEDED(hrc)
6836 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6837 {
6838 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6839 int vrc;
6840
6841 if (aAutostartEnabled)
6842 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6843 else
6844 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6845
6846 if (RT_SUCCESS(vrc))
6847 {
6848 hrc = mHWData.backupEx();
6849 if (SUCCEEDED(hrc))
6850 {
6851 i_setModified(IsModified_MachineData);
6852 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6853 }
6854 }
6855 else if (vrc == VERR_NOT_SUPPORTED)
6856 hrc = setError(VBOX_E_NOT_SUPPORTED,
6857 tr("The VM autostart feature is not supported on this platform"));
6858 else if (vrc == VERR_PATH_NOT_FOUND)
6859 hrc = setError(E_FAIL,
6860 tr("The path to the autostart database is not set"));
6861 else
6862 hrc = setError(E_UNEXPECTED,
6863 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6864 aAutostartEnabled ? "Adding" : "Removing",
6865 mUserData->s.strName.c_str(), vrc);
6866 }
6867 return hrc;
6868}
6869
6870HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6871{
6872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6873
6874 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6875
6876 return S_OK;
6877}
6878
6879HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6880{
6881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6882 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6883 if (SUCCEEDED(hrc))
6884 {
6885 hrc = mHWData.backupEx();
6886 if (SUCCEEDED(hrc))
6887 {
6888 i_setModified(IsModified_MachineData);
6889 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6890 }
6891 }
6892 return hrc;
6893}
6894
6895HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6896{
6897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6898
6899 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6900
6901 return S_OK;
6902}
6903
6904HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6905{
6906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6907 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6908 if ( SUCCEEDED(hrc)
6909 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6910 {
6911 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6912 int vrc;
6913
6914 if (aAutostopType != AutostopType_Disabled)
6915 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6916 else
6917 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6918
6919 if (RT_SUCCESS(vrc))
6920 {
6921 hrc = mHWData.backupEx();
6922 if (SUCCEEDED(hrc))
6923 {
6924 i_setModified(IsModified_MachineData);
6925 mHWData->mAutostart.enmAutostopType = aAutostopType;
6926 }
6927 }
6928 else if (vrc == VERR_NOT_SUPPORTED)
6929 hrc = setError(VBOX_E_NOT_SUPPORTED,
6930 tr("The VM autostop feature is not supported on this platform"));
6931 else if (vrc == VERR_PATH_NOT_FOUND)
6932 hrc = setError(E_FAIL,
6933 tr("The path to the autostart database is not set"));
6934 else
6935 hrc = setError(E_UNEXPECTED,
6936 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6937 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6938 mUserData->s.strName.c_str(), vrc);
6939 }
6940 return hrc;
6941}
6942
6943HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6944{
6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6946
6947 aDefaultFrontend = mHWData->mDefaultFrontend;
6948
6949 return S_OK;
6950}
6951
6952HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6953{
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6956 if (SUCCEEDED(hrc))
6957 {
6958 hrc = mHWData.backupEx();
6959 if (SUCCEEDED(hrc))
6960 {
6961 i_setModified(IsModified_MachineData);
6962 mHWData->mDefaultFrontend = aDefaultFrontend;
6963 }
6964 }
6965 return hrc;
6966}
6967
6968HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6969{
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971 size_t cbIcon = mUserData->mIcon.size();
6972 aIcon.resize(cbIcon);
6973 if (cbIcon)
6974 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6975 return S_OK;
6976}
6977
6978HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6979{
6980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6981 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6982 if (SUCCEEDED(hrc))
6983 {
6984 i_setModified(IsModified_MachineData);
6985 mUserData.backup();
6986 size_t cbIcon = aIcon.size();
6987 mUserData->mIcon.resize(cbIcon);
6988 if (cbIcon)
6989 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6990 }
6991 return hrc;
6992}
6993
6994HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6995{
6996#ifdef VBOX_WITH_USB
6997 *aUSBProxyAvailable = true;
6998#else
6999 *aUSBProxyAvailable = false;
7000#endif
7001 return S_OK;
7002}
7003
7004HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7005 ComPtr<IProgress> &aProgress)
7006{
7007 ComObjPtr<Progress> pP;
7008 Progress *ppP = pP;
7009 IProgress *iP = static_cast<IProgress *>(ppP);
7010 IProgress **pProgress = &iP;
7011
7012 IMachine *pTarget = aTarget;
7013
7014 /* Convert the options. */
7015 RTCList<CloneOptions_T> optList;
7016 if (aOptions.size())
7017 for (size_t i = 0; i < aOptions.size(); ++i)
7018 optList.append(aOptions[i]);
7019
7020 if (optList.contains(CloneOptions_Link))
7021 {
7022 if (!i_isSnapshotMachine())
7023 return setError(E_INVALIDARG,
7024 tr("Linked clone can only be created from a snapshot"));
7025 if (aMode != CloneMode_MachineState)
7026 return setError(E_INVALIDARG,
7027 tr("Linked clone can only be created for a single machine state"));
7028 }
7029 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7030
7031 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7032
7033 HRESULT rc = pWorker->start(pProgress);
7034
7035 pP = static_cast<Progress *>(*pProgress);
7036 pP.queryInterfaceTo(aProgress.asOutParam());
7037
7038 return rc;
7039
7040}
7041
7042// public methods for internal purposes
7043/////////////////////////////////////////////////////////////////////////////
7044
7045/**
7046 * Adds the given IsModified_* flag to the dirty flags of the machine.
7047 * This must be called either during i_loadSettings or under the machine write lock.
7048 * @param fl
7049 */
7050void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7051{
7052 mData->flModifications |= fl;
7053 if (fAllowStateModification && i_isStateModificationAllowed())
7054 mData->mCurrentStateModified = true;
7055}
7056
7057/**
7058 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7059 * care of the write locking.
7060 *
7061 * @param fModifications The flag to add.
7062 */
7063void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7064{
7065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7066 i_setModified(fModification, fAllowStateModification);
7067}
7068
7069/**
7070 * Saves the registry entry of this machine to the given configuration node.
7071 *
7072 * @param aEntryNode Node to save the registry entry to.
7073 *
7074 * @note locks this object for reading.
7075 */
7076HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7077{
7078 AutoLimitedCaller autoCaller(this);
7079 AssertComRCReturnRC(autoCaller.rc());
7080
7081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7082
7083 data.uuid = mData->mUuid;
7084 data.strSettingsFile = mData->m_strConfigFile;
7085
7086 return S_OK;
7087}
7088
7089/**
7090 * Calculates the absolute path of the given path taking the directory of the
7091 * machine settings file as the current directory.
7092 *
7093 * @param aPath Path to calculate the absolute path for.
7094 * @param aResult Where to put the result (used only on success, can be the
7095 * same Utf8Str instance as passed in @a aPath).
7096 * @return IPRT result.
7097 *
7098 * @note Locks this object for reading.
7099 */
7100int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7101{
7102 AutoCaller autoCaller(this);
7103 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7104
7105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7106
7107 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7108
7109 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7110
7111 strSettingsDir.stripFilename();
7112 char folder[RTPATH_MAX];
7113 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7114 if (RT_SUCCESS(vrc))
7115 aResult = folder;
7116
7117 return vrc;
7118}
7119
7120/**
7121 * Copies strSource to strTarget, making it relative to the machine folder
7122 * if it is a subdirectory thereof, or simply copying it otherwise.
7123 *
7124 * @param strSource Path to evaluate and copy.
7125 * @param strTarget Buffer to receive target path.
7126 *
7127 * @note Locks this object for reading.
7128 */
7129void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7130 Utf8Str &strTarget)
7131{
7132 AutoCaller autoCaller(this);
7133 AssertComRCReturn(autoCaller.rc(), (void)0);
7134
7135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7136
7137 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7138 // use strTarget as a temporary buffer to hold the machine settings dir
7139 strTarget = mData->m_strConfigFileFull;
7140 strTarget.stripFilename();
7141 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7142 {
7143 // is relative: then append what's left
7144 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7145 // for empty paths (only possible for subdirs) use "." to avoid
7146 // triggering default settings for not present config attributes.
7147 if (strTarget.isEmpty())
7148 strTarget = ".";
7149 }
7150 else
7151 // is not relative: then overwrite
7152 strTarget = strSource;
7153}
7154
7155/**
7156 * Returns the full path to the machine's log folder in the
7157 * \a aLogFolder argument.
7158 */
7159void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7160{
7161 AutoCaller autoCaller(this);
7162 AssertComRCReturnVoid(autoCaller.rc());
7163
7164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7165
7166 char szTmp[RTPATH_MAX];
7167 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7168 if (RT_SUCCESS(vrc))
7169 {
7170 if (szTmp[0] && !mUserData.isNull())
7171 {
7172 char szTmp2[RTPATH_MAX];
7173 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7174 if (RT_SUCCESS(vrc))
7175 aLogFolder = BstrFmt("%s%c%s",
7176 szTmp2,
7177 RTPATH_DELIMITER,
7178 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7179 }
7180 else
7181 vrc = VERR_PATH_IS_RELATIVE;
7182 }
7183
7184 if (RT_FAILURE(vrc))
7185 {
7186 // fallback if VBOX_USER_LOGHOME is not set or invalid
7187 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7188 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7189 aLogFolder.append(RTPATH_DELIMITER);
7190 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7191 }
7192}
7193
7194/**
7195 * Returns the full path to the machine's log file for an given index.
7196 */
7197Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7198 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7199{
7200 Utf8Str logFolder;
7201 getLogFolder(logFolder);
7202 Assert(logFolder.length());
7203 Utf8Str log;
7204 if (idx == 0)
7205 log = Utf8StrFmt("%s%cVBox.log",
7206 logFolder.c_str(), RTPATH_DELIMITER);
7207 else
7208 log = Utf8StrFmt("%s%cVBox.log.%d",
7209 logFolder.c_str(), RTPATH_DELIMITER, idx);
7210 return log;
7211}
7212
7213/**
7214 * Returns the full path to the machine's (hardened) startup log file.
7215 */
7216Utf8Str Machine::i_getStartupLogFilename(void)
7217{
7218 Utf8Str strFilename;
7219 getLogFolder(strFilename);
7220 Assert(strFilename.length());
7221 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7222 return strFilename;
7223}
7224
7225
7226/**
7227 * Composes a unique saved state filename based on the current system time. The filename is
7228 * granular to the second so this will work so long as no more than one snapshot is taken on
7229 * a machine per second.
7230 *
7231 * Before version 4.1, we used this formula for saved state files:
7232 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7233 * which no longer works because saved state files can now be shared between the saved state of the
7234 * "saved" machine and an online snapshot, and the following would cause problems:
7235 * 1) save machine
7236 * 2) create online snapshot from that machine state --> reusing saved state file
7237 * 3) save machine again --> filename would be reused, breaking the online snapshot
7238 *
7239 * So instead we now use a timestamp.
7240 *
7241 * @param str
7242 */
7243
7244void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7245{
7246 AutoCaller autoCaller(this);
7247 AssertComRCReturnVoid(autoCaller.rc());
7248
7249 {
7250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7251 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7252 }
7253
7254 RTTIMESPEC ts;
7255 RTTimeNow(&ts);
7256 RTTIME time;
7257 RTTimeExplode(&time, &ts);
7258
7259 strStateFilePath += RTPATH_DELIMITER;
7260 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7261 time.i32Year, time.u8Month, time.u8MonthDay,
7262 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7263}
7264
7265/**
7266 * Returns the full path to the default video capture file.
7267 */
7268void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7269{
7270 AutoCaller autoCaller(this);
7271 AssertComRCReturnVoid(autoCaller.rc());
7272
7273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7274
7275 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7276 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7277 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7278}
7279
7280/**
7281 * Returns whether at least one USB controller is present for the VM.
7282 */
7283bool Machine::i_isUSBControllerPresent()
7284{
7285 AutoCaller autoCaller(this);
7286 AssertComRCReturn(autoCaller.rc(), false);
7287
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289
7290 return (mUSBControllers->size() > 0);
7291}
7292
7293/**
7294 * @note Locks this object for writing, calls the client process
7295 * (inside the lock).
7296 */
7297HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7298 const Utf8Str &strFrontend,
7299 const Utf8Str &strEnvironment,
7300 ProgressProxy *aProgress)
7301{
7302 LogFlowThisFuncEnter();
7303
7304 AssertReturn(aControl, E_FAIL);
7305 AssertReturn(aProgress, E_FAIL);
7306 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7307
7308 AutoCaller autoCaller(this);
7309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7310
7311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7312
7313 if (!mData->mRegistered)
7314 return setError(E_UNEXPECTED,
7315 tr("The machine '%s' is not registered"),
7316 mUserData->s.strName.c_str());
7317
7318 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7319
7320 if ( mData->mSession.mState == SessionState_Locked
7321 || mData->mSession.mState == SessionState_Spawning
7322 || mData->mSession.mState == SessionState_Unlocking)
7323 return setError(VBOX_E_INVALID_OBJECT_STATE,
7324 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7325 mUserData->s.strName.c_str());
7326
7327 /* may not be busy */
7328 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7329
7330 /* get the path to the executable */
7331 char szPath[RTPATH_MAX];
7332 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7333 size_t cchBufLeft = strlen(szPath);
7334 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7335 szPath[cchBufLeft] = 0;
7336 char *pszNamePart = szPath + cchBufLeft;
7337 cchBufLeft = sizeof(szPath) - cchBufLeft;
7338
7339 int vrc = VINF_SUCCESS;
7340 RTPROCESS pid = NIL_RTPROCESS;
7341
7342 RTENV env = RTENV_DEFAULT;
7343
7344 if (!strEnvironment.isEmpty())
7345 {
7346 char *newEnvStr = NULL;
7347
7348 do
7349 {
7350 /* clone the current environment */
7351 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7352 AssertRCBreakStmt(vrc2, vrc = vrc2);
7353
7354 newEnvStr = RTStrDup(strEnvironment.c_str());
7355 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7356
7357 /* put new variables to the environment
7358 * (ignore empty variable names here since RTEnv API
7359 * intentionally doesn't do that) */
7360 char *var = newEnvStr;
7361 for (char *p = newEnvStr; *p; ++p)
7362 {
7363 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7364 {
7365 *p = '\0';
7366 if (*var)
7367 {
7368 char *val = strchr(var, '=');
7369 if (val)
7370 {
7371 *val++ = '\0';
7372 vrc2 = RTEnvSetEx(env, var, val);
7373 }
7374 else
7375 vrc2 = RTEnvUnsetEx(env, var);
7376 if (RT_FAILURE(vrc2))
7377 break;
7378 }
7379 var = p + 1;
7380 }
7381 }
7382 if (RT_SUCCESS(vrc2) && *var)
7383 vrc2 = RTEnvPutEx(env, var);
7384
7385 AssertRCBreakStmt(vrc2, vrc = vrc2);
7386 }
7387 while (0);
7388
7389 if (newEnvStr != NULL)
7390 RTStrFree(newEnvStr);
7391 }
7392
7393 /* Hardened startup logging */
7394#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7395 Utf8Str strSupStartLogArg("--sup-startup-log=");
7396 {
7397 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7398 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7399 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7400 {
7401 Utf8Str strStartupLogDir = strStartupLogFile;
7402 strStartupLogDir.stripFilename();
7403 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7404 file without stripping the file. */
7405 }
7406 strSupStartLogArg.append(strStartupLogFile);
7407 }
7408 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7409#else
7410 const char *pszSupStartupLogArg = NULL;
7411#endif
7412
7413
7414#ifdef VBOX_WITH_QTGUI
7415 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7416 {
7417# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7418 /* Modify the base path so that we don't need to use ".." below. */
7419 RTPathStripTrailingSlash(szPath);
7420 RTPathStripFilename(szPath);
7421 cchBufLeft = strlen(szPath);
7422 pszNamePart = szPath + cchBufLeft;
7423 cchBufLeft = sizeof(szPath) - cchBufLeft;
7424
7425# define OSX_APP_NAME "VirtualBoxVM"
7426# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7427
7428 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7429 if ( strAppOverride.contains(".")
7430 || strAppOverride.contains("/")
7431 || strAppOverride.contains("\\")
7432 || strAppOverride.contains(":"))
7433 strAppOverride.setNull();
7434 Utf8Str strAppPath;
7435 if (!strAppOverride.isEmpty())
7436 {
7437 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7438 Utf8Str strFullPath(szPath);
7439 strFullPath.append(strAppPath);
7440 /* there is a race, but people using this deserve the failure */
7441 if (!RTFileExists(strFullPath.c_str()))
7442 strAppOverride.setNull();
7443 }
7444 if (strAppOverride.isEmpty())
7445 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7446 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7447 strcpy(pszNamePart, strAppPath.c_str());
7448# else
7449 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7450 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7451 strcpy(pszNamePart, s_szVirtualBox_exe);
7452# endif
7453
7454 Utf8Str idStr = mData->mUuid.toString();
7455 const char *apszArgs[] =
7456 {
7457 szPath,
7458 "--comment", mUserData->s.strName.c_str(),
7459 "--startvm", idStr.c_str(),
7460 "--no-startvm-errormsgbox",
7461 pszSupStartupLogArg,
7462 NULL
7463 };
7464 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7465 }
7466#else /* !VBOX_WITH_QTGUI */
7467 if (0)
7468 ;
7469#endif /* VBOX_WITH_QTGUI */
7470
7471 else
7472
7473#ifdef VBOX_WITH_VBOXSDL
7474 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7475 {
7476 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7477 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7478 strcpy(pszNamePart, s_szVBoxSDL_exe);
7479
7480 Utf8Str idStr = mData->mUuid.toString();
7481 const char *apszArgs[] =
7482 {
7483 szPath,
7484 "--comment", mUserData->s.strName.c_str(),
7485 "--startvm", idStr.c_str(),
7486 pszSupStartupLogArg,
7487 NULL
7488 };
7489 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7490 }
7491#else /* !VBOX_WITH_VBOXSDL */
7492 if (0)
7493 ;
7494#endif /* !VBOX_WITH_VBOXSDL */
7495
7496 else
7497
7498#ifdef VBOX_WITH_HEADLESS
7499 if ( strFrontend == "headless"
7500 || strFrontend == "capture"
7501 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7502 )
7503 {
7504 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7505 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7506 * and a VM works even if the server has not been installed.
7507 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7508 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7509 * differently in 4.0 and 3.x.
7510 */
7511 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7512 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7513 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7514
7515 Utf8Str idStr = mData->mUuid.toString();
7516 const char *apszArgs[] =
7517 {
7518 szPath,
7519 "--comment", mUserData->s.strName.c_str(),
7520 "--startvm", idStr.c_str(),
7521 "--vrde", "config",
7522 0, /* For "--capture". */
7523 0, /* For "--sup-startup-log". */
7524 0
7525 };
7526 unsigned iArg = 7;
7527 if (strFrontend == "capture")
7528 apszArgs[iArg++] = "--capture";
7529 apszArgs[iArg++] = pszSupStartupLogArg;
7530
7531# ifdef RT_OS_WINDOWS
7532 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7533# else
7534 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7535# endif
7536 }
7537#else /* !VBOX_WITH_HEADLESS */
7538 if (0)
7539 ;
7540#endif /* !VBOX_WITH_HEADLESS */
7541 else
7542 {
7543 RTEnvDestroy(env);
7544 return setError(E_INVALIDARG,
7545 tr("Invalid frontend name: '%s'"),
7546 strFrontend.c_str());
7547 }
7548
7549 RTEnvDestroy(env);
7550
7551 if (RT_FAILURE(vrc))
7552 return setError(VBOX_E_IPRT_ERROR,
7553 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7554 mUserData->s.strName.c_str(), vrc);
7555
7556 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7557
7558 /*
7559 * Note that we don't release the lock here before calling the client,
7560 * because it doesn't need to call us back if called with a NULL argument.
7561 * Releasing the lock here is dangerous because we didn't prepare the
7562 * launch data yet, but the client we've just started may happen to be
7563 * too fast and call LockMachine() that will fail (because of PID, etc.),
7564 * so that the Machine will never get out of the Spawning session state.
7565 */
7566
7567 /* inform the session that it will be a remote one */
7568 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7569#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7570 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7571#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7572 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7573#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7574 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7575
7576 if (FAILED(rc))
7577 {
7578 /* restore the session state */
7579 mData->mSession.mState = SessionState_Unlocked;
7580 alock.release();
7581 mParent->i_addProcessToReap(pid);
7582 /* The failure may occur w/o any error info (from RPC), so provide one */
7583 return setError(VBOX_E_VM_ERROR,
7584 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7585 }
7586
7587 /* attach launch data to the machine */
7588 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7589 mData->mSession.mRemoteControls.push_back(aControl);
7590 mData->mSession.mProgress = aProgress;
7591 mData->mSession.mPID = pid;
7592 mData->mSession.mState = SessionState_Spawning;
7593 mData->mSession.mType = strFrontend;
7594
7595 alock.release();
7596 mParent->i_addProcessToReap(pid);
7597
7598 LogFlowThisFuncLeave();
7599 return S_OK;
7600}
7601
7602/**
7603 * Returns @c true if the given session machine instance has an open direct
7604 * session (and optionally also for direct sessions which are closing) and
7605 * returns the session control machine instance if so.
7606 *
7607 * Note that when the method returns @c false, the arguments remain unchanged.
7608 *
7609 * @param aMachine Session machine object.
7610 * @param aControl Direct session control object (optional).
7611 * @param aAllowClosing If true then additionally a session which is currently
7612 * being closed will also be allowed.
7613 *
7614 * @note locks this object for reading.
7615 */
7616bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7617 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7618 bool aAllowClosing /*= false*/)
7619{
7620 AutoLimitedCaller autoCaller(this);
7621 AssertComRCReturn(autoCaller.rc(), false);
7622
7623 /* just return false for inaccessible machines */
7624 if (getObjectState().getState() != ObjectState::Ready)
7625 return false;
7626
7627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7628
7629 if ( mData->mSession.mState == SessionState_Locked
7630 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7631 )
7632 {
7633 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7634
7635 aMachine = mData->mSession.mMachine;
7636
7637 if (aControl != NULL)
7638 *aControl = mData->mSession.mDirectControl;
7639
7640 return true;
7641 }
7642
7643 return false;
7644}
7645
7646/**
7647 * Returns @c true if the given machine has an spawning direct session.
7648 *
7649 * @note locks this object for reading.
7650 */
7651bool Machine::i_isSessionSpawning()
7652{
7653 AutoLimitedCaller autoCaller(this);
7654 AssertComRCReturn(autoCaller.rc(), false);
7655
7656 /* just return false for inaccessible machines */
7657 if (getObjectState().getState() != ObjectState::Ready)
7658 return false;
7659
7660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7661
7662 if (mData->mSession.mState == SessionState_Spawning)
7663 return true;
7664
7665 return false;
7666}
7667
7668/**
7669 * Called from the client watcher thread to check for unexpected client process
7670 * death during Session_Spawning state (e.g. before it successfully opened a
7671 * direct session).
7672 *
7673 * On Win32 and on OS/2, this method is called only when we've got the
7674 * direct client's process termination notification, so it always returns @c
7675 * true.
7676 *
7677 * On other platforms, this method returns @c true if the client process is
7678 * terminated and @c false if it's still alive.
7679 *
7680 * @note Locks this object for writing.
7681 */
7682bool Machine::i_checkForSpawnFailure()
7683{
7684 AutoCaller autoCaller(this);
7685 if (!autoCaller.isOk())
7686 {
7687 /* nothing to do */
7688 LogFlowThisFunc(("Already uninitialized!\n"));
7689 return true;
7690 }
7691
7692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7693
7694 if (mData->mSession.mState != SessionState_Spawning)
7695 {
7696 /* nothing to do */
7697 LogFlowThisFunc(("Not spawning any more!\n"));
7698 return true;
7699 }
7700
7701 HRESULT rc = S_OK;
7702
7703 /* PID not yet initialized, skip check. */
7704 if (mData->mSession.mPID == NIL_RTPROCESS)
7705 return false;
7706
7707 RTPROCSTATUS status;
7708 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7709
7710 if (vrc != VERR_PROCESS_RUNNING)
7711 {
7712 Utf8Str strExtraInfo;
7713
7714#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7715 /* If the startup logfile exists and is of non-zero length, tell the
7716 user to look there for more details to encourage them to attach it
7717 when reporting startup issues. */
7718 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7719 uint64_t cbStartupLogFile = 0;
7720 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7721 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7722 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7723#endif
7724
7725 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7726 rc = setError(E_FAIL,
7727 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7728 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7729 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7730 rc = setError(E_FAIL,
7731 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7732 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7733 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7734 rc = setError(E_FAIL,
7735 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7736 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7737 else
7738 rc = setError(E_FAIL,
7739 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7740 i_getName().c_str(), vrc, strExtraInfo.c_str());
7741 }
7742
7743 if (FAILED(rc))
7744 {
7745 /* Close the remote session, remove the remote control from the list
7746 * and reset session state to Closed (@note keep the code in sync with
7747 * the relevant part in LockMachine()). */
7748
7749 Assert(mData->mSession.mRemoteControls.size() == 1);
7750 if (mData->mSession.mRemoteControls.size() == 1)
7751 {
7752 ErrorInfoKeeper eik;
7753 mData->mSession.mRemoteControls.front()->Uninitialize();
7754 }
7755
7756 mData->mSession.mRemoteControls.clear();
7757 mData->mSession.mState = SessionState_Unlocked;
7758
7759 /* finalize the progress after setting the state */
7760 if (!mData->mSession.mProgress.isNull())
7761 {
7762 mData->mSession.mProgress->notifyComplete(rc);
7763 mData->mSession.mProgress.setNull();
7764 }
7765
7766 mData->mSession.mPID = NIL_RTPROCESS;
7767
7768 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7769 return true;
7770 }
7771
7772 return false;
7773}
7774
7775/**
7776 * Checks whether the machine can be registered. If so, commits and saves
7777 * all settings.
7778 *
7779 * @note Must be called from mParent's write lock. Locks this object and
7780 * children for writing.
7781 */
7782HRESULT Machine::i_prepareRegister()
7783{
7784 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7785
7786 AutoLimitedCaller autoCaller(this);
7787 AssertComRCReturnRC(autoCaller.rc());
7788
7789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7790
7791 /* wait for state dependents to drop to zero */
7792 i_ensureNoStateDependencies();
7793
7794 if (!mData->mAccessible)
7795 return setError(VBOX_E_INVALID_OBJECT_STATE,
7796 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7797 mUserData->s.strName.c_str(),
7798 mData->mUuid.toString().c_str());
7799
7800 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7801
7802 if (mData->mRegistered)
7803 return setError(VBOX_E_INVALID_OBJECT_STATE,
7804 tr("The machine '%s' with UUID {%s} is already registered"),
7805 mUserData->s.strName.c_str(),
7806 mData->mUuid.toString().c_str());
7807
7808 HRESULT rc = S_OK;
7809
7810 // Ensure the settings are saved. If we are going to be registered and
7811 // no config file exists yet, create it by calling i_saveSettings() too.
7812 if ( (mData->flModifications)
7813 || (!mData->pMachineConfigFile->fileExists())
7814 )
7815 {
7816 rc = i_saveSettings(NULL);
7817 // no need to check whether VirtualBox.xml needs saving too since
7818 // we can't have a machine XML file rename pending
7819 if (FAILED(rc)) return rc;
7820 }
7821
7822 /* more config checking goes here */
7823
7824 if (SUCCEEDED(rc))
7825 {
7826 /* we may have had implicit modifications we want to fix on success */
7827 i_commit();
7828
7829 mData->mRegistered = true;
7830 }
7831 else
7832 {
7833 /* we may have had implicit modifications we want to cancel on failure*/
7834 i_rollback(false /* aNotify */);
7835 }
7836
7837 return rc;
7838}
7839
7840/**
7841 * Increases the number of objects dependent on the machine state or on the
7842 * registered state. Guarantees that these two states will not change at least
7843 * until #releaseStateDependency() is called.
7844 *
7845 * Depending on the @a aDepType value, additional state checks may be made.
7846 * These checks will set extended error info on failure. See
7847 * #checkStateDependency() for more info.
7848 *
7849 * If this method returns a failure, the dependency is not added and the caller
7850 * is not allowed to rely on any particular machine state or registration state
7851 * value and may return the failed result code to the upper level.
7852 *
7853 * @param aDepType Dependency type to add.
7854 * @param aState Current machine state (NULL if not interested).
7855 * @param aRegistered Current registered state (NULL if not interested).
7856 *
7857 * @note Locks this object for writing.
7858 */
7859HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7860 MachineState_T *aState /* = NULL */,
7861 BOOL *aRegistered /* = NULL */)
7862{
7863 AutoCaller autoCaller(this);
7864 AssertComRCReturnRC(autoCaller.rc());
7865
7866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7867
7868 HRESULT rc = i_checkStateDependency(aDepType);
7869 if (FAILED(rc)) return rc;
7870
7871 {
7872 if (mData->mMachineStateChangePending != 0)
7873 {
7874 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7875 * drop to zero so don't add more. It may make sense to wait a bit
7876 * and retry before reporting an error (since the pending state
7877 * transition should be really quick) but let's just assert for
7878 * now to see if it ever happens on practice. */
7879
7880 AssertFailed();
7881
7882 return setError(E_ACCESSDENIED,
7883 tr("Machine state change is in progress. Please retry the operation later."));
7884 }
7885
7886 ++mData->mMachineStateDeps;
7887 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7888 }
7889
7890 if (aState)
7891 *aState = mData->mMachineState;
7892 if (aRegistered)
7893 *aRegistered = mData->mRegistered;
7894
7895 return S_OK;
7896}
7897
7898/**
7899 * Decreases the number of objects dependent on the machine state.
7900 * Must always complete the #addStateDependency() call after the state
7901 * dependency is no more necessary.
7902 */
7903void Machine::i_releaseStateDependency()
7904{
7905 AutoCaller autoCaller(this);
7906 AssertComRCReturnVoid(autoCaller.rc());
7907
7908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7909
7910 /* releaseStateDependency() w/o addStateDependency()? */
7911 AssertReturnVoid(mData->mMachineStateDeps != 0);
7912 -- mData->mMachineStateDeps;
7913
7914 if (mData->mMachineStateDeps == 0)
7915 {
7916 /* inform i_ensureNoStateDependencies() that there are no more deps */
7917 if (mData->mMachineStateChangePending != 0)
7918 {
7919 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7920 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7921 }
7922 }
7923}
7924
7925Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7926{
7927 /* start with nothing found */
7928 Utf8Str strResult("");
7929
7930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7931
7932 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7933 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7934 // found:
7935 strResult = it->second; // source is a Utf8Str
7936
7937 return strResult;
7938}
7939
7940// protected methods
7941/////////////////////////////////////////////////////////////////////////////
7942
7943/**
7944 * Performs machine state checks based on the @a aDepType value. If a check
7945 * fails, this method will set extended error info, otherwise it will return
7946 * S_OK. It is supposed, that on failure, the caller will immediately return
7947 * the return value of this method to the upper level.
7948 *
7949 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7950 *
7951 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7952 * current state of this machine object allows to change settings of the
7953 * machine (i.e. the machine is not registered, or registered but not running
7954 * and not saved). It is useful to call this method from Machine setters
7955 * before performing any change.
7956 *
7957 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7958 * as for MutableStateDep except that if the machine is saved, S_OK is also
7959 * returned. This is useful in setters which allow changing machine
7960 * properties when it is in the saved state.
7961 *
7962 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7963 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7964 * Aborted).
7965 *
7966 * @param aDepType Dependency type to check.
7967 *
7968 * @note Non Machine based classes should use #addStateDependency() and
7969 * #releaseStateDependency() methods or the smart AutoStateDependency
7970 * template.
7971 *
7972 * @note This method must be called from under this object's read or write
7973 * lock.
7974 */
7975HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7976{
7977 switch (aDepType)
7978 {
7979 case AnyStateDep:
7980 {
7981 break;
7982 }
7983 case MutableStateDep:
7984 {
7985 if ( mData->mRegistered
7986 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7987 Paused should actually be included here... (Live Migration) */
7988 || ( mData->mMachineState != MachineState_Paused
7989 && mData->mMachineState != MachineState_Running
7990 && mData->mMachineState != MachineState_Aborted
7991 && mData->mMachineState != MachineState_Teleported
7992 && mData->mMachineState != MachineState_PoweredOff
7993 )
7994 )
7995 )
7996 return setError(VBOX_E_INVALID_VM_STATE,
7997 tr("The machine is not mutable (state is %s)"),
7998 Global::stringifyMachineState(mData->mMachineState));
7999 break;
8000 }
8001 case MutableOrSavedStateDep:
8002 {
8003 if ( mData->mRegistered
8004 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8005 Paused should actually be included here... (Live Migration) */
8006 || ( mData->mMachineState != MachineState_Paused
8007 && mData->mMachineState != MachineState_Running
8008 && mData->mMachineState != MachineState_Aborted
8009 && mData->mMachineState != MachineState_Teleported
8010 && mData->mMachineState != MachineState_Saved
8011 && mData->mMachineState != MachineState_PoweredOff
8012 )
8013 )
8014 )
8015 return setError(VBOX_E_INVALID_VM_STATE,
8016 tr("The machine is not mutable (state is %s)"),
8017 Global::stringifyMachineState(mData->mMachineState));
8018 break;
8019 }
8020 case OfflineStateDep:
8021 {
8022 if ( mData->mRegistered
8023 && ( !i_isSessionMachine()
8024 || ( mData->mMachineState != MachineState_PoweredOff
8025 && mData->mMachineState != MachineState_Saved
8026 && mData->mMachineState != MachineState_Aborted
8027 && mData->mMachineState != MachineState_Teleported
8028 )
8029 )
8030 )
8031 return setError(VBOX_E_INVALID_VM_STATE,
8032 tr("The machine is not offline (state is %s)"),
8033 Global::stringifyMachineState(mData->mMachineState));
8034 break;
8035 }
8036 }
8037
8038 return S_OK;
8039}
8040
8041/**
8042 * Helper to initialize all associated child objects and allocate data
8043 * structures.
8044 *
8045 * This method must be called as a part of the object's initialization procedure
8046 * (usually done in the #init() method).
8047 *
8048 * @note Must be called only from #init() or from #registeredInit().
8049 */
8050HRESULT Machine::initDataAndChildObjects()
8051{
8052 AutoCaller autoCaller(this);
8053 AssertComRCReturnRC(autoCaller.rc());
8054 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8055 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8056
8057 AssertReturn(!mData->mAccessible, E_FAIL);
8058
8059 /* allocate data structures */
8060 mSSData.allocate();
8061 mUserData.allocate();
8062 mHWData.allocate();
8063 mMediaData.allocate();
8064 mStorageControllers.allocate();
8065 mUSBControllers.allocate();
8066
8067 /* initialize mOSTypeId */
8068 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8069
8070 /* create associated BIOS settings object */
8071 unconst(mBIOSSettings).createObject();
8072 mBIOSSettings->init(this);
8073
8074 /* create an associated VRDE object (default is disabled) */
8075 unconst(mVRDEServer).createObject();
8076 mVRDEServer->init(this);
8077
8078 /* create associated serial port objects */
8079 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8080 {
8081 unconst(mSerialPorts[slot]).createObject();
8082 mSerialPorts[slot]->init(this, slot);
8083 }
8084
8085 /* create associated parallel port objects */
8086 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8087 {
8088 unconst(mParallelPorts[slot]).createObject();
8089 mParallelPorts[slot]->init(this, slot);
8090 }
8091
8092 /* create the audio adapter object (always present, default is disabled) */
8093 unconst(mAudioAdapter).createObject();
8094 mAudioAdapter->init(this);
8095
8096 /* create the USB device filters object (always present) */
8097 unconst(mUSBDeviceFilters).createObject();
8098 mUSBDeviceFilters->init(this);
8099
8100 /* create associated network adapter objects */
8101 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8102 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8103 {
8104 unconst(mNetworkAdapters[slot]).createObject();
8105 mNetworkAdapters[slot]->init(this, slot);
8106 }
8107
8108 /* create the bandwidth control */
8109 unconst(mBandwidthControl).createObject();
8110 mBandwidthControl->init(this);
8111
8112 return S_OK;
8113}
8114
8115/**
8116 * Helper to uninitialize all associated child objects and to free all data
8117 * structures.
8118 *
8119 * This method must be called as a part of the object's uninitialization
8120 * procedure (usually done in the #uninit() method).
8121 *
8122 * @note Must be called only from #uninit() or from #registeredInit().
8123 */
8124void Machine::uninitDataAndChildObjects()
8125{
8126 AutoCaller autoCaller(this);
8127 AssertComRCReturnVoid(autoCaller.rc());
8128 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8129 || getObjectState().getState() == ObjectState::Limited);
8130
8131 /* tell all our other child objects we've been uninitialized */
8132 if (mBandwidthControl)
8133 {
8134 mBandwidthControl->uninit();
8135 unconst(mBandwidthControl).setNull();
8136 }
8137
8138 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8139 {
8140 if (mNetworkAdapters[slot])
8141 {
8142 mNetworkAdapters[slot]->uninit();
8143 unconst(mNetworkAdapters[slot]).setNull();
8144 }
8145 }
8146
8147 if (mUSBDeviceFilters)
8148 {
8149 mUSBDeviceFilters->uninit();
8150 unconst(mUSBDeviceFilters).setNull();
8151 }
8152
8153 if (mAudioAdapter)
8154 {
8155 mAudioAdapter->uninit();
8156 unconst(mAudioAdapter).setNull();
8157 }
8158
8159 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8160 {
8161 if (mParallelPorts[slot])
8162 {
8163 mParallelPorts[slot]->uninit();
8164 unconst(mParallelPorts[slot]).setNull();
8165 }
8166 }
8167
8168 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8169 {
8170 if (mSerialPorts[slot])
8171 {
8172 mSerialPorts[slot]->uninit();
8173 unconst(mSerialPorts[slot]).setNull();
8174 }
8175 }
8176
8177 if (mVRDEServer)
8178 {
8179 mVRDEServer->uninit();
8180 unconst(mVRDEServer).setNull();
8181 }
8182
8183 if (mBIOSSettings)
8184 {
8185 mBIOSSettings->uninit();
8186 unconst(mBIOSSettings).setNull();
8187 }
8188
8189 /* Deassociate media (only when a real Machine or a SnapshotMachine
8190 * instance is uninitialized; SessionMachine instances refer to real
8191 * Machine media). This is necessary for a clean re-initialization of
8192 * the VM after successfully re-checking the accessibility state. Note
8193 * that in case of normal Machine or SnapshotMachine uninitialization (as
8194 * a result of unregistering or deleting the snapshot), outdated media
8195 * attachments will already be uninitialized and deleted, so this
8196 * code will not affect them. */
8197 if ( !!mMediaData
8198 && (!i_isSessionMachine())
8199 )
8200 {
8201 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8202 it != mMediaData->mAttachments.end();
8203 ++it)
8204 {
8205 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8206 if (pMedium.isNull())
8207 continue;
8208 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8209 AssertComRC(rc);
8210 }
8211 }
8212
8213 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8214 {
8215 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8216 if (mData->mFirstSnapshot)
8217 {
8218 // snapshots tree is protected by machine write lock; strictly
8219 // this isn't necessary here since we're deleting the entire
8220 // machine, but otherwise we assert in Snapshot::uninit()
8221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8222 mData->mFirstSnapshot->uninit();
8223 mData->mFirstSnapshot.setNull();
8224 }
8225
8226 mData->mCurrentSnapshot.setNull();
8227 }
8228
8229 /* free data structures (the essential mData structure is not freed here
8230 * since it may be still in use) */
8231 mMediaData.free();
8232 mStorageControllers.free();
8233 mUSBControllers.free();
8234 mHWData.free();
8235 mUserData.free();
8236 mSSData.free();
8237}
8238
8239/**
8240 * Returns a pointer to the Machine object for this machine that acts like a
8241 * parent for complex machine data objects such as shared folders, etc.
8242 *
8243 * For primary Machine objects and for SnapshotMachine objects, returns this
8244 * object's pointer itself. For SessionMachine objects, returns the peer
8245 * (primary) machine pointer.
8246 */
8247Machine* Machine::i_getMachine()
8248{
8249 if (i_isSessionMachine())
8250 return (Machine*)mPeer;
8251 return this;
8252}
8253
8254/**
8255 * Makes sure that there are no machine state dependents. If necessary, waits
8256 * for the number of dependents to drop to zero.
8257 *
8258 * Make sure this method is called from under this object's write lock to
8259 * guarantee that no new dependents may be added when this method returns
8260 * control to the caller.
8261 *
8262 * @note Locks this object for writing. The lock will be released while waiting
8263 * (if necessary).
8264 *
8265 * @warning To be used only in methods that change the machine state!
8266 */
8267void Machine::i_ensureNoStateDependencies()
8268{
8269 AssertReturnVoid(isWriteLockOnCurrentThread());
8270
8271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8272
8273 /* Wait for all state dependents if necessary */
8274 if (mData->mMachineStateDeps != 0)
8275 {
8276 /* lazy semaphore creation */
8277 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8278 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8279
8280 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8281 mData->mMachineStateDeps));
8282
8283 ++mData->mMachineStateChangePending;
8284
8285 /* reset the semaphore before waiting, the last dependent will signal
8286 * it */
8287 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8288
8289 alock.release();
8290
8291 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8292
8293 alock.acquire();
8294
8295 -- mData->mMachineStateChangePending;
8296 }
8297}
8298
8299/**
8300 * Changes the machine state and informs callbacks.
8301 *
8302 * This method is not intended to fail so it either returns S_OK or asserts (and
8303 * returns a failure).
8304 *
8305 * @note Locks this object for writing.
8306 */
8307HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8308{
8309 LogFlowThisFuncEnter();
8310 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8311
8312 AutoCaller autoCaller(this);
8313 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8314
8315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8316
8317 /* wait for state dependents to drop to zero */
8318 i_ensureNoStateDependencies();
8319
8320 if (mData->mMachineState != aMachineState)
8321 {
8322 mData->mMachineState = aMachineState;
8323
8324 RTTimeNow(&mData->mLastStateChange);
8325
8326 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8327 }
8328
8329 LogFlowThisFuncLeave();
8330 return S_OK;
8331}
8332
8333/**
8334 * Searches for a shared folder with the given logical name
8335 * in the collection of shared folders.
8336 *
8337 * @param aName logical name of the shared folder
8338 * @param aSharedFolder where to return the found object
8339 * @param aSetError whether to set the error info if the folder is
8340 * not found
8341 * @return
8342 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8343 *
8344 * @note
8345 * must be called from under the object's lock!
8346 */
8347HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8348 ComObjPtr<SharedFolder> &aSharedFolder,
8349 bool aSetError /* = false */)
8350{
8351 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8352 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8353 it != mHWData->mSharedFolders.end();
8354 ++it)
8355 {
8356 SharedFolder *pSF = *it;
8357 AutoCaller autoCaller(pSF);
8358 if (pSF->i_getName() == aName)
8359 {
8360 aSharedFolder = pSF;
8361 rc = S_OK;
8362 break;
8363 }
8364 }
8365
8366 if (aSetError && FAILED(rc))
8367 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8368
8369 return rc;
8370}
8371
8372/**
8373 * Initializes all machine instance data from the given settings structures
8374 * from XML. The exception is the machine UUID which needs special handling
8375 * depending on the caller's use case, so the caller needs to set that herself.
8376 *
8377 * This gets called in several contexts during machine initialization:
8378 *
8379 * -- When machine XML exists on disk already and needs to be loaded into memory,
8380 * for example, from registeredInit() to load all registered machines on
8381 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8382 * attached to the machine should be part of some media registry already.
8383 *
8384 * -- During OVF import, when a machine config has been constructed from an
8385 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8386 * ensure that the media listed as attachments in the config (which have
8387 * been imported from the OVF) receive the correct registry ID.
8388 *
8389 * -- During VM cloning.
8390 *
8391 * @param config Machine settings from XML.
8392 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8393 * for each attached medium in the config.
8394 * @return
8395 */
8396HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8397 const Guid *puuidRegistry)
8398{
8399 // copy name, description, OS type, teleporter, UTC etc.
8400 mUserData->s = config.machineUserData;
8401
8402 // Decode the Icon overide data from config userdata and set onto Machine.
8403 #define DECODE_STR_MAX _1M
8404 const char* pszStr = config.machineUserData.ovIcon.c_str();
8405 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8406 if (cbOut > DECODE_STR_MAX)
8407 return setError(E_FAIL,
8408 tr("Icon Data too long.'%d' > '%d'"),
8409 cbOut,
8410 DECODE_STR_MAX);
8411 mUserData->mIcon.resize(cbOut);
8412 int vrc = VINF_SUCCESS;
8413 if (cbOut)
8414 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8415 if (RT_FAILURE(vrc))
8416 {
8417 mUserData->mIcon.resize(0);
8418 return setError(E_FAIL,
8419 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8420 pszStr,
8421 vrc);
8422 }
8423
8424 // look up the object by Id to check it is valid
8425 ComPtr<IGuestOSType> guestOSType;
8426 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8427 guestOSType.asOutParam());
8428 if (FAILED(rc)) return rc;
8429
8430 // stateFile (optional)
8431 if (config.strStateFile.isEmpty())
8432 mSSData->strStateFilePath.setNull();
8433 else
8434 {
8435 Utf8Str stateFilePathFull(config.strStateFile);
8436 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8437 if (RT_FAILURE(vrc))
8438 return setError(E_FAIL,
8439 tr("Invalid saved state file path '%s' (%Rrc)"),
8440 config.strStateFile.c_str(),
8441 vrc);
8442 mSSData->strStateFilePath = stateFilePathFull;
8443 }
8444
8445 // snapshot folder needs special processing so set it again
8446 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8447 if (FAILED(rc)) return rc;
8448
8449 /* Copy the extra data items (Not in any case config is already the same as
8450 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8451 * make sure the extra data map is copied). */
8452 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8453
8454 /* currentStateModified (optional, default is true) */
8455 mData->mCurrentStateModified = config.fCurrentStateModified;
8456
8457 mData->mLastStateChange = config.timeLastStateChange;
8458
8459 /*
8460 * note: all mUserData members must be assigned prior this point because
8461 * we need to commit changes in order to let mUserData be shared by all
8462 * snapshot machine instances.
8463 */
8464 mUserData.commitCopy();
8465
8466 // machine registry, if present (must be loaded before snapshots)
8467 if (config.canHaveOwnMediaRegistry())
8468 {
8469 // determine machine folder
8470 Utf8Str strMachineFolder = i_getSettingsFileFull();
8471 strMachineFolder.stripFilename();
8472 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8473 config.mediaRegistry,
8474 strMachineFolder);
8475 if (FAILED(rc)) return rc;
8476 }
8477
8478 /* Snapshot node (optional) */
8479 size_t cRootSnapshots;
8480 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8481 {
8482 // there must be only one root snapshot
8483 Assert(cRootSnapshots == 1);
8484
8485 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8486
8487 rc = i_loadSnapshot(snap,
8488 config.uuidCurrentSnapshot,
8489 NULL); // no parent == first snapshot
8490 if (FAILED(rc)) return rc;
8491 }
8492
8493 // hardware data
8494 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8495 if (FAILED(rc)) return rc;
8496
8497 // load storage controllers
8498 rc = i_loadStorageControllers(config.storageMachine,
8499 puuidRegistry,
8500 NULL /* puuidSnapshot */);
8501 if (FAILED(rc)) return rc;
8502
8503 /*
8504 * NOTE: the assignment below must be the last thing to do,
8505 * otherwise it will be not possible to change the settings
8506 * somewhere in the code above because all setters will be
8507 * blocked by i_checkStateDependency(MutableStateDep).
8508 */
8509
8510 /* set the machine state to Aborted or Saved when appropriate */
8511 if (config.fAborted)
8512 {
8513 mSSData->strStateFilePath.setNull();
8514
8515 /* no need to use i_setMachineState() during init() */
8516 mData->mMachineState = MachineState_Aborted;
8517 }
8518 else if (!mSSData->strStateFilePath.isEmpty())
8519 {
8520 /* no need to use i_setMachineState() during init() */
8521 mData->mMachineState = MachineState_Saved;
8522 }
8523
8524 // after loading settings, we are no longer different from the XML on disk
8525 mData->flModifications = 0;
8526
8527 return S_OK;
8528}
8529
8530/**
8531 * Recursively loads all snapshots starting from the given.
8532 *
8533 * @param aNode <Snapshot> node.
8534 * @param aCurSnapshotId Current snapshot ID from the settings file.
8535 * @param aParentSnapshot Parent snapshot.
8536 */
8537HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8538 const Guid &aCurSnapshotId,
8539 Snapshot *aParentSnapshot)
8540{
8541 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8542 AssertReturn(!i_isSessionMachine(), E_FAIL);
8543
8544 HRESULT rc = S_OK;
8545
8546 Utf8Str strStateFile;
8547 if (!data.strStateFile.isEmpty())
8548 {
8549 /* optional */
8550 strStateFile = data.strStateFile;
8551 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8552 if (RT_FAILURE(vrc))
8553 return setError(E_FAIL,
8554 tr("Invalid saved state file path '%s' (%Rrc)"),
8555 strStateFile.c_str(),
8556 vrc);
8557 }
8558
8559 /* create a snapshot machine object */
8560 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8561 pSnapshotMachine.createObject();
8562 rc = pSnapshotMachine->initFromSettings(this,
8563 data.hardware,
8564 &data.debugging,
8565 &data.autostart,
8566 data.storage,
8567 data.uuid.ref(),
8568 strStateFile);
8569 if (FAILED(rc)) return rc;
8570
8571 /* create a snapshot object */
8572 ComObjPtr<Snapshot> pSnapshot;
8573 pSnapshot.createObject();
8574 /* initialize the snapshot */
8575 rc = pSnapshot->init(mParent, // VirtualBox object
8576 data.uuid,
8577 data.strName,
8578 data.strDescription,
8579 data.timestamp,
8580 pSnapshotMachine,
8581 aParentSnapshot);
8582 if (FAILED(rc)) return rc;
8583
8584 /* memorize the first snapshot if necessary */
8585 if (!mData->mFirstSnapshot)
8586 mData->mFirstSnapshot = pSnapshot;
8587
8588 /* memorize the current snapshot when appropriate */
8589 if ( !mData->mCurrentSnapshot
8590 && pSnapshot->i_getId() == aCurSnapshotId
8591 )
8592 mData->mCurrentSnapshot = pSnapshot;
8593
8594 // now create the children
8595 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8596 it != data.llChildSnapshots.end();
8597 ++it)
8598 {
8599 const settings::Snapshot &childData = *it;
8600 // recurse
8601 rc = i_loadSnapshot(childData,
8602 aCurSnapshotId,
8603 pSnapshot); // parent = the one we created above
8604 if (FAILED(rc)) return rc;
8605 }
8606
8607 return rc;
8608}
8609
8610/**
8611 * Loads settings into mHWData.
8612 *
8613 * @param data Reference to the hardware settings.
8614 * @param pDbg Pointer to the debugging settings.
8615 * @param pAutostart Pointer to the autostart settings.
8616 */
8617HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8618 const settings::Autostart *pAutostart)
8619{
8620 AssertReturn(!i_isSessionMachine(), E_FAIL);
8621
8622 HRESULT rc = S_OK;
8623
8624 try
8625 {
8626 /* The hardware version attribute (optional). */
8627 mHWData->mHWVersion = data.strVersion;
8628 mHWData->mHardwareUUID = data.uuid;
8629
8630 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8631 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8632 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8633 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8634 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8635 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8636 mHWData->mPAEEnabled = data.fPAE;
8637 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8638 mHWData->mLongMode = data.enmLongMode;
8639 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8640 mHWData->mCPUCount = data.cCPUs;
8641 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8642 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8643
8644 // cpu
8645 if (mHWData->mCPUHotPlugEnabled)
8646 {
8647 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8648 it != data.llCpus.end();
8649 ++it)
8650 {
8651 const settings::Cpu &cpu = *it;
8652
8653 mHWData->mCPUAttached[cpu.ulId] = true;
8654 }
8655 }
8656
8657 // cpuid leafs
8658 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8659 it != data.llCpuIdLeafs.end();
8660 ++it)
8661 {
8662 const settings::CpuIdLeaf &leaf = *it;
8663
8664 switch (leaf.ulId)
8665 {
8666 case 0x0:
8667 case 0x1:
8668 case 0x2:
8669 case 0x3:
8670 case 0x4:
8671 case 0x5:
8672 case 0x6:
8673 case 0x7:
8674 case 0x8:
8675 case 0x9:
8676 case 0xA:
8677 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8678 break;
8679
8680 case 0x80000000:
8681 case 0x80000001:
8682 case 0x80000002:
8683 case 0x80000003:
8684 case 0x80000004:
8685 case 0x80000005:
8686 case 0x80000006:
8687 case 0x80000007:
8688 case 0x80000008:
8689 case 0x80000009:
8690 case 0x8000000A:
8691 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8692 break;
8693
8694 default:
8695 /* just ignore */
8696 break;
8697 }
8698 }
8699
8700 mHWData->mMemorySize = data.ulMemorySizeMB;
8701 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8702
8703 // boot order
8704 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8705 {
8706 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8707 if (it == data.mapBootOrder.end())
8708 mHWData->mBootOrder[i] = DeviceType_Null;
8709 else
8710 mHWData->mBootOrder[i] = it->second;
8711 }
8712
8713 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8714 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8715 mHWData->mMonitorCount = data.cMonitors;
8716 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8717 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8718 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8719 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8720 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8721 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8722 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8723 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8724 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8725 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8726 if (!data.strVideoCaptureFile.isEmpty())
8727 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8728 else
8729 mHWData->mVideoCaptureFile.setNull();
8730 mHWData->mFirmwareType = data.firmwareType;
8731 mHWData->mPointingHIDType = data.pointingHIDType;
8732 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8733 mHWData->mChipsetType = data.chipsetType;
8734 mHWData->mParavirtProvider = data.paravirtProvider;
8735 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8736 mHWData->mHPETEnabled = data.fHPETEnabled;
8737
8738 /* VRDEServer */
8739 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8740 if (FAILED(rc)) return rc;
8741
8742 /* BIOS */
8743 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8744 if (FAILED(rc)) return rc;
8745
8746 // Bandwidth control (must come before network adapters)
8747 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8748 if (FAILED(rc)) return rc;
8749
8750 /* Shared folders */
8751 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8752 it != data.usbSettings.llUSBControllers.end();
8753 ++it)
8754 {
8755 const settings::USBController &settingsCtrl = *it;
8756 ComObjPtr<USBController> newCtrl;
8757
8758 newCtrl.createObject();
8759 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8760 mUSBControllers->push_back(newCtrl);
8761 }
8762
8763 /* USB device filters */
8764 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8765 if (FAILED(rc)) return rc;
8766
8767 // network adapters
8768 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8769 size_t oldCount = mNetworkAdapters.size();
8770 if (newCount > oldCount)
8771 {
8772 mNetworkAdapters.resize(newCount);
8773 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8774 {
8775 unconst(mNetworkAdapters[slot]).createObject();
8776 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8777 }
8778 }
8779 else if (newCount < oldCount)
8780 mNetworkAdapters.resize(newCount);
8781 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8782 it != data.llNetworkAdapters.end();
8783 ++it)
8784 {
8785 const settings::NetworkAdapter &nic = *it;
8786
8787 /* slot unicity is guaranteed by XML Schema */
8788 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8789 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8790 if (FAILED(rc)) return rc;
8791 }
8792
8793 // serial ports
8794 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8795 it != data.llSerialPorts.end();
8796 ++it)
8797 {
8798 const settings::SerialPort &s = *it;
8799
8800 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8801 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8802 if (FAILED(rc)) return rc;
8803 }
8804
8805 // parallel ports (optional)
8806 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8807 it != data.llParallelPorts.end();
8808 ++it)
8809 {
8810 const settings::ParallelPort &p = *it;
8811
8812 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8813 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8814 if (FAILED(rc)) return rc;
8815 }
8816
8817 /* AudioAdapter */
8818 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8819 if (FAILED(rc)) return rc;
8820
8821 /* Shared folders */
8822 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8823 it != data.llSharedFolders.end();
8824 ++it)
8825 {
8826 const settings::SharedFolder &sf = *it;
8827
8828 ComObjPtr<SharedFolder> sharedFolder;
8829 /* Check for double entries. Not allowed! */
8830 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8831 if (SUCCEEDED(rc))
8832 return setError(VBOX_E_OBJECT_IN_USE,
8833 tr("Shared folder named '%s' already exists"),
8834 sf.strName.c_str());
8835
8836 /* Create the new shared folder. Don't break on error. This will be
8837 * reported when the machine starts. */
8838 sharedFolder.createObject();
8839 rc = sharedFolder->init(i_getMachine(),
8840 sf.strName,
8841 sf.strHostPath,
8842 RT_BOOL(sf.fWritable),
8843 RT_BOOL(sf.fAutoMount),
8844 false /* fFailOnError */);
8845 if (FAILED(rc)) return rc;
8846 mHWData->mSharedFolders.push_back(sharedFolder);
8847 }
8848
8849 // Clipboard
8850 mHWData->mClipboardMode = data.clipboardMode;
8851
8852 // drag'n'drop
8853 mHWData->mDnDMode = data.dndMode;
8854
8855 // guest settings
8856 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8857
8858 // IO settings
8859 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8860 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8861
8862 // Host PCI devices
8863 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8864 it != data.pciAttachments.end();
8865 ++it)
8866 {
8867 const settings::HostPCIDeviceAttachment &hpda = *it;
8868 ComObjPtr<PCIDeviceAttachment> pda;
8869
8870 pda.createObject();
8871 pda->i_loadSettings(this, hpda);
8872 mHWData->mPCIDeviceAssignments.push_back(pda);
8873 }
8874
8875 /*
8876 * (The following isn't really real hardware, but it lives in HWData
8877 * for reasons of convenience.)
8878 */
8879
8880#ifdef VBOX_WITH_GUEST_PROPS
8881 /* Guest properties (optional) */
8882 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8883 it != data.llGuestProperties.end();
8884 ++it)
8885 {
8886 const settings::GuestProperty &prop = *it;
8887 uint32_t fFlags = guestProp::NILFLAG;
8888 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8889 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8890 mHWData->mGuestProperties[prop.strName] = property;
8891 }
8892
8893 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8894#endif /* VBOX_WITH_GUEST_PROPS defined */
8895
8896 rc = i_loadDebugging(pDbg);
8897 if (FAILED(rc))
8898 return rc;
8899
8900 mHWData->mAutostart = *pAutostart;
8901
8902 /* default frontend */
8903 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8904 }
8905 catch(std::bad_alloc &)
8906 {
8907 return E_OUTOFMEMORY;
8908 }
8909
8910 AssertComRC(rc);
8911 return rc;
8912}
8913
8914/**
8915 * Called from Machine::loadHardware() to load the debugging settings of the
8916 * machine.
8917 *
8918 * @param pDbg Pointer to the settings.
8919 */
8920HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8921{
8922 mHWData->mDebugging = *pDbg;
8923 /* no more processing currently required, this will probably change. */
8924 return S_OK;
8925}
8926
8927/**
8928 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8929 *
8930 * @param data
8931 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8932 * @param puuidSnapshot
8933 * @return
8934 */
8935HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8936 const Guid *puuidRegistry,
8937 const Guid *puuidSnapshot)
8938{
8939 AssertReturn(!i_isSessionMachine(), E_FAIL);
8940
8941 HRESULT rc = S_OK;
8942
8943 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8944 it != data.llStorageControllers.end();
8945 ++it)
8946 {
8947 const settings::StorageController &ctlData = *it;
8948
8949 ComObjPtr<StorageController> pCtl;
8950 /* Try to find one with the name first. */
8951 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8952 if (SUCCEEDED(rc))
8953 return setError(VBOX_E_OBJECT_IN_USE,
8954 tr("Storage controller named '%s' already exists"),
8955 ctlData.strName.c_str());
8956
8957 pCtl.createObject();
8958 rc = pCtl->init(this,
8959 ctlData.strName,
8960 ctlData.storageBus,
8961 ctlData.ulInstance,
8962 ctlData.fBootable);
8963 if (FAILED(rc)) return rc;
8964
8965 mStorageControllers->push_back(pCtl);
8966
8967 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8968 if (FAILED(rc)) return rc;
8969
8970 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8971 if (FAILED(rc)) return rc;
8972
8973 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8974 if (FAILED(rc)) return rc;
8975
8976 /* Set IDE emulation settings (only for AHCI controller). */
8977 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8978 {
8979 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8980 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8981 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8982 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8983 )
8984 return rc;
8985 }
8986
8987 /* Load the attached devices now. */
8988 rc = i_loadStorageDevices(pCtl,
8989 ctlData,
8990 puuidRegistry,
8991 puuidSnapshot);
8992 if (FAILED(rc)) return rc;
8993 }
8994
8995 return S_OK;
8996}
8997
8998/**
8999 * Called from i_loadStorageControllers for a controller's devices.
9000 *
9001 * @param aStorageController
9002 * @param data
9003 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9004 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9005 * @return
9006 */
9007HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9008 const settings::StorageController &data,
9009 const Guid *puuidRegistry,
9010 const Guid *puuidSnapshot)
9011{
9012 HRESULT rc = S_OK;
9013
9014 /* paranoia: detect duplicate attachments */
9015 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9016 it != data.llAttachedDevices.end();
9017 ++it)
9018 {
9019 const settings::AttachedDevice &ad = *it;
9020
9021 for (settings::AttachedDevicesList::const_iterator it2 = it;
9022 it2 != data.llAttachedDevices.end();
9023 ++it2)
9024 {
9025 if (it == it2)
9026 continue;
9027
9028 const settings::AttachedDevice &ad2 = *it2;
9029
9030 if ( ad.lPort == ad2.lPort
9031 && ad.lDevice == ad2.lDevice)
9032 {
9033 return setError(E_FAIL,
9034 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9035 aStorageController->i_getName().c_str(),
9036 ad.lPort,
9037 ad.lDevice,
9038 mUserData->s.strName.c_str());
9039 }
9040 }
9041 }
9042
9043 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9044 it != data.llAttachedDevices.end();
9045 ++it)
9046 {
9047 const settings::AttachedDevice &dev = *it;
9048 ComObjPtr<Medium> medium;
9049
9050 switch (dev.deviceType)
9051 {
9052 case DeviceType_Floppy:
9053 case DeviceType_DVD:
9054 if (dev.strHostDriveSrc.isNotEmpty())
9055 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9056 false /* fRefresh */, medium);
9057 else
9058 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9059 dev.uuid,
9060 false /* fRefresh */,
9061 false /* aSetError */,
9062 medium);
9063 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9064 // This is not an error. The host drive or UUID might have vanished, so just go
9065 // ahead without this removeable medium attachment
9066 rc = S_OK;
9067 break;
9068
9069 case DeviceType_HardDisk:
9070 {
9071 /* find a hard disk by UUID */
9072 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9073 if (FAILED(rc))
9074 {
9075 if (i_isSnapshotMachine())
9076 {
9077 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9078 // so the user knows that the bad disk is in a snapshot somewhere
9079 com::ErrorInfo info;
9080 return setError(E_FAIL,
9081 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9082 puuidSnapshot->raw(),
9083 info.getText().raw());
9084 }
9085 else
9086 return rc;
9087 }
9088
9089 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9090
9091 if (medium->i_getType() == MediumType_Immutable)
9092 {
9093 if (i_isSnapshotMachine())
9094 return setError(E_FAIL,
9095 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9096 "of the virtual machine '%s' ('%s')"),
9097 medium->i_getLocationFull().c_str(),
9098 dev.uuid.raw(),
9099 puuidSnapshot->raw(),
9100 mUserData->s.strName.c_str(),
9101 mData->m_strConfigFileFull.c_str());
9102
9103 return setError(E_FAIL,
9104 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9105 medium->i_getLocationFull().c_str(),
9106 dev.uuid.raw(),
9107 mUserData->s.strName.c_str(),
9108 mData->m_strConfigFileFull.c_str());
9109 }
9110
9111 if (medium->i_getType() == MediumType_MultiAttach)
9112 {
9113 if (i_isSnapshotMachine())
9114 return setError(E_FAIL,
9115 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9116 "of the virtual machine '%s' ('%s')"),
9117 medium->i_getLocationFull().c_str(),
9118 dev.uuid.raw(),
9119 puuidSnapshot->raw(),
9120 mUserData->s.strName.c_str(),
9121 mData->m_strConfigFileFull.c_str());
9122
9123 return setError(E_FAIL,
9124 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9125 medium->i_getLocationFull().c_str(),
9126 dev.uuid.raw(),
9127 mUserData->s.strName.c_str(),
9128 mData->m_strConfigFileFull.c_str());
9129 }
9130
9131 if ( !i_isSnapshotMachine()
9132 && medium->i_getChildren().size() != 0
9133 )
9134 return setError(E_FAIL,
9135 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9136 "because it has %d differencing child hard disks"),
9137 medium->i_getLocationFull().c_str(),
9138 dev.uuid.raw(),
9139 mUserData->s.strName.c_str(),
9140 mData->m_strConfigFileFull.c_str(),
9141 medium->i_getChildren().size());
9142
9143 if (i_findAttachment(mMediaData->mAttachments,
9144 medium))
9145 return setError(E_FAIL,
9146 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9147 medium->i_getLocationFull().c_str(),
9148 dev.uuid.raw(),
9149 mUserData->s.strName.c_str(),
9150 mData->m_strConfigFileFull.c_str());
9151
9152 break;
9153 }
9154
9155 default:
9156 return setError(E_FAIL,
9157 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9158 medium->i_getLocationFull().c_str(),
9159 mUserData->s.strName.c_str(),
9160 mData->m_strConfigFileFull.c_str());
9161 }
9162
9163 if (FAILED(rc))
9164 break;
9165
9166 /* Bandwidth groups are loaded at this point. */
9167 ComObjPtr<BandwidthGroup> pBwGroup;
9168
9169 if (!dev.strBwGroup.isEmpty())
9170 {
9171 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9172 if (FAILED(rc))
9173 return setError(E_FAIL,
9174 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9175 medium->i_getLocationFull().c_str(),
9176 dev.strBwGroup.c_str(),
9177 mUserData->s.strName.c_str(),
9178 mData->m_strConfigFileFull.c_str());
9179 pBwGroup->i_reference();
9180 }
9181
9182 const Bstr controllerName = aStorageController->i_getName();
9183 ComObjPtr<MediumAttachment> pAttachment;
9184 pAttachment.createObject();
9185 rc = pAttachment->init(this,
9186 medium,
9187 controllerName,
9188 dev.lPort,
9189 dev.lDevice,
9190 dev.deviceType,
9191 false,
9192 dev.fPassThrough,
9193 dev.fTempEject,
9194 dev.fNonRotational,
9195 dev.fDiscard,
9196 dev.fHotPluggable,
9197 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9198 if (FAILED(rc)) break;
9199
9200 /* associate the medium with this machine and snapshot */
9201 if (!medium.isNull())
9202 {
9203 AutoCaller medCaller(medium);
9204 if (FAILED(medCaller.rc())) return medCaller.rc();
9205 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9206
9207 if (i_isSnapshotMachine())
9208 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9209 else
9210 rc = medium->i_addBackReference(mData->mUuid);
9211 /* If the medium->addBackReference fails it sets an appropriate
9212 * error message, so no need to do any guesswork here. */
9213
9214 if (puuidRegistry)
9215 // caller wants registry ID to be set on all attached media (OVF import case)
9216 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9217 }
9218
9219 if (FAILED(rc))
9220 break;
9221
9222 /* back up mMediaData to let registeredInit() properly rollback on failure
9223 * (= limited accessibility) */
9224 i_setModified(IsModified_Storage);
9225 mMediaData.backup();
9226 mMediaData->mAttachments.push_back(pAttachment);
9227 }
9228
9229 return rc;
9230}
9231
9232/**
9233 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9234 *
9235 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9236 * @param aSnapshot where to return the found snapshot
9237 * @param aSetError true to set extended error info on failure
9238 */
9239HRESULT Machine::i_findSnapshotById(const Guid &aId,
9240 ComObjPtr<Snapshot> &aSnapshot,
9241 bool aSetError /* = false */)
9242{
9243 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9244
9245 if (!mData->mFirstSnapshot)
9246 {
9247 if (aSetError)
9248 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9249 return E_FAIL;
9250 }
9251
9252 if (aId.isZero())
9253 aSnapshot = mData->mFirstSnapshot;
9254 else
9255 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9256
9257 if (!aSnapshot)
9258 {
9259 if (aSetError)
9260 return setError(E_FAIL,
9261 tr("Could not find a snapshot with UUID {%s}"),
9262 aId.toString().c_str());
9263 return E_FAIL;
9264 }
9265
9266 return S_OK;
9267}
9268
9269/**
9270 * Returns the snapshot with the given name or fails of no such snapshot.
9271 *
9272 * @param aName snapshot name to find
9273 * @param aSnapshot where to return the found snapshot
9274 * @param aSetError true to set extended error info on failure
9275 */
9276HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9277 ComObjPtr<Snapshot> &aSnapshot,
9278 bool aSetError /* = false */)
9279{
9280 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9281
9282 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9283
9284 if (!mData->mFirstSnapshot)
9285 {
9286 if (aSetError)
9287 return setError(VBOX_E_OBJECT_NOT_FOUND,
9288 tr("This machine does not have any snapshots"));
9289 return VBOX_E_OBJECT_NOT_FOUND;
9290 }
9291
9292 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9293
9294 if (!aSnapshot)
9295 {
9296 if (aSetError)
9297 return setError(VBOX_E_OBJECT_NOT_FOUND,
9298 tr("Could not find a snapshot named '%s'"), strName.c_str());
9299 return VBOX_E_OBJECT_NOT_FOUND;
9300 }
9301
9302 return S_OK;
9303}
9304
9305/**
9306 * Returns a storage controller object with the given name.
9307 *
9308 * @param aName storage controller name to find
9309 * @param aStorageController where to return the found storage controller
9310 * @param aSetError true to set extended error info on failure
9311 */
9312HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9313 ComObjPtr<StorageController> &aStorageController,
9314 bool aSetError /* = false */)
9315{
9316 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9317
9318 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9319 it != mStorageControllers->end();
9320 ++it)
9321 {
9322 if ((*it)->i_getName() == aName)
9323 {
9324 aStorageController = (*it);
9325 return S_OK;
9326 }
9327 }
9328
9329 if (aSetError)
9330 return setError(VBOX_E_OBJECT_NOT_FOUND,
9331 tr("Could not find a storage controller named '%s'"),
9332 aName.c_str());
9333 return VBOX_E_OBJECT_NOT_FOUND;
9334}
9335
9336/**
9337 * Returns a USB controller object with the given name.
9338 *
9339 * @param aName USB controller name to find
9340 * @param aUSBController where to return the found USB controller
9341 * @param aSetError true to set extended error info on failure
9342 */
9343HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9344 ComObjPtr<USBController> &aUSBController,
9345 bool aSetError /* = false */)
9346{
9347 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9348
9349 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9350 it != mUSBControllers->end();
9351 ++it)
9352 {
9353 if ((*it)->i_getName() == aName)
9354 {
9355 aUSBController = (*it);
9356 return S_OK;
9357 }
9358 }
9359
9360 if (aSetError)
9361 return setError(VBOX_E_OBJECT_NOT_FOUND,
9362 tr("Could not find a storage controller named '%s'"),
9363 aName.c_str());
9364 return VBOX_E_OBJECT_NOT_FOUND;
9365}
9366
9367/**
9368 * Returns the number of USB controller instance of the given type.
9369 *
9370 * @param enmType USB controller type.
9371 */
9372ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9373{
9374 ULONG cCtrls = 0;
9375
9376 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9377 it != mUSBControllers->end();
9378 ++it)
9379 {
9380 if ((*it)->i_getControllerType() == enmType)
9381 cCtrls++;
9382 }
9383
9384 return cCtrls;
9385}
9386
9387HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9388 MediaData::AttachmentList &atts)
9389{
9390 AutoCaller autoCaller(this);
9391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9392
9393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9394
9395 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9396 it != mMediaData->mAttachments.end();
9397 ++it)
9398 {
9399 const ComObjPtr<MediumAttachment> &pAtt = *it;
9400 // should never happen, but deal with NULL pointers in the list.
9401 AssertStmt(!pAtt.isNull(), continue);
9402
9403 // getControllerName() needs caller+read lock
9404 AutoCaller autoAttCaller(pAtt);
9405 if (FAILED(autoAttCaller.rc()))
9406 {
9407 atts.clear();
9408 return autoAttCaller.rc();
9409 }
9410 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9411
9412 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9413 atts.push_back(pAtt);
9414 }
9415
9416 return S_OK;
9417}
9418
9419
9420/**
9421 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9422 * file if the machine name was changed and about creating a new settings file
9423 * if this is a new machine.
9424 *
9425 * @note Must be never called directly but only from #saveSettings().
9426 */
9427HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9428{
9429 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9430
9431 HRESULT rc = S_OK;
9432
9433 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9434
9435 /// @todo need to handle primary group change, too
9436
9437 /* attempt to rename the settings file if machine name is changed */
9438 if ( mUserData->s.fNameSync
9439 && mUserData.isBackedUp()
9440 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9441 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9442 )
9443 {
9444 bool dirRenamed = false;
9445 bool fileRenamed = false;
9446
9447 Utf8Str configFile, newConfigFile;
9448 Utf8Str configFilePrev, newConfigFilePrev;
9449 Utf8Str configDir, newConfigDir;
9450
9451 do
9452 {
9453 int vrc = VINF_SUCCESS;
9454
9455 Utf8Str name = mUserData.backedUpData()->s.strName;
9456 Utf8Str newName = mUserData->s.strName;
9457 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9458 if (group == "/")
9459 group.setNull();
9460 Utf8Str newGroup = mUserData->s.llGroups.front();
9461 if (newGroup == "/")
9462 newGroup.setNull();
9463
9464 configFile = mData->m_strConfigFileFull;
9465
9466 /* first, rename the directory if it matches the group and machine name */
9467 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9468 group.c_str(), RTPATH_DELIMITER, name.c_str());
9469 /** @todo hack, make somehow use of ComposeMachineFilename */
9470 if (mUserData->s.fDirectoryIncludesUUID)
9471 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9472 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9473 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9474 /** @todo hack, make somehow use of ComposeMachineFilename */
9475 if (mUserData->s.fDirectoryIncludesUUID)
9476 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9477 configDir = configFile;
9478 configDir.stripFilename();
9479 newConfigDir = configDir;
9480 if ( configDir.length() >= groupPlusName.length()
9481 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9482 groupPlusName.c_str()))
9483 {
9484 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9485 Utf8Str newConfigBaseDir(newConfigDir);
9486 newConfigDir.append(newGroupPlusName);
9487 /* consistency: use \ if appropriate on the platform */
9488 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9489 /* new dir and old dir cannot be equal here because of 'if'
9490 * above and because name != newName */
9491 Assert(configDir != newConfigDir);
9492 if (!fSettingsFileIsNew)
9493 {
9494 /* perform real rename only if the machine is not new */
9495 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9496 if ( vrc == VERR_FILE_NOT_FOUND
9497 || vrc == VERR_PATH_NOT_FOUND)
9498 {
9499 /* create the parent directory, then retry renaming */
9500 Utf8Str parent(newConfigDir);
9501 parent.stripFilename();
9502 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9503 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9504 }
9505 if (RT_FAILURE(vrc))
9506 {
9507 rc = setError(E_FAIL,
9508 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9509 configDir.c_str(),
9510 newConfigDir.c_str(),
9511 vrc);
9512 break;
9513 }
9514 /* delete subdirectories which are no longer needed */
9515 Utf8Str dir(configDir);
9516 dir.stripFilename();
9517 while (dir != newConfigBaseDir && dir != ".")
9518 {
9519 vrc = RTDirRemove(dir.c_str());
9520 if (RT_FAILURE(vrc))
9521 break;
9522 dir.stripFilename();
9523 }
9524 dirRenamed = true;
9525 }
9526 }
9527
9528 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9529 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9530
9531 /* then try to rename the settings file itself */
9532 if (newConfigFile != configFile)
9533 {
9534 /* get the path to old settings file in renamed directory */
9535 configFile = Utf8StrFmt("%s%c%s",
9536 newConfigDir.c_str(),
9537 RTPATH_DELIMITER,
9538 RTPathFilename(configFile.c_str()));
9539 if (!fSettingsFileIsNew)
9540 {
9541 /* perform real rename only if the machine is not new */
9542 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9543 if (RT_FAILURE(vrc))
9544 {
9545 rc = setError(E_FAIL,
9546 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9547 configFile.c_str(),
9548 newConfigFile.c_str(),
9549 vrc);
9550 break;
9551 }
9552 fileRenamed = true;
9553 configFilePrev = configFile;
9554 configFilePrev += "-prev";
9555 newConfigFilePrev = newConfigFile;
9556 newConfigFilePrev += "-prev";
9557 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9558 }
9559 }
9560
9561 // update m_strConfigFileFull amd mConfigFile
9562 mData->m_strConfigFileFull = newConfigFile;
9563 // compute the relative path too
9564 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9565
9566 // store the old and new so that VirtualBox::i_saveSettings() can update
9567 // the media registry
9568 if ( mData->mRegistered
9569 && (configDir != newConfigDir || configFile != newConfigFile))
9570 {
9571 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9572
9573 if (pfNeedsGlobalSaveSettings)
9574 *pfNeedsGlobalSaveSettings = true;
9575 }
9576
9577 // in the saved state file path, replace the old directory with the new directory
9578 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9579 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9580
9581 // and do the same thing for the saved state file paths of all the online snapshots
9582 if (mData->mFirstSnapshot)
9583 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9584 newConfigDir.c_str());
9585 }
9586 while (0);
9587
9588 if (FAILED(rc))
9589 {
9590 /* silently try to rename everything back */
9591 if (fileRenamed)
9592 {
9593 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9594 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9595 }
9596 if (dirRenamed)
9597 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9598 }
9599
9600 if (FAILED(rc)) return rc;
9601 }
9602
9603 if (fSettingsFileIsNew)
9604 {
9605 /* create a virgin config file */
9606 int vrc = VINF_SUCCESS;
9607
9608 /* ensure the settings directory exists */
9609 Utf8Str path(mData->m_strConfigFileFull);
9610 path.stripFilename();
9611 if (!RTDirExists(path.c_str()))
9612 {
9613 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9614 if (RT_FAILURE(vrc))
9615 {
9616 return setError(E_FAIL,
9617 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9618 path.c_str(),
9619 vrc);
9620 }
9621 }
9622
9623 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9624 path = Utf8Str(mData->m_strConfigFileFull);
9625 RTFILE f = NIL_RTFILE;
9626 vrc = RTFileOpen(&f, path.c_str(),
9627 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9628 if (RT_FAILURE(vrc))
9629 return setError(E_FAIL,
9630 tr("Could not create the settings file '%s' (%Rrc)"),
9631 path.c_str(),
9632 vrc);
9633 RTFileClose(f);
9634 }
9635
9636 return rc;
9637}
9638
9639/**
9640 * Saves and commits machine data, user data and hardware data.
9641 *
9642 * Note that on failure, the data remains uncommitted.
9643 *
9644 * @a aFlags may combine the following flags:
9645 *
9646 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9647 * Used when saving settings after an operation that makes them 100%
9648 * correspond to the settings from the current snapshot.
9649 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9650 * #isReallyModified() returns false. This is necessary for cases when we
9651 * change machine data directly, not through the backup()/commit() mechanism.
9652 * - SaveS_Force: settings will be saved without doing a deep compare of the
9653 * settings structures. This is used when this is called because snapshots
9654 * have changed to avoid the overhead of the deep compare.
9655 *
9656 * @note Must be called from under this object's write lock. Locks children for
9657 * writing.
9658 *
9659 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9660 * initialized to false and that will be set to true by this function if
9661 * the caller must invoke VirtualBox::i_saveSettings() because the global
9662 * settings have changed. This will happen if a machine rename has been
9663 * saved and the global machine and media registries will therefore need
9664 * updating.
9665 */
9666HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9667 int aFlags /*= 0*/)
9668{
9669 LogFlowThisFuncEnter();
9670
9671 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9672
9673 /* make sure child objects are unable to modify the settings while we are
9674 * saving them */
9675 i_ensureNoStateDependencies();
9676
9677 AssertReturn(!i_isSnapshotMachine(),
9678 E_FAIL);
9679
9680 HRESULT rc = S_OK;
9681 bool fNeedsWrite = false;
9682
9683 /* First, prepare to save settings. It will care about renaming the
9684 * settings directory and file if the machine name was changed and about
9685 * creating a new settings file if this is a new machine. */
9686 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9687 if (FAILED(rc)) return rc;
9688
9689 // keep a pointer to the current settings structures
9690 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9691 settings::MachineConfigFile *pNewConfig = NULL;
9692
9693 try
9694 {
9695 // make a fresh one to have everyone write stuff into
9696 pNewConfig = new settings::MachineConfigFile(NULL);
9697 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9698
9699 // now go and copy all the settings data from COM to the settings structures
9700 // (this calles i_saveSettings() on all the COM objects in the machine)
9701 i_copyMachineDataToSettings(*pNewConfig);
9702
9703 if (aFlags & SaveS_ResetCurStateModified)
9704 {
9705 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9706 mData->mCurrentStateModified = FALSE;
9707 fNeedsWrite = true; // always, no need to compare
9708 }
9709 else if (aFlags & SaveS_Force)
9710 {
9711 fNeedsWrite = true; // always, no need to compare
9712 }
9713 else
9714 {
9715 if (!mData->mCurrentStateModified)
9716 {
9717 // do a deep compare of the settings that we just saved with the settings
9718 // previously stored in the config file; this invokes MachineConfigFile::operator==
9719 // which does a deep compare of all the settings, which is expensive but less expensive
9720 // than writing out XML in vain
9721 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9722
9723 // could still be modified if any settings changed
9724 mData->mCurrentStateModified = fAnySettingsChanged;
9725
9726 fNeedsWrite = fAnySettingsChanged;
9727 }
9728 else
9729 fNeedsWrite = true;
9730 }
9731
9732 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9733
9734 if (fNeedsWrite)
9735 // now spit it all out!
9736 pNewConfig->write(mData->m_strConfigFileFull);
9737
9738 mData->pMachineConfigFile = pNewConfig;
9739 delete pOldConfig;
9740 i_commit();
9741
9742 // after saving settings, we are no longer different from the XML on disk
9743 mData->flModifications = 0;
9744 }
9745 catch (HRESULT err)
9746 {
9747 // we assume that error info is set by the thrower
9748 rc = err;
9749
9750 // restore old config
9751 delete pNewConfig;
9752 mData->pMachineConfigFile = pOldConfig;
9753 }
9754 catch (...)
9755 {
9756 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9757 }
9758
9759 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9760 {
9761 /* Fire the data change event, even on failure (since we've already
9762 * committed all data). This is done only for SessionMachines because
9763 * mutable Machine instances are always not registered (i.e. private
9764 * to the client process that creates them) and thus don't need to
9765 * inform callbacks. */
9766 if (i_isSessionMachine())
9767 mParent->i_onMachineDataChange(mData->mUuid);
9768 }
9769
9770 LogFlowThisFunc(("rc=%08X\n", rc));
9771 LogFlowThisFuncLeave();
9772 return rc;
9773}
9774
9775/**
9776 * Implementation for saving the machine settings into the given
9777 * settings::MachineConfigFile instance. This copies machine extradata
9778 * from the previous machine config file in the instance data, if any.
9779 *
9780 * This gets called from two locations:
9781 *
9782 * -- Machine::i_saveSettings(), during the regular XML writing;
9783 *
9784 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9785 * exported to OVF and we write the VirtualBox proprietary XML
9786 * into a <vbox:Machine> tag.
9787 *
9788 * This routine fills all the fields in there, including snapshots, *except*
9789 * for the following:
9790 *
9791 * -- fCurrentStateModified. There is some special logic associated with that.
9792 *
9793 * The caller can then call MachineConfigFile::write() or do something else
9794 * with it.
9795 *
9796 * Caller must hold the machine lock!
9797 *
9798 * This throws XML errors and HRESULT, so the caller must have a catch block!
9799 */
9800void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9801{
9802 // deep copy extradata
9803 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9804
9805 config.uuid = mData->mUuid;
9806
9807 // copy name, description, OS type, teleport, UTC etc.
9808 config.machineUserData = mUserData->s;
9809
9810 // Encode the Icon Override data from Machine and store on config userdata.
9811 std::vector<BYTE> iconByte;
9812 getIcon(iconByte);
9813 ssize_t cbData = iconByte.size();
9814 if (cbData > 0)
9815 {
9816 ssize_t cchOut = RTBase64EncodedLength(cbData);
9817 Utf8Str strIconData;
9818 strIconData.reserve(cchOut+1);
9819 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9820 strIconData.mutableRaw(), strIconData.capacity(),
9821 NULL);
9822 if (RT_FAILURE(vrc))
9823 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9824 strIconData.jolt();
9825 config.machineUserData.ovIcon = strIconData;
9826 }
9827 else
9828 config.machineUserData.ovIcon.setNull();
9829
9830 if ( mData->mMachineState == MachineState_Saved
9831 || mData->mMachineState == MachineState_Restoring
9832 // when deleting a snapshot we may or may not have a saved state in the current state,
9833 // so let's not assert here please
9834 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9835 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9836 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9837 && (!mSSData->strStateFilePath.isEmpty())
9838 )
9839 )
9840 {
9841 Assert(!mSSData->strStateFilePath.isEmpty());
9842 /* try to make the file name relative to the settings file dir */
9843 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9844 }
9845 else
9846 {
9847 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9848 config.strStateFile.setNull();
9849 }
9850
9851 if (mData->mCurrentSnapshot)
9852 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9853 else
9854 config.uuidCurrentSnapshot.clear();
9855
9856 config.timeLastStateChange = mData->mLastStateChange;
9857 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9858 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9859
9860 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9861 if (FAILED(rc)) throw rc;
9862
9863 rc = i_saveStorageControllers(config.storageMachine);
9864 if (FAILED(rc)) throw rc;
9865
9866 // save machine's media registry if this is VirtualBox 4.0 or later
9867 if (config.canHaveOwnMediaRegistry())
9868 {
9869 // determine machine folder
9870 Utf8Str strMachineFolder = i_getSettingsFileFull();
9871 strMachineFolder.stripFilename();
9872 mParent->i_saveMediaRegistry(config.mediaRegistry,
9873 i_getId(), // only media with registry ID == machine UUID
9874 strMachineFolder);
9875 // this throws HRESULT
9876 }
9877
9878 // save snapshots
9879 rc = i_saveAllSnapshots(config);
9880 if (FAILED(rc)) throw rc;
9881}
9882
9883/**
9884 * Saves all snapshots of the machine into the given machine config file. Called
9885 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9886 * @param config
9887 * @return
9888 */
9889HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9890{
9891 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9892
9893 HRESULT rc = S_OK;
9894
9895 try
9896 {
9897 config.llFirstSnapshot.clear();
9898
9899 if (mData->mFirstSnapshot)
9900 {
9901 settings::Snapshot snapNew;
9902 config.llFirstSnapshot.push_back(snapNew);
9903
9904 // get reference to the fresh copy of the snapshot on the list and
9905 // work on that copy directly to avoid excessive copying later
9906 settings::Snapshot &snap = config.llFirstSnapshot.front();
9907
9908 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9909 if (FAILED(rc)) throw rc;
9910 }
9911
9912// if (mType == IsSessionMachine)
9913// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9914
9915 }
9916 catch (HRESULT err)
9917 {
9918 /* we assume that error info is set by the thrower */
9919 rc = err;
9920 }
9921 catch (...)
9922 {
9923 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9924 }
9925
9926 return rc;
9927}
9928
9929/**
9930 * Saves the VM hardware configuration. It is assumed that the
9931 * given node is empty.
9932 *
9933 * @param data Reference to the settings object for the hardware config.
9934 * @param pDbg Pointer to the settings object for the debugging config
9935 * which happens to live in mHWData.
9936 * @param pAutostart Pointer to the settings object for the autostart config
9937 * which happens to live in mHWData.
9938 */
9939HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9940 settings::Autostart *pAutostart)
9941{
9942 HRESULT rc = S_OK;
9943
9944 try
9945 {
9946 /* The hardware version attribute (optional).
9947 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9948 if ( mHWData->mHWVersion == "1"
9949 && mSSData->strStateFilePath.isEmpty()
9950 )
9951 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9952 other point needs to be found where this can be done. */
9953
9954 data.strVersion = mHWData->mHWVersion;
9955 data.uuid = mHWData->mHardwareUUID;
9956
9957 // CPU
9958 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9959 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9960 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9961 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9962 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9963 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9964 data.fPAE = !!mHWData->mPAEEnabled;
9965 data.enmLongMode = mHWData->mLongMode;
9966 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9967 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9968
9969 /* Standard and Extended CPUID leafs. */
9970 data.llCpuIdLeafs.clear();
9971 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9972 {
9973 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9974 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9975 }
9976 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9977 {
9978 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9979 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9980 }
9981
9982 data.cCPUs = mHWData->mCPUCount;
9983 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9984 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9985
9986 data.llCpus.clear();
9987 if (data.fCpuHotPlug)
9988 {
9989 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9990 {
9991 if (mHWData->mCPUAttached[idx])
9992 {
9993 settings::Cpu cpu;
9994 cpu.ulId = idx;
9995 data.llCpus.push_back(cpu);
9996 }
9997 }
9998 }
9999
10000 // memory
10001 data.ulMemorySizeMB = mHWData->mMemorySize;
10002 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10003
10004 // firmware
10005 data.firmwareType = mHWData->mFirmwareType;
10006
10007 // HID
10008 data.pointingHIDType = mHWData->mPointingHIDType;
10009 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10010
10011 // chipset
10012 data.chipsetType = mHWData->mChipsetType;
10013
10014 // paravirt
10015 data.paravirtProvider = mHWData->mParavirtProvider;
10016
10017
10018 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10019
10020 // HPET
10021 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10022
10023 // boot order
10024 data.mapBootOrder.clear();
10025 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10026 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10027
10028 // display
10029 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10030 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10031 data.cMonitors = mHWData->mMonitorCount;
10032 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10033 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10034 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10035 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10036 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10037 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10038 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10039 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10040 {
10041 if (mHWData->maVideoCaptureScreens[i])
10042 ASMBitSet(&data.u64VideoCaptureScreens, i);
10043 else
10044 ASMBitClear(&data.u64VideoCaptureScreens, i);
10045 }
10046 /* store relative video capture file if possible */
10047 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10048
10049 /* VRDEServer settings (optional) */
10050 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10051 if (FAILED(rc)) throw rc;
10052
10053 /* BIOS (required) */
10054 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10055 if (FAILED(rc)) throw rc;
10056
10057 /* USB Controller (required) */
10058 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10059 {
10060 ComObjPtr<USBController> ctrl = *it;
10061 settings::USBController settingsCtrl;
10062
10063 settingsCtrl.strName = ctrl->i_getName();
10064 settingsCtrl.enmType = ctrl->i_getControllerType();
10065
10066 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10067 }
10068
10069 /* USB device filters (required) */
10070 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10071 if (FAILED(rc)) throw rc;
10072
10073 /* Network adapters (required) */
10074 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10075 data.llNetworkAdapters.clear();
10076 /* Write out only the nominal number of network adapters for this
10077 * chipset type. Since Machine::commit() hasn't been called there
10078 * may be extra NIC settings in the vector. */
10079 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10080 {
10081 settings::NetworkAdapter nic;
10082 nic.ulSlot = (uint32_t)slot;
10083 /* paranoia check... must not be NULL, but must not crash either. */
10084 if (mNetworkAdapters[slot])
10085 {
10086 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10087 if (FAILED(rc)) throw rc;
10088
10089 data.llNetworkAdapters.push_back(nic);
10090 }
10091 }
10092
10093 /* Serial ports */
10094 data.llSerialPorts.clear();
10095 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10096 {
10097 settings::SerialPort s;
10098 s.ulSlot = slot;
10099 rc = mSerialPorts[slot]->i_saveSettings(s);
10100 if (FAILED(rc)) return rc;
10101
10102 data.llSerialPorts.push_back(s);
10103 }
10104
10105 /* Parallel ports */
10106 data.llParallelPorts.clear();
10107 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10108 {
10109 settings::ParallelPort p;
10110 p.ulSlot = slot;
10111 rc = mParallelPorts[slot]->i_saveSettings(p);
10112 if (FAILED(rc)) return rc;
10113
10114 data.llParallelPorts.push_back(p);
10115 }
10116
10117 /* Audio adapter */
10118 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10119 if (FAILED(rc)) return rc;
10120
10121 /* Shared folders */
10122 data.llSharedFolders.clear();
10123 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10124 it != mHWData->mSharedFolders.end();
10125 ++it)
10126 {
10127 SharedFolder *pSF = *it;
10128 AutoCaller sfCaller(pSF);
10129 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10130 settings::SharedFolder sf;
10131 sf.strName = pSF->i_getName();
10132 sf.strHostPath = pSF->i_getHostPath();
10133 sf.fWritable = !!pSF->i_isWritable();
10134 sf.fAutoMount = !!pSF->i_isAutoMounted();
10135
10136 data.llSharedFolders.push_back(sf);
10137 }
10138
10139 // clipboard
10140 data.clipboardMode = mHWData->mClipboardMode;
10141
10142 // drag'n'drop
10143 data.dndMode = mHWData->mDnDMode;
10144
10145 /* Guest */
10146 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10147
10148 // IO settings
10149 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10150 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10151
10152 /* BandwidthControl (required) */
10153 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10154 if (FAILED(rc)) throw rc;
10155
10156 /* Host PCI devices */
10157 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10158 it != mHWData->mPCIDeviceAssignments.end();
10159 ++it)
10160 {
10161 ComObjPtr<PCIDeviceAttachment> pda = *it;
10162 settings::HostPCIDeviceAttachment hpda;
10163
10164 rc = pda->i_saveSettings(hpda);
10165 if (FAILED(rc)) throw rc;
10166
10167 data.pciAttachments.push_back(hpda);
10168 }
10169
10170
10171 // guest properties
10172 data.llGuestProperties.clear();
10173#ifdef VBOX_WITH_GUEST_PROPS
10174 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10175 it != mHWData->mGuestProperties.end();
10176 ++it)
10177 {
10178 HWData::GuestProperty property = it->second;
10179
10180 /* Remove transient guest properties at shutdown unless we
10181 * are saving state */
10182 if ( ( mData->mMachineState == MachineState_PoweredOff
10183 || mData->mMachineState == MachineState_Aborted
10184 || mData->mMachineState == MachineState_Teleported)
10185 && ( property.mFlags & guestProp::TRANSIENT
10186 || property.mFlags & guestProp::TRANSRESET))
10187 continue;
10188 settings::GuestProperty prop;
10189 prop.strName = it->first;
10190 prop.strValue = property.strValue;
10191 prop.timestamp = property.mTimestamp;
10192 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10193 guestProp::writeFlags(property.mFlags, szFlags);
10194 prop.strFlags = szFlags;
10195
10196 data.llGuestProperties.push_back(prop);
10197 }
10198
10199 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10200 /* I presume this doesn't require a backup(). */
10201 mData->mGuestPropertiesModified = FALSE;
10202#endif /* VBOX_WITH_GUEST_PROPS defined */
10203
10204 *pDbg = mHWData->mDebugging;
10205 *pAutostart = mHWData->mAutostart;
10206
10207 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10208 }
10209 catch(std::bad_alloc &)
10210 {
10211 return E_OUTOFMEMORY;
10212 }
10213
10214 AssertComRC(rc);
10215 return rc;
10216}
10217
10218/**
10219 * Saves the storage controller configuration.
10220 *
10221 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10222 */
10223HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10224{
10225 data.llStorageControllers.clear();
10226
10227 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10228 it != mStorageControllers->end();
10229 ++it)
10230 {
10231 HRESULT rc;
10232 ComObjPtr<StorageController> pCtl = *it;
10233
10234 settings::StorageController ctl;
10235 ctl.strName = pCtl->i_getName();
10236 ctl.controllerType = pCtl->i_getControllerType();
10237 ctl.storageBus = pCtl->i_getStorageBus();
10238 ctl.ulInstance = pCtl->i_getInstance();
10239 ctl.fBootable = pCtl->i_getBootable();
10240
10241 /* Save the port count. */
10242 ULONG portCount;
10243 rc = pCtl->COMGETTER(PortCount)(&portCount);
10244 ComAssertComRCRet(rc, rc);
10245 ctl.ulPortCount = portCount;
10246
10247 /* Save fUseHostIOCache */
10248 BOOL fUseHostIOCache;
10249 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10250 ComAssertComRCRet(rc, rc);
10251 ctl.fUseHostIOCache = !!fUseHostIOCache;
10252
10253 /* Save IDE emulation settings. */
10254 if (ctl.controllerType == StorageControllerType_IntelAhci)
10255 {
10256 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10257 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10258 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10259 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10260 )
10261 ComAssertComRCRet(rc, rc);
10262 }
10263
10264 /* save the devices now. */
10265 rc = i_saveStorageDevices(pCtl, ctl);
10266 ComAssertComRCRet(rc, rc);
10267
10268 data.llStorageControllers.push_back(ctl);
10269 }
10270
10271 return S_OK;
10272}
10273
10274/**
10275 * Saves the hard disk configuration.
10276 */
10277HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10278 settings::StorageController &data)
10279{
10280 MediaData::AttachmentList atts;
10281
10282 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10283 if (FAILED(rc)) return rc;
10284
10285 data.llAttachedDevices.clear();
10286 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10287 it != atts.end();
10288 ++it)
10289 {
10290 settings::AttachedDevice dev;
10291 IMediumAttachment *iA = *it;
10292 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10293 Medium *pMedium = pAttach->i_getMedium();
10294
10295 dev.deviceType = pAttach->i_getType();
10296 dev.lPort = pAttach->i_getPort();
10297 dev.lDevice = pAttach->i_getDevice();
10298 dev.fPassThrough = pAttach->i_getPassthrough();
10299 dev.fHotPluggable = pAttach->i_getHotPluggable();
10300 if (pMedium)
10301 {
10302 if (pMedium->i_isHostDrive())
10303 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10304 else
10305 dev.uuid = pMedium->i_getId();
10306 dev.fTempEject = pAttach->i_getTempEject();
10307 dev.fNonRotational = pAttach->i_getNonRotational();
10308 dev.fDiscard = pAttach->i_getDiscard();
10309 }
10310
10311 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10312
10313 data.llAttachedDevices.push_back(dev);
10314 }
10315
10316 return S_OK;
10317}
10318
10319/**
10320 * Saves machine state settings as defined by aFlags
10321 * (SaveSTS_* values).
10322 *
10323 * @param aFlags Combination of SaveSTS_* flags.
10324 *
10325 * @note Locks objects for writing.
10326 */
10327HRESULT Machine::i_saveStateSettings(int aFlags)
10328{
10329 if (aFlags == 0)
10330 return S_OK;
10331
10332 AutoCaller autoCaller(this);
10333 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10334
10335 /* This object's write lock is also necessary to serialize file access
10336 * (prevent concurrent reads and writes) */
10337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10338
10339 HRESULT rc = S_OK;
10340
10341 Assert(mData->pMachineConfigFile);
10342
10343 try
10344 {
10345 if (aFlags & SaveSTS_CurStateModified)
10346 mData->pMachineConfigFile->fCurrentStateModified = true;
10347
10348 if (aFlags & SaveSTS_StateFilePath)
10349 {
10350 if (!mSSData->strStateFilePath.isEmpty())
10351 /* try to make the file name relative to the settings file dir */
10352 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10353 else
10354 mData->pMachineConfigFile->strStateFile.setNull();
10355 }
10356
10357 if (aFlags & SaveSTS_StateTimeStamp)
10358 {
10359 Assert( mData->mMachineState != MachineState_Aborted
10360 || mSSData->strStateFilePath.isEmpty());
10361
10362 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10363
10364 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10365//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10366 }
10367
10368 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10369 }
10370 catch (...)
10371 {
10372 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10373 }
10374
10375 return rc;
10376}
10377
10378/**
10379 * Ensures that the given medium is added to a media registry. If this machine
10380 * was created with 4.0 or later, then the machine registry is used. Otherwise
10381 * the global VirtualBox media registry is used.
10382 *
10383 * Caller must NOT hold machine lock, media tree or any medium locks!
10384 *
10385 * @param pMedium
10386 */
10387void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10388{
10389 /* Paranoia checks: do not hold machine or media tree locks. */
10390 AssertReturnVoid(!isWriteLockOnCurrentThread());
10391 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10392
10393 ComObjPtr<Medium> pBase;
10394 {
10395 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10396 pBase = pMedium->i_getBase();
10397 }
10398
10399 /* Paranoia checks: do not hold medium locks. */
10400 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10401 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10402
10403 // decide which medium registry to use now that the medium is attached:
10404 Guid uuid;
10405 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10406 // machine XML is VirtualBox 4.0 or higher:
10407 uuid = i_getId(); // machine UUID
10408 else
10409 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10410
10411 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10412 mParent->i_markRegistryModified(uuid);
10413
10414 /* For more complex hard disk structures it can happen that the base
10415 * medium isn't yet associated with any medium registry. Do that now. */
10416 if (pMedium != pBase)
10417 {
10418 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10419 mParent->i_markRegistryModified(uuid);
10420 }
10421}
10422
10423/**
10424 * Creates differencing hard disks for all normal hard disks attached to this
10425 * machine and a new set of attachments to refer to created disks.
10426 *
10427 * Used when taking a snapshot or when deleting the current state. Gets called
10428 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10429 *
10430 * This method assumes that mMediaData contains the original hard disk attachments
10431 * it needs to create diffs for. On success, these attachments will be replaced
10432 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10433 * called to delete created diffs which will also rollback mMediaData and restore
10434 * whatever was backed up before calling this method.
10435 *
10436 * Attachments with non-normal hard disks are left as is.
10437 *
10438 * If @a aOnline is @c false then the original hard disks that require implicit
10439 * diffs will be locked for reading. Otherwise it is assumed that they are
10440 * already locked for writing (when the VM was started). Note that in the latter
10441 * case it is responsibility of the caller to lock the newly created diffs for
10442 * writing if this method succeeds.
10443 *
10444 * @param aProgress Progress object to run (must contain at least as
10445 * many operations left as the number of hard disks
10446 * attached).
10447 * @param aOnline Whether the VM was online prior to this operation.
10448 *
10449 * @note The progress object is not marked as completed, neither on success nor
10450 * on failure. This is a responsibility of the caller.
10451 *
10452 * @note Locks this object and the media tree for writing.
10453 */
10454HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10455 ULONG aWeight,
10456 bool aOnline)
10457{
10458 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10459
10460 AutoCaller autoCaller(this);
10461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10462
10463 AutoMultiWriteLock2 alock(this->lockHandle(),
10464 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10465
10466 /* must be in a protective state because we release the lock below */
10467 AssertReturn( mData->mMachineState == MachineState_Saving
10468 || mData->mMachineState == MachineState_LiveSnapshotting
10469 || mData->mMachineState == MachineState_RestoringSnapshot
10470 || mData->mMachineState == MachineState_DeletingSnapshot
10471 , E_FAIL);
10472
10473 HRESULT rc = S_OK;
10474
10475 // use appropriate locked media map (online or offline)
10476 MediumLockListMap lockedMediaOffline;
10477 MediumLockListMap *lockedMediaMap;
10478 if (aOnline)
10479 lockedMediaMap = &mData->mSession.mLockedMedia;
10480 else
10481 lockedMediaMap = &lockedMediaOffline;
10482
10483 try
10484 {
10485 if (!aOnline)
10486 {
10487 /* lock all attached hard disks early to detect "in use"
10488 * situations before creating actual diffs */
10489 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10490 it != mMediaData->mAttachments.end();
10491 ++it)
10492 {
10493 MediumAttachment* pAtt = *it;
10494 if (pAtt->i_getType() == DeviceType_HardDisk)
10495 {
10496 Medium* pMedium = pAtt->i_getMedium();
10497 Assert(pMedium);
10498
10499 MediumLockList *pMediumLockList(new MediumLockList());
10500 alock.release();
10501 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10502 false /* fMediumLockWrite */,
10503 NULL,
10504 *pMediumLockList);
10505 alock.acquire();
10506 if (FAILED(rc))
10507 {
10508 delete pMediumLockList;
10509 throw rc;
10510 }
10511 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10512 if (FAILED(rc))
10513 {
10514 throw setError(rc,
10515 tr("Collecting locking information for all attached media failed"));
10516 }
10517 }
10518 }
10519
10520 /* Now lock all media. If this fails, nothing is locked. */
10521 alock.release();
10522 rc = lockedMediaMap->Lock();
10523 alock.acquire();
10524 if (FAILED(rc))
10525 {
10526 throw setError(rc,
10527 tr("Locking of attached media failed"));
10528 }
10529 }
10530
10531 /* remember the current list (note that we don't use backup() since
10532 * mMediaData may be already backed up) */
10533 MediaData::AttachmentList atts = mMediaData->mAttachments;
10534
10535 /* start from scratch */
10536 mMediaData->mAttachments.clear();
10537
10538 /* go through remembered attachments and create diffs for normal hard
10539 * disks and attach them */
10540 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10541 it != atts.end();
10542 ++it)
10543 {
10544 MediumAttachment* pAtt = *it;
10545
10546 DeviceType_T devType = pAtt->i_getType();
10547 Medium* pMedium = pAtt->i_getMedium();
10548
10549 if ( devType != DeviceType_HardDisk
10550 || pMedium == NULL
10551 || pMedium->i_getType() != MediumType_Normal)
10552 {
10553 /* copy the attachment as is */
10554
10555 /** @todo the progress object created in Console::TakeSnaphot
10556 * only expects operations for hard disks. Later other
10557 * device types need to show up in the progress as well. */
10558 if (devType == DeviceType_HardDisk)
10559 {
10560 if (pMedium == NULL)
10561 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10562 aWeight); // weight
10563 else
10564 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10565 pMedium->i_getBase()->i_getName().c_str()).raw(),
10566 aWeight); // weight
10567 }
10568
10569 mMediaData->mAttachments.push_back(pAtt);
10570 continue;
10571 }
10572
10573 /* need a diff */
10574 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10575 pMedium->i_getBase()->i_getName().c_str()).raw(),
10576 aWeight); // weight
10577
10578 Utf8Str strFullSnapshotFolder;
10579 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10580
10581 ComObjPtr<Medium> diff;
10582 diff.createObject();
10583 // store the diff in the same registry as the parent
10584 // (this cannot fail here because we can't create implicit diffs for
10585 // unregistered images)
10586 Guid uuidRegistryParent;
10587 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10588 Assert(fInRegistry); NOREF(fInRegistry);
10589 rc = diff->init(mParent,
10590 pMedium->i_getPreferredDiffFormat(),
10591 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10592 uuidRegistryParent);
10593 if (FAILED(rc)) throw rc;
10594
10595 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10596 * the push_back? Looks like we're going to release medium with the
10597 * wrong kind of lock (general issue with if we fail anywhere at all)
10598 * and an orphaned VDI in the snapshots folder. */
10599
10600 /* update the appropriate lock list */
10601 MediumLockList *pMediumLockList;
10602 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10603 AssertComRCThrowRC(rc);
10604 if (aOnline)
10605 {
10606 alock.release();
10607 /* The currently attached medium will be read-only, change
10608 * the lock type to read. */
10609 rc = pMediumLockList->Update(pMedium, false);
10610 alock.acquire();
10611 AssertComRCThrowRC(rc);
10612 }
10613
10614 /* release the locks before the potentially lengthy operation */
10615 alock.release();
10616 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10617 pMediumLockList,
10618 NULL /* aProgress */,
10619 true /* aWait */);
10620 alock.acquire();
10621 if (FAILED(rc)) throw rc;
10622
10623 /* actual lock list update is done in Medium::commitMedia */
10624
10625 rc = diff->i_addBackReference(mData->mUuid);
10626 AssertComRCThrowRC(rc);
10627
10628 /* add a new attachment */
10629 ComObjPtr<MediumAttachment> attachment;
10630 attachment.createObject();
10631 rc = attachment->init(this,
10632 diff,
10633 pAtt->i_getControllerName(),
10634 pAtt->i_getPort(),
10635 pAtt->i_getDevice(),
10636 DeviceType_HardDisk,
10637 true /* aImplicit */,
10638 false /* aPassthrough */,
10639 false /* aTempEject */,
10640 pAtt->i_getNonRotational(),
10641 pAtt->i_getDiscard(),
10642 pAtt->i_getHotPluggable(),
10643 pAtt->i_getBandwidthGroup());
10644 if (FAILED(rc)) throw rc;
10645
10646 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10647 AssertComRCThrowRC(rc);
10648 mMediaData->mAttachments.push_back(attachment);
10649 }
10650 }
10651 catch (HRESULT aRC) { rc = aRC; }
10652
10653 /* unlock all hard disks we locked when there is no VM */
10654 if (!aOnline)
10655 {
10656 ErrorInfoKeeper eik;
10657
10658 HRESULT rc1 = lockedMediaMap->Clear();
10659 AssertComRC(rc1);
10660 }
10661
10662 return rc;
10663}
10664
10665/**
10666 * Deletes implicit differencing hard disks created either by
10667 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10668 *
10669 * Note that to delete hard disks created by #AttachDevice() this method is
10670 * called from #fixupMedia() when the changes are rolled back.
10671 *
10672 * @note Locks this object and the media tree for writing.
10673 */
10674HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10675{
10676 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10677
10678 AutoCaller autoCaller(this);
10679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10680
10681 AutoMultiWriteLock2 alock(this->lockHandle(),
10682 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10683
10684 /* We absolutely must have backed up state. */
10685 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10686
10687 /* Check if there are any implicitly created diff images. */
10688 bool fImplicitDiffs = false;
10689 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10690 it != mMediaData->mAttachments.end();
10691 ++it)
10692 {
10693 const ComObjPtr<MediumAttachment> &pAtt = *it;
10694 if (pAtt->i_isImplicit())
10695 {
10696 fImplicitDiffs = true;
10697 break;
10698 }
10699 }
10700 /* If there is nothing to do, leave early. This saves lots of image locking
10701 * effort. It also avoids a MachineStateChanged event without real reason.
10702 * This is important e.g. when loading a VM config, because there should be
10703 * no events. Otherwise API clients can become thoroughly confused for
10704 * inaccessible VMs (the code for loading VM configs uses this method for
10705 * cleanup if the config makes no sense), as they take such events as an
10706 * indication that the VM is alive, and they would force the VM config to
10707 * be reread, leading to an endless loop. */
10708 if (!fImplicitDiffs)
10709 return S_OK;
10710
10711 HRESULT rc = S_OK;
10712 MachineState_T oldState = mData->mMachineState;
10713
10714 /* will release the lock before the potentially lengthy operation,
10715 * so protect with the special state (unless already protected) */
10716 if ( oldState != MachineState_Saving
10717 && oldState != MachineState_LiveSnapshotting
10718 && oldState != MachineState_RestoringSnapshot
10719 && oldState != MachineState_DeletingSnapshot
10720 && oldState != MachineState_DeletingSnapshotOnline
10721 && oldState != MachineState_DeletingSnapshotPaused
10722 )
10723 i_setMachineState(MachineState_SettingUp);
10724
10725 // use appropriate locked media map (online or offline)
10726 MediumLockListMap lockedMediaOffline;
10727 MediumLockListMap *lockedMediaMap;
10728 if (aOnline)
10729 lockedMediaMap = &mData->mSession.mLockedMedia;
10730 else
10731 lockedMediaMap = &lockedMediaOffline;
10732
10733 try
10734 {
10735 if (!aOnline)
10736 {
10737 /* lock all attached hard disks early to detect "in use"
10738 * situations before deleting actual diffs */
10739 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10740 it != mMediaData->mAttachments.end();
10741 ++it)
10742 {
10743 MediumAttachment* pAtt = *it;
10744 if (pAtt->i_getType() == DeviceType_HardDisk)
10745 {
10746 Medium* pMedium = pAtt->i_getMedium();
10747 Assert(pMedium);
10748
10749 MediumLockList *pMediumLockList(new MediumLockList());
10750 alock.release();
10751 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10752 false /* fMediumLockWrite */,
10753 NULL,
10754 *pMediumLockList);
10755 alock.acquire();
10756
10757 if (FAILED(rc))
10758 {
10759 delete pMediumLockList;
10760 throw rc;
10761 }
10762
10763 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10764 if (FAILED(rc))
10765 throw rc;
10766 }
10767 }
10768
10769 if (FAILED(rc))
10770 throw rc;
10771 } // end of offline
10772
10773 /* Lock lists are now up to date and include implicitly created media */
10774
10775 /* Go through remembered attachments and delete all implicitly created
10776 * diffs and fix up the attachment information */
10777 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10778 MediaData::AttachmentList implicitAtts;
10779 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10780 it != mMediaData->mAttachments.end();
10781 ++it)
10782 {
10783 ComObjPtr<MediumAttachment> pAtt = *it;
10784 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10785 if (pMedium.isNull())
10786 continue;
10787
10788 // Implicit attachments go on the list for deletion and back references are removed.
10789 if (pAtt->i_isImplicit())
10790 {
10791 /* Deassociate and mark for deletion */
10792 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10793 rc = pMedium->i_removeBackReference(mData->mUuid);
10794 if (FAILED(rc))
10795 throw rc;
10796 implicitAtts.push_back(pAtt);
10797 continue;
10798 }
10799
10800 /* Was this medium attached before? */
10801 if (!i_findAttachment(oldAtts, pMedium))
10802 {
10803 /* no: de-associate */
10804 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10805 rc = pMedium->i_removeBackReference(mData->mUuid);
10806 if (FAILED(rc))
10807 throw rc;
10808 continue;
10809 }
10810 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10811 }
10812
10813 /* If there are implicit attachments to delete, throw away the lock
10814 * map contents (which will unlock all media) since the medium
10815 * attachments will be rolled back. Below we need to completely
10816 * recreate the lock map anyway since it is infinitely complex to
10817 * do this incrementally (would need reconstructing each attachment
10818 * change, which would be extremely hairy). */
10819 if (implicitAtts.size() != 0)
10820 {
10821 ErrorInfoKeeper eik;
10822
10823 HRESULT rc1 = lockedMediaMap->Clear();
10824 AssertComRC(rc1);
10825 }
10826
10827 /* rollback hard disk changes */
10828 mMediaData.rollback();
10829
10830 MultiResult mrc(S_OK);
10831
10832 // Delete unused implicit diffs.
10833 if (implicitAtts.size() != 0)
10834 {
10835 alock.release();
10836
10837 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10838 {
10839 // Remove medium associated with this attachment.
10840 ComObjPtr<MediumAttachment> pAtt = *it;
10841 Assert(pAtt);
10842 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10843 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10844 Assert(pMedium);
10845
10846 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10847 // continue on delete failure, just collect error messages
10848 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10849 pMedium->i_getLocationFull().c_str() ));
10850 mrc = rc;
10851 }
10852
10853 alock.acquire();
10854
10855 /* if there is a VM recreate media lock map as mentioned above,
10856 * otherwise it is a waste of time and we leave things unlocked */
10857 if (aOnline)
10858 {
10859 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10860 /* must never be NULL, but better safe than sorry */
10861 if (!pMachine.isNull())
10862 {
10863 alock.release();
10864 rc = mData->mSession.mMachine->i_lockMedia();
10865 alock.acquire();
10866 if (FAILED(rc))
10867 throw rc;
10868 }
10869 }
10870 }
10871 }
10872 catch (HRESULT aRC) {rc = aRC;}
10873
10874 if (mData->mMachineState == MachineState_SettingUp)
10875 i_setMachineState(oldState);
10876
10877 /* unlock all hard disks we locked when there is no VM */
10878 if (!aOnline)
10879 {
10880 ErrorInfoKeeper eik;
10881
10882 HRESULT rc1 = lockedMediaMap->Clear();
10883 AssertComRC(rc1);
10884 }
10885
10886 return rc;
10887}
10888
10889
10890/**
10891 * Looks through the given list of media attachments for one with the given parameters
10892 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10893 * can be searched as well if needed.
10894 *
10895 * @param list
10896 * @param aControllerName
10897 * @param aControllerPort
10898 * @param aDevice
10899 * @return
10900 */
10901MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10902 IN_BSTR aControllerName,
10903 LONG aControllerPort,
10904 LONG aDevice)
10905{
10906 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10907 {
10908 MediumAttachment *pAttach = *it;
10909 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10910 return pAttach;
10911 }
10912
10913 return NULL;
10914}
10915
10916/**
10917 * Looks through the given list of media attachments for one with the given parameters
10918 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10919 * can be searched as well if needed.
10920 *
10921 * @param list
10922 * @param aControllerName
10923 * @param aControllerPort
10924 * @param aDevice
10925 * @return
10926 */
10927MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10928 ComObjPtr<Medium> pMedium)
10929{
10930 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10931 {
10932 MediumAttachment *pAttach = *it;
10933 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10934 if (pMediumThis == pMedium)
10935 return pAttach;
10936 }
10937
10938 return NULL;
10939}
10940
10941/**
10942 * Looks through the given list of media attachments for one with the given parameters
10943 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10944 * can be searched as well if needed.
10945 *
10946 * @param list
10947 * @param aControllerName
10948 * @param aControllerPort
10949 * @param aDevice
10950 * @return
10951 */
10952MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10953 Guid &id)
10954{
10955 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10956 {
10957 MediumAttachment *pAttach = *it;
10958 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10959 if (pMediumThis->i_getId() == id)
10960 return pAttach;
10961 }
10962
10963 return NULL;
10964}
10965
10966/**
10967 * Main implementation for Machine::DetachDevice. This also gets called
10968 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10969 *
10970 * @param pAttach Medium attachment to detach.
10971 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10972 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10973 * SnapshotMachine, and this must be its snapshot.
10974 * @return
10975 */
10976HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10977 AutoWriteLock &writeLock,
10978 Snapshot *pSnapshot)
10979{
10980 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10981 DeviceType_T mediumType = pAttach->i_getType();
10982
10983 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10984
10985 if (pAttach->i_isImplicit())
10986 {
10987 /* attempt to implicitly delete the implicitly created diff */
10988
10989 /// @todo move the implicit flag from MediumAttachment to Medium
10990 /// and forbid any hard disk operation when it is implicit. Or maybe
10991 /// a special media state for it to make it even more simple.
10992
10993 Assert(mMediaData.isBackedUp());
10994
10995 /* will release the lock before the potentially lengthy operation, so
10996 * protect with the special state */
10997 MachineState_T oldState = mData->mMachineState;
10998 i_setMachineState(MachineState_SettingUp);
10999
11000 writeLock.release();
11001
11002 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11003 true /*aWait*/);
11004
11005 writeLock.acquire();
11006
11007 i_setMachineState(oldState);
11008
11009 if (FAILED(rc)) return rc;
11010 }
11011
11012 i_setModified(IsModified_Storage);
11013 mMediaData.backup();
11014 mMediaData->mAttachments.remove(pAttach);
11015
11016 if (!oldmedium.isNull())
11017 {
11018 // if this is from a snapshot, do not defer detachment to commitMedia()
11019 if (pSnapshot)
11020 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11021 // else if non-hard disk media, do not defer detachment to commitMedia() either
11022 else if (mediumType != DeviceType_HardDisk)
11023 oldmedium->i_removeBackReference(mData->mUuid);
11024 }
11025
11026 return S_OK;
11027}
11028
11029/**
11030 * Goes thru all media of the given list and
11031 *
11032 * 1) calls i_detachDevice() on each of them for this machine and
11033 * 2) adds all Medium objects found in the process to the given list,
11034 * depending on cleanupMode.
11035 *
11036 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11037 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11038 * media to the list.
11039 *
11040 * This gets called from Machine::Unregister, both for the actual Machine and
11041 * the SnapshotMachine objects that might be found in the snapshots.
11042 *
11043 * Requires caller and locking. The machine lock must be passed in because it
11044 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11045 *
11046 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11047 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11048 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11049 * Full, then all media get added;
11050 * otherwise no media get added.
11051 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11052 * @return
11053 */
11054HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11055 Snapshot *pSnapshot,
11056 CleanupMode_T cleanupMode,
11057 MediaList &llMedia)
11058{
11059 Assert(isWriteLockOnCurrentThread());
11060
11061 HRESULT rc;
11062
11063 // make a temporary list because i_detachDevice invalidates iterators into
11064 // mMediaData->mAttachments
11065 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11066
11067 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11068 {
11069 ComObjPtr<MediumAttachment> &pAttach = *it;
11070 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11071
11072 if (!pMedium.isNull())
11073 {
11074 AutoCaller mac(pMedium);
11075 if (FAILED(mac.rc())) return mac.rc();
11076 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11077 DeviceType_T devType = pMedium->i_getDeviceType();
11078 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11079 && devType == DeviceType_HardDisk)
11080 || (cleanupMode == CleanupMode_Full)
11081 )
11082 {
11083 llMedia.push_back(pMedium);
11084 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11085 /*
11086 * Search for medias which are not attached to any machine, but
11087 * in the chain to an attached disk. Mediums are only consided
11088 * if they are:
11089 * - have only one child
11090 * - no references to any machines
11091 * - are of normal medium type
11092 */
11093 while (!pParent.isNull())
11094 {
11095 AutoCaller mac1(pParent);
11096 if (FAILED(mac1.rc())) return mac1.rc();
11097 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11098 if (pParent->i_getChildren().size() == 1)
11099 {
11100 if ( pParent->i_getMachineBackRefCount() == 0
11101 && pParent->i_getType() == MediumType_Normal
11102 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11103 llMedia.push_back(pParent);
11104 }
11105 else
11106 break;
11107 pParent = pParent->i_getParent();
11108 }
11109 }
11110 }
11111
11112 // real machine: then we need to use the proper method
11113 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11114
11115 if (FAILED(rc))
11116 return rc;
11117 }
11118
11119 return S_OK;
11120}
11121
11122/**
11123 * Perform deferred hard disk detachments.
11124 *
11125 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11126 * backed up).
11127 *
11128 * If @a aOnline is @c true then this method will also unlock the old hard disks
11129 * for which the new implicit diffs were created and will lock these new diffs for
11130 * writing.
11131 *
11132 * @param aOnline Whether the VM was online prior to this operation.
11133 *
11134 * @note Locks this object for writing!
11135 */
11136void Machine::i_commitMedia(bool aOnline /*= false*/)
11137{
11138 AutoCaller autoCaller(this);
11139 AssertComRCReturnVoid(autoCaller.rc());
11140
11141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11142
11143 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11144
11145 HRESULT rc = S_OK;
11146
11147 /* no attach/detach operations -- nothing to do */
11148 if (!mMediaData.isBackedUp())
11149 return;
11150
11151 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11152 bool fMediaNeedsLocking = false;
11153
11154 /* enumerate new attachments */
11155 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11156 it != mMediaData->mAttachments.end();
11157 ++it)
11158 {
11159 MediumAttachment *pAttach = *it;
11160
11161 pAttach->i_commit();
11162
11163 Medium* pMedium = pAttach->i_getMedium();
11164 bool fImplicit = pAttach->i_isImplicit();
11165
11166 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11167 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11168 fImplicit));
11169
11170 /** @todo convert all this Machine-based voodoo to MediumAttachment
11171 * based commit logic. */
11172 if (fImplicit)
11173 {
11174 /* convert implicit attachment to normal */
11175 pAttach->i_setImplicit(false);
11176
11177 if ( aOnline
11178 && pMedium
11179 && pAttach->i_getType() == DeviceType_HardDisk
11180 )
11181 {
11182 ComObjPtr<Medium> parent = pMedium->i_getParent();
11183 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11184
11185 /* update the appropriate lock list */
11186 MediumLockList *pMediumLockList;
11187 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11188 AssertComRC(rc);
11189 if (pMediumLockList)
11190 {
11191 /* unlock if there's a need to change the locking */
11192 if (!fMediaNeedsLocking)
11193 {
11194 rc = mData->mSession.mLockedMedia.Unlock();
11195 AssertComRC(rc);
11196 fMediaNeedsLocking = true;
11197 }
11198 rc = pMediumLockList->Update(parent, false);
11199 AssertComRC(rc);
11200 rc = pMediumLockList->Append(pMedium, true);
11201 AssertComRC(rc);
11202 }
11203 }
11204
11205 continue;
11206 }
11207
11208 if (pMedium)
11209 {
11210 /* was this medium attached before? */
11211 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11212 {
11213 MediumAttachment *pOldAttach = *oldIt;
11214 if (pOldAttach->i_getMedium() == pMedium)
11215 {
11216 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11217
11218 /* yes: remove from old to avoid de-association */
11219 oldAtts.erase(oldIt);
11220 break;
11221 }
11222 }
11223 }
11224 }
11225
11226 /* enumerate remaining old attachments and de-associate from the
11227 * current machine state */
11228 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11229 {
11230 MediumAttachment *pAttach = *it;
11231 Medium* pMedium = pAttach->i_getMedium();
11232
11233 /* Detach only hard disks, since DVD/floppy media is detached
11234 * instantly in MountMedium. */
11235 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11236 {
11237 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11238
11239 /* now de-associate from the current machine state */
11240 rc = pMedium->i_removeBackReference(mData->mUuid);
11241 AssertComRC(rc);
11242
11243 if (aOnline)
11244 {
11245 /* unlock since medium is not used anymore */
11246 MediumLockList *pMediumLockList;
11247 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11248 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11249 {
11250 /* this happens for online snapshots, there the attachment
11251 * is changing, but only to a diff image created under
11252 * the old one, so there is no separate lock list */
11253 Assert(!pMediumLockList);
11254 }
11255 else
11256 {
11257 AssertComRC(rc);
11258 if (pMediumLockList)
11259 {
11260 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11261 AssertComRC(rc);
11262 }
11263 }
11264 }
11265 }
11266 }
11267
11268 /* take media locks again so that the locking state is consistent */
11269 if (fMediaNeedsLocking)
11270 {
11271 Assert(aOnline);
11272 rc = mData->mSession.mLockedMedia.Lock();
11273 AssertComRC(rc);
11274 }
11275
11276 /* commit the hard disk changes */
11277 mMediaData.commit();
11278
11279 if (i_isSessionMachine())
11280 {
11281 /*
11282 * Update the parent machine to point to the new owner.
11283 * This is necessary because the stored parent will point to the
11284 * session machine otherwise and cause crashes or errors later
11285 * when the session machine gets invalid.
11286 */
11287 /** @todo Change the MediumAttachment class to behave like any other
11288 * class in this regard by creating peer MediumAttachment
11289 * objects for session machines and share the data with the peer
11290 * machine.
11291 */
11292 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11293 it != mMediaData->mAttachments.end();
11294 ++it)
11295 (*it)->i_updateParentMachine(mPeer);
11296
11297 /* attach new data to the primary machine and reshare it */
11298 mPeer->mMediaData.attach(mMediaData);
11299 }
11300
11301 return;
11302}
11303
11304/**
11305 * Perform deferred deletion of implicitly created diffs.
11306 *
11307 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11308 * backed up).
11309 *
11310 * @note Locks this object for writing!
11311 */
11312void Machine::i_rollbackMedia()
11313{
11314 AutoCaller autoCaller(this);
11315 AssertComRCReturnVoid(autoCaller.rc());
11316
11317 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11318 LogFlowThisFunc(("Entering rollbackMedia\n"));
11319
11320 HRESULT rc = S_OK;
11321
11322 /* no attach/detach operations -- nothing to do */
11323 if (!mMediaData.isBackedUp())
11324 return;
11325
11326 /* enumerate new attachments */
11327 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11328 it != mMediaData->mAttachments.end();
11329 ++it)
11330 {
11331 MediumAttachment *pAttach = *it;
11332 /* Fix up the backrefs for DVD/floppy media. */
11333 if (pAttach->i_getType() != DeviceType_HardDisk)
11334 {
11335 Medium* pMedium = pAttach->i_getMedium();
11336 if (pMedium)
11337 {
11338 rc = pMedium->i_removeBackReference(mData->mUuid);
11339 AssertComRC(rc);
11340 }
11341 }
11342
11343 (*it)->i_rollback();
11344
11345 pAttach = *it;
11346 /* Fix up the backrefs for DVD/floppy media. */
11347 if (pAttach->i_getType() != DeviceType_HardDisk)
11348 {
11349 Medium* pMedium = pAttach->i_getMedium();
11350 if (pMedium)
11351 {
11352 rc = pMedium->i_addBackReference(mData->mUuid);
11353 AssertComRC(rc);
11354 }
11355 }
11356 }
11357
11358 /** @todo convert all this Machine-based voodoo to MediumAttachment
11359 * based rollback logic. */
11360 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11361
11362 return;
11363}
11364
11365/**
11366 * Returns true if the settings file is located in the directory named exactly
11367 * as the machine; this means, among other things, that the machine directory
11368 * should be auto-renamed.
11369 *
11370 * @param aSettingsDir if not NULL, the full machine settings file directory
11371 * name will be assigned there.
11372 *
11373 * @note Doesn't lock anything.
11374 * @note Not thread safe (must be called from this object's lock).
11375 */
11376bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11377{
11378 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11379 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11380 if (aSettingsDir)
11381 *aSettingsDir = strMachineDirName;
11382 strMachineDirName.stripPath(); // vmname
11383 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11384 strConfigFileOnly.stripPath() // vmname.vbox
11385 .stripSuffix(); // vmname
11386 /** @todo hack, make somehow use of ComposeMachineFilename */
11387 if (mUserData->s.fDirectoryIncludesUUID)
11388 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11389
11390 AssertReturn(!strMachineDirName.isEmpty(), false);
11391 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11392
11393 return strMachineDirName == strConfigFileOnly;
11394}
11395
11396/**
11397 * Discards all changes to machine settings.
11398 *
11399 * @param aNotify Whether to notify the direct session about changes or not.
11400 *
11401 * @note Locks objects for writing!
11402 */
11403void Machine::i_rollback(bool aNotify)
11404{
11405 AutoCaller autoCaller(this);
11406 AssertComRCReturn(autoCaller.rc(), (void)0);
11407
11408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11409
11410 if (!mStorageControllers.isNull())
11411 {
11412 if (mStorageControllers.isBackedUp())
11413 {
11414 /* unitialize all new devices (absent in the backed up list). */
11415 StorageControllerList::const_iterator it = mStorageControllers->begin();
11416 StorageControllerList *backedList = mStorageControllers.backedUpData();
11417 while (it != mStorageControllers->end())
11418 {
11419 if ( std::find(backedList->begin(), backedList->end(), *it)
11420 == backedList->end()
11421 )
11422 {
11423 (*it)->uninit();
11424 }
11425 ++it;
11426 }
11427
11428 /* restore the list */
11429 mStorageControllers.rollback();
11430 }
11431
11432 /* rollback any changes to devices after restoring the list */
11433 if (mData->flModifications & IsModified_Storage)
11434 {
11435 StorageControllerList::const_iterator it = mStorageControllers->begin();
11436 while (it != mStorageControllers->end())
11437 {
11438 (*it)->i_rollback();
11439 ++it;
11440 }
11441 }
11442 }
11443
11444 if (!mUSBControllers.isNull())
11445 {
11446 if (mUSBControllers.isBackedUp())
11447 {
11448 /* unitialize all new devices (absent in the backed up list). */
11449 USBControllerList::const_iterator it = mUSBControllers->begin();
11450 USBControllerList *backedList = mUSBControllers.backedUpData();
11451 while (it != mUSBControllers->end())
11452 {
11453 if ( std::find(backedList->begin(), backedList->end(), *it)
11454 == backedList->end()
11455 )
11456 {
11457 (*it)->uninit();
11458 }
11459 ++it;
11460 }
11461
11462 /* restore the list */
11463 mUSBControllers.rollback();
11464 }
11465
11466 /* rollback any changes to devices after restoring the list */
11467 if (mData->flModifications & IsModified_USB)
11468 {
11469 USBControllerList::const_iterator it = mUSBControllers->begin();
11470 while (it != mUSBControllers->end())
11471 {
11472 (*it)->i_rollback();
11473 ++it;
11474 }
11475 }
11476 }
11477
11478 mUserData.rollback();
11479
11480 mHWData.rollback();
11481
11482 if (mData->flModifications & IsModified_Storage)
11483 i_rollbackMedia();
11484
11485 if (mBIOSSettings)
11486 mBIOSSettings->i_rollback();
11487
11488 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11489 mVRDEServer->i_rollback();
11490
11491 if (mAudioAdapter)
11492 mAudioAdapter->i_rollback();
11493
11494 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11495 mUSBDeviceFilters->i_rollback();
11496
11497 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11498 mBandwidthControl->i_rollback();
11499
11500 if (!mHWData.isNull())
11501 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11502 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11503 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11504 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11505
11506 if (mData->flModifications & IsModified_NetworkAdapters)
11507 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11508 if ( mNetworkAdapters[slot]
11509 && mNetworkAdapters[slot]->i_isModified())
11510 {
11511 mNetworkAdapters[slot]->i_rollback();
11512 networkAdapters[slot] = mNetworkAdapters[slot];
11513 }
11514
11515 if (mData->flModifications & IsModified_SerialPorts)
11516 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11517 if ( mSerialPorts[slot]
11518 && mSerialPorts[slot]->i_isModified())
11519 {
11520 mSerialPorts[slot]->i_rollback();
11521 serialPorts[slot] = mSerialPorts[slot];
11522 }
11523
11524 if (mData->flModifications & IsModified_ParallelPorts)
11525 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11526 if ( mParallelPorts[slot]
11527 && mParallelPorts[slot]->i_isModified())
11528 {
11529 mParallelPorts[slot]->i_rollback();
11530 parallelPorts[slot] = mParallelPorts[slot];
11531 }
11532
11533 if (aNotify)
11534 {
11535 /* inform the direct session about changes */
11536
11537 ComObjPtr<Machine> that = this;
11538 uint32_t flModifications = mData->flModifications;
11539 alock.release();
11540
11541 if (flModifications & IsModified_SharedFolders)
11542 that->i_onSharedFolderChange();
11543
11544 if (flModifications & IsModified_VRDEServer)
11545 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11546 if (flModifications & IsModified_USB)
11547 that->i_onUSBControllerChange();
11548
11549 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11550 if (networkAdapters[slot])
11551 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11552 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11553 if (serialPorts[slot])
11554 that->i_onSerialPortChange(serialPorts[slot]);
11555 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11556 if (parallelPorts[slot])
11557 that->i_onParallelPortChange(parallelPorts[slot]);
11558
11559 if (flModifications & IsModified_Storage)
11560 that->i_onStorageControllerChange();
11561
11562#if 0
11563 if (flModifications & IsModified_BandwidthControl)
11564 that->onBandwidthControlChange();
11565#endif
11566 }
11567}
11568
11569/**
11570 * Commits all the changes to machine settings.
11571 *
11572 * Note that this operation is supposed to never fail.
11573 *
11574 * @note Locks this object and children for writing.
11575 */
11576void Machine::i_commit()
11577{
11578 AutoCaller autoCaller(this);
11579 AssertComRCReturnVoid(autoCaller.rc());
11580
11581 AutoCaller peerCaller(mPeer);
11582 AssertComRCReturnVoid(peerCaller.rc());
11583
11584 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11585
11586 /*
11587 * use safe commit to ensure Snapshot machines (that share mUserData)
11588 * will still refer to a valid memory location
11589 */
11590 mUserData.commitCopy();
11591
11592 mHWData.commit();
11593
11594 if (mMediaData.isBackedUp())
11595 i_commitMedia(Global::IsOnline(mData->mMachineState));
11596
11597 mBIOSSettings->i_commit();
11598 mVRDEServer->i_commit();
11599 mAudioAdapter->i_commit();
11600 mUSBDeviceFilters->i_commit();
11601 mBandwidthControl->i_commit();
11602
11603 /* Since mNetworkAdapters is a list which might have been changed (resized)
11604 * without using the Backupable<> template we need to handle the copying
11605 * of the list entries manually, including the creation of peers for the
11606 * new objects. */
11607 bool commitNetworkAdapters = false;
11608 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11609 if (mPeer)
11610 {
11611 /* commit everything, even the ones which will go away */
11612 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11613 mNetworkAdapters[slot]->i_commit();
11614 /* copy over the new entries, creating a peer and uninit the original */
11615 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11616 for (size_t slot = 0; slot < newSize; slot++)
11617 {
11618 /* look if this adapter has a peer device */
11619 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11620 if (!peer)
11621 {
11622 /* no peer means the adapter is a newly created one;
11623 * create a peer owning data this data share it with */
11624 peer.createObject();
11625 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11626 }
11627 mPeer->mNetworkAdapters[slot] = peer;
11628 }
11629 /* uninit any no longer needed network adapters */
11630 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11631 mNetworkAdapters[slot]->uninit();
11632 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11633 {
11634 if (mPeer->mNetworkAdapters[slot])
11635 mPeer->mNetworkAdapters[slot]->uninit();
11636 }
11637 /* Keep the original network adapter count until this point, so that
11638 * discarding a chipset type change will not lose settings. */
11639 mNetworkAdapters.resize(newSize);
11640 mPeer->mNetworkAdapters.resize(newSize);
11641 }
11642 else
11643 {
11644 /* we have no peer (our parent is the newly created machine);
11645 * just commit changes to the network adapters */
11646 commitNetworkAdapters = true;
11647 }
11648 if (commitNetworkAdapters)
11649 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11650 mNetworkAdapters[slot]->i_commit();
11651
11652 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11653 mSerialPorts[slot]->i_commit();
11654 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11655 mParallelPorts[slot]->i_commit();
11656
11657 bool commitStorageControllers = false;
11658
11659 if (mStorageControllers.isBackedUp())
11660 {
11661 mStorageControllers.commit();
11662
11663 if (mPeer)
11664 {
11665 /* Commit all changes to new controllers (this will reshare data with
11666 * peers for those who have peers) */
11667 StorageControllerList *newList = new StorageControllerList();
11668 StorageControllerList::const_iterator it = mStorageControllers->begin();
11669 while (it != mStorageControllers->end())
11670 {
11671 (*it)->i_commit();
11672
11673 /* look if this controller has a peer device */
11674 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11675 if (!peer)
11676 {
11677 /* no peer means the device is a newly created one;
11678 * create a peer owning data this device share it with */
11679 peer.createObject();
11680 peer->init(mPeer, *it, true /* aReshare */);
11681 }
11682 else
11683 {
11684 /* remove peer from the old list */
11685 mPeer->mStorageControllers->remove(peer);
11686 }
11687 /* and add it to the new list */
11688 newList->push_back(peer);
11689
11690 ++it;
11691 }
11692
11693 /* uninit old peer's controllers that are left */
11694 it = mPeer->mStorageControllers->begin();
11695 while (it != mPeer->mStorageControllers->end())
11696 {
11697 (*it)->uninit();
11698 ++it;
11699 }
11700
11701 /* attach new list of controllers to our peer */
11702 mPeer->mStorageControllers.attach(newList);
11703 }
11704 else
11705 {
11706 /* we have no peer (our parent is the newly created machine);
11707 * just commit changes to devices */
11708 commitStorageControllers = true;
11709 }
11710 }
11711 else
11712 {
11713 /* the list of controllers itself is not changed,
11714 * just commit changes to controllers themselves */
11715 commitStorageControllers = true;
11716 }
11717
11718 if (commitStorageControllers)
11719 {
11720 StorageControllerList::const_iterator it = mStorageControllers->begin();
11721 while (it != mStorageControllers->end())
11722 {
11723 (*it)->i_commit();
11724 ++it;
11725 }
11726 }
11727
11728 bool commitUSBControllers = false;
11729
11730 if (mUSBControllers.isBackedUp())
11731 {
11732 mUSBControllers.commit();
11733
11734 if (mPeer)
11735 {
11736 /* Commit all changes to new controllers (this will reshare data with
11737 * peers for those who have peers) */
11738 USBControllerList *newList = new USBControllerList();
11739 USBControllerList::const_iterator it = mUSBControllers->begin();
11740 while (it != mUSBControllers->end())
11741 {
11742 (*it)->i_commit();
11743
11744 /* look if this controller has a peer device */
11745 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11746 if (!peer)
11747 {
11748 /* no peer means the device is a newly created one;
11749 * create a peer owning data this device share it with */
11750 peer.createObject();
11751 peer->init(mPeer, *it, true /* aReshare */);
11752 }
11753 else
11754 {
11755 /* remove peer from the old list */
11756 mPeer->mUSBControllers->remove(peer);
11757 }
11758 /* and add it to the new list */
11759 newList->push_back(peer);
11760
11761 ++it;
11762 }
11763
11764 /* uninit old peer's controllers that are left */
11765 it = mPeer->mUSBControllers->begin();
11766 while (it != mPeer->mUSBControllers->end())
11767 {
11768 (*it)->uninit();
11769 ++it;
11770 }
11771
11772 /* attach new list of controllers to our peer */
11773 mPeer->mUSBControllers.attach(newList);
11774 }
11775 else
11776 {
11777 /* we have no peer (our parent is the newly created machine);
11778 * just commit changes to devices */
11779 commitUSBControllers = true;
11780 }
11781 }
11782 else
11783 {
11784 /* the list of controllers itself is not changed,
11785 * just commit changes to controllers themselves */
11786 commitUSBControllers = true;
11787 }
11788
11789 if (commitUSBControllers)
11790 {
11791 USBControllerList::const_iterator it = mUSBControllers->begin();
11792 while (it != mUSBControllers->end())
11793 {
11794 (*it)->i_commit();
11795 ++it;
11796 }
11797 }
11798
11799 if (i_isSessionMachine())
11800 {
11801 /* attach new data to the primary machine and reshare it */
11802 mPeer->mUserData.attach(mUserData);
11803 mPeer->mHWData.attach(mHWData);
11804 /* mMediaData is reshared by fixupMedia */
11805 // mPeer->mMediaData.attach(mMediaData);
11806 Assert(mPeer->mMediaData.data() == mMediaData.data());
11807 }
11808}
11809
11810/**
11811 * Copies all the hardware data from the given machine.
11812 *
11813 * Currently, only called when the VM is being restored from a snapshot. In
11814 * particular, this implies that the VM is not running during this method's
11815 * call.
11816 *
11817 * @note This method must be called from under this object's lock.
11818 *
11819 * @note This method doesn't call #commit(), so all data remains backed up and
11820 * unsaved.
11821 */
11822void Machine::i_copyFrom(Machine *aThat)
11823{
11824 AssertReturnVoid(!i_isSnapshotMachine());
11825 AssertReturnVoid(aThat->i_isSnapshotMachine());
11826
11827 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11828
11829 mHWData.assignCopy(aThat->mHWData);
11830
11831 // create copies of all shared folders (mHWData after attaching a copy
11832 // contains just references to original objects)
11833 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11834 it != mHWData->mSharedFolders.end();
11835 ++it)
11836 {
11837 ComObjPtr<SharedFolder> folder;
11838 folder.createObject();
11839 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11840 AssertComRC(rc);
11841 *it = folder;
11842 }
11843
11844 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11845 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11846 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11847 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11848 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11849
11850 /* create private copies of all controllers */
11851 mStorageControllers.backup();
11852 mStorageControllers->clear();
11853 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11854 it != aThat->mStorageControllers->end();
11855 ++it)
11856 {
11857 ComObjPtr<StorageController> ctrl;
11858 ctrl.createObject();
11859 ctrl->initCopy(this, *it);
11860 mStorageControllers->push_back(ctrl);
11861 }
11862
11863 /* create private copies of all USB controllers */
11864 mUSBControllers.backup();
11865 mUSBControllers->clear();
11866 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11867 it != aThat->mUSBControllers->end();
11868 ++it)
11869 {
11870 ComObjPtr<USBController> ctrl;
11871 ctrl.createObject();
11872 ctrl->initCopy(this, *it);
11873 mUSBControllers->push_back(ctrl);
11874 }
11875
11876 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11877 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11878 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11879 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11880 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11881 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11882 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11883}
11884
11885/**
11886 * Returns whether the given storage controller is hotplug capable.
11887 *
11888 * @returns true if the controller supports hotplugging
11889 * false otherwise.
11890 * @param enmCtrlType The controller type to check for.
11891 */
11892bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11893{
11894 ComPtr<ISystemProperties> systemProperties;
11895 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11896 if (FAILED(rc))
11897 return false;
11898
11899 BOOL aHotplugCapable = FALSE;
11900 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11901
11902 return RT_BOOL(aHotplugCapable);
11903}
11904
11905#ifdef VBOX_WITH_RESOURCE_USAGE_API
11906
11907void Machine::i_getDiskList(MediaList &list)
11908{
11909 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11910 it != mMediaData->mAttachments.end();
11911 ++it)
11912 {
11913 MediumAttachment* pAttach = *it;
11914 /* just in case */
11915 AssertStmt(pAttach, continue);
11916
11917 AutoCaller localAutoCallerA(pAttach);
11918 if (FAILED(localAutoCallerA.rc())) continue;
11919
11920 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11921
11922 if (pAttach->i_getType() == DeviceType_HardDisk)
11923 list.push_back(pAttach->i_getMedium());
11924 }
11925}
11926
11927void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11928{
11929 AssertReturnVoid(isWriteLockOnCurrentThread());
11930 AssertPtrReturnVoid(aCollector);
11931
11932 pm::CollectorHAL *hal = aCollector->getHAL();
11933 /* Create sub metrics */
11934 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11935 "Percentage of processor time spent in user mode by the VM process.");
11936 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11937 "Percentage of processor time spent in kernel mode by the VM process.");
11938 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11939 "Size of resident portion of VM process in memory.");
11940 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11941 "Actual size of all VM disks combined.");
11942 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11943 "Network receive rate.");
11944 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11945 "Network transmit rate.");
11946 /* Create and register base metrics */
11947 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11948 cpuLoadUser, cpuLoadKernel);
11949 aCollector->registerBaseMetric(cpuLoad);
11950 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11951 ramUsageUsed);
11952 aCollector->registerBaseMetric(ramUsage);
11953 MediaList disks;
11954 i_getDiskList(disks);
11955 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11956 diskUsageUsed);
11957 aCollector->registerBaseMetric(diskUsage);
11958
11959 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11960 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11961 new pm::AggregateAvg()));
11962 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11963 new pm::AggregateMin()));
11964 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11965 new pm::AggregateMax()));
11966 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11967 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11968 new pm::AggregateAvg()));
11969 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11970 new pm::AggregateMin()));
11971 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11972 new pm::AggregateMax()));
11973
11974 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11975 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11976 new pm::AggregateAvg()));
11977 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11978 new pm::AggregateMin()));
11979 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11980 new pm::AggregateMax()));
11981
11982 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11983 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11984 new pm::AggregateAvg()));
11985 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11986 new pm::AggregateMin()));
11987 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11988 new pm::AggregateMax()));
11989
11990
11991 /* Guest metrics collector */
11992 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11993 aCollector->registerGuest(mCollectorGuest);
11994 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11995 this, __PRETTY_FUNCTION__, mCollectorGuest));
11996
11997 /* Create sub metrics */
11998 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11999 "Percentage of processor time spent in user mode as seen by the guest.");
12000 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12001 "Percentage of processor time spent in kernel mode as seen by the guest.");
12002 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12003 "Percentage of processor time spent idling as seen by the guest.");
12004
12005 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12006 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12007 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12008 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12009 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12010 pm::SubMetric *guestMemCache = new pm::SubMetric(
12011 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12012
12013 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12014 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12015
12016 /* Create and register base metrics */
12017 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12018 machineNetRx, machineNetTx);
12019 aCollector->registerBaseMetric(machineNetRate);
12020
12021 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12022 guestLoadUser, guestLoadKernel, guestLoadIdle);
12023 aCollector->registerBaseMetric(guestCpuLoad);
12024
12025 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12026 guestMemTotal, guestMemFree,
12027 guestMemBalloon, guestMemShared,
12028 guestMemCache, guestPagedTotal);
12029 aCollector->registerBaseMetric(guestCpuMem);
12030
12031 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12032 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12033 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12034 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12035
12036 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12037 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12038 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12039 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12040
12041 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12042 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12043 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12044 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12045
12046 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12047 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12048 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12049 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12050
12051 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12052 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12053 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12054 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12055
12056 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12057 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12058 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12059 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12060
12061 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12062 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12063 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12064 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12065
12066 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12070
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12075
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12078 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12079 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12080
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12082 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12083 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12084 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12085}
12086
12087void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12088{
12089 AssertReturnVoid(isWriteLockOnCurrentThread());
12090
12091 if (aCollector)
12092 {
12093 aCollector->unregisterMetricsFor(aMachine);
12094 aCollector->unregisterBaseMetricsFor(aMachine);
12095 }
12096}
12097
12098#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12099
12100
12101////////////////////////////////////////////////////////////////////////////////
12102
12103DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12104
12105HRESULT SessionMachine::FinalConstruct()
12106{
12107 LogFlowThisFunc(("\n"));
12108
12109 mClientToken = NULL;
12110
12111 return BaseFinalConstruct();
12112}
12113
12114void SessionMachine::FinalRelease()
12115{
12116 LogFlowThisFunc(("\n"));
12117
12118 Assert(!mClientToken);
12119 /* paranoia, should not hang around any more */
12120 if (mClientToken)
12121 {
12122 delete mClientToken;
12123 mClientToken = NULL;
12124 }
12125
12126 uninit(Uninit::Unexpected);
12127
12128 BaseFinalRelease();
12129}
12130
12131/**
12132 * @note Must be called only by Machine::LockMachine() from its own write lock.
12133 */
12134HRESULT SessionMachine::init(Machine *aMachine)
12135{
12136 LogFlowThisFuncEnter();
12137 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12138
12139 AssertReturn(aMachine, E_INVALIDARG);
12140
12141 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12142
12143 /* Enclose the state transition NotReady->InInit->Ready */
12144 AutoInitSpan autoInitSpan(this);
12145 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12146
12147 HRESULT rc = S_OK;
12148
12149 /* create the machine client token */
12150 try
12151 {
12152 mClientToken = new ClientToken(aMachine, this);
12153 if (!mClientToken->isReady())
12154 {
12155 delete mClientToken;
12156 mClientToken = NULL;
12157 rc = E_FAIL;
12158 }
12159 }
12160 catch (std::bad_alloc &)
12161 {
12162 rc = E_OUTOFMEMORY;
12163 }
12164 if (FAILED(rc))
12165 return rc;
12166
12167 /* memorize the peer Machine */
12168 unconst(mPeer) = aMachine;
12169 /* share the parent pointer */
12170 unconst(mParent) = aMachine->mParent;
12171
12172 /* take the pointers to data to share */
12173 mData.share(aMachine->mData);
12174 mSSData.share(aMachine->mSSData);
12175
12176 mUserData.share(aMachine->mUserData);
12177 mHWData.share(aMachine->mHWData);
12178 mMediaData.share(aMachine->mMediaData);
12179
12180 mStorageControllers.allocate();
12181 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12182 it != aMachine->mStorageControllers->end();
12183 ++it)
12184 {
12185 ComObjPtr<StorageController> ctl;
12186 ctl.createObject();
12187 ctl->init(this, *it);
12188 mStorageControllers->push_back(ctl);
12189 }
12190
12191 mUSBControllers.allocate();
12192 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12193 it != aMachine->mUSBControllers->end();
12194 ++it)
12195 {
12196 ComObjPtr<USBController> ctl;
12197 ctl.createObject();
12198 ctl->init(this, *it);
12199 mUSBControllers->push_back(ctl);
12200 }
12201
12202 unconst(mBIOSSettings).createObject();
12203 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12204 /* create another VRDEServer object that will be mutable */
12205 unconst(mVRDEServer).createObject();
12206 mVRDEServer->init(this, aMachine->mVRDEServer);
12207 /* create another audio adapter object that will be mutable */
12208 unconst(mAudioAdapter).createObject();
12209 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12210 /* create a list of serial ports that will be mutable */
12211 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12212 {
12213 unconst(mSerialPorts[slot]).createObject();
12214 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12215 }
12216 /* create a list of parallel ports that will be mutable */
12217 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12218 {
12219 unconst(mParallelPorts[slot]).createObject();
12220 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12221 }
12222
12223 /* create another USB device filters object that will be mutable */
12224 unconst(mUSBDeviceFilters).createObject();
12225 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12226
12227 /* create a list of network adapters that will be mutable */
12228 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12229 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12230 {
12231 unconst(mNetworkAdapters[slot]).createObject();
12232 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12233 }
12234
12235 /* create another bandwidth control object that will be mutable */
12236 unconst(mBandwidthControl).createObject();
12237 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12238
12239 /* default is to delete saved state on Saved -> PoweredOff transition */
12240 mRemoveSavedState = true;
12241
12242 /* Confirm a successful initialization when it's the case */
12243 autoInitSpan.setSucceeded();
12244
12245 miNATNetworksStarted = 0;
12246
12247 LogFlowThisFuncLeave();
12248 return rc;
12249}
12250
12251/**
12252 * Uninitializes this session object. If the reason is other than
12253 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12254 * or the client watcher code.
12255 *
12256 * @param aReason uninitialization reason
12257 *
12258 * @note Locks mParent + this object for writing.
12259 */
12260void SessionMachine::uninit(Uninit::Reason aReason)
12261{
12262 LogFlowThisFuncEnter();
12263 LogFlowThisFunc(("reason=%d\n", aReason));
12264
12265 /*
12266 * Strongly reference ourselves to prevent this object deletion after
12267 * mData->mSession.mMachine.setNull() below (which can release the last
12268 * reference and call the destructor). Important: this must be done before
12269 * accessing any members (and before AutoUninitSpan that does it as well).
12270 * This self reference will be released as the very last step on return.
12271 */
12272 ComObjPtr<SessionMachine> selfRef = this;
12273
12274 /* Enclose the state transition Ready->InUninit->NotReady */
12275 AutoUninitSpan autoUninitSpan(this);
12276 if (autoUninitSpan.uninitDone())
12277 {
12278 LogFlowThisFunc(("Already uninitialized\n"));
12279 LogFlowThisFuncLeave();
12280 return;
12281 }
12282
12283 if (autoUninitSpan.initFailed())
12284 {
12285 /* We've been called by init() because it's failed. It's not really
12286 * necessary (nor it's safe) to perform the regular uninit sequence
12287 * below, the following is enough.
12288 */
12289 LogFlowThisFunc(("Initialization failed.\n"));
12290 /* destroy the machine client token */
12291 if (mClientToken)
12292 {
12293 delete mClientToken;
12294 mClientToken = NULL;
12295 }
12296 uninitDataAndChildObjects();
12297 mData.free();
12298 unconst(mParent) = NULL;
12299 unconst(mPeer) = NULL;
12300 LogFlowThisFuncLeave();
12301 return;
12302 }
12303
12304 MachineState_T lastState;
12305 {
12306 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12307 lastState = mData->mMachineState;
12308 }
12309 NOREF(lastState);
12310
12311#ifdef VBOX_WITH_USB
12312 // release all captured USB devices, but do this before requesting the locks below
12313 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12314 {
12315 /* Console::captureUSBDevices() is called in the VM process only after
12316 * setting the machine state to Starting or Restoring.
12317 * Console::detachAllUSBDevices() will be called upon successful
12318 * termination. So, we need to release USB devices only if there was
12319 * an abnormal termination of a running VM.
12320 *
12321 * This is identical to SessionMachine::DetachAllUSBDevices except
12322 * for the aAbnormal argument. */
12323 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12324 AssertComRC(rc);
12325 NOREF(rc);
12326
12327 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12328 if (service)
12329 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12330 }
12331#endif /* VBOX_WITH_USB */
12332
12333 // we need to lock this object in uninit() because the lock is shared
12334 // with mPeer (as well as data we modify below). mParent lock is needed
12335 // by several calls to it, and USB needs host lock.
12336 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12337
12338#ifdef VBOX_WITH_RESOURCE_USAGE_API
12339 /*
12340 * It is safe to call Machine::i_unregisterMetrics() here because
12341 * PerformanceCollector::samplerCallback no longer accesses guest methods
12342 * holding the lock.
12343 */
12344 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12345 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12346 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12347 this, __PRETTY_FUNCTION__, mCollectorGuest));
12348 if (mCollectorGuest)
12349 {
12350 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12351 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12352 mCollectorGuest = NULL;
12353 }
12354#endif
12355
12356 if (aReason == Uninit::Abnormal)
12357 {
12358 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12359 Global::IsOnlineOrTransient(lastState)));
12360
12361 /* reset the state to Aborted */
12362 if (mData->mMachineState != MachineState_Aborted)
12363 i_setMachineState(MachineState_Aborted);
12364 }
12365
12366 // any machine settings modified?
12367 if (mData->flModifications)
12368 {
12369 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12370 i_rollback(false /* aNotify */);
12371 }
12372
12373 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12374 || !mConsoleTaskData.mSnapshot);
12375 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12376 {
12377 LogWarningThisFunc(("canceling failed save state request!\n"));
12378 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12379 }
12380 else if (!mConsoleTaskData.mSnapshot.isNull())
12381 {
12382 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12383
12384 /* delete all differencing hard disks created (this will also attach
12385 * their parents back by rolling back mMediaData) */
12386 i_rollbackMedia();
12387
12388 // delete the saved state file (it might have been already created)
12389 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12390 // think it's still in use
12391 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12392 mConsoleTaskData.mSnapshot->uninit();
12393 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12394 }
12395
12396 mData->mSession.mPID = NIL_RTPROCESS;
12397
12398 if (aReason == Uninit::Unexpected)
12399 {
12400 /* Uninitialization didn't come from #checkForDeath(), so tell the
12401 * client watcher thread to update the set of machines that have open
12402 * sessions. */
12403 mParent->i_updateClientWatcher();
12404 }
12405
12406 /* uninitialize all remote controls */
12407 if (mData->mSession.mRemoteControls.size())
12408 {
12409 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12410 mData->mSession.mRemoteControls.size()));
12411
12412 Data::Session::RemoteControlList::iterator it =
12413 mData->mSession.mRemoteControls.begin();
12414 while (it != mData->mSession.mRemoteControls.end())
12415 {
12416 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12417 HRESULT rc = (*it)->Uninitialize();
12418 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12419 if (FAILED(rc))
12420 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12421 ++it;
12422 }
12423 mData->mSession.mRemoteControls.clear();
12424 }
12425
12426 /* Remove all references to the NAT network service. The service will stop
12427 * if all references (also from other VMs) are removed. */
12428 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12429 {
12430 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12431 {
12432 NetworkAttachmentType_T type;
12433 HRESULT hrc;
12434
12435 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12436 if ( SUCCEEDED(hrc)
12437 && type == NetworkAttachmentType_NATNetwork)
12438 {
12439 Bstr name;
12440 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12441 if (SUCCEEDED(hrc))
12442 {
12443 multilock.release();
12444 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12445 mUserData->s.strName.c_str(), name.raw()));
12446 mParent->i_natNetworkRefDec(name.raw());
12447 multilock.acquire();
12448 }
12449 }
12450 }
12451 }
12452
12453 /*
12454 * An expected uninitialization can come only from #checkForDeath().
12455 * Otherwise it means that something's gone really wrong (for example,
12456 * the Session implementation has released the VirtualBox reference
12457 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12458 * etc). However, it's also possible, that the client releases the IPC
12459 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12460 * but the VirtualBox release event comes first to the server process.
12461 * This case is practically possible, so we should not assert on an
12462 * unexpected uninit, just log a warning.
12463 */
12464
12465 if ((aReason == Uninit::Unexpected))
12466 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12467
12468 if (aReason != Uninit::Normal)
12469 {
12470 mData->mSession.mDirectControl.setNull();
12471 }
12472 else
12473 {
12474 /* this must be null here (see #OnSessionEnd()) */
12475 Assert(mData->mSession.mDirectControl.isNull());
12476 Assert(mData->mSession.mState == SessionState_Unlocking);
12477 Assert(!mData->mSession.mProgress.isNull());
12478 }
12479 if (mData->mSession.mProgress)
12480 {
12481 if (aReason == Uninit::Normal)
12482 mData->mSession.mProgress->i_notifyComplete(S_OK);
12483 else
12484 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12485 COM_IIDOF(ISession),
12486 getComponentName(),
12487 tr("The VM session was aborted"));
12488 mData->mSession.mProgress.setNull();
12489 }
12490
12491 /* remove the association between the peer machine and this session machine */
12492 Assert( (SessionMachine*)mData->mSession.mMachine == this
12493 || aReason == Uninit::Unexpected);
12494
12495 /* reset the rest of session data */
12496 mData->mSession.mMachine.setNull();
12497 mData->mSession.mState = SessionState_Unlocked;
12498 mData->mSession.mType.setNull();
12499
12500 /* destroy the machine client token before leaving the exclusive lock */
12501 if (mClientToken)
12502 {
12503 delete mClientToken;
12504 mClientToken = NULL;
12505 }
12506
12507 /* fire an event */
12508 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12509
12510 uninitDataAndChildObjects();
12511
12512 /* free the essential data structure last */
12513 mData.free();
12514
12515 /* release the exclusive lock before setting the below two to NULL */
12516 multilock.release();
12517
12518 unconst(mParent) = NULL;
12519 unconst(mPeer) = NULL;
12520
12521 LogFlowThisFuncLeave();
12522}
12523
12524// util::Lockable interface
12525////////////////////////////////////////////////////////////////////////////////
12526
12527/**
12528 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12529 * with the primary Machine instance (mPeer).
12530 */
12531RWLockHandle *SessionMachine::lockHandle() const
12532{
12533 AssertReturn(mPeer != NULL, NULL);
12534 return mPeer->lockHandle();
12535}
12536
12537// IInternalMachineControl methods
12538////////////////////////////////////////////////////////////////////////////////
12539
12540/**
12541 * Passes collected guest statistics to performance collector object
12542 */
12543HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12544 ULONG aCpuKernel, ULONG aCpuIdle,
12545 ULONG aMemTotal, ULONG aMemFree,
12546 ULONG aMemBalloon, ULONG aMemShared,
12547 ULONG aMemCache, ULONG aPageTotal,
12548 ULONG aAllocVMM, ULONG aFreeVMM,
12549 ULONG aBalloonedVMM, ULONG aSharedVMM,
12550 ULONG aVmNetRx, ULONG aVmNetTx)
12551{
12552#ifdef VBOX_WITH_RESOURCE_USAGE_API
12553 if (mCollectorGuest)
12554 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12555 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12556 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12557 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12558
12559 return S_OK;
12560#else
12561 NOREF(aValidStats);
12562 NOREF(aCpuUser);
12563 NOREF(aCpuKernel);
12564 NOREF(aCpuIdle);
12565 NOREF(aMemTotal);
12566 NOREF(aMemFree);
12567 NOREF(aMemBalloon);
12568 NOREF(aMemShared);
12569 NOREF(aMemCache);
12570 NOREF(aPageTotal);
12571 NOREF(aAllocVMM);
12572 NOREF(aFreeVMM);
12573 NOREF(aBalloonedVMM);
12574 NOREF(aSharedVMM);
12575 NOREF(aVmNetRx);
12576 NOREF(aVmNetTx);
12577 return E_NOTIMPL;
12578#endif
12579}
12580
12581/**
12582 * @note Locks this object for writing.
12583 */
12584HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12585{
12586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12587
12588 mRemoveSavedState = RT_BOOL(aRemove);
12589
12590 return S_OK;
12591}
12592
12593/**
12594 * @note Locks the same as #i_setMachineState() does.
12595 */
12596HRESULT SessionMachine::updateState(MachineState_T aState)
12597{
12598 return i_setMachineState(aState);
12599}
12600
12601/**
12602 * @note Locks this object for writing.
12603 */
12604HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12605{
12606 IProgress* pProgress(aProgress);
12607
12608 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12609
12610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12611
12612 if (mData->mSession.mState != SessionState_Locked)
12613 return VBOX_E_INVALID_OBJECT_STATE;
12614
12615 if (!mData->mSession.mProgress.isNull())
12616 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12617
12618 /* If we didn't reference the NAT network service yet, add a reference to
12619 * force a start */
12620 if (miNATNetworksStarted < 1)
12621 {
12622 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12623 {
12624 NetworkAttachmentType_T type;
12625 HRESULT hrc;
12626 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12627 if ( SUCCEEDED(hrc)
12628 && type == NetworkAttachmentType_NATNetwork)
12629 {
12630 Bstr name;
12631 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12632 if (SUCCEEDED(hrc))
12633 {
12634 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12635 mUserData->s.strName.c_str(), name.raw()));
12636 mPeer->lockHandle()->unlockWrite();
12637 mParent->i_natNetworkRefInc(name.raw());
12638#ifdef RT_LOCK_STRICT
12639 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12640#else
12641 mPeer->lockHandle()->lockWrite();
12642#endif
12643 }
12644 }
12645 }
12646 miNATNetworksStarted++;
12647 }
12648
12649 LogFlowThisFunc(("returns S_OK.\n"));
12650 return S_OK;
12651}
12652
12653/**
12654 * @note Locks this object for writing.
12655 */
12656HRESULT SessionMachine::endPowerUp(LONG aResult)
12657{
12658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12659
12660 if (mData->mSession.mState != SessionState_Locked)
12661 return VBOX_E_INVALID_OBJECT_STATE;
12662
12663 /* Finalize the LaunchVMProcess progress object. */
12664 if (mData->mSession.mProgress)
12665 {
12666 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12667 mData->mSession.mProgress.setNull();
12668 }
12669
12670 if (SUCCEEDED((HRESULT)aResult))
12671 {
12672#ifdef VBOX_WITH_RESOURCE_USAGE_API
12673 /* The VM has been powered up successfully, so it makes sense
12674 * now to offer the performance metrics for a running machine
12675 * object. Doing it earlier wouldn't be safe. */
12676 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12677 mData->mSession.mPID);
12678#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12679 }
12680
12681 return S_OK;
12682}
12683
12684/**
12685 * @note Locks this object for writing.
12686 */
12687HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12688{
12689 LogFlowThisFuncEnter();
12690
12691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12692
12693 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12694 E_FAIL);
12695
12696 /* create a progress object to track operation completion */
12697 ComObjPtr<Progress> pProgress;
12698 pProgress.createObject();
12699 pProgress->init(i_getVirtualBox(),
12700 static_cast<IMachine *>(this) /* aInitiator */,
12701 Bstr(tr("Stopping the virtual machine")).raw(),
12702 FALSE /* aCancelable */);
12703
12704 /* fill in the console task data */
12705 mConsoleTaskData.mLastState = mData->mMachineState;
12706 mConsoleTaskData.mProgress = pProgress;
12707
12708 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12709 i_setMachineState(MachineState_Stopping);
12710
12711 pProgress.queryInterfaceTo(aProgress.asOutParam());
12712
12713 return S_OK;
12714}
12715
12716/**
12717 * @note Locks this object for writing.
12718 */
12719HRESULT SessionMachine::endPoweringDown(LONG aResult,
12720 const com::Utf8Str &aErrMsg)
12721{
12722 LogFlowThisFuncEnter();
12723
12724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12725
12726 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12727 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12728 && mConsoleTaskData.mLastState != MachineState_Null,
12729 E_FAIL);
12730
12731 /*
12732 * On failure, set the state to the state we had when BeginPoweringDown()
12733 * was called (this is expected by Console::PowerDown() and the associated
12734 * task). On success the VM process already changed the state to
12735 * MachineState_PoweredOff, so no need to do anything.
12736 */
12737 if (FAILED(aResult))
12738 i_setMachineState(mConsoleTaskData.mLastState);
12739
12740 /* notify the progress object about operation completion */
12741 Assert(mConsoleTaskData.mProgress);
12742 if (SUCCEEDED(aResult))
12743 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12744 else
12745 {
12746 if (aErrMsg.length())
12747 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12748 COM_IIDOF(ISession),
12749 getComponentName(),
12750 aErrMsg.c_str());
12751 else
12752 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12753 }
12754
12755 /* clear out the temporary saved state data */
12756 mConsoleTaskData.mLastState = MachineState_Null;
12757 mConsoleTaskData.mProgress.setNull();
12758
12759 LogFlowThisFuncLeave();
12760 return S_OK;
12761}
12762
12763
12764/**
12765 * Goes through the USB filters of the given machine to see if the given
12766 * device matches any filter or not.
12767 *
12768 * @note Locks the same as USBController::hasMatchingFilter() does.
12769 */
12770HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12771 BOOL *aMatched,
12772 ULONG *aMaskedInterfaces)
12773{
12774 LogFlowThisFunc(("\n"));
12775
12776#ifdef VBOX_WITH_USB
12777 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12778#else
12779 NOREF(aDevice);
12780 NOREF(aMaskedInterfaces);
12781 *aMatched = FALSE;
12782#endif
12783
12784 return S_OK;
12785}
12786
12787/**
12788 * @note Locks the same as Host::captureUSBDevice() does.
12789 */
12790HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId)
12791{
12792 LogFlowThisFunc(("\n"));
12793
12794#ifdef VBOX_WITH_USB
12795 /* if captureDeviceForVM() fails, it must have set extended error info */
12796 clearError();
12797 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12798 if (FAILED(rc)) return rc;
12799
12800 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12801 AssertReturn(service, E_FAIL);
12802 return service->captureDeviceForVM(this, aId.ref());
12803#else
12804 NOREF(aId);
12805 return E_NOTIMPL;
12806#endif
12807}
12808
12809/**
12810 * @note Locks the same as Host::detachUSBDevice() does.
12811 */
12812HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12813 BOOL aDone)
12814{
12815 LogFlowThisFunc(("\n"));
12816
12817#ifdef VBOX_WITH_USB
12818 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12819 AssertReturn(service, E_FAIL);
12820 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12821#else
12822 NOREF(aId);
12823 NOREF(aDone);
12824 return E_NOTIMPL;
12825#endif
12826}
12827
12828/**
12829 * Inserts all machine filters to the USB proxy service and then calls
12830 * Host::autoCaptureUSBDevices().
12831 *
12832 * Called by Console from the VM process upon VM startup.
12833 *
12834 * @note Locks what called methods lock.
12835 */
12836HRESULT SessionMachine::autoCaptureUSBDevices()
12837{
12838 LogFlowThisFunc(("\n"));
12839
12840#ifdef VBOX_WITH_USB
12841 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12842 AssertComRC(rc);
12843 NOREF(rc);
12844
12845 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12846 AssertReturn(service, E_FAIL);
12847 return service->autoCaptureDevicesForVM(this);
12848#else
12849 return S_OK;
12850#endif
12851}
12852
12853/**
12854 * Removes all machine filters from the USB proxy service and then calls
12855 * Host::detachAllUSBDevices().
12856 *
12857 * Called by Console from the VM process upon normal VM termination or by
12858 * SessionMachine::uninit() upon abnormal VM termination (from under the
12859 * Machine/SessionMachine lock).
12860 *
12861 * @note Locks what called methods lock.
12862 */
12863HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12864{
12865 LogFlowThisFunc(("\n"));
12866
12867#ifdef VBOX_WITH_USB
12868 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12869 AssertComRC(rc);
12870 NOREF(rc);
12871
12872 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12873 AssertReturn(service, E_FAIL);
12874 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12875#else
12876 NOREF(aDone);
12877 return S_OK;
12878#endif
12879}
12880
12881/**
12882 * @note Locks this object for writing.
12883 */
12884HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12885 ComPtr<IProgress> &aProgress)
12886{
12887 LogFlowThisFuncEnter();
12888
12889 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12890 /*
12891 * We don't assert below because it might happen that a non-direct session
12892 * informs us it is closed right after we've been uninitialized -- it's ok.
12893 */
12894
12895 /* get IInternalSessionControl interface */
12896 ComPtr<IInternalSessionControl> control(aSession);
12897
12898 ComAssertRet(!control.isNull(), E_INVALIDARG);
12899
12900 /* Creating a Progress object requires the VirtualBox lock, and
12901 * thus locking it here is required by the lock order rules. */
12902 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12903
12904 if (control == mData->mSession.mDirectControl)
12905 {
12906 /* The direct session is being normally closed by the client process
12907 * ----------------------------------------------------------------- */
12908
12909 /* go to the closing state (essential for all open*Session() calls and
12910 * for #checkForDeath()) */
12911 Assert(mData->mSession.mState == SessionState_Locked);
12912 mData->mSession.mState = SessionState_Unlocking;
12913
12914 /* set direct control to NULL to release the remote instance */
12915 mData->mSession.mDirectControl.setNull();
12916 LogFlowThisFunc(("Direct control is set to NULL\n"));
12917
12918 if (mData->mSession.mProgress)
12919 {
12920 /* finalize the progress, someone might wait if a frontend
12921 * closes the session before powering on the VM. */
12922 mData->mSession.mProgress->notifyComplete(E_FAIL,
12923 COM_IIDOF(ISession),
12924 getComponentName(),
12925 tr("The VM session was closed before any attempt to power it on"));
12926 mData->mSession.mProgress.setNull();
12927 }
12928
12929 /* Create the progress object the client will use to wait until
12930 * #checkForDeath() is called to uninitialize this session object after
12931 * it releases the IPC semaphore.
12932 * Note! Because we're "reusing" mProgress here, this must be a proxy
12933 * object just like for LaunchVMProcess. */
12934 Assert(mData->mSession.mProgress.isNull());
12935 ComObjPtr<ProgressProxy> progress;
12936 progress.createObject();
12937 ComPtr<IUnknown> pPeer(mPeer);
12938 progress->init(mParent, pPeer,
12939 Bstr(tr("Closing session")).raw(),
12940 FALSE /* aCancelable */);
12941 progress.queryInterfaceTo(aProgress.asOutParam());
12942 mData->mSession.mProgress = progress;
12943 }
12944 else
12945 {
12946 /* the remote session is being normally closed */
12947 Data::Session::RemoteControlList::iterator it =
12948 mData->mSession.mRemoteControls.begin();
12949 while (it != mData->mSession.mRemoteControls.end())
12950 {
12951 if (control == *it)
12952 break;
12953 ++it;
12954 }
12955 BOOL found = it != mData->mSession.mRemoteControls.end();
12956 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12957 E_INVALIDARG);
12958 // This MUST be erase(it), not remove(*it) as the latter triggers a
12959 // very nasty use after free due to the place where the value "lives".
12960 mData->mSession.mRemoteControls.erase(it);
12961 }
12962
12963 /* signal the client watcher thread, because the client is going away */
12964 mParent->i_updateClientWatcher();
12965
12966 LogFlowThisFuncLeave();
12967 return S_OK;
12968}
12969
12970/**
12971 * @note Locks this object for writing.
12972 */
12973HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12974 com::Utf8Str &aStateFilePath)
12975{
12976 LogFlowThisFuncEnter();
12977
12978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12979
12980 AssertReturn( mData->mMachineState == MachineState_Paused
12981 && mConsoleTaskData.mLastState == MachineState_Null
12982 && mConsoleTaskData.strStateFilePath.isEmpty(),
12983 E_FAIL);
12984
12985 /* create a progress object to track operation completion */
12986 ComObjPtr<Progress> pProgress;
12987 pProgress.createObject();
12988 pProgress->init(i_getVirtualBox(),
12989 static_cast<IMachine *>(this) /* aInitiator */,
12990 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12991 FALSE /* aCancelable */);
12992
12993 /* stateFilePath is null when the machine is not running */
12994 if (mData->mMachineState == MachineState_Paused)
12995 i_composeSavedStateFilename(aStateFilePath);
12996
12997 /* fill in the console task data */
12998 mConsoleTaskData.mLastState = mData->mMachineState;
12999 mConsoleTaskData.strStateFilePath = aStateFilePath;
13000 mConsoleTaskData.mProgress = pProgress;
13001
13002 /* set the state to Saving (this is expected by Console::SaveState()) */
13003 i_setMachineState(MachineState_Saving);
13004
13005 pProgress.queryInterfaceTo(aProgress.asOutParam());
13006
13007 return S_OK;
13008}
13009
13010/**
13011 * @note Locks mParent + this object for writing.
13012 */
13013HRESULT SessionMachine::endSavingState(LONG aResult,
13014 const com::Utf8Str &aErrMsg)
13015{
13016 LogFlowThisFunc(("\n"));
13017
13018 /* endSavingState() need mParent lock */
13019 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13020
13021 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13022 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13023 && mConsoleTaskData.mLastState != MachineState_Null
13024 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13025 E_FAIL);
13026
13027 /*
13028 * On failure, set the state to the state we had when BeginSavingState()
13029 * was called (this is expected by Console::SaveState() and the associated
13030 * task). On success the VM process already changed the state to
13031 * MachineState_Saved, so no need to do anything.
13032 */
13033 if (FAILED(aResult))
13034 i_setMachineState(mConsoleTaskData.mLastState);
13035
13036 return i_endSavingState(aResult, aErrMsg);
13037}
13038
13039/**
13040 * @note Locks this object for writing.
13041 */
13042HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13043{
13044 LogFlowThisFunc(("\n"));
13045
13046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13047
13048 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13049 || mData->mMachineState == MachineState_Teleported
13050 || mData->mMachineState == MachineState_Aborted
13051 , E_FAIL); /** @todo setError. */
13052
13053 com::Utf8Str stateFilePathFull;
13054 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13055 if (RT_FAILURE(vrc))
13056 return setError(VBOX_E_FILE_ERROR,
13057 tr("Invalid saved state file path '%s' (%Rrc)"),
13058 aSavedStateFile.c_str(),
13059 vrc);
13060
13061 mSSData->strStateFilePath = stateFilePathFull;
13062
13063 /* The below i_setMachineState() will detect the state transition and will
13064 * update the settings file */
13065
13066 return i_setMachineState(MachineState_Saved);
13067}
13068
13069HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13070 std::vector<com::Utf8Str> &aValues,
13071 std::vector<LONG64> &aTimestamps,
13072 std::vector<com::Utf8Str> &aFlags)
13073{
13074 LogFlowThisFunc(("\n"));
13075
13076#ifdef VBOX_WITH_GUEST_PROPS
13077 using namespace guestProp;
13078
13079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13080
13081 size_t cEntries = mHWData->mGuestProperties.size();
13082 aNames.resize(cEntries);
13083 aValues.resize(cEntries);
13084 aTimestamps.resize(cEntries);
13085 aFlags.resize(cEntries);
13086
13087 size_t i = 0;
13088 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13089 it != mHWData->mGuestProperties.end();
13090 ++it, ++i)
13091 {
13092 char szFlags[MAX_FLAGS_LEN + 1];
13093 aNames[i] = it->first;
13094 aValues[i] = it->second.strValue;
13095 aTimestamps[i] = it->second.mTimestamp;
13096
13097 /* If it is NULL, keep it NULL. */
13098 if (it->second.mFlags)
13099 {
13100 writeFlags(it->second.mFlags, szFlags);
13101 aFlags[i] = szFlags;
13102 }
13103 else
13104 aFlags[i] = "";
13105 }
13106 return S_OK;
13107#else
13108 ReturnComNotImplemented();
13109#endif
13110}
13111
13112HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13113 const com::Utf8Str &aValue,
13114 LONG64 aTimestamp,
13115 const com::Utf8Str &aFlags)
13116{
13117 LogFlowThisFunc(("\n"));
13118
13119#ifdef VBOX_WITH_GUEST_PROPS
13120 using namespace guestProp;
13121
13122 try
13123 {
13124 /*
13125 * Convert input up front.
13126 */
13127 uint32_t fFlags = NILFLAG;
13128 if (aFlags.length())
13129 {
13130 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13131 AssertRCReturn(vrc, E_INVALIDARG);
13132 }
13133
13134 /*
13135 * Now grab the object lock, validate the state and do the update.
13136 */
13137
13138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13139
13140 switch (mData->mMachineState)
13141 {
13142 case MachineState_Paused:
13143 case MachineState_Running:
13144 case MachineState_Teleporting:
13145 case MachineState_TeleportingPausedVM:
13146 case MachineState_LiveSnapshotting:
13147 case MachineState_DeletingSnapshotOnline:
13148 case MachineState_DeletingSnapshotPaused:
13149 case MachineState_Saving:
13150 case MachineState_Stopping:
13151 break;
13152
13153 default:
13154 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13155 VBOX_E_INVALID_VM_STATE);
13156 }
13157
13158 i_setModified(IsModified_MachineData);
13159 mHWData.backup();
13160
13161 bool fDelete = !aValue.length();
13162 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13163 if (it != mHWData->mGuestProperties.end())
13164 {
13165 if (!fDelete)
13166 {
13167 it->second.strValue = aValue;
13168 it->second.mTimestamp = aTimestamp;
13169 it->second.mFlags = fFlags;
13170 }
13171 else
13172 mHWData->mGuestProperties.erase(it);
13173
13174 mData->mGuestPropertiesModified = TRUE;
13175 }
13176 else if (!fDelete)
13177 {
13178 HWData::GuestProperty prop;
13179 prop.strValue = aValue;
13180 prop.mTimestamp = aTimestamp;
13181 prop.mFlags = fFlags;
13182
13183 mHWData->mGuestProperties[aName] = prop;
13184 mData->mGuestPropertiesModified = TRUE;
13185 }
13186
13187 /*
13188 * Send a callback notification if appropriate
13189 */
13190 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13191 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13192 RTSTR_MAX,
13193 aName.c_str(),
13194 RTSTR_MAX, NULL)
13195 )
13196 {
13197 alock.release();
13198
13199 mParent->i_onGuestPropertyChange(mData->mUuid,
13200 Bstr(aName).raw(),
13201 Bstr(aValue).raw(),
13202 Bstr(aFlags).raw());
13203 }
13204 }
13205 catch (...)
13206 {
13207 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13208 }
13209 return S_OK;
13210#else
13211 ReturnComNotImplemented();
13212#endif
13213}
13214
13215
13216HRESULT SessionMachine::lockMedia()
13217{
13218 AutoMultiWriteLock2 alock(this->lockHandle(),
13219 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13220
13221 AssertReturn( mData->mMachineState == MachineState_Starting
13222 || mData->mMachineState == MachineState_Restoring
13223 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13224
13225 clearError();
13226 alock.release();
13227 return i_lockMedia();
13228}
13229
13230HRESULT SessionMachine::unlockMedia()
13231{
13232 HRESULT hrc = i_unlockMedia();
13233 return hrc;
13234}
13235
13236HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13237 ComPtr<IMediumAttachment> &aNewAttachment)
13238{
13239 // request the host lock first, since might be calling Host methods for getting host drives;
13240 // next, protect the media tree all the while we're in here, as well as our member variables
13241 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13242 this->lockHandle(),
13243 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13244
13245 IMediumAttachment *iAttach = aAttachment;
13246 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13247
13248 Bstr ctrlName;
13249 LONG lPort;
13250 LONG lDevice;
13251 bool fTempEject;
13252 {
13253 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13254
13255 /* Need to query the details first, as the IMediumAttachment reference
13256 * might be to the original settings, which we are going to change. */
13257 ctrlName = pAttach->i_getControllerName();
13258 lPort = pAttach->i_getPort();
13259 lDevice = pAttach->i_getDevice();
13260 fTempEject = pAttach->i_getTempEject();
13261 }
13262
13263 if (!fTempEject)
13264 {
13265 /* Remember previously mounted medium. The medium before taking the
13266 * backup is not necessarily the same thing. */
13267 ComObjPtr<Medium> oldmedium;
13268 oldmedium = pAttach->i_getMedium();
13269
13270 i_setModified(IsModified_Storage);
13271 mMediaData.backup();
13272
13273 // The backup operation makes the pAttach reference point to the
13274 // old settings. Re-get the correct reference.
13275 pAttach = i_findAttachment(mMediaData->mAttachments,
13276 ctrlName.raw(),
13277 lPort,
13278 lDevice);
13279
13280 {
13281 AutoCaller autoAttachCaller(this);
13282 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13283
13284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13285 if (!oldmedium.isNull())
13286 oldmedium->i_removeBackReference(mData->mUuid);
13287
13288 pAttach->i_updateMedium(NULL);
13289 pAttach->i_updateEjected();
13290 }
13291
13292 i_setModified(IsModified_Storage);
13293 }
13294 else
13295 {
13296 {
13297 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13298 pAttach->i_updateEjected();
13299 }
13300 }
13301
13302 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13303
13304 return S_OK;
13305}
13306
13307// public methods only for internal purposes
13308/////////////////////////////////////////////////////////////////////////////
13309
13310#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13311/**
13312 * Called from the client watcher thread to check for expected or unexpected
13313 * death of the client process that has a direct session to this machine.
13314 *
13315 * On Win32 and on OS/2, this method is called only when we've got the
13316 * mutex (i.e. the client has either died or terminated normally) so it always
13317 * returns @c true (the client is terminated, the session machine is
13318 * uninitialized).
13319 *
13320 * On other platforms, the method returns @c true if the client process has
13321 * terminated normally or abnormally and the session machine was uninitialized,
13322 * and @c false if the client process is still alive.
13323 *
13324 * @note Locks this object for writing.
13325 */
13326bool SessionMachine::i_checkForDeath()
13327{
13328 Uninit::Reason reason;
13329 bool terminated = false;
13330
13331 /* Enclose autoCaller with a block because calling uninit() from under it
13332 * will deadlock. */
13333 {
13334 AutoCaller autoCaller(this);
13335 if (!autoCaller.isOk())
13336 {
13337 /* return true if not ready, to cause the client watcher to exclude
13338 * the corresponding session from watching */
13339 LogFlowThisFunc(("Already uninitialized!\n"));
13340 return true;
13341 }
13342
13343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13344
13345 /* Determine the reason of death: if the session state is Closing here,
13346 * everything is fine. Otherwise it means that the client did not call
13347 * OnSessionEnd() before it released the IPC semaphore. This may happen
13348 * either because the client process has abnormally terminated, or
13349 * because it simply forgot to call ISession::Close() before exiting. We
13350 * threat the latter also as an abnormal termination (see
13351 * Session::uninit() for details). */
13352 reason = mData->mSession.mState == SessionState_Unlocking ?
13353 Uninit::Normal :
13354 Uninit::Abnormal;
13355
13356 if (mClientToken)
13357 terminated = mClientToken->release();
13358 } /* AutoCaller block */
13359
13360 if (terminated)
13361 uninit(reason);
13362
13363 return terminated;
13364}
13365
13366void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13367{
13368 LogFlowThisFunc(("\n"));
13369
13370 strTokenId.setNull();
13371
13372 AutoCaller autoCaller(this);
13373 AssertComRCReturnVoid(autoCaller.rc());
13374
13375 Assert(mClientToken);
13376 if (mClientToken)
13377 mClientToken->getId(strTokenId);
13378}
13379#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13380IToken *SessionMachine::i_getToken()
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384 AutoCaller autoCaller(this);
13385 AssertComRCReturn(autoCaller.rc(), NULL);
13386
13387 Assert(mClientToken);
13388 if (mClientToken)
13389 return mClientToken->getToken();
13390 else
13391 return NULL;
13392}
13393#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13394
13395Machine::ClientToken *SessionMachine::i_getClientToken()
13396{
13397 LogFlowThisFunc(("\n"));
13398
13399 AutoCaller autoCaller(this);
13400 AssertComRCReturn(autoCaller.rc(), NULL);
13401
13402 return mClientToken;
13403}
13404
13405
13406/**
13407 * @note Locks this object for reading.
13408 */
13409HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13410{
13411 LogFlowThisFunc(("\n"));
13412
13413 AutoCaller autoCaller(this);
13414 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13415
13416 ComPtr<IInternalSessionControl> directControl;
13417 {
13418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13419 directControl = mData->mSession.mDirectControl;
13420 }
13421
13422 /* ignore notifications sent after #OnSessionEnd() is called */
13423 if (!directControl)
13424 return S_OK;
13425
13426 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13427}
13428
13429/**
13430 * @note Locks this object for reading.
13431 */
13432HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13433 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13434 IN_BSTR aGuestIp, LONG aGuestPort)
13435{
13436 LogFlowThisFunc(("\n"));
13437
13438 AutoCaller autoCaller(this);
13439 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13440
13441 ComPtr<IInternalSessionControl> directControl;
13442 {
13443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13444 directControl = mData->mSession.mDirectControl;
13445 }
13446
13447 /* ignore notifications sent after #OnSessionEnd() is called */
13448 if (!directControl)
13449 return S_OK;
13450 /*
13451 * instead acting like callback we ask IVirtualBox deliver corresponding event
13452 */
13453
13454 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13455 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13456 return S_OK;
13457}
13458
13459/**
13460 * @note Locks this object for reading.
13461 */
13462HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466 AutoCaller autoCaller(this);
13467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13468
13469 ComPtr<IInternalSessionControl> directControl;
13470 {
13471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13472 directControl = mData->mSession.mDirectControl;
13473 }
13474
13475 /* ignore notifications sent after #OnSessionEnd() is called */
13476 if (!directControl)
13477 return S_OK;
13478
13479 return directControl->OnSerialPortChange(serialPort);
13480}
13481
13482/**
13483 * @note Locks this object for reading.
13484 */
13485HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489 AutoCaller autoCaller(this);
13490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13491
13492 ComPtr<IInternalSessionControl> directControl;
13493 {
13494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13495 directControl = mData->mSession.mDirectControl;
13496 }
13497
13498 /* ignore notifications sent after #OnSessionEnd() is called */
13499 if (!directControl)
13500 return S_OK;
13501
13502 return directControl->OnParallelPortChange(parallelPort);
13503}
13504
13505/**
13506 * @note Locks this object for reading.
13507 */
13508HRESULT SessionMachine::i_onStorageControllerChange()
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512 AutoCaller autoCaller(this);
13513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13514
13515 ComPtr<IInternalSessionControl> directControl;
13516 {
13517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13518 directControl = mData->mSession.mDirectControl;
13519 }
13520
13521 /* ignore notifications sent after #OnSessionEnd() is called */
13522 if (!directControl)
13523 return S_OK;
13524
13525 return directControl->OnStorageControllerChange();
13526}
13527
13528/**
13529 * @note Locks this object for reading.
13530 */
13531HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13532{
13533 LogFlowThisFunc(("\n"));
13534
13535 AutoCaller autoCaller(this);
13536 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13537
13538 ComPtr<IInternalSessionControl> directControl;
13539 {
13540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13541 directControl = mData->mSession.mDirectControl;
13542 }
13543
13544 /* ignore notifications sent after #OnSessionEnd() is called */
13545 if (!directControl)
13546 return S_OK;
13547
13548 return directControl->OnMediumChange(aAttachment, aForce);
13549}
13550
13551/**
13552 * @note Locks this object for reading.
13553 */
13554HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13555{
13556 LogFlowThisFunc(("\n"));
13557
13558 AutoCaller autoCaller(this);
13559 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13560
13561 ComPtr<IInternalSessionControl> directControl;
13562 {
13563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13564 directControl = mData->mSession.mDirectControl;
13565 }
13566
13567 /* ignore notifications sent after #OnSessionEnd() is called */
13568 if (!directControl)
13569 return S_OK;
13570
13571 return directControl->OnCPUChange(aCPU, aRemove);
13572}
13573
13574HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13575{
13576 LogFlowThisFunc(("\n"));
13577
13578 AutoCaller autoCaller(this);
13579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13580
13581 ComPtr<IInternalSessionControl> directControl;
13582 {
13583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13584 directControl = mData->mSession.mDirectControl;
13585 }
13586
13587 /* ignore notifications sent after #OnSessionEnd() is called */
13588 if (!directControl)
13589 return S_OK;
13590
13591 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13592}
13593
13594/**
13595 * @note Locks this object for reading.
13596 */
13597HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13598{
13599 LogFlowThisFunc(("\n"));
13600
13601 AutoCaller autoCaller(this);
13602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13603
13604 ComPtr<IInternalSessionControl> directControl;
13605 {
13606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13607 directControl = mData->mSession.mDirectControl;
13608 }
13609
13610 /* ignore notifications sent after #OnSessionEnd() is called */
13611 if (!directControl)
13612 return S_OK;
13613
13614 return directControl->OnVRDEServerChange(aRestart);
13615}
13616
13617/**
13618 * @note Locks this object for reading.
13619 */
13620HRESULT SessionMachine::i_onVideoCaptureChange()
13621{
13622 LogFlowThisFunc(("\n"));
13623
13624 AutoCaller autoCaller(this);
13625 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13626
13627 ComPtr<IInternalSessionControl> directControl;
13628 {
13629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13630 directControl = mData->mSession.mDirectControl;
13631 }
13632
13633 /* ignore notifications sent after #OnSessionEnd() is called */
13634 if (!directControl)
13635 return S_OK;
13636
13637 return directControl->OnVideoCaptureChange();
13638}
13639
13640/**
13641 * @note Locks this object for reading.
13642 */
13643HRESULT SessionMachine::i_onUSBControllerChange()
13644{
13645 LogFlowThisFunc(("\n"));
13646
13647 AutoCaller autoCaller(this);
13648 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13649
13650 ComPtr<IInternalSessionControl> directControl;
13651 {
13652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13653 directControl = mData->mSession.mDirectControl;
13654 }
13655
13656 /* ignore notifications sent after #OnSessionEnd() is called */
13657 if (!directControl)
13658 return S_OK;
13659
13660 return directControl->OnUSBControllerChange();
13661}
13662
13663/**
13664 * @note Locks this object for reading.
13665 */
13666HRESULT SessionMachine::i_onSharedFolderChange()
13667{
13668 LogFlowThisFunc(("\n"));
13669
13670 AutoCaller autoCaller(this);
13671 AssertComRCReturnRC(autoCaller.rc());
13672
13673 ComPtr<IInternalSessionControl> directControl;
13674 {
13675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13676 directControl = mData->mSession.mDirectControl;
13677 }
13678
13679 /* ignore notifications sent after #OnSessionEnd() is called */
13680 if (!directControl)
13681 return S_OK;
13682
13683 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13684}
13685
13686/**
13687 * @note Locks this object for reading.
13688 */
13689HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13690{
13691 LogFlowThisFunc(("\n"));
13692
13693 AutoCaller autoCaller(this);
13694 AssertComRCReturnRC(autoCaller.rc());
13695
13696 ComPtr<IInternalSessionControl> directControl;
13697 {
13698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13699 directControl = mData->mSession.mDirectControl;
13700 }
13701
13702 /* ignore notifications sent after #OnSessionEnd() is called */
13703 if (!directControl)
13704 return S_OK;
13705
13706 return directControl->OnClipboardModeChange(aClipboardMode);
13707}
13708
13709/**
13710 * @note Locks this object for reading.
13711 */
13712HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13713{
13714 LogFlowThisFunc(("\n"));
13715
13716 AutoCaller autoCaller(this);
13717 AssertComRCReturnRC(autoCaller.rc());
13718
13719 ComPtr<IInternalSessionControl> directControl;
13720 {
13721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13722 directControl = mData->mSession.mDirectControl;
13723 }
13724
13725 /* ignore notifications sent after #OnSessionEnd() is called */
13726 if (!directControl)
13727 return S_OK;
13728
13729 return directControl->OnDnDModeChange(aDnDMode);
13730}
13731
13732/**
13733 * @note Locks this object for reading.
13734 */
13735HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13736{
13737 LogFlowThisFunc(("\n"));
13738
13739 AutoCaller autoCaller(this);
13740 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13741
13742 ComPtr<IInternalSessionControl> directControl;
13743 {
13744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13745 directControl = mData->mSession.mDirectControl;
13746 }
13747
13748 /* ignore notifications sent after #OnSessionEnd() is called */
13749 if (!directControl)
13750 return S_OK;
13751
13752 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13753}
13754
13755/**
13756 * @note Locks this object for reading.
13757 */
13758HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13759{
13760 LogFlowThisFunc(("\n"));
13761
13762 AutoCaller autoCaller(this);
13763 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13764
13765 ComPtr<IInternalSessionControl> directControl;
13766 {
13767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13768 directControl = mData->mSession.mDirectControl;
13769 }
13770
13771 /* ignore notifications sent after #OnSessionEnd() is called */
13772 if (!directControl)
13773 return S_OK;
13774
13775 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13776}
13777
13778/**
13779 * Returns @c true if this machine's USB controller reports it has a matching
13780 * filter for the given USB device and @c false otherwise.
13781 *
13782 * @note locks this object for reading.
13783 */
13784bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13785{
13786 AutoCaller autoCaller(this);
13787 /* silently return if not ready -- this method may be called after the
13788 * direct machine session has been called */
13789 if (!autoCaller.isOk())
13790 return false;
13791
13792#ifdef VBOX_WITH_USB
13793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13794
13795 switch (mData->mMachineState)
13796 {
13797 case MachineState_Starting:
13798 case MachineState_Restoring:
13799 case MachineState_TeleportingIn:
13800 case MachineState_Paused:
13801 case MachineState_Running:
13802 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13803 * elsewhere... */
13804 alock.release();
13805 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13806 default: break;
13807 }
13808#else
13809 NOREF(aDevice);
13810 NOREF(aMaskedIfs);
13811#endif
13812 return false;
13813}
13814
13815/**
13816 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13817 */
13818HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13819 IVirtualBoxErrorInfo *aError,
13820 ULONG aMaskedIfs)
13821{
13822 LogFlowThisFunc(("\n"));
13823
13824 AutoCaller autoCaller(this);
13825
13826 /* This notification may happen after the machine object has been
13827 * uninitialized (the session was closed), so don't assert. */
13828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13829
13830 ComPtr<IInternalSessionControl> directControl;
13831 {
13832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13833 directControl = mData->mSession.mDirectControl;
13834 }
13835
13836 /* fail on notifications sent after #OnSessionEnd() is called, it is
13837 * expected by the caller */
13838 if (!directControl)
13839 return E_FAIL;
13840
13841 /* No locks should be held at this point. */
13842 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13843 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13844
13845 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13846}
13847
13848/**
13849 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13850 */
13851HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13852 IVirtualBoxErrorInfo *aError)
13853{
13854 LogFlowThisFunc(("\n"));
13855
13856 AutoCaller autoCaller(this);
13857
13858 /* This notification may happen after the machine object has been
13859 * uninitialized (the session was closed), so don't assert. */
13860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13861
13862 ComPtr<IInternalSessionControl> directControl;
13863 {
13864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13865 directControl = mData->mSession.mDirectControl;
13866 }
13867
13868 /* fail on notifications sent after #OnSessionEnd() is called, it is
13869 * expected by the caller */
13870 if (!directControl)
13871 return E_FAIL;
13872
13873 /* No locks should be held at this point. */
13874 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13875 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13876
13877 return directControl->OnUSBDeviceDetach(aId, aError);
13878}
13879
13880// protected methods
13881/////////////////////////////////////////////////////////////////////////////
13882
13883/**
13884 * Helper method to finalize saving the state.
13885 *
13886 * @note Must be called from under this object's lock.
13887 *
13888 * @param aRc S_OK if the snapshot has been taken successfully
13889 * @param aErrMsg human readable error message for failure
13890 *
13891 * @note Locks mParent + this objects for writing.
13892 */
13893HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13894{
13895 LogFlowThisFuncEnter();
13896
13897 AutoCaller autoCaller(this);
13898 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13899
13900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13901
13902 HRESULT rc = S_OK;
13903
13904 if (SUCCEEDED(aRc))
13905 {
13906 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13907
13908 /* save all VM settings */
13909 rc = i_saveSettings(NULL);
13910 // no need to check whether VirtualBox.xml needs saving also since
13911 // we can't have a name change pending at this point
13912 }
13913 else
13914 {
13915 // delete the saved state file (it might have been already created);
13916 // we need not check whether this is shared with a snapshot here because
13917 // we certainly created this saved state file here anew
13918 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13919 }
13920
13921 /* notify the progress object about operation completion */
13922 Assert(mConsoleTaskData.mProgress);
13923 if (SUCCEEDED(aRc))
13924 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13925 else
13926 {
13927 if (aErrMsg.length())
13928 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13929 COM_IIDOF(ISession),
13930 getComponentName(),
13931 aErrMsg.c_str());
13932 else
13933 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13934 }
13935
13936 /* clear out the temporary saved state data */
13937 mConsoleTaskData.mLastState = MachineState_Null;
13938 mConsoleTaskData.strStateFilePath.setNull();
13939 mConsoleTaskData.mProgress.setNull();
13940
13941 LogFlowThisFuncLeave();
13942 return rc;
13943}
13944
13945/**
13946 * Deletes the given file if it is no longer in use by either the current machine state
13947 * (if the machine is "saved") or any of the machine's snapshots.
13948 *
13949 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13950 * but is different for each SnapshotMachine. When calling this, the order of calling this
13951 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13952 * is therefore critical. I know, it's all rather messy.
13953 *
13954 * @param strStateFile
13955 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13956 * the test for whether the saved state file is in use.
13957 */
13958void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13959 Snapshot *pSnapshotToIgnore)
13960{
13961 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13962 if ( (strStateFile.isNotEmpty())
13963 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13964 )
13965 // ... and it must also not be shared with other snapshots
13966 if ( !mData->mFirstSnapshot
13967 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13968 // this checks the SnapshotMachine's state file paths
13969 )
13970 RTFileDelete(strStateFile.c_str());
13971}
13972
13973/**
13974 * Locks the attached media.
13975 *
13976 * All attached hard disks are locked for writing and DVD/floppy are locked for
13977 * reading. Parents of attached hard disks (if any) are locked for reading.
13978 *
13979 * This method also performs accessibility check of all media it locks: if some
13980 * media is inaccessible, the method will return a failure and a bunch of
13981 * extended error info objects per each inaccessible medium.
13982 *
13983 * Note that this method is atomic: if it returns a success, all media are
13984 * locked as described above; on failure no media is locked at all (all
13985 * succeeded individual locks will be undone).
13986 *
13987 * The caller is responsible for doing the necessary state sanity checks.
13988 *
13989 * The locks made by this method must be undone by calling #unlockMedia() when
13990 * no more needed.
13991 */
13992HRESULT SessionMachine::i_lockMedia()
13993{
13994 AutoCaller autoCaller(this);
13995 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13996
13997 AutoMultiWriteLock2 alock(this->lockHandle(),
13998 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13999
14000 /* bail out if trying to lock things with already set up locking */
14001 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14002
14003 MultiResult mrc(S_OK);
14004
14005 /* Collect locking information for all medium objects attached to the VM. */
14006 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14007 it != mMediaData->mAttachments.end();
14008 ++it)
14009 {
14010 MediumAttachment* pAtt = *it;
14011 DeviceType_T devType = pAtt->i_getType();
14012 Medium *pMedium = pAtt->i_getMedium();
14013
14014 MediumLockList *pMediumLockList(new MediumLockList());
14015 // There can be attachments without a medium (floppy/dvd), and thus
14016 // it's impossible to create a medium lock list. It still makes sense
14017 // to have the empty medium lock list in the map in case a medium is
14018 // attached later.
14019 if (pMedium != NULL)
14020 {
14021 MediumType_T mediumType = pMedium->i_getType();
14022 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14023 || mediumType == MediumType_Shareable;
14024 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14025
14026 alock.release();
14027 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14028 !fIsReadOnlyLock /* fMediumLockWrite */,
14029 NULL,
14030 *pMediumLockList);
14031 alock.acquire();
14032 if (FAILED(mrc))
14033 {
14034 delete pMediumLockList;
14035 mData->mSession.mLockedMedia.Clear();
14036 break;
14037 }
14038 }
14039
14040 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14041 if (FAILED(rc))
14042 {
14043 mData->mSession.mLockedMedia.Clear();
14044 mrc = setError(rc,
14045 tr("Collecting locking information for all attached media failed"));
14046 break;
14047 }
14048 }
14049
14050 if (SUCCEEDED(mrc))
14051 {
14052 /* Now lock all media. If this fails, nothing is locked. */
14053 alock.release();
14054 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14055 alock.acquire();
14056 if (FAILED(rc))
14057 {
14058 mrc = setError(rc,
14059 tr("Locking of attached media failed"));
14060 }
14061 }
14062
14063 return mrc;
14064}
14065
14066/**
14067 * Undoes the locks made by by #lockMedia().
14068 */
14069HRESULT SessionMachine::i_unlockMedia()
14070{
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14073
14074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14075
14076 /* we may be holding important error info on the current thread;
14077 * preserve it */
14078 ErrorInfoKeeper eik;
14079
14080 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14081 AssertComRC(rc);
14082 return rc;
14083}
14084
14085/**
14086 * Helper to change the machine state (reimplementation).
14087 *
14088 * @note Locks this object for writing.
14089 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14090 * it can cause crashes in random places due to unexpectedly committing
14091 * the current settings. The caller is responsible for that. The call
14092 * to saveStateSettings is fine, because this method does not commit.
14093 */
14094HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14095{
14096 LogFlowThisFuncEnter();
14097 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14098
14099 AutoCaller autoCaller(this);
14100 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14101
14102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14103
14104 MachineState_T oldMachineState = mData->mMachineState;
14105
14106 AssertMsgReturn(oldMachineState != aMachineState,
14107 ("oldMachineState=%s, aMachineState=%s\n",
14108 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14109 E_FAIL);
14110
14111 HRESULT rc = S_OK;
14112
14113 int stsFlags = 0;
14114 bool deleteSavedState = false;
14115
14116 /* detect some state transitions */
14117
14118 if ( ( oldMachineState == MachineState_Saved
14119 && aMachineState == MachineState_Restoring)
14120 || ( ( oldMachineState == MachineState_PoweredOff
14121 || oldMachineState == MachineState_Teleported
14122 || oldMachineState == MachineState_Aborted
14123 )
14124 && ( aMachineState == MachineState_TeleportingIn
14125 || aMachineState == MachineState_Starting
14126 )
14127 )
14128 )
14129 {
14130 /* The EMT thread is about to start */
14131
14132 /* Nothing to do here for now... */
14133
14134 /// @todo NEWMEDIA don't let mDVDDrive and other children
14135 /// change anything when in the Starting/Restoring state
14136 }
14137 else if ( ( oldMachineState == MachineState_Running
14138 || oldMachineState == MachineState_Paused
14139 || oldMachineState == MachineState_Teleporting
14140 || oldMachineState == MachineState_LiveSnapshotting
14141 || oldMachineState == MachineState_Stuck
14142 || oldMachineState == MachineState_Starting
14143 || oldMachineState == MachineState_Stopping
14144 || oldMachineState == MachineState_Saving
14145 || oldMachineState == MachineState_Restoring
14146 || oldMachineState == MachineState_TeleportingPausedVM
14147 || oldMachineState == MachineState_TeleportingIn
14148 )
14149 && ( aMachineState == MachineState_PoweredOff
14150 || aMachineState == MachineState_Saved
14151 || aMachineState == MachineState_Teleported
14152 || aMachineState == MachineState_Aborted
14153 )
14154 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14155 * snapshot */
14156 && ( mConsoleTaskData.mSnapshot.isNull()
14157 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14158 )
14159 )
14160 {
14161 /* The EMT thread has just stopped, unlock attached media. Note that as
14162 * opposed to locking that is done from Console, we do unlocking here
14163 * because the VM process may have aborted before having a chance to
14164 * properly unlock all media it locked. */
14165
14166 unlockMedia();
14167 }
14168
14169 if (oldMachineState == MachineState_Restoring)
14170 {
14171 if (aMachineState != MachineState_Saved)
14172 {
14173 /*
14174 * delete the saved state file once the machine has finished
14175 * restoring from it (note that Console sets the state from
14176 * Restoring to Saved if the VM couldn't restore successfully,
14177 * to give the user an ability to fix an error and retry --
14178 * we keep the saved state file in this case)
14179 */
14180 deleteSavedState = true;
14181 }
14182 }
14183 else if ( oldMachineState == MachineState_Saved
14184 && ( aMachineState == MachineState_PoweredOff
14185 || aMachineState == MachineState_Aborted
14186 || aMachineState == MachineState_Teleported
14187 )
14188 )
14189 {
14190 /*
14191 * delete the saved state after Console::ForgetSavedState() is called
14192 * or if the VM process (owning a direct VM session) crashed while the
14193 * VM was Saved
14194 */
14195
14196 /// @todo (dmik)
14197 // Not sure that deleting the saved state file just because of the
14198 // client death before it attempted to restore the VM is a good
14199 // thing. But when it crashes we need to go to the Aborted state
14200 // which cannot have the saved state file associated... The only
14201 // way to fix this is to make the Aborted condition not a VM state
14202 // but a bool flag: i.e., when a crash occurs, set it to true and
14203 // change the state to PoweredOff or Saved depending on the
14204 // saved state presence.
14205
14206 deleteSavedState = true;
14207 mData->mCurrentStateModified = TRUE;
14208 stsFlags |= SaveSTS_CurStateModified;
14209 }
14210
14211 if ( aMachineState == MachineState_Starting
14212 || aMachineState == MachineState_Restoring
14213 || aMachineState == MachineState_TeleportingIn
14214 )
14215 {
14216 /* set the current state modified flag to indicate that the current
14217 * state is no more identical to the state in the
14218 * current snapshot */
14219 if (!mData->mCurrentSnapshot.isNull())
14220 {
14221 mData->mCurrentStateModified = TRUE;
14222 stsFlags |= SaveSTS_CurStateModified;
14223 }
14224 }
14225
14226 if (deleteSavedState)
14227 {
14228 if (mRemoveSavedState)
14229 {
14230 Assert(!mSSData->strStateFilePath.isEmpty());
14231
14232 // it is safe to delete the saved state file if ...
14233 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14234 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14235 // ... none of the snapshots share the saved state file
14236 )
14237 RTFileDelete(mSSData->strStateFilePath.c_str());
14238 }
14239
14240 mSSData->strStateFilePath.setNull();
14241 stsFlags |= SaveSTS_StateFilePath;
14242 }
14243
14244 /* redirect to the underlying peer machine */
14245 mPeer->i_setMachineState(aMachineState);
14246
14247 if ( aMachineState == MachineState_PoweredOff
14248 || aMachineState == MachineState_Teleported
14249 || aMachineState == MachineState_Aborted
14250 || aMachineState == MachineState_Saved)
14251 {
14252 /* the machine has stopped execution
14253 * (or the saved state file was adopted) */
14254 stsFlags |= SaveSTS_StateTimeStamp;
14255 }
14256
14257 if ( ( oldMachineState == MachineState_PoweredOff
14258 || oldMachineState == MachineState_Aborted
14259 || oldMachineState == MachineState_Teleported
14260 )
14261 && aMachineState == MachineState_Saved)
14262 {
14263 /* the saved state file was adopted */
14264 Assert(!mSSData->strStateFilePath.isEmpty());
14265 stsFlags |= SaveSTS_StateFilePath;
14266 }
14267
14268#ifdef VBOX_WITH_GUEST_PROPS
14269 if ( aMachineState == MachineState_PoweredOff
14270 || aMachineState == MachineState_Aborted
14271 || aMachineState == MachineState_Teleported)
14272 {
14273 /* Make sure any transient guest properties get removed from the
14274 * property store on shutdown. */
14275
14276 HWData::GuestPropertyMap::const_iterator it;
14277 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14278 if (!fNeedsSaving)
14279 for (it = mHWData->mGuestProperties.begin();
14280 it != mHWData->mGuestProperties.end(); ++it)
14281 if ( (it->second.mFlags & guestProp::TRANSIENT)
14282 || (it->second.mFlags & guestProp::TRANSRESET))
14283 {
14284 fNeedsSaving = true;
14285 break;
14286 }
14287 if (fNeedsSaving)
14288 {
14289 mData->mCurrentStateModified = TRUE;
14290 stsFlags |= SaveSTS_CurStateModified;
14291 }
14292 }
14293#endif
14294
14295 rc = i_saveStateSettings(stsFlags);
14296
14297 if ( ( oldMachineState != MachineState_PoweredOff
14298 && oldMachineState != MachineState_Aborted
14299 && oldMachineState != MachineState_Teleported
14300 )
14301 && ( aMachineState == MachineState_PoweredOff
14302 || aMachineState == MachineState_Aborted
14303 || aMachineState == MachineState_Teleported
14304 )
14305 )
14306 {
14307 /* we've been shut down for any reason */
14308 /* no special action so far */
14309 }
14310
14311 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14312 LogFlowThisFuncLeave();
14313 return rc;
14314}
14315
14316/**
14317 * Sends the current machine state value to the VM process.
14318 *
14319 * @note Locks this object for reading, then calls a client process.
14320 */
14321HRESULT SessionMachine::i_updateMachineStateOnClient()
14322{
14323 AutoCaller autoCaller(this);
14324 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14325
14326 ComPtr<IInternalSessionControl> directControl;
14327 {
14328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14329 AssertReturn(!!mData, E_FAIL);
14330 directControl = mData->mSession.mDirectControl;
14331
14332 /* directControl may be already set to NULL here in #OnSessionEnd()
14333 * called too early by the direct session process while there is still
14334 * some operation (like deleting the snapshot) in progress. The client
14335 * process in this case is waiting inside Session::close() for the
14336 * "end session" process object to complete, while #uninit() called by
14337 * #checkForDeath() on the Watcher thread is waiting for the pending
14338 * operation to complete. For now, we accept this inconsistent behavior
14339 * and simply do nothing here. */
14340
14341 if (mData->mSession.mState == SessionState_Unlocking)
14342 return S_OK;
14343
14344 AssertReturn(!directControl.isNull(), E_FAIL);
14345 }
14346
14347 return directControl->UpdateMachineState(mData->mMachineState);
14348}
14349
14350HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14351{
14352 NOREF(aRemove);
14353 ReturnComNotImplemented();
14354}
14355
14356HRESULT Machine::updateState(MachineState_T aState)
14357{
14358 NOREF(aState);
14359 ReturnComNotImplemented();
14360}
14361
14362HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14363{
14364 NOREF(aProgress);
14365 ReturnComNotImplemented();
14366}
14367
14368HRESULT Machine::endPowerUp(LONG aResult)
14369{
14370 NOREF(aResult);
14371 ReturnComNotImplemented();
14372}
14373
14374HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14375{
14376 NOREF(aProgress);
14377 ReturnComNotImplemented();
14378}
14379
14380HRESULT Machine::endPoweringDown(LONG aResult,
14381 const com::Utf8Str &aErrMsg)
14382{
14383 NOREF(aResult);
14384 NOREF(aErrMsg);
14385 ReturnComNotImplemented();
14386}
14387
14388HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14389 BOOL *aMatched,
14390 ULONG *aMaskedInterfaces)
14391{
14392 NOREF(aDevice);
14393 NOREF(aMatched);
14394 NOREF(aMaskedInterfaces);
14395 ReturnComNotImplemented();
14396
14397}
14398
14399HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14400{
14401 NOREF(aId);
14402 ReturnComNotImplemented();
14403}
14404
14405HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14406 BOOL aDone)
14407{
14408 NOREF(aId);
14409 NOREF(aDone);
14410 ReturnComNotImplemented();
14411}
14412
14413HRESULT Machine::autoCaptureUSBDevices()
14414{
14415 ReturnComNotImplemented();
14416}
14417
14418HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14419{
14420 NOREF(aDone);
14421 ReturnComNotImplemented();
14422}
14423
14424HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14425 ComPtr<IProgress> &aProgress)
14426{
14427 NOREF(aSession);
14428 NOREF(aProgress);
14429 ReturnComNotImplemented();
14430}
14431
14432HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14433 com::Utf8Str &aStateFilePath)
14434{
14435 NOREF(aProgress);
14436 NOREF(aStateFilePath);
14437 ReturnComNotImplemented();
14438}
14439
14440HRESULT Machine::endSavingState(LONG aResult,
14441 const com::Utf8Str &aErrMsg)
14442{
14443 NOREF(aResult);
14444 NOREF(aErrMsg);
14445 ReturnComNotImplemented();
14446}
14447
14448HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14449{
14450 NOREF(aSavedStateFile);
14451 ReturnComNotImplemented();
14452}
14453
14454HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14455 const com::Utf8Str &aName,
14456 const com::Utf8Str &aDescription,
14457 const ComPtr<IProgress> &aConsoleProgress,
14458 BOOL aFTakingSnapshotOnline,
14459 com::Utf8Str &aStateFilePath)
14460{
14461 NOREF(aInitiator);
14462 NOREF(aName);
14463 NOREF(aDescription);
14464 NOREF(aConsoleProgress);
14465 NOREF(aFTakingSnapshotOnline);
14466 NOREF(aStateFilePath);
14467 ReturnComNotImplemented();
14468}
14469
14470HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14471{
14472 NOREF(aSuccess);
14473 ReturnComNotImplemented();
14474}
14475
14476HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14477 const com::Guid &aStartId,
14478 const com::Guid &aEndId,
14479 BOOL aDeleteAllChildren,
14480 MachineState_T *aMachineState,
14481 ComPtr<IProgress> &aProgress)
14482{
14483 NOREF(aInitiator);
14484 NOREF(aStartId);
14485 NOREF(aEndId);
14486 NOREF(aDeleteAllChildren);
14487 NOREF(aMachineState);
14488 NOREF(aProgress);
14489 ReturnComNotImplemented();
14490}
14491
14492HRESULT Machine::finishOnlineMergeMedium()
14493{
14494 ReturnComNotImplemented();
14495}
14496
14497HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14498 const ComPtr<ISnapshot> &aSnapshot,
14499 MachineState_T *aMachineState,
14500 ComPtr<IProgress> &aProgress)
14501{
14502 NOREF(aInitiator);
14503 NOREF(aSnapshot);
14504 NOREF(aMachineState);
14505 NOREF(aProgress);
14506 ReturnComNotImplemented();
14507}
14508
14509HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14510 std::vector<com::Utf8Str> &aValues,
14511 std::vector<LONG64> &aTimestamps,
14512 std::vector<com::Utf8Str> &aFlags)
14513{
14514 NOREF(aNames);
14515 NOREF(aValues);
14516 NOREF(aTimestamps);
14517 NOREF(aFlags);
14518 ReturnComNotImplemented();
14519}
14520
14521HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14522 const com::Utf8Str &aValue,
14523 LONG64 aTimestamp,
14524 const com::Utf8Str &aFlags)
14525{
14526 NOREF(aName);
14527 NOREF(aValue);
14528 NOREF(aTimestamp);
14529 NOREF(aFlags);
14530 ReturnComNotImplemented();
14531}
14532
14533HRESULT Machine::lockMedia()
14534{
14535 ReturnComNotImplemented();
14536}
14537
14538HRESULT Machine::unlockMedia()
14539{
14540 ReturnComNotImplemented();
14541}
14542
14543HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14544 ComPtr<IMediumAttachment> &aNewAttachment)
14545{
14546 NOREF(aAttachment);
14547 NOREF(aNewAttachment);
14548 ReturnComNotImplemented();
14549}
14550
14551HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14552 ULONG aCpuUser,
14553 ULONG aCpuKernel,
14554 ULONG aCpuIdle,
14555 ULONG aMemTotal,
14556 ULONG aMemFree,
14557 ULONG aMemBalloon,
14558 ULONG aMemShared,
14559 ULONG aMemCache,
14560 ULONG aPagedTotal,
14561 ULONG aMemAllocTotal,
14562 ULONG aMemFreeTotal,
14563 ULONG aMemBalloonTotal,
14564 ULONG aMemSharedTotal,
14565 ULONG aVmNetRx,
14566 ULONG aVmNetTx)
14567{
14568 NOREF(aValidStats);
14569 NOREF(aCpuUser);
14570 NOREF(aCpuKernel);
14571 NOREF(aCpuIdle);
14572 NOREF(aMemTotal);
14573 NOREF(aMemFree);
14574 NOREF(aMemBalloon);
14575 NOREF(aMemShared);
14576 NOREF(aMemCache);
14577 NOREF(aPagedTotal);
14578 NOREF(aMemAllocTotal);
14579 NOREF(aMemFreeTotal);
14580 NOREF(aMemBalloonTotal);
14581 NOREF(aMemSharedTotal);
14582 NOREF(aVmNetRx);
14583 NOREF(aVmNetTx);
14584 ReturnComNotImplemented();
14585}
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