VirtualBox

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

Last change on this file since 53390 was 53354, checked in by vboxsync, 10 years ago

R7524 - needs testing in VBoxManage.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 498.8 KB
Line 
1/* $Id: MachineImpl.cpp 53354 2014-11-19 18:32:03Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "Windows10"
1284 || mUserData->s.strOsType == "Windows10_64"
1285 || mUserData->s.strOsType == "Windows81"
1286 || mUserData->s.strOsType == "Windows81_64"
1287 || mUserData->s.strOsType == "Windows8"
1288 || mUserData->s.strOsType == "Windows8_64"
1289 || mUserData->s.strOsType == "Windows7"
1290 || mUserData->s.strOsType == "Windows7_64"
1291 || mUserData->s.strOsType == "WindowsVista"
1292 || mUserData->s.strOsType == "WindowsVista_64"
1293 || mUserData->s.strOsType == "Windows2012"
1294 || mUserData->s.strOsType == "Windows2012_64"
1295 || mUserData->s.strOsType == "Windows2008"
1296 || mUserData->s.strOsType == "Windows2008_64")
1297 {
1298 *aParavirtProvider = ParavirtProvider_HyperV;
1299 }
1300 else
1301 *aParavirtProvider = ParavirtProvider_None;
1302 break;
1303 }
1304 }
1305 break;
1306 }
1307 }
1308
1309 Assert( *aParavirtProvider == ParavirtProvider_None
1310 || *aParavirtProvider == ParavirtProvider_Minimal
1311 || *aParavirtProvider == ParavirtProvider_HyperV);
1312 return S_OK;
1313}
1314
1315HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1316{
1317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1318
1319 aHardwareVersion = mHWData->mHWVersion;
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1325{
1326 /* check known version */
1327 Utf8Str hwVersion = aHardwareVersion;
1328 if ( hwVersion.compare("1") != 0
1329 && hwVersion.compare("2") != 0)
1330 return setError(E_INVALIDARG,
1331 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1332
1333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 HRESULT rc = i_checkStateDependency(MutableStateDep);
1336 if (FAILED(rc)) return rc;
1337
1338 i_setModified(IsModified_MachineData);
1339 mHWData.backup();
1340 mHWData->mHWVersion = aHardwareVersion;
1341
1342 return S_OK;
1343}
1344
1345HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1346{
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 aHardwareUUID = mHWData->mHardwareUUID;
1351 else
1352 aHardwareUUID = mData->mUuid;
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1358{
1359 if (!aHardwareUUID.isValid())
1360 return E_INVALIDARG;
1361
1362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1363
1364 HRESULT rc = i_checkStateDependency(MutableStateDep);
1365 if (FAILED(rc)) return rc;
1366
1367 i_setModified(IsModified_MachineData);
1368 mHWData.backup();
1369 if (aHardwareUUID == mData->mUuid)
1370 mHWData->mHardwareUUID.clear();
1371 else
1372 mHWData->mHardwareUUID = aHardwareUUID;
1373
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 *aMemorySize = mHWData->mMemorySize;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setMemorySize(ULONG aMemorySize)
1387{
1388 /* check RAM limits */
1389 if ( aMemorySize < MM_RAM_MIN_IN_MB
1390 || aMemorySize > MM_RAM_MAX_IN_MB
1391 )
1392 return setError(E_INVALIDARG,
1393 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1394 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 mHWData->mMemorySize = aMemorySize;
1404
1405 return S_OK;
1406}
1407
1408HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1409{
1410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 *aCPUCount = mHWData->mCPUCount;
1413
1414 return S_OK;
1415}
1416
1417HRESULT Machine::setCPUCount(ULONG aCPUCount)
1418{
1419 /* check CPU limits */
1420 if ( aCPUCount < SchemaDefs::MinCPUCount
1421 || aCPUCount > SchemaDefs::MaxCPUCount
1422 )
1423 return setError(E_INVALIDARG,
1424 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1425 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1426
1427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1430 if (mHWData->mCPUHotPlugEnabled)
1431 {
1432 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1433 {
1434 if (mHWData->mCPUAttached[idx])
1435 return setError(E_INVALIDARG,
1436 tr("There is still a CPU attached to socket %lu."
1437 "Detach the CPU before removing the socket"),
1438 aCPUCount, idx+1);
1439 }
1440 }
1441
1442 HRESULT rc = i_checkStateDependency(MutableStateDep);
1443 if (FAILED(rc)) return rc;
1444
1445 i_setModified(IsModified_MachineData);
1446 mHWData.backup();
1447 mHWData->mCPUCount = aCPUCount;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1462{
1463 HRESULT rc = S_OK;
1464
1465 /* check throttle limits */
1466 if ( aCPUExecutionCap < 1
1467 || aCPUExecutionCap > 100
1468 )
1469 return setError(E_INVALIDARG,
1470 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1471 aCPUExecutionCap, 1, 100);
1472
1473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 alock.release();
1476 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1477 alock.acquire();
1478 if (FAILED(rc)) return rc;
1479
1480 i_setModified(IsModified_MachineData);
1481 mHWData.backup();
1482 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1483
1484 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1485 if (Global::IsOnline(mData->mMachineState))
1486 i_saveSettings(NULL);
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1492{
1493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
1495 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1496
1497 return S_OK;
1498}
1499
1500HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1501{
1502 HRESULT rc = S_OK;
1503
1504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1505
1506 rc = i_checkStateDependency(MutableStateDep);
1507 if (FAILED(rc)) return rc;
1508
1509 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1510 {
1511 if (aCPUHotPlugEnabled)
1512 {
1513 i_setModified(IsModified_MachineData);
1514 mHWData.backup();
1515
1516 /* Add the amount of CPUs currently attached */
1517 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1518 mHWData->mCPUAttached[i] = true;
1519 }
1520 else
1521 {
1522 /*
1523 * We can disable hotplug only if the amount of maximum CPUs is equal
1524 * to the amount of attached CPUs
1525 */
1526 unsigned cCpusAttached = 0;
1527 unsigned iHighestId = 0;
1528
1529 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1530 {
1531 if (mHWData->mCPUAttached[i])
1532 {
1533 cCpusAttached++;
1534 iHighestId = i;
1535 }
1536 }
1537
1538 if ( (cCpusAttached != mHWData->mCPUCount)
1539 || (iHighestId >= mHWData->mCPUCount))
1540 return setError(E_INVALIDARG,
1541 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1542
1543 i_setModified(IsModified_MachineData);
1544 mHWData.backup();
1545 }
1546 }
1547
1548 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1549
1550 return rc;
1551}
1552
1553HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1554{
1555#ifdef VBOX_WITH_USB_CARDREADER
1556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1557
1558 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1559
1560 return S_OK;
1561#else
1562 NOREF(aEmulatedUSBCardReaderEnabled);
1563 return E_NOTIMPL;
1564#endif
1565}
1566
1567HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1568{
1569#ifdef VBOX_WITH_USB_CARDREADER
1570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 HRESULT rc = i_checkStateDependency(MutableStateDep);
1573 if (FAILED(rc)) return rc;
1574
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1578
1579 return S_OK;
1580#else
1581 NOREF(aEmulatedUSBCardReaderEnabled);
1582 return E_NOTIMPL;
1583#endif
1584}
1585
1586HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1587{
1588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1589
1590 *aHPETEnabled = mHWData->mHPETEnabled;
1591
1592 return S_OK;
1593}
1594
1595HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1596{
1597 HRESULT rc = S_OK;
1598
1599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 rc = i_checkStateDependency(MutableStateDep);
1602 if (FAILED(rc)) return rc;
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606
1607 mHWData->mHPETEnabled = aHPETEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1621{
1622 HRESULT rc = S_OK;
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 i_setModified(IsModified_MachineData);
1627 mHWData.backup();
1628 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1629
1630 alock.release();
1631 rc = i_onVideoCaptureChange();
1632 alock.acquire();
1633 if (FAILED(rc))
1634 {
1635 /*
1636 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1637 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1638 * determine if it should start or stop capturing. Therefore we need to manually
1639 * undo change.
1640 */
1641 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1642 return rc;
1643 }
1644
1645 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1646 if (Global::IsOnline(mData->mMachineState))
1647 i_saveSettings(NULL);
1648
1649 return rc;
1650}
1651
1652HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1656 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1657 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1658 return S_OK;
1659}
1660
1661HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1662{
1663 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1664 bool fChanged = false;
1665
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667
1668 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1669 {
1670 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1671 {
1672 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1673 fChanged = true;
1674 }
1675 }
1676 if (fChanged)
1677 {
1678 alock.release();
1679 HRESULT rc = i_onVideoCaptureChange();
1680 alock.acquire();
1681 if (FAILED(rc)) return rc;
1682 i_setModified(IsModified_MachineData);
1683
1684 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1685 if (Global::IsOnline(mData->mMachineState))
1686 i_saveSettings(NULL);
1687 }
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1693{
1694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1695 if (mHWData->mVideoCaptureFile.isEmpty())
1696 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1697 else
1698 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1699 return S_OK;
1700}
1701
1702HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1703{
1704 Utf8Str strFile(aVideoCaptureFile);
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 if ( Global::IsOnline(mData->mMachineState)
1708 && mHWData->mVideoCaptureEnabled)
1709 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1710
1711 if (!RTPathStartsWithRoot(strFile.c_str()))
1712 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1713
1714 if (!strFile.isEmpty())
1715 {
1716 Utf8Str defaultFile;
1717 i_getDefaultVideoCaptureFile(defaultFile);
1718 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1719 strFile.setNull();
1720 }
1721
1722 i_setModified(IsModified_MachineData);
1723 mHWData.backup();
1724 mHWData->mVideoCaptureFile = strFile;
1725
1726 return S_OK;
1727}
1728
1729HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1730{
1731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1732 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1737{
1738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 if ( Global::IsOnline(mData->mMachineState)
1741 && mHWData->mVideoCaptureEnabled)
1742 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1743
1744 i_setModified(IsModified_MachineData);
1745 mHWData.backup();
1746 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1747
1748 return S_OK;
1749}
1750
1751HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1752{
1753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1754 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1755 return S_OK;
1756}
1757
1758HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1759{
1760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 if ( Global::IsOnline(mData->mMachineState)
1763 && mHWData->mVideoCaptureEnabled)
1764 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1765
1766 i_setModified(IsModified_MachineData);
1767 mHWData.backup();
1768 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1769
1770 return S_OK;
1771}
1772
1773HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1774{
1775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1776 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1777 return S_OK;
1778}
1779
1780HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1781{
1782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 if ( Global::IsOnline(mData->mMachineState)
1785 && mHWData->mVideoCaptureEnabled)
1786 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1787
1788 i_setModified(IsModified_MachineData);
1789 mHWData.backup();
1790 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1791
1792 return S_OK;
1793}
1794
1795HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1796{
1797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1798 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1799 return S_OK;
1800}
1801
1802HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1803{
1804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1805
1806 if ( Global::IsOnline(mData->mMachineState)
1807 && mHWData->mVideoCaptureEnabled)
1808 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1809
1810 i_setModified(IsModified_MachineData);
1811 mHWData.backup();
1812 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1813
1814 return S_OK;
1815}
1816
1817HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1818{
1819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1820 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1821 return S_OK;
1822}
1823
1824HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1825{
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 if ( Global::IsOnline(mData->mMachineState)
1829 && mHWData->mVideoCaptureEnabled)
1830 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1831
1832 i_setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1835
1836 return S_OK;
1837}
1838
1839HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1840{
1841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1842 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1843 return S_OK;
1844}
1845
1846HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1847{
1848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 if ( Global::IsOnline(mData->mMachineState)
1851 && mHWData->mVideoCaptureEnabled)
1852 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1853
1854 i_setModified(IsModified_MachineData);
1855 mHWData.backup();
1856 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1857
1858 return S_OK;
1859}
1860
1861HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1862{
1863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1864
1865 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 if ( Global::IsOnline(mData->mMachineState)
1874 && mHWData->mVideoCaptureEnabled)
1875 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1876
1877 i_setModified(IsModified_MachineData);
1878 mHWData.backup();
1879 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1889
1890 return S_OK;
1891}
1892
1893HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1894{
1895 switch (aGraphicsControllerType)
1896 {
1897 case GraphicsControllerType_Null:
1898 case GraphicsControllerType_VBoxVGA:
1899#ifdef VBOX_WITH_VMSVGA
1900 case GraphicsControllerType_VMSVGA:
1901#endif
1902 break;
1903 default:
1904 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1905 }
1906
1907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 HRESULT rc = i_checkStateDependency(MutableStateDep);
1910 if (FAILED(rc)) return rc;
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1920{
1921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1922
1923 *aVRAMSize = mHWData->mVRAMSize;
1924
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1929{
1930 /* check VRAM limits */
1931 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1932 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1933 return setError(E_INVALIDARG,
1934 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1935 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1936
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 HRESULT rc = i_checkStateDependency(MutableStateDep);
1940 if (FAILED(rc)) return rc;
1941
1942 i_setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mVRAMSize = aVRAMSize;
1945
1946 return S_OK;
1947}
1948
1949/** @todo this method should not be public */
1950HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1955
1956 return S_OK;
1957}
1958
1959/**
1960 * Set the memory balloon size.
1961 *
1962 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1963 * we have to make sure that we never call IGuest from here.
1964 */
1965HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1966{
1967 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1968#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1969 /* check limits */
1970 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1971 return setError(E_INVALIDARG,
1972 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1973 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1974
1975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1976
1977 i_setModified(IsModified_MachineData);
1978 mHWData.backup();
1979 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1980
1981 return S_OK;
1982#else
1983 NOREF(aMemoryBalloonSize);
1984 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1985#endif
1986}
1987
1988HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1989{
1990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1993 return S_OK;
1994}
1995
1996HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1997{
1998#ifdef VBOX_WITH_PAGE_SHARING
1999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2000
2001 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2005 return S_OK;
2006#else
2007 NOREF(aPageFusionEnabled);
2008 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2009#endif
2010}
2011
2012HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2013{
2014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2015
2016 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2017
2018 return S_OK;
2019}
2020
2021HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2022{
2023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2024
2025 HRESULT rc = i_checkStateDependency(MutableStateDep);
2026 if (FAILED(rc)) return rc;
2027
2028 /** @todo check validity! */
2029
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2033
2034 return S_OK;
2035}
2036
2037
2038HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2039{
2040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2041
2042 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2043
2044 return S_OK;
2045}
2046
2047HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2048{
2049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2050
2051 HRESULT rc = i_checkStateDependency(MutableStateDep);
2052 if (FAILED(rc)) return rc;
2053
2054 /** @todo check validity! */
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2058
2059 return S_OK;
2060}
2061
2062HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2063{
2064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2065
2066 *aMonitorCount = mHWData->mMonitorCount;
2067
2068 return S_OK;
2069}
2070
2071HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2072{
2073 /* make sure monitor count is a sensible number */
2074 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2075 return setError(E_INVALIDARG,
2076 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2077 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2078
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 HRESULT rc = i_checkStateDependency(MutableStateDep);
2082 if (FAILED(rc)) return rc;
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMonitorCount = aMonitorCount;
2087
2088 return S_OK;
2089}
2090
2091HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2092{
2093 /* mBIOSSettings is constant during life time, no need to lock */
2094 aBIOSSettings = mBIOSSettings;
2095
2096 return S_OK;
2097}
2098
2099HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2100{
2101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 switch (aProperty)
2104 {
2105 case CPUPropertyType_PAE:
2106 *aValue = mHWData->mPAEEnabled;
2107 break;
2108
2109 case CPUPropertyType_Synthetic:
2110 *aValue = mHWData->mSyntheticCpu;
2111 break;
2112
2113 case CPUPropertyType_LongMode:
2114 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2115 *aValue = TRUE;
2116 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2117 *aValue = FALSE;
2118#if HC_ARCH_BITS == 64
2119 else
2120 *aValue = TRUE;
2121#else
2122 else
2123 {
2124 *aValue = FALSE;
2125
2126 ComPtr<IGuestOSType> ptrGuestOSType;
2127 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2128 if (SUCCEEDED(hrc2))
2129 {
2130 BOOL fIs64Bit = FALSE;
2131 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2132 if (SUCCEEDED(hrc2) && fIs64Bit)
2133 {
2134 ComObjPtr<Host> ptrHost = mParent->i_host();
2135 alock.release();
2136
2137 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2138 if (FAILED(hrc2))
2139 *aValue = FALSE;
2140 }
2141 }
2142 }
2143#endif
2144 break;
2145
2146 case CPUPropertyType_TripleFaultReset:
2147 *aValue = mHWData->mTripleFaultReset;
2148 break;
2149
2150 default:
2151 return E_INVALIDARG;
2152 }
2153 return S_OK;
2154}
2155
2156HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2157{
2158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 HRESULT rc = i_checkStateDependency(MutableStateDep);
2161 if (FAILED(rc)) return rc;
2162
2163 switch (aProperty)
2164 {
2165 case CPUPropertyType_PAE:
2166 i_setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mPAEEnabled = !!aValue;
2169 break;
2170
2171 case CPUPropertyType_Synthetic:
2172 i_setModified(IsModified_MachineData);
2173 mHWData.backup();
2174 mHWData->mSyntheticCpu = !!aValue;
2175 break;
2176
2177 case CPUPropertyType_LongMode:
2178 i_setModified(IsModified_MachineData);
2179 mHWData.backup();
2180 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2181 break;
2182
2183 case CPUPropertyType_TripleFaultReset:
2184 i_setModified(IsModified_MachineData);
2185 mHWData.backup();
2186 mHWData->mTripleFaultReset = !!aValue;
2187 break;
2188
2189 default:
2190 return E_INVALIDARG;
2191 }
2192 return S_OK;
2193}
2194
2195HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2196{
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 switch(aId)
2200 {
2201 case 0x0:
2202 case 0x1:
2203 case 0x2:
2204 case 0x3:
2205 case 0x4:
2206 case 0x5:
2207 case 0x6:
2208 case 0x7:
2209 case 0x8:
2210 case 0x9:
2211 case 0xA:
2212 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2213 return E_INVALIDARG;
2214
2215 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2216 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2217 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2218 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2219 break;
2220
2221 case 0x80000000:
2222 case 0x80000001:
2223 case 0x80000002:
2224 case 0x80000003:
2225 case 0x80000004:
2226 case 0x80000005:
2227 case 0x80000006:
2228 case 0x80000007:
2229 case 0x80000008:
2230 case 0x80000009:
2231 case 0x8000000A:
2232 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2233 return E_INVALIDARG;
2234
2235 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2236 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2237 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2238 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2239 break;
2240
2241 default:
2242 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2243 }
2244 return S_OK;
2245}
2246
2247
2248HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2249{
2250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2251
2252 HRESULT rc = i_checkStateDependency(MutableStateDep);
2253 if (FAILED(rc)) return rc;
2254
2255 switch(aId)
2256 {
2257 case 0x0:
2258 case 0x1:
2259 case 0x2:
2260 case 0x3:
2261 case 0x4:
2262 case 0x5:
2263 case 0x6:
2264 case 0x7:
2265 case 0x8:
2266 case 0x9:
2267 case 0xA:
2268 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2269 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2270 i_setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2273 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2274 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2275 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2276 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2277 break;
2278
2279 case 0x80000000:
2280 case 0x80000001:
2281 case 0x80000002:
2282 case 0x80000003:
2283 case 0x80000004:
2284 case 0x80000005:
2285 case 0x80000006:
2286 case 0x80000007:
2287 case 0x80000008:
2288 case 0x80000009:
2289 case 0x8000000A:
2290 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2291 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2292 i_setModified(IsModified_MachineData);
2293 mHWData.backup();
2294 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2295 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2296 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2297 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2298 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2299 break;
2300
2301 default:
2302 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2303 }
2304 return S_OK;
2305}
2306
2307HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2308{
2309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
2311 HRESULT rc = i_checkStateDependency(MutableStateDep);
2312 if (FAILED(rc)) return rc;
2313
2314 switch(aId)
2315 {
2316 case 0x0:
2317 case 0x1:
2318 case 0x2:
2319 case 0x3:
2320 case 0x4:
2321 case 0x5:
2322 case 0x6:
2323 case 0x7:
2324 case 0x8:
2325 case 0x9:
2326 case 0xA:
2327 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2328 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 /* Invalidate leaf. */
2332 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2333 break;
2334
2335 case 0x80000000:
2336 case 0x80000001:
2337 case 0x80000002:
2338 case 0x80000003:
2339 case 0x80000004:
2340 case 0x80000005:
2341 case 0x80000006:
2342 case 0x80000007:
2343 case 0x80000008:
2344 case 0x80000009:
2345 case 0x8000000A:
2346 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2347 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2348 i_setModified(IsModified_MachineData);
2349 mHWData.backup();
2350 /* Invalidate leaf. */
2351 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2352 break;
2353
2354 default:
2355 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2356 }
2357 return S_OK;
2358}
2359
2360HRESULT Machine::removeAllCPUIDLeaves()
2361{
2362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 HRESULT rc = i_checkStateDependency(MutableStateDep);
2365 if (FAILED(rc)) return rc;
2366
2367 i_setModified(IsModified_MachineData);
2368 mHWData.backup();
2369
2370 /* Invalidate all standard leafs. */
2371 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2372 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2373
2374 /* Invalidate all extended leafs. */
2375 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2376 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2377
2378 return S_OK;
2379}
2380HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2381{
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 switch(aProperty)
2385 {
2386 case HWVirtExPropertyType_Enabled:
2387 *aValue = mHWData->mHWVirtExEnabled;
2388 break;
2389
2390 case HWVirtExPropertyType_VPID:
2391 *aValue = mHWData->mHWVirtExVPIDEnabled;
2392 break;
2393
2394 case HWVirtExPropertyType_NestedPaging:
2395 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2396 break;
2397
2398 case HWVirtExPropertyType_UnrestrictedExecution:
2399 *aValue = mHWData->mHWVirtExUXEnabled;
2400 break;
2401
2402 case HWVirtExPropertyType_LargePages:
2403 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2404#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2405 *aValue = FALSE;
2406#endif
2407 break;
2408
2409 case HWVirtExPropertyType_Force:
2410 *aValue = mHWData->mHWVirtExForceEnabled;
2411 break;
2412
2413 default:
2414 return E_INVALIDARG;
2415 }
2416 return S_OK;
2417}
2418
2419HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2420{
2421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 HRESULT rc = i_checkStateDependency(MutableStateDep);
2424 if (FAILED(rc)) return rc;
2425
2426 switch(aProperty)
2427 {
2428 case HWVirtExPropertyType_Enabled:
2429 i_setModified(IsModified_MachineData);
2430 mHWData.backup();
2431 mHWData->mHWVirtExEnabled = !!aValue;
2432 break;
2433
2434 case HWVirtExPropertyType_VPID:
2435 i_setModified(IsModified_MachineData);
2436 mHWData.backup();
2437 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2438 break;
2439
2440 case HWVirtExPropertyType_NestedPaging:
2441 i_setModified(IsModified_MachineData);
2442 mHWData.backup();
2443 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2444 break;
2445
2446 case HWVirtExPropertyType_UnrestrictedExecution:
2447 i_setModified(IsModified_MachineData);
2448 mHWData.backup();
2449 mHWData->mHWVirtExUXEnabled = !!aValue;
2450 break;
2451
2452 case HWVirtExPropertyType_LargePages:
2453 i_setModified(IsModified_MachineData);
2454 mHWData.backup();
2455 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2456 break;
2457
2458 case HWVirtExPropertyType_Force:
2459 i_setModified(IsModified_MachineData);
2460 mHWData.backup();
2461 mHWData->mHWVirtExForceEnabled = !!aValue;
2462 break;
2463
2464 default:
2465 return E_INVALIDARG;
2466 }
2467
2468 return S_OK;
2469}
2470
2471HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2472{
2473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2474
2475 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2476
2477 return S_OK;
2478}
2479
2480HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2481{
2482 /* @todo (r=dmik):
2483 * 1. Allow to change the name of the snapshot folder containing snapshots
2484 * 2. Rename the folder on disk instead of just changing the property
2485 * value (to be smart and not to leave garbage). Note that it cannot be
2486 * done here because the change may be rolled back. Thus, the right
2487 * place is #saveSettings().
2488 */
2489
2490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 HRESULT rc = i_checkStateDependency(MutableStateDep);
2493 if (FAILED(rc)) return rc;
2494
2495 if (!mData->mCurrentSnapshot.isNull())
2496 return setError(E_FAIL,
2497 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2498
2499 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2500
2501 if (strSnapshotFolder.isEmpty())
2502 strSnapshotFolder = "Snapshots";
2503 int vrc = i_calculateFullPath(strSnapshotFolder,
2504 strSnapshotFolder);
2505 if (RT_FAILURE(vrc))
2506 return setError(E_FAIL,
2507 tr("Invalid snapshot folder '%s' (%Rrc)"),
2508 strSnapshotFolder.c_str(), vrc);
2509
2510 i_setModified(IsModified_MachineData);
2511 mUserData.backup();
2512
2513 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2514
2515 return S_OK;
2516}
2517
2518HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2519{
2520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2521
2522 aMediumAttachments.resize(mMediaData->mAttachments.size());
2523 size_t i = 0;
2524 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2525 it != mMediaData->mAttachments.end(); ++it, ++i)
2526 aMediumAttachments[i] = *it;
2527
2528 return S_OK;
2529}
2530
2531HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 Assert(!!mVRDEServer);
2536
2537 aVRDEServer = mVRDEServer;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2543{
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 aAudioAdapter = mAudioAdapter;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2552{
2553#ifdef VBOX_WITH_VUSB
2554 clearError();
2555 MultiResult rc(S_OK);
2556
2557# ifdef VBOX_WITH_USB
2558 rc = mParent->i_host()->i_checkUSBProxyService();
2559 if (FAILED(rc)) return rc;
2560# endif
2561
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 USBControllerList data = *mUSBControllers.data();
2565 aUSBControllers.resize(data.size());
2566 size_t i = 0;
2567 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2568 aUSBControllers[i] = *it;
2569
2570 return S_OK;
2571#else
2572 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2573 * extended error info to indicate that USB is simply not available
2574 * (w/o treating it as a failure), for example, as in OSE */
2575 NOREF(aUSBControllers);
2576 ReturnComNotImplemented();
2577#endif /* VBOX_WITH_VUSB */
2578}
2579
2580HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2581{
2582#ifdef VBOX_WITH_VUSB
2583 clearError();
2584 MultiResult rc(S_OK);
2585
2586# ifdef VBOX_WITH_USB
2587 rc = mParent->i_host()->i_checkUSBProxyService();
2588 if (FAILED(rc)) return rc;
2589# endif
2590
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 aUSBDeviceFilters = mUSBDeviceFilters;
2594 return rc;
2595#else
2596 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2597 * extended error info to indicate that USB is simply not available
2598 * (w/o treating it as a failure), for example, as in OSE */
2599 NOREF(aUSBDeviceFilters);
2600 ReturnComNotImplemented();
2601#endif /* VBOX_WITH_VUSB */
2602}
2603
2604HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 aSettingsFilePath = mData->m_strConfigFileFull;
2609
2610 return S_OK;
2611}
2612
2613HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2614{
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 HRESULT rc = i_checkStateDependency(MutableStateDep);
2618 if (FAILED(rc)) return rc;
2619
2620 if (!mData->pMachineConfigFile->fileExists())
2621 // this is a new machine, and no config file exists yet:
2622 *aSettingsModified = TRUE;
2623 else
2624 *aSettingsModified = (mData->flModifications != 0);
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2630{
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 *aSessionState = mData->mSession.mState;
2635
2636 return S_OK;
2637}
2638
2639HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 aSessionType = mData->mSession.mType;
2644
2645 return S_OK;
2646}
2647
2648HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2649{
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 *aSessionPID = mData->mSession.mPID;
2653
2654 return S_OK;
2655}
2656
2657HRESULT Machine::getState(MachineState_T *aState)
2658{
2659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 *aState = mData->mMachineState;
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2667{
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 aStateFilePath = mSSData->strStateFilePath;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 i_getLogFolder(aLogFolder);
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aCurrentSnapshot = mData->mCurrentSnapshot;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2707 ? 0
2708 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2714{
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 /* Note: for machines with no snapshots, we always return FALSE
2718 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2719 * reasons :) */
2720
2721 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2722 ? FALSE
2723 : mData->mCurrentStateModified;
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aSharedFolders.resize(mHWData->mSharedFolders.size());
2733 size_t i = 0;
2734 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2735 it != mHWData->mSharedFolders.end(); ++i, ++it)
2736 aSharedFolders[i] = *it;
2737
2738 return S_OK;
2739}
2740
2741HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2742{
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 *aClipboardMode = mHWData->mClipboardMode;
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2751{
2752 HRESULT rc = S_OK;
2753
2754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 alock.release();
2757 rc = i_onClipboardModeChange(aClipboardMode);
2758 alock.acquire();
2759 if (FAILED(rc)) return rc;
2760
2761 i_setModified(IsModified_MachineData);
2762 mHWData.backup();
2763 mHWData->mClipboardMode = aClipboardMode;
2764
2765 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2766 if (Global::IsOnline(mData->mMachineState))
2767 i_saveSettings(NULL);
2768
2769 return S_OK;
2770}
2771
2772HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 *aDnDMode = mHWData->mDnDMode;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2782{
2783 HRESULT rc = S_OK;
2784
2785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 alock.release();
2788 rc = i_onDnDModeChange(aDnDMode);
2789
2790 alock.acquire();
2791 if (FAILED(rc)) return rc;
2792
2793 i_setModified(IsModified_MachineData);
2794 mHWData.backup();
2795 mHWData->mDnDMode = aDnDMode;
2796
2797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2798 if (Global::IsOnline(mData->mMachineState))
2799 i_saveSettings(NULL);
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 try
2809 {
2810 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2811 }
2812 catch (...)
2813 {
2814 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2815 }
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2821{
2822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 HRESULT rc = i_checkStateDependency(MutableStateDep);
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2830 return rc;
2831}
2832
2833HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2834{
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836 StorageControllerList data = *mStorageControllers.data();
2837 size_t i = 0;
2838 aStorageControllers.resize(data.size());
2839 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2840 aStorageControllers[i] = *it;
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aEnabled = mUserData->s.fTeleporterEnabled;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 /* Only allow it to be set to true when PoweredOff or Aborted.
2858 (Clearing it is always permitted.) */
2859 if ( aTeleporterEnabled
2860 && mData->mRegistered
2861 && ( !i_isSessionMachine()
2862 || ( mData->mMachineState != MachineState_PoweredOff
2863 && mData->mMachineState != MachineState_Teleported
2864 && mData->mMachineState != MachineState_Aborted
2865 )
2866 )
2867 )
2868 return setError(VBOX_E_INVALID_VM_STATE,
2869 tr("The machine is not powered off (state is %s)"),
2870 Global::stringifyMachineState(mData->mMachineState));
2871
2872 i_setModified(IsModified_MachineData);
2873 mUserData.backup();
2874 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2884
2885 return S_OK;
2886}
2887
2888HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2889{
2890 if (aTeleporterPort >= _64K)
2891 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2892
2893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 HRESULT rc = i_checkStateDependency(MutableStateDep);
2896 if (FAILED(rc)) return rc;
2897
2898 i_setModified(IsModified_MachineData);
2899 mUserData.backup();
2900 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2906{
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2915{
2916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 HRESULT rc = i_checkStateDependency(MutableStateDep);
2919 if (FAILED(rc)) return rc;
2920
2921 i_setModified(IsModified_MachineData);
2922 mUserData.backup();
2923 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2924
2925 return S_OK;
2926}
2927
2928HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2929{
2930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2931 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2937{
2938 /*
2939 * Hash the password first.
2940 */
2941 com::Utf8Str aT = aTeleporterPassword;
2942
2943 if (!aT.isEmpty())
2944 {
2945 if (VBoxIsPasswordHashed(&aT))
2946 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2947 VBoxHashPassword(&aT);
2948 }
2949
2950 /*
2951 * Do the update.
2952 */
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2955 if (SUCCEEDED(hrc))
2956 {
2957 i_setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterPassword = aT;
2960 }
2961
2962 return hrc;
2963}
2964
2965HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2966{
2967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2970 return S_OK;
2971}
2972
2973HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2974{
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 /* @todo deal with running state change. */
2978 HRESULT rc = i_checkStateDependency(MutableStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mUserData.backup();
2983 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2984 return S_OK;
2985}
2986
2987HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2988{
2989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2990
2991 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2992 return S_OK;
2993}
2994
2995HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2996{
2997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 /* @todo deal with running state change. */
3000 HRESULT rc = i_checkStateDependency(MutableStateDep);
3001 if (FAILED(rc)) return rc;
3002
3003 i_setModified(IsModified_MachineData);
3004 mUserData.backup();
3005 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3006 return S_OK;
3007}
3008
3009HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3010{
3011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3014 return S_OK;
3015}
3016
3017HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3018{
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 /* @todo deal with running state change. */
3022 HRESULT rc = i_checkStateDependency(MutableStateDep);
3023 if (FAILED(rc)) return rc;
3024
3025 i_setModified(IsModified_MachineData);
3026 mUserData.backup();
3027 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3028 return S_OK;
3029}
3030
3031HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3032{
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3036
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3041{
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = i_checkStateDependency(MutableStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3064{
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 /* @todo deal with running state change. */
3068 HRESULT rc = i_checkStateDependency(MutableStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 i_setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3074 return S_OK;
3075}
3076
3077HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3078{
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3082
3083 return S_OK;
3084}
3085
3086HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3087{
3088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 /* Only allow it to be set to true when PoweredOff or Aborted.
3091 (Clearing it is always permitted.) */
3092 if ( aRTCUseUTC
3093 && mData->mRegistered
3094 && ( !i_isSessionMachine()
3095 || ( mData->mMachineState != MachineState_PoweredOff
3096 && mData->mMachineState != MachineState_Teleported
3097 && mData->mMachineState != MachineState_Aborted
3098 )
3099 )
3100 )
3101 return setError(VBOX_E_INVALID_VM_STATE,
3102 tr("The machine is not powered off (state is %s)"),
3103 Global::stringifyMachineState(mData->mMachineState));
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3117
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 HRESULT rc = i_checkStateDependency(MutableStateDep);
3126 if (FAILED(rc)) return rc;
3127
3128 i_setModified(IsModified_MachineData);
3129 mHWData.backup();
3130 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3131
3132 return S_OK;
3133}
3134
3135HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3136{
3137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 *aIOCacheSize = mHWData->mIOCacheSize;
3140
3141 return S_OK;
3142}
3143
3144HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3145{
3146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3147
3148 HRESULT rc = i_checkStateDependency(MutableStateDep);
3149 if (FAILED(rc)) return rc;
3150
3151 i_setModified(IsModified_MachineData);
3152 mHWData.backup();
3153 mHWData->mIOCacheSize = aIOCacheSize;
3154
3155 return S_OK;
3156}
3157
3158
3159/**
3160 * @note Locks objects!
3161 */
3162HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3163 LockType_T aLockType)
3164
3165{
3166 /* check the session state */
3167 SessionState_T state;
3168 HRESULT rc = aSession->COMGETTER(State)(&state);
3169 if (FAILED(rc)) return rc;
3170
3171 if (state != SessionState_Unlocked)
3172 return setError(VBOX_E_INVALID_OBJECT_STATE,
3173 tr("The given session is busy"));
3174
3175 // get the client's IInternalSessionControl interface
3176 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3177 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3178 E_INVALIDARG);
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 if (!mData->mRegistered)
3183 return setError(E_UNEXPECTED,
3184 tr("The machine '%s' is not registered"),
3185 mUserData->s.strName.c_str());
3186
3187 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3188
3189 SessionState_T oldState = mData->mSession.mState;
3190 /* Hack: in case the session is closing and there is a progress object
3191 * which allows waiting for the session to be closed, take the opportunity
3192 * and do a limited wait (max. 1 second). This helps a lot when the system
3193 * is busy and thus session closing can take a little while. */
3194 if ( mData->mSession.mState == SessionState_Unlocking
3195 && mData->mSession.mProgress)
3196 {
3197 alock.release();
3198 mData->mSession.mProgress->WaitForCompletion(1000);
3199 alock.acquire();
3200 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3201 }
3202
3203 // try again now
3204 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3205 // (i.e. session machine exists)
3206 && (aLockType == LockType_Shared) // caller wants a shared link to the
3207 // existing session that holds the write lock:
3208 )
3209 {
3210 // OK, share the session... we are now dealing with three processes:
3211 // 1) VBoxSVC (where this code runs);
3212 // 2) process C: the caller's client process (who wants a shared session);
3213 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3214
3215 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3216 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3217 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3218 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3219 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3220
3221 /*
3222 * Release the lock before calling the client process. It's safe here
3223 * since the only thing to do after we get the lock again is to add
3224 * the remote control to the list (which doesn't directly influence
3225 * anything).
3226 */
3227 alock.release();
3228
3229 // get the console of the session holding the write lock (this is a remote call)
3230 ComPtr<IConsole> pConsoleW;
3231 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3232 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3233 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3234 if (FAILED(rc))
3235 // the failure may occur w/o any error info (from RPC), so provide one
3236 return setError(VBOX_E_VM_ERROR,
3237 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3238
3239 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3240
3241 // share the session machine and W's console with the caller's session
3242 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3243 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3244 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3245
3246 if (FAILED(rc))
3247 // the failure may occur w/o any error info (from RPC), so provide one
3248 return setError(VBOX_E_VM_ERROR,
3249 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3250 alock.acquire();
3251
3252 // need to revalidate the state after acquiring the lock again
3253 if (mData->mSession.mState != SessionState_Locked)
3254 {
3255 pSessionControl->Uninitialize();
3256 return setError(VBOX_E_INVALID_SESSION_STATE,
3257 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3258 mUserData->s.strName.c_str());
3259 }
3260
3261 // add the caller's session to the list
3262 mData->mSession.mRemoteControls.push_back(pSessionControl);
3263 }
3264 else if ( mData->mSession.mState == SessionState_Locked
3265 || mData->mSession.mState == SessionState_Unlocking
3266 )
3267 {
3268 // sharing not permitted, or machine still unlocking:
3269 return setError(VBOX_E_INVALID_OBJECT_STATE,
3270 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3271 mUserData->s.strName.c_str());
3272 }
3273 else
3274 {
3275 // machine is not locked: then write-lock the machine (create the session machine)
3276
3277 // must not be busy
3278 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3279
3280 // get the caller's session PID
3281 RTPROCESS pid = NIL_RTPROCESS;
3282 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3283 pSessionControl->GetPID((ULONG*)&pid);
3284 Assert(pid != NIL_RTPROCESS);
3285
3286 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3287
3288 if (fLaunchingVMProcess)
3289 {
3290 if (mData->mSession.mPID == NIL_RTPROCESS)
3291 {
3292 // two or more clients racing for a lock, the one which set the
3293 // session state to Spawning will win, the others will get an
3294 // error as we can't decide here if waiting a little would help
3295 // (only for shared locks this would avoid an error)
3296 return setError(VBOX_E_INVALID_OBJECT_STATE,
3297 tr("The machine '%s' already has a lock request pending"),
3298 mUserData->s.strName.c_str());
3299 }
3300
3301 // this machine is awaiting for a spawning session to be opened:
3302 // then the calling process must be the one that got started by
3303 // LaunchVMProcess()
3304
3305 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3306 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3307
3308#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3309 /* Hardened windows builds spawns three processes when a VM is
3310 launched, the 3rd one is the one that will end up here. */
3311 RTPROCESS ppid;
3312 int rc = RTProcQueryParent(pid, &ppid);
3313 if (RT_SUCCESS(rc))
3314 rc = RTProcQueryParent(ppid, &ppid);
3315 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3316 || rc == VERR_ACCESS_DENIED)
3317 {
3318 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3319 mData->mSession.mPID = pid;
3320 }
3321#endif
3322
3323 if (mData->mSession.mPID != pid)
3324 return setError(E_ACCESSDENIED,
3325 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3326 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3327 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3328 }
3329
3330 // create the mutable SessionMachine from the current machine
3331 ComObjPtr<SessionMachine> sessionMachine;
3332 sessionMachine.createObject();
3333 rc = sessionMachine->init(this);
3334 AssertComRC(rc);
3335
3336 /* NOTE: doing return from this function after this point but
3337 * before the end is forbidden since it may call SessionMachine::uninit()
3338 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3339 * lock while still holding the Machine lock in alock so that a deadlock
3340 * is possible due to the wrong lock order. */
3341
3342 if (SUCCEEDED(rc))
3343 {
3344 /*
3345 * Set the session state to Spawning to protect against subsequent
3346 * attempts to open a session and to unregister the machine after
3347 * we release the lock.
3348 */
3349 SessionState_T origState = mData->mSession.mState;
3350 mData->mSession.mState = SessionState_Spawning;
3351
3352#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3353 /* Get the client token ID to be passed to the client process */
3354 Utf8Str strTokenId;
3355 sessionMachine->i_getTokenId(strTokenId);
3356 Assert(!strTokenId.isEmpty());
3357#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3358 /* Get the client token to be passed to the client process */
3359 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3360 /* The token is now "owned" by pToken, fix refcount */
3361 if (!pToken.isNull())
3362 pToken->Release();
3363#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3364
3365 /*
3366 * Release the lock before calling the client process -- it will call
3367 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3368 * because the state is Spawning, so that LaunchVMProcess() and
3369 * LockMachine() calls will fail. This method, called before we
3370 * acquire the lock again, will fail because of the wrong PID.
3371 *
3372 * Note that mData->mSession.mRemoteControls accessed outside
3373 * the lock may not be modified when state is Spawning, so it's safe.
3374 */
3375 alock.release();
3376
3377 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3378#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3379 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3380#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3381 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3382 /* Now the token is owned by the client process. */
3383 pToken.setNull();
3384#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3385 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3386
3387 /* The failure may occur w/o any error info (from RPC), so provide one */
3388 if (FAILED(rc))
3389 setError(VBOX_E_VM_ERROR,
3390 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3391
3392 if ( SUCCEEDED(rc)
3393 && fLaunchingVMProcess
3394 )
3395 {
3396 /* complete the remote session initialization */
3397
3398 /* get the console from the direct session */
3399 ComPtr<IConsole> console;
3400 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3401 ComAssertComRC(rc);
3402
3403 if (SUCCEEDED(rc) && !console)
3404 {
3405 ComAssert(!!console);
3406 rc = E_FAIL;
3407 }
3408
3409 /* assign machine & console to the remote session */
3410 if (SUCCEEDED(rc))
3411 {
3412 /*
3413 * after LaunchVMProcess(), the first and the only
3414 * entry in remoteControls is that remote session
3415 */
3416 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3417 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3418 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3419
3420 /* The failure may occur w/o any error info (from RPC), so provide one */
3421 if (FAILED(rc))
3422 setError(VBOX_E_VM_ERROR,
3423 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3424 }
3425
3426 if (FAILED(rc))
3427 pSessionControl->Uninitialize();
3428 }
3429
3430 /* acquire the lock again */
3431 alock.acquire();
3432
3433 /* Restore the session state */
3434 mData->mSession.mState = origState;
3435 }
3436
3437 // finalize spawning anyway (this is why we don't return on errors above)
3438 if (fLaunchingVMProcess)
3439 {
3440 /* Note that the progress object is finalized later */
3441 /** @todo Consider checking mData->mSession.mProgress for cancellation
3442 * around here. */
3443
3444 /* We don't reset mSession.mPID here because it is necessary for
3445 * SessionMachine::uninit() to reap the child process later. */
3446
3447 if (FAILED(rc))
3448 {
3449 /* Close the remote session, remove the remote control from the list
3450 * and reset session state to Closed (@note keep the code in sync
3451 * with the relevant part in checkForSpawnFailure()). */
3452
3453 Assert(mData->mSession.mRemoteControls.size() == 1);
3454 if (mData->mSession.mRemoteControls.size() == 1)
3455 {
3456 ErrorInfoKeeper eik;
3457 mData->mSession.mRemoteControls.front()->Uninitialize();
3458 }
3459
3460 mData->mSession.mRemoteControls.clear();
3461 mData->mSession.mState = SessionState_Unlocked;
3462 }
3463 }
3464 else
3465 {
3466 /* memorize PID of the directly opened session */
3467 if (SUCCEEDED(rc))
3468 mData->mSession.mPID = pid;
3469 }
3470
3471 if (SUCCEEDED(rc))
3472 {
3473 /* memorize the direct session control and cache IUnknown for it */
3474 mData->mSession.mDirectControl = pSessionControl;
3475 mData->mSession.mState = SessionState_Locked;
3476 /* associate the SessionMachine with this Machine */
3477 mData->mSession.mMachine = sessionMachine;
3478
3479 /* request an IUnknown pointer early from the remote party for later
3480 * identity checks (it will be internally cached within mDirectControl
3481 * at least on XPCOM) */
3482 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3483 NOREF(unk);
3484 }
3485
3486 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3487 * would break the lock order */
3488 alock.release();
3489
3490 /* uninitialize the created session machine on failure */
3491 if (FAILED(rc))
3492 sessionMachine->uninit();
3493
3494 }
3495
3496 if (SUCCEEDED(rc))
3497 {
3498 /*
3499 * tell the client watcher thread to update the set of
3500 * machines that have open sessions
3501 */
3502 mParent->i_updateClientWatcher();
3503
3504 if (oldState != SessionState_Locked)
3505 /* fire an event */
3506 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3507 }
3508
3509 return rc;
3510}
3511
3512/**
3513 * @note Locks objects!
3514 */
3515HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3516 const com::Utf8Str &aType,
3517 const com::Utf8Str &aEnvironment,
3518 ComPtr<IProgress> &aProgress)
3519{
3520 Utf8Str strFrontend(aType);
3521 /* "emergencystop" doesn't need the session, so skip the checks/interface
3522 * retrieval. This code doesn't quite fit in here, but introducing a
3523 * special API method would be even more effort, and would require explicit
3524 * support by every API client. It's better to hide the feature a bit. */
3525 if (strFrontend != "emergencystop")
3526 CheckComArgNotNull(aSession);
3527
3528 HRESULT rc = S_OK;
3529 if (strFrontend.isEmpty())
3530 {
3531 Bstr bstrFrontend;
3532 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3533 if (FAILED(rc))
3534 return rc;
3535 strFrontend = bstrFrontend;
3536 if (strFrontend.isEmpty())
3537 {
3538 ComPtr<ISystemProperties> systemProperties;
3539 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3540 if (FAILED(rc))
3541 return rc;
3542 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3543 if (FAILED(rc))
3544 return rc;
3545 strFrontend = bstrFrontend;
3546 }
3547 /* paranoia - emergencystop is not a valid default */
3548 if (strFrontend == "emergencystop")
3549 strFrontend = Utf8Str::Empty;
3550 }
3551 /* default frontend: Qt GUI */
3552 if (strFrontend.isEmpty())
3553 strFrontend = "GUI/Qt";
3554
3555 if (strFrontend != "emergencystop")
3556 {
3557 /* check the session state */
3558 SessionState_T state;
3559 rc = aSession->COMGETTER(State)(&state);
3560 if (FAILED(rc))
3561 return rc;
3562
3563 if (state != SessionState_Unlocked)
3564 return setError(VBOX_E_INVALID_OBJECT_STATE,
3565 tr("The given session is busy"));
3566
3567 /* get the IInternalSessionControl interface */
3568 ComPtr<IInternalSessionControl> control(aSession);
3569 ComAssertMsgRet(!control.isNull(),
3570 ("No IInternalSessionControl interface"),
3571 E_INVALIDARG);
3572
3573 /* get the teleporter enable state for the progress object init. */
3574 BOOL fTeleporterEnabled;
3575 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3576 if (FAILED(rc))
3577 return rc;
3578
3579 /* create a progress object */
3580 ComObjPtr<ProgressProxy> progress;
3581 progress.createObject();
3582 rc = progress->init(mParent,
3583 static_cast<IMachine*>(this),
3584 Bstr(tr("Starting VM")).raw(),
3585 TRUE /* aCancelable */,
3586 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3587 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3588 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3589 2 /* uFirstOperationWeight */,
3590 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3591
3592 if (SUCCEEDED(rc))
3593 {
3594 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3595 if (SUCCEEDED(rc))
3596 {
3597 aProgress = progress;
3598
3599 /* signal the client watcher thread */
3600 mParent->i_updateClientWatcher();
3601
3602 /* fire an event */
3603 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3604 }
3605 }
3606 }
3607 else
3608 {
3609 /* no progress object - either instant success or failure */
3610 aProgress = NULL;
3611
3612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3613
3614 if (mData->mSession.mState != SessionState_Locked)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("The machine '%s' is not locked by a session"),
3617 mUserData->s.strName.c_str());
3618
3619 /* must have a VM process associated - do not kill normal API clients
3620 * with an open session */
3621 if (!Global::IsOnline(mData->mMachineState))
3622 return setError(VBOX_E_INVALID_OBJECT_STATE,
3623 tr("The machine '%s' does not have a VM process"),
3624 mUserData->s.strName.c_str());
3625
3626 /* forcibly terminate the VM process */
3627 if (mData->mSession.mPID != NIL_RTPROCESS)
3628 RTProcTerminate(mData->mSession.mPID);
3629
3630 /* signal the client watcher thread, as most likely the client has
3631 * been terminated */
3632 mParent->i_updateClientWatcher();
3633 }
3634
3635 return rc;
3636}
3637
3638HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3639{
3640 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3641 return setError(E_INVALIDARG,
3642 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3643 aPosition, SchemaDefs::MaxBootPosition);
3644
3645 if (aDevice == DeviceType_USB)
3646 return setError(E_NOTIMPL,
3647 tr("Booting from USB device is currently not supported"));
3648
3649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3650
3651 HRESULT rc = i_checkStateDependency(MutableStateDep);
3652 if (FAILED(rc)) return rc;
3653
3654 i_setModified(IsModified_MachineData);
3655 mHWData.backup();
3656 mHWData->mBootOrder[aPosition - 1] = aDevice;
3657
3658 return S_OK;
3659}
3660
3661HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3662{
3663 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3664 return setError(E_INVALIDARG,
3665 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3666 aPosition, SchemaDefs::MaxBootPosition);
3667
3668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3669
3670 *aDevice = mHWData->mBootOrder[aPosition - 1];
3671
3672 return S_OK;
3673}
3674
3675HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3676 LONG aControllerPort,
3677 LONG aDevice,
3678 DeviceType_T aType,
3679 const ComPtr<IMedium> &aMedium)
3680{
3681 IMedium *aM = aMedium;
3682 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3683 aName.c_str(), aControllerPort, aDevice, aType, aM));
3684
3685 // request the host lock first, since might be calling Host methods for getting host drives;
3686 // next, protect the media tree all the while we're in here, as well as our member variables
3687 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3688 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3689
3690 HRESULT rc = i_checkStateDependency(MutableStateDep);
3691 if (FAILED(rc)) return rc;
3692
3693 /// @todo NEWMEDIA implicit machine registration
3694 if (!mData->mRegistered)
3695 return setError(VBOX_E_INVALID_OBJECT_STATE,
3696 tr("Cannot attach storage devices to an unregistered machine"));
3697
3698 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3699
3700 /* Check for an existing controller. */
3701 ComObjPtr<StorageController> ctl;
3702 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3703 if (FAILED(rc)) return rc;
3704
3705 StorageControllerType_T ctrlType;
3706 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3707 if (FAILED(rc))
3708 return setError(E_FAIL,
3709 tr("Could not get type of controller '%s'"),
3710 aName.c_str());
3711
3712 bool fSilent = false;
3713 Utf8Str strReconfig;
3714
3715 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3716 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3717 if ( mData->mMachineState == MachineState_Paused
3718 && strReconfig == "1")
3719 fSilent = true;
3720
3721 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3722 bool fHotplug = false;
3723 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3724 fHotplug = true;
3725
3726 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3727 return setError(VBOX_E_INVALID_VM_STATE,
3728 tr("Controller '%s' does not support hotplugging"),
3729 aName.c_str());
3730
3731 // check that the port and device are not out of range
3732 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3733 if (FAILED(rc)) return rc;
3734
3735 /* check if the device slot is already busy */
3736 MediumAttachment *pAttachTemp;
3737 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3738 Bstr(aName).raw(),
3739 aControllerPort,
3740 aDevice)))
3741 {
3742 Medium *pMedium = pAttachTemp->i_getMedium();
3743 if (pMedium)
3744 {
3745 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3746 return setError(VBOX_E_OBJECT_IN_USE,
3747 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3748 pMedium->i_getLocationFull().c_str(),
3749 aControllerPort,
3750 aDevice,
3751 aName.c_str());
3752 }
3753 else
3754 return setError(VBOX_E_OBJECT_IN_USE,
3755 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3756 aControllerPort, aDevice, aName.c_str());
3757 }
3758
3759 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3760 if (aMedium && medium.isNull())
3761 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3762
3763 AutoCaller mediumCaller(medium);
3764 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3765
3766 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3767
3768 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3769 && !medium.isNull()
3770 )
3771 return setError(VBOX_E_OBJECT_IN_USE,
3772 tr("Medium '%s' is already attached to this virtual machine"),
3773 medium->i_getLocationFull().c_str());
3774
3775 if (!medium.isNull())
3776 {
3777 MediumType_T mtype = medium->i_getType();
3778 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3779 // For DVDs it's not written to the config file, so needs no global config
3780 // version bump. For floppies it's a new attribute "type", which is ignored
3781 // by older VirtualBox version, so needs no global config version bump either.
3782 // For hard disks this type is not accepted.
3783 if (mtype == MediumType_MultiAttach)
3784 {
3785 // This type is new with VirtualBox 4.0 and therefore requires settings
3786 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3787 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3788 // two reasons: The medium type is a property of the media registry tree, which
3789 // can reside in the global config file (for pre-4.0 media); we would therefore
3790 // possibly need to bump the global config version. We don't want to do that though
3791 // because that might make downgrading to pre-4.0 impossible.
3792 // As a result, we can only use these two new types if the medium is NOT in the
3793 // global registry:
3794 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3795 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3796 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3797 )
3798 return setError(VBOX_E_INVALID_OBJECT_STATE,
3799 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3800 "to machines that were created with VirtualBox 4.0 or later"),
3801 medium->i_getLocationFull().c_str());
3802 }
3803 }
3804
3805 bool fIndirect = false;
3806 if (!medium.isNull())
3807 fIndirect = medium->i_isReadOnly();
3808 bool associate = true;
3809
3810 do
3811 {
3812 if ( aType == DeviceType_HardDisk
3813 && mMediaData.isBackedUp())
3814 {
3815 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3816
3817 /* check if the medium was attached to the VM before we started
3818 * changing attachments in which case the attachment just needs to
3819 * be restored */
3820 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3821 {
3822 AssertReturn(!fIndirect, E_FAIL);
3823
3824 /* see if it's the same bus/channel/device */
3825 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3826 {
3827 /* the simplest case: restore the whole attachment
3828 * and return, nothing else to do */
3829 mMediaData->mAttachments.push_back(pAttachTemp);
3830
3831 /* Reattach the medium to the VM. */
3832 if (fHotplug || fSilent)
3833 {
3834 mediumLock.release();
3835 treeLock.release();
3836 alock.release();
3837
3838 MediumLockList *pMediumLockList(new MediumLockList());
3839
3840 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3841 true /* fMediumLockWrite */,
3842 NULL,
3843 *pMediumLockList);
3844 alock.acquire();
3845 if (FAILED(rc))
3846 delete pMediumLockList;
3847 else
3848 {
3849 mData->mSession.mLockedMedia.Unlock();
3850 alock.release();
3851 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3852 mData->mSession.mLockedMedia.Lock();
3853 alock.acquire();
3854 }
3855 alock.release();
3856
3857 if (SUCCEEDED(rc))
3858 {
3859 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3860 /* Remove lock list in case of error. */
3861 if (FAILED(rc))
3862 {
3863 mData->mSession.mLockedMedia.Unlock();
3864 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3865 mData->mSession.mLockedMedia.Lock();
3866 }
3867 }
3868 }
3869
3870 return S_OK;
3871 }
3872
3873 /* bus/channel/device differ; we need a new attachment object,
3874 * but don't try to associate it again */
3875 associate = false;
3876 break;
3877 }
3878 }
3879
3880 /* go further only if the attachment is to be indirect */
3881 if (!fIndirect)
3882 break;
3883
3884 /* perform the so called smart attachment logic for indirect
3885 * attachments. Note that smart attachment is only applicable to base
3886 * hard disks. */
3887
3888 if (medium->i_getParent().isNull())
3889 {
3890 /* first, investigate the backup copy of the current hard disk
3891 * attachments to make it possible to re-attach existing diffs to
3892 * another device slot w/o losing their contents */
3893 if (mMediaData.isBackedUp())
3894 {
3895 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3896
3897 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3898 uint32_t foundLevel = 0;
3899
3900 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3901 {
3902 uint32_t level = 0;
3903 MediumAttachment *pAttach = *it;
3904 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3905 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3906 if (pMedium.isNull())
3907 continue;
3908
3909 if (pMedium->i_getBase(&level) == medium)
3910 {
3911 /* skip the hard disk if its currently attached (we
3912 * cannot attach the same hard disk twice) */
3913 if (i_findAttachment(mMediaData->mAttachments,
3914 pMedium))
3915 continue;
3916
3917 /* matched device, channel and bus (i.e. attached to the
3918 * same place) will win and immediately stop the search;
3919 * otherwise the attachment that has the youngest
3920 * descendant of medium will be used
3921 */
3922 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3923 {
3924 /* the simplest case: restore the whole attachment
3925 * and return, nothing else to do */
3926 mMediaData->mAttachments.push_back(*it);
3927
3928 /* Reattach the medium to the VM. */
3929 if (fHotplug || fSilent)
3930 {
3931 mediumLock.release();
3932 treeLock.release();
3933 alock.release();
3934
3935 MediumLockList *pMediumLockList(new MediumLockList());
3936
3937 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3938 true /* fMediumLockWrite */,
3939 NULL,
3940 *pMediumLockList);
3941 alock.acquire();
3942 if (FAILED(rc))
3943 delete pMediumLockList;
3944 else
3945 {
3946 mData->mSession.mLockedMedia.Unlock();
3947 alock.release();
3948 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3949 mData->mSession.mLockedMedia.Lock();
3950 alock.acquire();
3951 }
3952 alock.release();
3953
3954 if (SUCCEEDED(rc))
3955 {
3956 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3957 /* Remove lock list in case of error. */
3958 if (FAILED(rc))
3959 {
3960 mData->mSession.mLockedMedia.Unlock();
3961 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3962 mData->mSession.mLockedMedia.Lock();
3963 }
3964 }
3965 }
3966
3967 return S_OK;
3968 }
3969 else if ( foundIt == oldAtts.end()
3970 || level > foundLevel /* prefer younger */
3971 )
3972 {
3973 foundIt = it;
3974 foundLevel = level;
3975 }
3976 }
3977 }
3978
3979 if (foundIt != oldAtts.end())
3980 {
3981 /* use the previously attached hard disk */
3982 medium = (*foundIt)->i_getMedium();
3983 mediumCaller.attach(medium);
3984 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3985 mediumLock.attach(medium);
3986 /* not implicit, doesn't require association with this VM */
3987 fIndirect = false;
3988 associate = false;
3989 /* go right to the MediumAttachment creation */
3990 break;
3991 }
3992 }
3993
3994 /* must give up the medium lock and medium tree lock as below we
3995 * go over snapshots, which needs a lock with higher lock order. */
3996 mediumLock.release();
3997 treeLock.release();
3998
3999 /* then, search through snapshots for the best diff in the given
4000 * hard disk's chain to base the new diff on */
4001
4002 ComObjPtr<Medium> base;
4003 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4004 while (snap)
4005 {
4006 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4007
4008 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4009
4010 MediumAttachment *pAttachFound = NULL;
4011 uint32_t foundLevel = 0;
4012
4013 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4014 {
4015 MediumAttachment *pAttach = *it;
4016 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4017 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4018 if (pMedium.isNull())
4019 continue;
4020
4021 uint32_t level = 0;
4022 if (pMedium->i_getBase(&level) == medium)
4023 {
4024 /* matched device, channel and bus (i.e. attached to the
4025 * same place) will win and immediately stop the search;
4026 * otherwise the attachment that has the youngest
4027 * descendant of medium will be used
4028 */
4029 if ( pAttach->i_getDevice() == aDevice
4030 && pAttach->i_getPort() == aControllerPort
4031 && pAttach->i_getControllerName() == aName
4032 )
4033 {
4034 pAttachFound = pAttach;
4035 break;
4036 }
4037 else if ( !pAttachFound
4038 || level > foundLevel /* prefer younger */
4039 )
4040 {
4041 pAttachFound = pAttach;
4042 foundLevel = level;
4043 }
4044 }
4045 }
4046
4047 if (pAttachFound)
4048 {
4049 base = pAttachFound->i_getMedium();
4050 break;
4051 }
4052
4053 snap = snap->i_getParent();
4054 }
4055
4056 /* re-lock medium tree and the medium, as we need it below */
4057 treeLock.acquire();
4058 mediumLock.acquire();
4059
4060 /* found a suitable diff, use it as a base */
4061 if (!base.isNull())
4062 {
4063 medium = base;
4064 mediumCaller.attach(medium);
4065 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4066 mediumLock.attach(medium);
4067 }
4068 }
4069
4070 Utf8Str strFullSnapshotFolder;
4071 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4072
4073 ComObjPtr<Medium> diff;
4074 diff.createObject();
4075 // store this diff in the same registry as the parent
4076 Guid uuidRegistryParent;
4077 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4078 {
4079 // parent image has no registry: this can happen if we're attaching a new immutable
4080 // image that has not yet been attached (medium then points to the base and we're
4081 // creating the diff image for the immutable, and the parent is not yet registered);
4082 // put the parent in the machine registry then
4083 mediumLock.release();
4084 treeLock.release();
4085 alock.release();
4086 i_addMediumToRegistry(medium);
4087 alock.acquire();
4088 treeLock.acquire();
4089 mediumLock.acquire();
4090 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4091 }
4092 rc = diff->init(mParent,
4093 medium->i_getPreferredDiffFormat(),
4094 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4095 uuidRegistryParent,
4096 DeviceType_HardDisk);
4097 if (FAILED(rc)) return rc;
4098
4099 /* Apply the normal locking logic to the entire chain. */
4100 MediumLockList *pMediumLockList(new MediumLockList());
4101 mediumLock.release();
4102 treeLock.release();
4103 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4104 true /* fMediumLockWrite */,
4105 medium,
4106 *pMediumLockList);
4107 treeLock.acquire();
4108 mediumLock.acquire();
4109 if (SUCCEEDED(rc))
4110 {
4111 mediumLock.release();
4112 treeLock.release();
4113 rc = pMediumLockList->Lock();
4114 treeLock.acquire();
4115 mediumLock.acquire();
4116 if (FAILED(rc))
4117 setError(rc,
4118 tr("Could not lock medium when creating diff '%s'"),
4119 diff->i_getLocationFull().c_str());
4120 else
4121 {
4122 /* will release the lock before the potentially lengthy
4123 * operation, so protect with the special state */
4124 MachineState_T oldState = mData->mMachineState;
4125 i_setMachineState(MachineState_SettingUp);
4126
4127 mediumLock.release();
4128 treeLock.release();
4129 alock.release();
4130
4131 rc = medium->i_createDiffStorage(diff,
4132 MediumVariant_Standard,
4133 pMediumLockList,
4134 NULL /* aProgress */,
4135 true /* aWait */);
4136
4137 alock.acquire();
4138 treeLock.acquire();
4139 mediumLock.acquire();
4140
4141 i_setMachineState(oldState);
4142 }
4143 }
4144
4145 /* Unlock the media and free the associated memory. */
4146 delete pMediumLockList;
4147
4148 if (FAILED(rc)) return rc;
4149
4150 /* use the created diff for the actual attachment */
4151 medium = diff;
4152 mediumCaller.attach(medium);
4153 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4154 mediumLock.attach(medium);
4155 }
4156 while (0);
4157
4158 ComObjPtr<MediumAttachment> attachment;
4159 attachment.createObject();
4160 rc = attachment->init(this,
4161 medium,
4162 aName,
4163 aControllerPort,
4164 aDevice,
4165 aType,
4166 fIndirect,
4167 false /* fPassthrough */,
4168 false /* fTempEject */,
4169 false /* fNonRotational */,
4170 false /* fDiscard */,
4171 fHotplug /* fHotPluggable */,
4172 Utf8Str::Empty);
4173 if (FAILED(rc)) return rc;
4174
4175 if (associate && !medium.isNull())
4176 {
4177 // as the last step, associate the medium to the VM
4178 rc = medium->i_addBackReference(mData->mUuid);
4179 // here we can fail because of Deleting, or being in process of creating a Diff
4180 if (FAILED(rc)) return rc;
4181
4182 mediumLock.release();
4183 treeLock.release();
4184 alock.release();
4185 i_addMediumToRegistry(medium);
4186 alock.acquire();
4187 treeLock.acquire();
4188 mediumLock.acquire();
4189 }
4190
4191 /* success: finally remember the attachment */
4192 i_setModified(IsModified_Storage);
4193 mMediaData.backup();
4194 mMediaData->mAttachments.push_back(attachment);
4195
4196 mediumLock.release();
4197 treeLock.release();
4198 alock.release();
4199
4200 if (fHotplug || fSilent)
4201 {
4202 if (!medium.isNull())
4203 {
4204 MediumLockList *pMediumLockList(new MediumLockList());
4205
4206 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4207 true /* fMediumLockWrite */,
4208 NULL,
4209 *pMediumLockList);
4210 alock.acquire();
4211 if (FAILED(rc))
4212 delete pMediumLockList;
4213 else
4214 {
4215 mData->mSession.mLockedMedia.Unlock();
4216 alock.release();
4217 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4218 mData->mSession.mLockedMedia.Lock();
4219 alock.acquire();
4220 }
4221 alock.release();
4222 }
4223
4224 if (SUCCEEDED(rc))
4225 {
4226 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4227 /* Remove lock list in case of error. */
4228 if (FAILED(rc))
4229 {
4230 mData->mSession.mLockedMedia.Unlock();
4231 mData->mSession.mLockedMedia.Remove(attachment);
4232 mData->mSession.mLockedMedia.Lock();
4233 }
4234 }
4235 }
4236
4237 mParent->i_saveModifiedRegistries();
4238
4239 return rc;
4240}
4241
4242HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4243 LONG aDevice)
4244{
4245 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4246 aName.c_str(), aControllerPort, aDevice));
4247
4248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4249
4250 HRESULT rc = i_checkStateDependency(MutableStateDep);
4251 if (FAILED(rc)) return rc;
4252
4253 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4254
4255 /* Check for an existing controller. */
4256 ComObjPtr<StorageController> ctl;
4257 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4258 if (FAILED(rc)) return rc;
4259
4260 StorageControllerType_T ctrlType;
4261 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4262 if (FAILED(rc))
4263 return setError(E_FAIL,
4264 tr("Could not get type of controller '%s'"),
4265 aName.c_str());
4266
4267 bool fSilent = false;
4268 Utf8Str strReconfig;
4269
4270 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4271 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4272 if ( mData->mMachineState == MachineState_Paused
4273 && strReconfig == "1")
4274 fSilent = true;
4275
4276 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4277 bool fHotplug = false;
4278 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4279 fHotplug = true;
4280
4281 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4282 return setError(VBOX_E_INVALID_VM_STATE,
4283 tr("Controller '%s' does not support hotplugging"),
4284 aName.c_str());
4285
4286 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4287 Bstr(aName).raw(),
4288 aControllerPort,
4289 aDevice);
4290 if (!pAttach)
4291 return setError(VBOX_E_OBJECT_NOT_FOUND,
4292 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4293 aDevice, aControllerPort, aName.c_str());
4294
4295 if (fHotplug && !pAttach->i_getHotPluggable())
4296 return setError(VBOX_E_NOT_SUPPORTED,
4297 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4298 aDevice, aControllerPort, aName.c_str());
4299
4300 /*
4301 * The VM has to detach the device before we delete any implicit diffs.
4302 * If this fails we can roll back without loosing data.
4303 */
4304 if (fHotplug || fSilent)
4305 {
4306 alock.release();
4307 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4308 alock.acquire();
4309 }
4310 if (FAILED(rc)) return rc;
4311
4312 /* If we are here everything went well and we can delete the implicit now. */
4313 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4314
4315 alock.release();
4316
4317 mParent->i_saveModifiedRegistries();
4318
4319 return rc;
4320}
4321
4322HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4323 LONG aDevice, BOOL aPassthrough)
4324{
4325 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4326 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4327
4328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4329
4330 HRESULT rc = i_checkStateDependency(MutableStateDep);
4331 if (FAILED(rc)) return rc;
4332
4333 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4334
4335 if (Global::IsOnlineOrTransient(mData->mMachineState))
4336 return setError(VBOX_E_INVALID_VM_STATE,
4337 tr("Invalid machine state: %s"),
4338 Global::stringifyMachineState(mData->mMachineState));
4339
4340 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4341 Bstr(aName).raw(),
4342 aControllerPort,
4343 aDevice);
4344 if (!pAttach)
4345 return setError(VBOX_E_OBJECT_NOT_FOUND,
4346 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4347 aDevice, aControllerPort, aName.c_str());
4348
4349
4350 i_setModified(IsModified_Storage);
4351 mMediaData.backup();
4352
4353 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4354
4355 if (pAttach->i_getType() != DeviceType_DVD)
4356 return setError(E_INVALIDARG,
4357 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4358 aDevice, aControllerPort, aName.c_str());
4359 pAttach->i_updatePassthrough(!!aPassthrough);
4360
4361 return S_OK;
4362}
4363
4364HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4365 LONG aDevice, BOOL aTemporaryEject)
4366{
4367
4368 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4369 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4370
4371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 HRESULT rc = i_checkStateDependency(MutableStateDep);
4374 if (FAILED(rc)) return rc;
4375
4376 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4377 Bstr(aName).raw(),
4378 aControllerPort,
4379 aDevice);
4380 if (!pAttach)
4381 return setError(VBOX_E_OBJECT_NOT_FOUND,
4382 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4383 aDevice, aControllerPort, aName.c_str());
4384
4385
4386 i_setModified(IsModified_Storage);
4387 mMediaData.backup();
4388
4389 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4390
4391 if (pAttach->i_getType() != DeviceType_DVD)
4392 return setError(E_INVALIDARG,
4393 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4394 aDevice, aControllerPort, aName.c_str());
4395 pAttach->i_updateTempEject(!!aTemporaryEject);
4396
4397 return S_OK;
4398}
4399
4400HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4401 LONG aDevice, BOOL aNonRotational)
4402{
4403
4404 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4405 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4406
4407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4408
4409 HRESULT rc = i_checkStateDependency(MutableStateDep);
4410 if (FAILED(rc)) return rc;
4411
4412 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4413
4414 if (Global::IsOnlineOrTransient(mData->mMachineState))
4415 return setError(VBOX_E_INVALID_VM_STATE,
4416 tr("Invalid machine state: %s"),
4417 Global::stringifyMachineState(mData->mMachineState));
4418
4419 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4420 Bstr(aName).raw(),
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4426 aDevice, aControllerPort, aName.c_str());
4427
4428
4429 i_setModified(IsModified_Storage);
4430 mMediaData.backup();
4431
4432 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4433
4434 if (pAttach->i_getType() != DeviceType_HardDisk)
4435 return setError(E_INVALIDARG,
4436 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"),
4437 aDevice, aControllerPort, aName.c_str());
4438 pAttach->i_updateNonRotational(!!aNonRotational);
4439
4440 return S_OK;
4441}
4442
4443HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4444 LONG aDevice, BOOL aDiscard)
4445{
4446
4447 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4448 aName.c_str(), aControllerPort, aDevice, aDiscard));
4449
4450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4451
4452 HRESULT rc = i_checkStateDependency(MutableStateDep);
4453 if (FAILED(rc)) return rc;
4454
4455 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4456
4457 if (Global::IsOnlineOrTransient(mData->mMachineState))
4458 return setError(VBOX_E_INVALID_VM_STATE,
4459 tr("Invalid machine state: %s"),
4460 Global::stringifyMachineState(mData->mMachineState));
4461
4462 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4463 Bstr(aName).raw(),
4464 aControllerPort,
4465 aDevice);
4466 if (!pAttach)
4467 return setError(VBOX_E_OBJECT_NOT_FOUND,
4468 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471
4472 i_setModified(IsModified_Storage);
4473 mMediaData.backup();
4474
4475 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4476
4477 if (pAttach->i_getType() != DeviceType_HardDisk)
4478 return setError(E_INVALIDARG,
4479 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"),
4480 aDevice, aControllerPort, aName.c_str());
4481 pAttach->i_updateDiscard(!!aDiscard);
4482
4483 return S_OK;
4484}
4485
4486HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aHotPluggable)
4488{
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4490 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT rc = i_checkStateDependency(MutableStateDep);
4495 if (FAILED(rc)) return rc;
4496
4497 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4498
4499 if (Global::IsOnlineOrTransient(mData->mMachineState))
4500 return setError(VBOX_E_INVALID_VM_STATE,
4501 tr("Invalid machine state: %s"),
4502 Global::stringifyMachineState(mData->mMachineState));
4503
4504 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4505 Bstr(aName).raw(),
4506 aControllerPort,
4507 aDevice);
4508 if (!pAttach)
4509 return setError(VBOX_E_OBJECT_NOT_FOUND,
4510 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4511 aDevice, aControllerPort, aName.c_str());
4512
4513 /* Check for an existing controller. */
4514 ComObjPtr<StorageController> ctl;
4515 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4516 if (FAILED(rc)) return rc;
4517
4518 StorageControllerType_T ctrlType;
4519 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4520 if (FAILED(rc))
4521 return setError(E_FAIL,
4522 tr("Could not get type of controller '%s'"),
4523 aName.c_str());
4524
4525 if (!i_isControllerHotplugCapable(ctrlType))
4526 return setError(VBOX_E_NOT_SUPPORTED,
4527 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4528 aName.c_str());
4529
4530 i_setModified(IsModified_Storage);
4531 mMediaData.backup();
4532
4533 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4534
4535 if (pAttach->i_getType() == DeviceType_Floppy)
4536 return setError(E_INVALIDARG,
4537 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"),
4538 aDevice, aControllerPort, aName.c_str());
4539 pAttach->i_updateHotPluggable(!!aHotPluggable);
4540
4541 return S_OK;
4542}
4543
4544HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4545 LONG aDevice)
4546{
4547 int rc = S_OK;
4548 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4549 aName.c_str(), aControllerPort, aDevice));
4550
4551 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4552
4553 return rc;
4554}
4555
4556HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4557 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4558{
4559 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4560 aName.c_str(), aControllerPort, aDevice));
4561
4562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4563
4564 HRESULT rc = i_checkStateDependency(MutableStateDep);
4565 if (FAILED(rc)) return rc;
4566
4567 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4568
4569 if (Global::IsOnlineOrTransient(mData->mMachineState))
4570 return setError(VBOX_E_INVALID_VM_STATE,
4571 tr("Invalid machine state: %s"),
4572 Global::stringifyMachineState(mData->mMachineState));
4573
4574 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4575 Bstr(aName).raw(),
4576 aControllerPort,
4577 aDevice);
4578 if (!pAttach)
4579 return setError(VBOX_E_OBJECT_NOT_FOUND,
4580 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4581 aDevice, aControllerPort, aName.c_str());
4582
4583
4584 i_setModified(IsModified_Storage);
4585 mMediaData.backup();
4586
4587 IBandwidthGroup *iB = aBandwidthGroup;
4588 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4589 if (aBandwidthGroup && group.isNull())
4590 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4591
4592 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4593
4594 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4595 if (strBandwidthGroupOld.isNotEmpty())
4596 {
4597 /* Get the bandwidth group object and release it - this must not fail. */
4598 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4599 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4600 Assert(SUCCEEDED(rc));
4601
4602 pBandwidthGroupOld->i_release();
4603 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4604 }
4605
4606 if (!group.isNull())
4607 {
4608 group->i_reference();
4609 pAttach->i_updateBandwidthGroup(group->i_getName());
4610 }
4611
4612 return S_OK;
4613}
4614
4615HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4616 LONG aControllerPort,
4617 LONG aDevice,
4618 DeviceType_T aType)
4619{
4620 HRESULT rc = S_OK;
4621
4622 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4623 aName.c_str(), aControllerPort, aDevice, aType));
4624
4625 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4626
4627 return rc;
4628}
4629
4630
4631HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4632 LONG aControllerPort,
4633 LONG aDevice,
4634 BOOL aForce)
4635{
4636 int rc = S_OK;
4637 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4638 aName.c_str(), aControllerPort, aForce));
4639
4640 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4641
4642 return rc;
4643}
4644
4645HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4646 LONG aControllerPort,
4647 LONG aDevice,
4648 const ComPtr<IMedium> &aMedium,
4649 BOOL aForce)
4650{
4651 int rc = S_OK;
4652 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4653 aName.c_str(), aControllerPort, aDevice, aForce));
4654
4655 // request the host lock first, since might be calling Host methods for getting host drives;
4656 // next, protect the media tree all the while we're in here, as well as our member variables
4657 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4658 this->lockHandle(),
4659 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4660
4661 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4662 Bstr(aName).raw(),
4663 aControllerPort,
4664 aDevice);
4665 if (pAttach.isNull())
4666 return setError(VBOX_E_OBJECT_NOT_FOUND,
4667 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4668 aDevice, aControllerPort, aName.c_str());
4669
4670 /* Remember previously mounted medium. The medium before taking the
4671 * backup is not necessarily the same thing. */
4672 ComObjPtr<Medium> oldmedium;
4673 oldmedium = pAttach->i_getMedium();
4674
4675 IMedium *iM = aMedium;
4676 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4677 if (aMedium && pMedium.isNull())
4678 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4679
4680 AutoCaller mediumCaller(pMedium);
4681 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4682
4683 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4684 if (pMedium)
4685 {
4686 DeviceType_T mediumType = pAttach->i_getType();
4687 switch (mediumType)
4688 {
4689 case DeviceType_DVD:
4690 case DeviceType_Floppy:
4691 break;
4692
4693 default:
4694 return setError(VBOX_E_INVALID_OBJECT_STATE,
4695 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4696 aControllerPort,
4697 aDevice,
4698 aName.c_str());
4699 }
4700 }
4701
4702 i_setModified(IsModified_Storage);
4703 mMediaData.backup();
4704
4705 {
4706 // The backup operation makes the pAttach reference point to the
4707 // old settings. Re-get the correct reference.
4708 pAttach = i_findAttachment(mMediaData->mAttachments,
4709 Bstr(aName).raw(),
4710 aControllerPort,
4711 aDevice);
4712 if (!oldmedium.isNull())
4713 oldmedium->i_removeBackReference(mData->mUuid);
4714 if (!pMedium.isNull())
4715 {
4716 pMedium->i_addBackReference(mData->mUuid);
4717
4718 mediumLock.release();
4719 multiLock.release();
4720 i_addMediumToRegistry(pMedium);
4721 multiLock.acquire();
4722 mediumLock.acquire();
4723 }
4724
4725 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4726 pAttach->i_updateMedium(pMedium);
4727 }
4728
4729 i_setModified(IsModified_Storage);
4730
4731 mediumLock.release();
4732 multiLock.release();
4733 rc = i_onMediumChange(pAttach, aForce);
4734 multiLock.acquire();
4735 mediumLock.acquire();
4736
4737 /* On error roll back this change only. */
4738 if (FAILED(rc))
4739 {
4740 if (!pMedium.isNull())
4741 pMedium->i_removeBackReference(mData->mUuid);
4742 pAttach = i_findAttachment(mMediaData->mAttachments,
4743 Bstr(aName).raw(),
4744 aControllerPort,
4745 aDevice);
4746 /* If the attachment is gone in the meantime, bail out. */
4747 if (pAttach.isNull())
4748 return rc;
4749 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4750 if (!oldmedium.isNull())
4751 oldmedium->i_addBackReference(mData->mUuid);
4752 pAttach->i_updateMedium(oldmedium);
4753 }
4754
4755 mediumLock.release();
4756 multiLock.release();
4757
4758 mParent->i_saveModifiedRegistries();
4759
4760 return rc;
4761}
4762HRESULT Machine::getMedium(const com::Utf8Str &aName,
4763 LONG aControllerPort,
4764 LONG aDevice,
4765 ComPtr<IMedium> &aMedium)
4766{
4767 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4768 aName.c_str(), aControllerPort, aDevice));
4769
4770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 aMedium = NULL;
4773
4774 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4775 Bstr(aName).raw(),
4776 aControllerPort,
4777 aDevice);
4778 if (pAttach.isNull())
4779 return setError(VBOX_E_OBJECT_NOT_FOUND,
4780 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4781 aDevice, aControllerPort, aName.c_str());
4782
4783 aMedium = pAttach->i_getMedium();
4784
4785 return S_OK;
4786}
4787
4788HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4789{
4790
4791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4792
4793 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4794
4795 return S_OK;
4796}
4797
4798HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4799{
4800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4801
4802 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4803
4804 return S_OK;
4805}
4806
4807HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4808{
4809 /* Do not assert if slot is out of range, just return the advertised
4810 status. testdriver/vbox.py triggers this in logVmInfo. */
4811 if (aSlot >= mNetworkAdapters.size())
4812 return setError(E_INVALIDARG,
4813 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4814 aSlot, mNetworkAdapters.size());
4815
4816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4817
4818 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4819
4820 return S_OK;
4821}
4822
4823HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4824{
4825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4826
4827 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4828 size_t i = 0;
4829 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4830 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4831 ++it, ++i)
4832 aKeys[i] = it->first;
4833
4834 return S_OK;
4835}
4836
4837 /**
4838 * @note Locks this object for reading.
4839 */
4840HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4841 com::Utf8Str &aValue)
4842{
4843 /* start with nothing found */
4844 aValue = "";
4845
4846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4847
4848 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4849 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4850 // found:
4851 aValue = it->second; // source is a Utf8Str
4852
4853 /* return the result to caller (may be empty) */
4854 return S_OK;
4855}
4856
4857 /**
4858 * @note Locks mParent for writing + this object for writing.
4859 */
4860HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4861{
4862 Utf8Str strOldValue; // empty
4863
4864 // locking note: we only hold the read lock briefly to look up the old value,
4865 // then release it and call the onExtraCanChange callbacks. There is a small
4866 // chance of a race insofar as the callback might be called twice if two callers
4867 // change the same key at the same time, but that's a much better solution
4868 // than the deadlock we had here before. The actual changing of the extradata
4869 // is then performed under the write lock and race-free.
4870
4871 // look up the old value first; if nothing has changed then we need not do anything
4872 {
4873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4874 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4875 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4876 strOldValue = it->second;
4877 }
4878
4879 bool fChanged;
4880 if ((fChanged = (strOldValue != aValue)))
4881 {
4882 // ask for permission from all listeners outside the locks;
4883 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4884 // lock to copy the list of callbacks to invoke
4885 Bstr error;
4886 Bstr bstrValue(aValue);
4887
4888 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4889 {
4890 const char *sep = error.isEmpty() ? "" : ": ";
4891 CBSTR err = error.raw();
4892 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4893 sep, err));
4894 return setError(E_ACCESSDENIED,
4895 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4896 aKey.c_str(),
4897 aValue.c_str(),
4898 sep,
4899 err);
4900 }
4901
4902 // data is changing and change not vetoed: then write it out under the lock
4903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4904
4905 if (i_isSnapshotMachine())
4906 {
4907 HRESULT rc = i_checkStateDependency(MutableStateDep);
4908 if (FAILED(rc)) return rc;
4909 }
4910
4911 if (aValue.isEmpty())
4912 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4913 else
4914 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4915 // creates a new key if needed
4916
4917 bool fNeedsGlobalSaveSettings = false;
4918 // This saving of settings is tricky: there is no "old state" for the
4919 // extradata items at all (unlike all other settings), so the old/new
4920 // settings comparison would give a wrong result!
4921 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4922
4923 if (fNeedsGlobalSaveSettings)
4924 {
4925 // save the global settings; for that we should hold only the VirtualBox lock
4926 alock.release();
4927 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4928 mParent->i_saveSettings();
4929 }
4930 }
4931
4932 // fire notification outside the lock
4933 if (fChanged)
4934 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4935
4936 return S_OK;
4937}
4938
4939HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4940{
4941 aProgress = NULL;
4942 NOREF(aSettingsFilePath);
4943 ReturnComNotImplemented();
4944}
4945
4946HRESULT Machine::saveSettings()
4947{
4948 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4949
4950 /* when there was auto-conversion, we want to save the file even if
4951 * the VM is saved */
4952 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4953 if (FAILED(rc)) return rc;
4954
4955 /* the settings file path may never be null */
4956 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4957
4958 /* save all VM data excluding snapshots */
4959 bool fNeedsGlobalSaveSettings = false;
4960 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4961 mlock.release();
4962
4963 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4964 {
4965 // save the global settings; for that we should hold only the VirtualBox lock
4966 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4967 rc = mParent->i_saveSettings();
4968 }
4969
4970 return rc;
4971}
4972
4973
4974HRESULT Machine::discardSettings()
4975{
4976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4977
4978 HRESULT rc = i_checkStateDependency(MutableStateDep);
4979 if (FAILED(rc)) return rc;
4980
4981 /*
4982 * during this rollback, the session will be notified if data has
4983 * been actually changed
4984 */
4985 i_rollback(true /* aNotify */);
4986
4987 return S_OK;
4988}
4989
4990/** @note Locks objects! */
4991HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4992 std::vector<ComPtr<IMedium> > &aMedia)
4993{
4994 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4995 AutoLimitedCaller autoCaller(this);
4996 AssertComRCReturnRC(autoCaller.rc());
4997
4998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4999
5000 Guid id(i_getId());
5001
5002 if (mData->mSession.mState != SessionState_Unlocked)
5003 return setError(VBOX_E_INVALID_OBJECT_STATE,
5004 tr("Cannot unregister the machine '%s' while it is locked"),
5005 mUserData->s.strName.c_str());
5006
5007 // wait for state dependents to drop to zero
5008 i_ensureNoStateDependencies();
5009
5010 if (!mData->mAccessible)
5011 {
5012 // inaccessible maschines can only be unregistered; uninitialize ourselves
5013 // here because currently there may be no unregistered that are inaccessible
5014 // (this state combination is not supported). Note releasing the caller and
5015 // leaving the lock before calling uninit()
5016 alock.release();
5017 autoCaller.release();
5018
5019 uninit();
5020
5021 mParent->i_unregisterMachine(this, id);
5022 // calls VirtualBox::i_saveSettings()
5023
5024 return S_OK;
5025 }
5026
5027 HRESULT rc = S_OK;
5028
5029 // discard saved state
5030 if (mData->mMachineState == MachineState_Saved)
5031 {
5032 // add the saved state file to the list of files the caller should delete
5033 Assert(!mSSData->strStateFilePath.isEmpty());
5034 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5035
5036 mSSData->strStateFilePath.setNull();
5037
5038 // unconditionally set the machine state to powered off, we now
5039 // know no session has locked the machine
5040 mData->mMachineState = MachineState_PoweredOff;
5041 }
5042
5043 size_t cSnapshots = 0;
5044 if (mData->mFirstSnapshot)
5045 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5046 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5047 // fail now before we start detaching media
5048 return setError(VBOX_E_INVALID_OBJECT_STATE,
5049 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5050 mUserData->s.strName.c_str(), cSnapshots);
5051
5052 // This list collects the medium objects from all medium attachments
5053 // which we will detach from the machine and its snapshots, in a specific
5054 // order which allows for closing all media without getting "media in use"
5055 // errors, simply by going through the list from the front to the back:
5056 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5057 // and must be closed before the parent media from the snapshots, or closing the parents
5058 // will fail because they still have children);
5059 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5060 // the root ("first") snapshot of the machine.
5061 MediaList llMedia;
5062
5063 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5064 && mMediaData->mAttachments.size()
5065 )
5066 {
5067 // we have media attachments: detach them all and add the Medium objects to our list
5068 if (aCleanupMode != CleanupMode_UnregisterOnly)
5069 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5070 else
5071 return setError(VBOX_E_INVALID_OBJECT_STATE,
5072 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5073 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5074 }
5075
5076 if (cSnapshots)
5077 {
5078 // autoCleanup must be true here, or we would have failed above
5079
5080 // add the media from the medium attachments of the snapshots to llMedia
5081 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5082 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5083 // into the children first
5084
5085 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5086 MachineState_T oldState = mData->mMachineState;
5087 mData->mMachineState = MachineState_DeletingSnapshot;
5088
5089 // make a copy of the first snapshot so the refcount does not drop to 0
5090 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5091 // because of the AutoCaller voodoo)
5092 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5093
5094 // GO!
5095 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5096
5097 mData->mMachineState = oldState;
5098 }
5099
5100 if (FAILED(rc))
5101 {
5102 i_rollbackMedia();
5103 return rc;
5104 }
5105
5106 // commit all the media changes made above
5107 i_commitMedia();
5108
5109 mData->mRegistered = false;
5110
5111 // machine lock no longer needed
5112 alock.release();
5113
5114 // return media to caller
5115 size_t i = 0;
5116 aMedia.resize(llMedia.size());
5117 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5118 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5119
5120 mParent->i_unregisterMachine(this, id);
5121 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5122
5123 return S_OK;
5124}
5125
5126struct Machine::DeleteTask
5127{
5128 ComObjPtr<Machine> pMachine;
5129 RTCList<ComPtr<IMedium> > llMediums;
5130 StringsList llFilesToDelete;
5131 ComObjPtr<Progress> pProgress;
5132};
5133
5134HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5135{
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 HRESULT rc = i_checkStateDependency(MutableStateDep);
5139 if (FAILED(rc)) return rc;
5140
5141 if (mData->mRegistered)
5142 return setError(VBOX_E_INVALID_VM_STATE,
5143 tr("Cannot delete settings of a registered machine"));
5144
5145 DeleteTask *pTask = new DeleteTask;
5146 pTask->pMachine = this;
5147
5148 // collect files to delete
5149 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5150
5151 for (size_t i = 0; i < aMedia.size(); ++i)
5152 {
5153 IMedium *pIMedium(aMedia[i]);
5154 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5155 if (pMedium.isNull())
5156 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5157 SafeArray<BSTR> ids;
5158 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5159 if (FAILED(rc)) return rc;
5160 /* At this point the medium should not have any back references
5161 * anymore. If it has it is attached to another VM and *must* not
5162 * deleted. */
5163 if (ids.size() < 1)
5164 pTask->llMediums.append(pMedium);
5165 }
5166 if (mData->pMachineConfigFile->fileExists())
5167 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5168
5169 pTask->pProgress.createObject();
5170 pTask->pProgress->init(i_getVirtualBox(),
5171 static_cast<IMachine*>(this) /* aInitiator */,
5172 Bstr(tr("Deleting files")).raw(),
5173 true /* fCancellable */,
5174 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5175 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5176
5177 int vrc = RTThreadCreate(NULL,
5178 Machine::deleteThread,
5179 (void*)pTask,
5180 0,
5181 RTTHREADTYPE_MAIN_WORKER,
5182 0,
5183 "MachineDelete");
5184
5185 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5186
5187 if (RT_FAILURE(vrc))
5188 {
5189 delete pTask;
5190 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5191 }
5192
5193 LogFlowFuncLeave();
5194
5195 return S_OK;
5196}
5197
5198/**
5199 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5200 * calls Machine::deleteTaskWorker() on the actual machine object.
5201 * @param Thread
5202 * @param pvUser
5203 * @return
5204 */
5205/*static*/
5206DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5207{
5208 LogFlowFuncEnter();
5209
5210 DeleteTask *pTask = (DeleteTask*)pvUser;
5211 Assert(pTask);
5212 Assert(pTask->pMachine);
5213 Assert(pTask->pProgress);
5214
5215 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5216 pTask->pProgress->i_notifyComplete(rc);
5217
5218 delete pTask;
5219
5220 LogFlowFuncLeave();
5221
5222 NOREF(Thread);
5223
5224 return VINF_SUCCESS;
5225}
5226
5227/**
5228 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5229 * @param task
5230 * @return
5231 */
5232HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5233{
5234 AutoCaller autoCaller(this);
5235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5236
5237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5238
5239 HRESULT rc = S_OK;
5240
5241 try
5242 {
5243 ULONG uLogHistoryCount = 3;
5244 ComPtr<ISystemProperties> systemProperties;
5245 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5246 if (FAILED(rc)) throw rc;
5247
5248 if (!systemProperties.isNull())
5249 {
5250 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5251 if (FAILED(rc)) throw rc;
5252 }
5253
5254 MachineState_T oldState = mData->mMachineState;
5255 i_setMachineState(MachineState_SettingUp);
5256 alock.release();
5257 for (size_t i = 0; i < task.llMediums.size(); ++i)
5258 {
5259 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5260 {
5261 AutoCaller mac(pMedium);
5262 if (FAILED(mac.rc())) throw mac.rc();
5263 Utf8Str strLocation = pMedium->i_getLocationFull();
5264 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5265 if (FAILED(rc)) throw rc;
5266 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5267 }
5268 ComPtr<IProgress> pProgress2;
5269 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5270 if (FAILED(rc)) throw rc;
5271 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5272 if (FAILED(rc)) throw rc;
5273 /* Check the result of the asynchronous process. */
5274 LONG iRc;
5275 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5276 if (FAILED(rc)) throw rc;
5277 /* If the thread of the progress object has an error, then
5278 * retrieve the error info from there, or it'll be lost. */
5279 if (FAILED(iRc))
5280 throw setError(ProgressErrorInfo(pProgress2));
5281
5282 /* Close the medium, deliberately without checking the return
5283- * code, and without leaving any trace in the error info, as
5284- * a failure here is a very minor issue, which shouldn't happen
5285- * as above we even managed to delete the medium. */
5286 {
5287 ErrorInfoKeeper eik;
5288 pMedium->Close();
5289 }
5290 }
5291 i_setMachineState(oldState);
5292 alock.acquire();
5293
5294 // delete the files pushed on the task list by Machine::Delete()
5295 // (this includes saved states of the machine and snapshots and
5296 // medium storage files from the IMedium list passed in, and the
5297 // machine XML file)
5298 StringsList::const_iterator it = task.llFilesToDelete.begin();
5299 while (it != task.llFilesToDelete.end())
5300 {
5301 const Utf8Str &strFile = *it;
5302 LogFunc(("Deleting file %s\n", strFile.c_str()));
5303 int vrc = RTFileDelete(strFile.c_str());
5304 if (RT_FAILURE(vrc))
5305 throw setError(VBOX_E_IPRT_ERROR,
5306 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5307
5308 ++it;
5309 if (it == task.llFilesToDelete.end())
5310 {
5311 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5312 if (FAILED(rc)) throw rc;
5313 break;
5314 }
5315
5316 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5317 if (FAILED(rc)) throw rc;
5318 }
5319
5320 /* delete the settings only when the file actually exists */
5321 if (mData->pMachineConfigFile->fileExists())
5322 {
5323 /* Delete any backup or uncommitted XML files. Ignore failures.
5324 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5325 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5326 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5327 RTFileDelete(otherXml.c_str());
5328 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5329 RTFileDelete(otherXml.c_str());
5330
5331 /* delete the Logs folder, nothing important should be left
5332 * there (we don't check for errors because the user might have
5333 * some private files there that we don't want to delete) */
5334 Utf8Str logFolder;
5335 getLogFolder(logFolder);
5336 Assert(logFolder.length());
5337 if (RTDirExists(logFolder.c_str()))
5338 {
5339 /* Delete all VBox.log[.N] files from the Logs folder
5340 * (this must be in sync with the rotation logic in
5341 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5342 * files that may have been created by the GUI. */
5343 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5344 logFolder.c_str(), RTPATH_DELIMITER);
5345 RTFileDelete(log.c_str());
5346 log = Utf8StrFmt("%s%cVBox.png",
5347 logFolder.c_str(), RTPATH_DELIMITER);
5348 RTFileDelete(log.c_str());
5349 for (int i = uLogHistoryCount; i > 0; i--)
5350 {
5351 log = Utf8StrFmt("%s%cVBox.log.%d",
5352 logFolder.c_str(), RTPATH_DELIMITER, i);
5353 RTFileDelete(log.c_str());
5354 log = Utf8StrFmt("%s%cVBox.png.%d",
5355 logFolder.c_str(), RTPATH_DELIMITER, i);
5356 RTFileDelete(log.c_str());
5357 }
5358#if defined(RT_OS_WINDOWS)
5359 log = Utf8StrFmt("%s%cVBoxStartup.log",
5360 logFolder.c_str(), RTPATH_DELIMITER);
5361 RTFileDelete(log.c_str());
5362#endif
5363
5364 RTDirRemove(logFolder.c_str());
5365 }
5366
5367 /* delete the Snapshots folder, nothing important should be left
5368 * there (we don't check for errors because the user might have
5369 * some private files there that we don't want to delete) */
5370 Utf8Str strFullSnapshotFolder;
5371 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5372 Assert(!strFullSnapshotFolder.isEmpty());
5373 if (RTDirExists(strFullSnapshotFolder.c_str()))
5374 RTDirRemove(strFullSnapshotFolder.c_str());
5375
5376 // delete the directory that contains the settings file, but only
5377 // if it matches the VM name
5378 Utf8Str settingsDir;
5379 if (i_isInOwnDir(&settingsDir))
5380 RTDirRemove(settingsDir.c_str());
5381 }
5382
5383 alock.release();
5384
5385 mParent->i_saveModifiedRegistries();
5386 }
5387 catch (HRESULT aRC) { rc = aRC; }
5388
5389 return rc;
5390}
5391
5392HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5393{
5394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5395
5396 ComObjPtr<Snapshot> pSnapshot;
5397 HRESULT rc;
5398
5399 if (aNameOrId.isEmpty())
5400 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5401 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5402 else
5403 {
5404 Guid uuid(aNameOrId);
5405 if (uuid.isValid())
5406 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5407 else
5408 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5409 }
5410 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5411
5412 return rc;
5413}
5414
5415HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5416{
5417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5418
5419 HRESULT rc = i_checkStateDependency(MutableStateDep);
5420 if (FAILED(rc)) return rc;
5421
5422 ComObjPtr<SharedFolder> sharedFolder;
5423 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5424 if (SUCCEEDED(rc))
5425 return setError(VBOX_E_OBJECT_IN_USE,
5426 tr("Shared folder named '%s' already exists"),
5427 aName.c_str());
5428
5429 sharedFolder.createObject();
5430 rc = sharedFolder->init(i_getMachine(),
5431 aName,
5432 aHostPath,
5433 !!aWritable,
5434 !!aAutomount,
5435 true /* fFailOnError */);
5436 if (FAILED(rc)) return rc;
5437
5438 i_setModified(IsModified_SharedFolders);
5439 mHWData.backup();
5440 mHWData->mSharedFolders.push_back(sharedFolder);
5441
5442 /* inform the direct session if any */
5443 alock.release();
5444 i_onSharedFolderChange();
5445
5446 return S_OK;
5447}
5448
5449HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5450{
5451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5452
5453 HRESULT rc = i_checkStateDependency(MutableStateDep);
5454 if (FAILED(rc)) return rc;
5455
5456 ComObjPtr<SharedFolder> sharedFolder;
5457 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5458 if (FAILED(rc)) return rc;
5459
5460 i_setModified(IsModified_SharedFolders);
5461 mHWData.backup();
5462 mHWData->mSharedFolders.remove(sharedFolder);
5463
5464 /* inform the direct session if any */
5465 alock.release();
5466 i_onSharedFolderChange();
5467
5468 return S_OK;
5469}
5470
5471HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5472{
5473 /* start with No */
5474 *aCanShow = FALSE;
5475
5476 ComPtr<IInternalSessionControl> directControl;
5477 {
5478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5479
5480 if (mData->mSession.mState != SessionState_Locked)
5481 return setError(VBOX_E_INVALID_VM_STATE,
5482 tr("Machine is not locked for session (session state: %s)"),
5483 Global::stringifySessionState(mData->mSession.mState));
5484
5485 directControl = mData->mSession.mDirectControl;
5486 }
5487
5488 /* ignore calls made after #OnSessionEnd() is called */
5489 if (!directControl)
5490 return S_OK;
5491
5492 LONG64 dummy;
5493 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5494}
5495
5496HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5497{
5498 ComPtr<IInternalSessionControl> directControl;
5499 {
5500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5501
5502 if (mData->mSession.mState != SessionState_Locked)
5503 return setError(E_FAIL,
5504 tr("Machine is not locked for session (session state: %s)"),
5505 Global::stringifySessionState(mData->mSession.mState));
5506
5507 directControl = mData->mSession.mDirectControl;
5508 }
5509
5510 /* ignore calls made after #OnSessionEnd() is called */
5511 if (!directControl)
5512 return S_OK;
5513
5514 BOOL dummy;
5515 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5516}
5517
5518#ifdef VBOX_WITH_GUEST_PROPS
5519/**
5520 * Look up a guest property in VBoxSVC's internal structures.
5521 */
5522HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5523 com::Utf8Str &aValue,
5524 LONG64 *aTimestamp,
5525 com::Utf8Str &aFlags) const
5526{
5527 using namespace guestProp;
5528
5529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5530 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5531
5532 if (it != mHWData->mGuestProperties.end())
5533 {
5534 char szFlags[MAX_FLAGS_LEN + 1];
5535 aValue = it->second.strValue;
5536 *aTimestamp = it->second.mTimestamp;
5537 writeFlags(it->second.mFlags, szFlags);
5538 aFlags = Utf8Str(szFlags);
5539 }
5540
5541 return S_OK;
5542}
5543
5544/**
5545 * Query the VM that a guest property belongs to for the property.
5546 * @returns E_ACCESSDENIED if the VM process is not available or not
5547 * currently handling queries and the lookup should then be done in
5548 * VBoxSVC.
5549 */
5550HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5551 com::Utf8Str &aValue,
5552 LONG64 *aTimestamp,
5553 com::Utf8Str &aFlags) const
5554{
5555 HRESULT rc = S_OK;
5556 BSTR bValue = NULL;
5557 BSTR bFlags = NULL;
5558
5559 ComPtr<IInternalSessionControl> directControl;
5560 directControl = mData->mSession.mDirectControl;
5561
5562 /* fail if we were called after #OnSessionEnd() is called. This is a
5563 * silly race condition. */
5564
5565 /** @todo This code is bothering API clients (like python script clients) with
5566 * the AccessGuestProperty call, creating unncessary IPC. Need to
5567 * have a way of figuring out which kind of direct session it is... */
5568 if (!directControl)
5569 rc = E_ACCESSDENIED;
5570 else
5571 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5572 0 /* accessMode */,
5573 &bValue, aTimestamp, &bFlags);
5574
5575 aValue = bValue;
5576 aFlags = bFlags;
5577
5578 return rc;
5579}
5580#endif // VBOX_WITH_GUEST_PROPS
5581
5582HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5583 com::Utf8Str &aValue,
5584 LONG64 *aTimestamp,
5585 com::Utf8Str &aFlags)
5586{
5587#ifndef VBOX_WITH_GUEST_PROPS
5588 ReturnComNotImplemented();
5589#else // VBOX_WITH_GUEST_PROPS
5590
5591 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5592
5593 if (rc == E_ACCESSDENIED)
5594 /* The VM is not running or the service is not (yet) accessible */
5595 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5596 return rc;
5597#endif // VBOX_WITH_GUEST_PROPS
5598}
5599
5600HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5601{
5602 LONG64 dummyTimestamp;
5603 com::Utf8Str dummyFlags;
5604 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5605 return rc;
5606
5607}
5608HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5609{
5610 com::Utf8Str dummyFlags;
5611 com::Utf8Str dummyValue;
5612 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5613 return rc;
5614}
5615
5616#ifdef VBOX_WITH_GUEST_PROPS
5617/**
5618 * Set a guest property in VBoxSVC's internal structures.
5619 */
5620HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5621 const com::Utf8Str &aFlags, bool fDelete)
5622{
5623 using namespace guestProp;
5624
5625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5626 HRESULT rc = S_OK;
5627
5628 rc = i_checkStateDependency(MutableStateDep);
5629 if (FAILED(rc)) return rc;
5630
5631 try
5632 {
5633 uint32_t fFlags = NILFLAG;
5634 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5635 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5636
5637 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5638 if (it == mHWData->mGuestProperties.end())
5639 {
5640 if (!fDelete)
5641 {
5642 i_setModified(IsModified_MachineData);
5643 mHWData.backupEx();
5644
5645 RTTIMESPEC time;
5646 HWData::GuestProperty prop;
5647 prop.strValue = Bstr(aValue).raw();
5648 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5649 prop.mFlags = fFlags;
5650 mHWData->mGuestProperties[aName] = prop;
5651 }
5652 }
5653 else
5654 {
5655 if (it->second.mFlags & (RDONLYHOST))
5656 {
5657 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5658 }
5659 else
5660 {
5661 i_setModified(IsModified_MachineData);
5662 mHWData.backupEx();
5663
5664 /* The backupEx() operation invalidates our iterator,
5665 * so get a new one. */
5666 it = mHWData->mGuestProperties.find(aName);
5667 Assert(it != mHWData->mGuestProperties.end());
5668
5669 if (!fDelete)
5670 {
5671 RTTIMESPEC time;
5672 it->second.strValue = aValue;
5673 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5674 it->second.mFlags = fFlags;
5675 }
5676 else
5677 mHWData->mGuestProperties.erase(it);
5678 }
5679 }
5680
5681 if ( SUCCEEDED(rc)
5682 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5683 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5684 RTSTR_MAX,
5685 aName.c_str(),
5686 RTSTR_MAX,
5687 NULL)
5688 )
5689 )
5690 {
5691 alock.release();
5692
5693 mParent->i_onGuestPropertyChange(mData->mUuid,
5694 Bstr(aName).raw(),
5695 Bstr(aValue).raw(),
5696 Bstr(aFlags).raw());
5697 }
5698 }
5699 catch (std::bad_alloc &)
5700 {
5701 rc = E_OUTOFMEMORY;
5702 }
5703
5704 return rc;
5705}
5706
5707/**
5708 * Set a property on the VM that that property belongs to.
5709 * @returns E_ACCESSDENIED if the VM process is not available or not
5710 * currently handling queries and the setting should then be done in
5711 * VBoxSVC.
5712 */
5713HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5714 const com::Utf8Str &aFlags, bool fDelete)
5715{
5716 HRESULT rc;
5717
5718 try
5719 {
5720 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5721
5722 BSTR dummy = NULL; /* will not be changed (setter) */
5723 LONG64 dummy64;
5724 if (!directControl)
5725 rc = E_ACCESSDENIED;
5726 else
5727 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5728 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5729 fDelete? 2: 1 /* accessMode */,
5730 &dummy, &dummy64, &dummy);
5731 }
5732 catch (std::bad_alloc &)
5733 {
5734 rc = E_OUTOFMEMORY;
5735 }
5736
5737 return rc;
5738}
5739#endif // VBOX_WITH_GUEST_PROPS
5740
5741HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5742 const com::Utf8Str &aFlags)
5743{
5744#ifndef VBOX_WITH_GUEST_PROPS
5745 ReturnComNotImplemented();
5746#else // VBOX_WITH_GUEST_PROPS
5747 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5748 if (rc == E_ACCESSDENIED)
5749 /* The VM is not running or the service is not (yet) accessible */
5750 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5751 return rc;
5752#endif // VBOX_WITH_GUEST_PROPS
5753}
5754
5755HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5756{
5757 return setGuestProperty(aProperty, aValue, "");
5758}
5759
5760HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5761{
5762#ifndef VBOX_WITH_GUEST_PROPS
5763 ReturnComNotImplemented();
5764#else // VBOX_WITH_GUEST_PROPS
5765 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5766 if (rc == E_ACCESSDENIED)
5767 /* The VM is not running or the service is not (yet) accessible */
5768 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5769 return rc;
5770#endif // VBOX_WITH_GUEST_PROPS
5771}
5772
5773#ifdef VBOX_WITH_GUEST_PROPS
5774/**
5775 * Enumerate the guest properties in VBoxSVC's internal structures.
5776 */
5777HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5778 std::vector<com::Utf8Str> &aNames,
5779 std::vector<com::Utf8Str> &aValues,
5780 std::vector<LONG64> &aTimestamps,
5781 std::vector<com::Utf8Str> &aFlags)
5782{
5783 using namespace guestProp;
5784
5785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5786 Utf8Str strPatterns(aPatterns);
5787
5788 HWData::GuestPropertyMap propMap;
5789
5790 /*
5791 * Look for matching patterns and build up a list.
5792 */
5793 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5794 while (it != mHWData->mGuestProperties.end())
5795 {
5796 if ( strPatterns.isEmpty()
5797 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5798 RTSTR_MAX,
5799 it->first.c_str(),
5800 RTSTR_MAX,
5801 NULL)
5802 )
5803 propMap.insert(*it);
5804 it++;
5805 }
5806
5807 alock.release();
5808
5809 /*
5810 * And build up the arrays for returning the property information.
5811 */
5812 size_t cEntries = propMap.size();
5813
5814 aNames.resize(cEntries);
5815 aValues.resize(cEntries);
5816 aTimestamps.resize(cEntries);
5817 aFlags.resize(cEntries);
5818
5819 char szFlags[MAX_FLAGS_LEN + 1];
5820 size_t i= 0;
5821 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5822 {
5823 aNames[i] = it->first;
5824 aValues[i] = it->second.strValue;
5825 aTimestamps[i] = it->second.mTimestamp;
5826 writeFlags(it->second.mFlags, szFlags);
5827 aFlags[i] = Utf8Str(szFlags);
5828 }
5829
5830 return S_OK;
5831}
5832
5833/**
5834 * Enumerate the properties managed by a VM.
5835 * @returns E_ACCESSDENIED if the VM process is not available or not
5836 * currently handling queries and the setting should then be done in
5837 * VBoxSVC.
5838 */
5839HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5840 std::vector<com::Utf8Str> &aNames,
5841 std::vector<com::Utf8Str> &aValues,
5842 std::vector<LONG64> &aTimestamps,
5843 std::vector<com::Utf8Str> &aFlags)
5844{
5845 HRESULT rc;
5846 ComPtr<IInternalSessionControl> directControl;
5847 directControl = mData->mSession.mDirectControl;
5848
5849
5850 com::SafeArray<BSTR> bNames;
5851 com::SafeArray<BSTR> bValues;
5852 com::SafeArray<LONG64> bTimestamps;
5853 com::SafeArray<BSTR> bFlags;
5854
5855 if (!directControl)
5856 rc = E_ACCESSDENIED;
5857 else
5858 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5859 ComSafeArrayAsOutParam(bNames),
5860 ComSafeArrayAsOutParam(bValues),
5861 ComSafeArrayAsOutParam(bTimestamps),
5862 ComSafeArrayAsOutParam(bFlags));
5863 size_t i;
5864 aNames.resize(bNames.size());
5865 for (i = 0; i < bNames.size(); ++i)
5866 aNames[i] = Utf8Str(bNames[i]);
5867 aValues.resize(bValues.size());
5868 for (i = 0; i < bValues.size(); ++i)
5869 aValues[i] = Utf8Str(bValues[i]);
5870 aTimestamps.resize(bTimestamps.size());
5871 for (i = 0; i < bTimestamps.size(); ++i)
5872 aTimestamps[i] = bTimestamps[i];
5873 aFlags.resize(bFlags.size());
5874 for (i = 0; i < bFlags.size(); ++i)
5875 aFlags[i] = Utf8Str(bFlags[i]);
5876
5877 return rc;
5878}
5879#endif // VBOX_WITH_GUEST_PROPS
5880HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5881 std::vector<com::Utf8Str> &aNames,
5882 std::vector<com::Utf8Str> &aValues,
5883 std::vector<LONG64> &aTimestamps,
5884 std::vector<com::Utf8Str> &aFlags)
5885{
5886#ifndef VBOX_WITH_GUEST_PROPS
5887 ReturnComNotImplemented();
5888#else // VBOX_WITH_GUEST_PROPS
5889
5890 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5891
5892 if (rc == E_ACCESSDENIED)
5893 /* The VM is not running or the service is not (yet) accessible */
5894 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5895 return rc;
5896#endif // VBOX_WITH_GUEST_PROPS
5897}
5898
5899HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5900 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5901{
5902 MediaData::AttachmentList atts;
5903
5904 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5905 if (FAILED(rc)) return rc;
5906
5907 size_t i = 0;
5908 aMediumAttachments.resize(atts.size());
5909 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5910 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5911
5912 return S_OK;
5913}
5914
5915HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5916 LONG aControllerPort,
5917 LONG aDevice,
5918 ComPtr<IMediumAttachment> &aAttachment)
5919{
5920 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5921 aName.c_str(), aControllerPort, aDevice));
5922
5923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5924
5925 aAttachment = NULL;
5926
5927 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5928 Bstr(aName).raw(),
5929 aControllerPort,
5930 aDevice);
5931 if (pAttach.isNull())
5932 return setError(VBOX_E_OBJECT_NOT_FOUND,
5933 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5934 aDevice, aControllerPort, aName.c_str());
5935
5936 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5937
5938 return S_OK;
5939}
5940
5941
5942HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5943 StorageBus_T aConnectionType,
5944 ComPtr<IStorageController> &aController)
5945{
5946 if ( (aConnectionType <= StorageBus_Null)
5947 || (aConnectionType > StorageBus_USB))
5948 return setError(E_INVALIDARG,
5949 tr("Invalid connection type: %d"),
5950 aConnectionType);
5951
5952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5953
5954 HRESULT rc = i_checkStateDependency(MutableStateDep);
5955 if (FAILED(rc)) return rc;
5956
5957 /* try to find one with the name first. */
5958 ComObjPtr<StorageController> ctrl;
5959
5960 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5961 if (SUCCEEDED(rc))
5962 return setError(VBOX_E_OBJECT_IN_USE,
5963 tr("Storage controller named '%s' already exists"),
5964 aName.c_str());
5965
5966 ctrl.createObject();
5967
5968 /* get a new instance number for the storage controller */
5969 ULONG ulInstance = 0;
5970 bool fBootable = true;
5971 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5972 it != mStorageControllers->end();
5973 ++it)
5974 {
5975 if ((*it)->i_getStorageBus() == aConnectionType)
5976 {
5977 ULONG ulCurInst = (*it)->i_getInstance();
5978
5979 if (ulCurInst >= ulInstance)
5980 ulInstance = ulCurInst + 1;
5981
5982 /* Only one controller of each type can be marked as bootable. */
5983 if ((*it)->i_getBootable())
5984 fBootable = false;
5985 }
5986 }
5987
5988 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5989 if (FAILED(rc)) return rc;
5990
5991 i_setModified(IsModified_Storage);
5992 mStorageControllers.backup();
5993 mStorageControllers->push_back(ctrl);
5994
5995 ctrl.queryInterfaceTo(aController.asOutParam());
5996
5997 /* inform the direct session if any */
5998 alock.release();
5999 i_onStorageControllerChange();
6000
6001 return S_OK;
6002}
6003
6004HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6005 ComPtr<IStorageController> &aStorageController)
6006{
6007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6008
6009 ComObjPtr<StorageController> ctrl;
6010
6011 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6012 if (SUCCEEDED(rc))
6013 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6014
6015 return rc;
6016}
6017
6018HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6019 ComPtr<IStorageController> &aStorageController)
6020{
6021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6022
6023 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6024 it != mStorageControllers->end();
6025 ++it)
6026 {
6027 if ((*it)->i_getInstance() == aInstance)
6028 {
6029 (*it).queryInterfaceTo(aStorageController.asOutParam());
6030 return S_OK;
6031 }
6032 }
6033
6034 return setError(VBOX_E_OBJECT_NOT_FOUND,
6035 tr("Could not find a storage controller with instance number '%lu'"),
6036 aInstance);
6037}
6038
6039HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6040{
6041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6042
6043 HRESULT rc = i_checkStateDependency(MutableStateDep);
6044 if (FAILED(rc)) return rc;
6045
6046 ComObjPtr<StorageController> ctrl;
6047
6048 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6049 if (SUCCEEDED(rc))
6050 {
6051 /* Ensure that only one controller of each type is marked as bootable. */
6052 if (aBootable == TRUE)
6053 {
6054 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6055 it != mStorageControllers->end();
6056 ++it)
6057 {
6058 ComObjPtr<StorageController> aCtrl = (*it);
6059
6060 if ( (aCtrl->i_getName() != aName)
6061 && aCtrl->i_getBootable() == TRUE
6062 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6063 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6064 {
6065 aCtrl->i_setBootable(FALSE);
6066 break;
6067 }
6068 }
6069 }
6070
6071 if (SUCCEEDED(rc))
6072 {
6073 ctrl->i_setBootable(aBootable);
6074 i_setModified(IsModified_Storage);
6075 }
6076 }
6077
6078 if (SUCCEEDED(rc))
6079 {
6080 /* inform the direct session if any */
6081 alock.release();
6082 i_onStorageControllerChange();
6083 }
6084
6085 return rc;
6086}
6087
6088HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6089{
6090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6091
6092 HRESULT rc = i_checkStateDependency(MutableStateDep);
6093 if (FAILED(rc)) return rc;
6094
6095 ComObjPtr<StorageController> ctrl;
6096 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6097 if (FAILED(rc)) return rc;
6098
6099 {
6100 /* find all attached devices to the appropriate storage controller and detach them all */
6101 // make a temporary list because detachDevice invalidates iterators into
6102 // mMediaData->mAttachments
6103 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6104
6105 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6106 it != llAttachments2.end();
6107 ++it)
6108 {
6109 MediumAttachment *pAttachTemp = *it;
6110
6111 AutoCaller localAutoCaller(pAttachTemp);
6112 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6113
6114 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6115
6116 if (pAttachTemp->i_getControllerName() == aName)
6117 {
6118 rc = i_detachDevice(pAttachTemp, alock, NULL);
6119 if (FAILED(rc)) return rc;
6120 }
6121 }
6122 }
6123
6124 /* We can remove it now. */
6125 i_setModified(IsModified_Storage);
6126 mStorageControllers.backup();
6127
6128 ctrl->i_unshare();
6129
6130 mStorageControllers->remove(ctrl);
6131
6132 /* inform the direct session if any */
6133 alock.release();
6134 i_onStorageControllerChange();
6135
6136 return S_OK;
6137}
6138
6139HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6140 ComPtr<IUSBController> &aController)
6141{
6142 if ( (aType <= USBControllerType_Null)
6143 || (aType >= USBControllerType_Last))
6144 return setError(E_INVALIDARG,
6145 tr("Invalid USB controller type: %d"),
6146 aType);
6147
6148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 HRESULT rc = i_checkStateDependency(MutableStateDep);
6151 if (FAILED(rc)) return rc;
6152
6153 /* try to find one with the same type first. */
6154 ComObjPtr<USBController> ctrl;
6155
6156 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6157 if (SUCCEEDED(rc))
6158 return setError(VBOX_E_OBJECT_IN_USE,
6159 tr("USB controller named '%s' already exists"),
6160 aName.c_str());
6161
6162 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6163 ULONG maxInstances;
6164 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6165 if (FAILED(rc))
6166 return rc;
6167
6168 ULONG cInstances = i_getUSBControllerCountByType(aType);
6169 if (cInstances >= maxInstances)
6170 return setError(E_INVALIDARG,
6171 tr("Too many USB controllers of this type"));
6172
6173 ctrl.createObject();
6174
6175 rc = ctrl->init(this, aName, aType);
6176 if (FAILED(rc)) return rc;
6177
6178 i_setModified(IsModified_USB);
6179 mUSBControllers.backup();
6180 mUSBControllers->push_back(ctrl);
6181
6182 ctrl.queryInterfaceTo(aController.asOutParam());
6183
6184 /* inform the direct session if any */
6185 alock.release();
6186 i_onUSBControllerChange();
6187
6188 return S_OK;
6189}
6190
6191HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6192{
6193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6194
6195 ComObjPtr<USBController> ctrl;
6196
6197 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6198 if (SUCCEEDED(rc))
6199 ctrl.queryInterfaceTo(aController.asOutParam());
6200
6201 return rc;
6202}
6203
6204HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6205 ULONG *aControllers)
6206{
6207 if ( (aType <= USBControllerType_Null)
6208 || (aType >= USBControllerType_Last))
6209 return setError(E_INVALIDARG,
6210 tr("Invalid USB controller type: %d"),
6211 aType);
6212
6213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6214
6215 ComObjPtr<USBController> ctrl;
6216
6217 *aControllers = i_getUSBControllerCountByType(aType);
6218
6219 return S_OK;
6220}
6221
6222HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6223{
6224
6225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6226
6227 HRESULT rc = i_checkStateDependency(MutableStateDep);
6228 if (FAILED(rc)) return rc;
6229
6230 ComObjPtr<USBController> ctrl;
6231 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6232 if (FAILED(rc)) return rc;
6233
6234 i_setModified(IsModified_USB);
6235 mUSBControllers.backup();
6236
6237 ctrl->i_unshare();
6238
6239 mUSBControllers->remove(ctrl);
6240
6241 /* inform the direct session if any */
6242 alock.release();
6243 i_onUSBControllerChange();
6244
6245 return S_OK;
6246}
6247
6248HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6249 ULONG *aOriginX,
6250 ULONG *aOriginY,
6251 ULONG *aWidth,
6252 ULONG *aHeight,
6253 BOOL *aEnabled)
6254{
6255 uint32_t u32OriginX= 0;
6256 uint32_t u32OriginY= 0;
6257 uint32_t u32Width = 0;
6258 uint32_t u32Height = 0;
6259 uint16_t u16Flags = 0;
6260
6261 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6262 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6263 if (RT_FAILURE(vrc))
6264 {
6265#ifdef RT_OS_WINDOWS
6266 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6267 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6268 * So just assign fEnable to TRUE again.
6269 * The right fix would be to change GUI API wrappers to make sure that parameters
6270 * are changed only if API succeeds.
6271 */
6272 *aEnabled = TRUE;
6273#endif
6274 return setError(VBOX_E_IPRT_ERROR,
6275 tr("Saved guest size is not available (%Rrc)"),
6276 vrc);
6277 }
6278
6279 *aOriginX = u32OriginX;
6280 *aOriginY = u32OriginY;
6281 *aWidth = u32Width;
6282 *aHeight = u32Height;
6283 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6284
6285 return S_OK;
6286}
6287
6288HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6289{
6290 if (aScreenId != 0)
6291 return E_NOTIMPL;
6292
6293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6294
6295 uint8_t *pu8Data = NULL;
6296 uint32_t cbData = 0;
6297 uint32_t u32Width = 0;
6298 uint32_t u32Height = 0;
6299
6300 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6301
6302 if (RT_FAILURE(vrc))
6303 return setError(VBOX_E_IPRT_ERROR,
6304 tr("Saved screenshot data is not available (%Rrc)"),
6305 vrc);
6306
6307 *aSize = cbData;
6308 *aWidth = u32Width;
6309 *aHeight = u32Height;
6310
6311 freeSavedDisplayScreenshot(pu8Data);
6312
6313 return S_OK;
6314}
6315
6316HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6317 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6318{
6319 if (aScreenId != 0)
6320 return E_NOTIMPL;
6321
6322 if ( aBitmapFormat != BitmapFormat_BGR0
6323 && aBitmapFormat != BitmapFormat_BGRA
6324 && aBitmapFormat != BitmapFormat_RGBA
6325 && aBitmapFormat != BitmapFormat_PNG)
6326 return setError(E_NOTIMPL,
6327 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6328
6329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6330
6331 uint8_t *pu8Data = NULL;
6332 uint32_t cbData = 0;
6333 uint32_t u32Width = 0;
6334 uint32_t u32Height = 0;
6335
6336 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6337
6338 if (RT_FAILURE(vrc))
6339 return setError(VBOX_E_IPRT_ERROR,
6340 tr("Saved thumbnail data is not available (%Rrc)"),
6341 vrc);
6342
6343 HRESULT hr = S_OK;
6344
6345 *aWidth = u32Width;
6346 *aHeight = u32Height;
6347
6348 if (cbData > 0)
6349 {
6350 /* Convert pixels to the format expected by the API caller. */
6351 if (aBitmapFormat == BitmapFormat_BGR0)
6352 {
6353 /* [0] B, [1] G, [2] R, [3] 0. */
6354 aData.resize(cbData);
6355 memcpy(&aData.front(), pu8Data, cbData);
6356 }
6357 else if (aBitmapFormat == BitmapFormat_BGRA)
6358 {
6359 /* [0] B, [1] G, [2] R, [3] A. */
6360 aData.resize(cbData);
6361 for (uint32_t i = 0; i < cbData; i += 4)
6362 {
6363 aData[i] = pu8Data[i];
6364 aData[i + 1] = pu8Data[i + 1];
6365 aData[i + 2] = pu8Data[i + 2];
6366 aData[i + 3] = 0xff;
6367 }
6368 }
6369 else if (aBitmapFormat == BitmapFormat_RGBA)
6370 {
6371 /* [0] R, [1] G, [2] B, [3] A. */
6372 aData.resize(cbData);
6373 for (uint32_t i = 0; i < cbData; i += 4)
6374 {
6375 aData[i] = pu8Data[i + 2];
6376 aData[i + 1] = pu8Data[i + 1];
6377 aData[i + 2] = pu8Data[i];
6378 aData[i + 3] = 0xff;
6379 }
6380 }
6381 else if (aBitmapFormat == BitmapFormat_PNG)
6382 {
6383 uint8_t *pu8PNG = NULL;
6384 uint32_t cbPNG = 0;
6385 uint32_t cxPNG = 0;
6386 uint32_t cyPNG = 0;
6387
6388 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6389
6390 if (RT_SUCCESS(vrc))
6391 {
6392 aData.resize(cbPNG);
6393 if (cbPNG)
6394 memcpy(&aData.front(), pu8PNG, cbPNG);
6395 }
6396 else
6397 hr = setError(VBOX_E_IPRT_ERROR,
6398 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6399 vrc);
6400
6401 RTMemFree(pu8PNG);
6402 }
6403 }
6404
6405 freeSavedDisplayScreenshot(pu8Data);
6406
6407 return hr;
6408}
6409
6410HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6411{
6412 if (aScreenId != 0)
6413 return E_NOTIMPL;
6414
6415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6416
6417 uint8_t *pu8Data = NULL;
6418 uint32_t cbData = 0;
6419 uint32_t u32Width = 0;
6420 uint32_t u32Height = 0;
6421
6422 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6423
6424 if (RT_FAILURE(vrc))
6425 return setError(VBOX_E_IPRT_ERROR,
6426 tr("Saved screenshot data is not available (%Rrc)"),
6427 vrc);
6428
6429 *aSize = cbData;
6430 *aWidth = u32Width;
6431 *aHeight = u32Height;
6432
6433 freeSavedDisplayScreenshot(pu8Data);
6434
6435 return S_OK;
6436}
6437
6438HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6439{
6440 if (aScreenId != 0)
6441 return E_NOTIMPL;
6442
6443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6444
6445 uint8_t *pu8Data = NULL;
6446 uint32_t cbData = 0;
6447 uint32_t u32Width = 0;
6448 uint32_t u32Height = 0;
6449
6450 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6451
6452 if (RT_FAILURE(vrc))
6453 return setError(VBOX_E_IPRT_ERROR,
6454 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6455 vrc);
6456
6457 *aWidth = u32Width;
6458 *aHeight = u32Height;
6459
6460 aData.resize(cbData);
6461 if (cbData)
6462 memcpy(&aData.front(), pu8Data, cbData);
6463
6464 freeSavedDisplayScreenshot(pu8Data);
6465
6466 return S_OK;
6467}
6468
6469HRESULT Machine::hotPlugCPU(ULONG aCpu)
6470{
6471 HRESULT rc = S_OK;
6472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 if (!mHWData->mCPUHotPlugEnabled)
6475 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6476
6477 if (aCpu >= mHWData->mCPUCount)
6478 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6479
6480 if (mHWData->mCPUAttached[aCpu])
6481 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6482
6483 alock.release();
6484 rc = i_onCPUChange(aCpu, false);
6485 alock.acquire();
6486 if (FAILED(rc)) return rc;
6487
6488 i_setModified(IsModified_MachineData);
6489 mHWData.backup();
6490 mHWData->mCPUAttached[aCpu] = true;
6491
6492 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6493 if (Global::IsOnline(mData->mMachineState))
6494 i_saveSettings(NULL);
6495
6496 return S_OK;
6497}
6498
6499HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6500{
6501 HRESULT rc = S_OK;
6502
6503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6504
6505 if (!mHWData->mCPUHotPlugEnabled)
6506 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6507
6508 if (aCpu >= SchemaDefs::MaxCPUCount)
6509 return setError(E_INVALIDARG,
6510 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6511 SchemaDefs::MaxCPUCount);
6512
6513 if (!mHWData->mCPUAttached[aCpu])
6514 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6515
6516 /* CPU 0 can't be detached */
6517 if (aCpu == 0)
6518 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6519
6520 alock.release();
6521 rc = i_onCPUChange(aCpu, true);
6522 alock.acquire();
6523 if (FAILED(rc)) return rc;
6524
6525 i_setModified(IsModified_MachineData);
6526 mHWData.backup();
6527 mHWData->mCPUAttached[aCpu] = false;
6528
6529 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6530 if (Global::IsOnline(mData->mMachineState))
6531 i_saveSettings(NULL);
6532
6533 return S_OK;
6534}
6535
6536HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6537{
6538 *aAttached = false;
6539
6540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6541
6542 /* If hotplug is enabled the CPU is always enabled. */
6543 if (!mHWData->mCPUHotPlugEnabled)
6544 {
6545 if (aCpu < mHWData->mCPUCount)
6546 *aAttached = true;
6547 }
6548 else
6549 {
6550 if (aCpu < SchemaDefs::MaxCPUCount)
6551 *aAttached = mHWData->mCPUAttached[aCpu];
6552 }
6553
6554 return S_OK;
6555}
6556
6557HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6558{
6559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6560
6561 Utf8Str log = i_queryLogFilename(aIdx);
6562 if (!RTFileExists(log.c_str()))
6563 log.setNull();
6564 aFilename = log;
6565
6566 return S_OK;
6567}
6568
6569HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6570{
6571 if (aSize < 0)
6572 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6573
6574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6575
6576 HRESULT rc = S_OK;
6577 Utf8Str log = i_queryLogFilename(aIdx);
6578
6579 /* do not unnecessarily hold the lock while doing something which does
6580 * not need the lock and potentially takes a long time. */
6581 alock.release();
6582
6583 /* Limit the chunk size to 32K for now, as that gives better performance
6584 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6585 * One byte expands to approx. 25 bytes of breathtaking XML. */
6586 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6587 aData.resize(cbData);
6588
6589 RTFILE LogFile;
6590 int vrc = RTFileOpen(&LogFile, log.c_str(),
6591 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6592 if (RT_SUCCESS(vrc))
6593 {
6594 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6595 if (RT_SUCCESS(vrc))
6596 aData.resize(cbData);
6597 else
6598 rc = setError(VBOX_E_IPRT_ERROR,
6599 tr("Could not read log file '%s' (%Rrc)"),
6600 log.c_str(), vrc);
6601 RTFileClose(LogFile);
6602 }
6603 else
6604 rc = setError(VBOX_E_IPRT_ERROR,
6605 tr("Could not open log file '%s' (%Rrc)"),
6606 log.c_str(), vrc);
6607
6608 if (FAILED(rc))
6609 aData.resize(0);
6610
6611 return rc;
6612}
6613
6614
6615/**
6616 * Currently this method doesn't attach device to the running VM,
6617 * just makes sure it's plugged on next VM start.
6618 */
6619HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6620{
6621 // lock scope
6622 {
6623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 HRESULT rc = i_checkStateDependency(MutableStateDep);
6626 if (FAILED(rc)) return rc;
6627
6628 ChipsetType_T aChipset = ChipsetType_PIIX3;
6629 COMGETTER(ChipsetType)(&aChipset);
6630
6631 if (aChipset != ChipsetType_ICH9)
6632 {
6633 return setError(E_INVALIDARG,
6634 tr("Host PCI attachment only supported with ICH9 chipset"));
6635 }
6636
6637 // check if device with this host PCI address already attached
6638 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6639 it != mHWData->mPCIDeviceAssignments.end();
6640 ++it)
6641 {
6642 LONG iHostAddress = -1;
6643 ComPtr<PCIDeviceAttachment> pAttach;
6644 pAttach = *it;
6645 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6646 if (iHostAddress == aHostAddress)
6647 return setError(E_INVALIDARG,
6648 tr("Device with host PCI address already attached to this VM"));
6649 }
6650
6651 ComObjPtr<PCIDeviceAttachment> pda;
6652 char name[32];
6653
6654 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6655 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6656 Bstr bname(name);
6657 pda.createObject();
6658 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6659 i_setModified(IsModified_MachineData);
6660 mHWData.backup();
6661 mHWData->mPCIDeviceAssignments.push_back(pda);
6662 }
6663
6664 return S_OK;
6665}
6666
6667/**
6668 * Currently this method doesn't detach device from the running VM,
6669 * just makes sure it's not plugged on next VM start.
6670 */
6671HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6672{
6673 ComObjPtr<PCIDeviceAttachment> pAttach;
6674 bool fRemoved = false;
6675 HRESULT rc;
6676
6677 // lock scope
6678 {
6679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6680
6681 rc = i_checkStateDependency(MutableStateDep);
6682 if (FAILED(rc)) return rc;
6683
6684 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6685 it != mHWData->mPCIDeviceAssignments.end();
6686 ++it)
6687 {
6688 LONG iHostAddress = -1;
6689 pAttach = *it;
6690 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6691 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6692 {
6693 i_setModified(IsModified_MachineData);
6694 mHWData.backup();
6695 mHWData->mPCIDeviceAssignments.remove(pAttach);
6696 fRemoved = true;
6697 break;
6698 }
6699 }
6700 }
6701
6702
6703 /* Fire event outside of the lock */
6704 if (fRemoved)
6705 {
6706 Assert(!pAttach.isNull());
6707 ComPtr<IEventSource> es;
6708 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6709 Assert(SUCCEEDED(rc));
6710 Bstr mid;
6711 rc = this->COMGETTER(Id)(mid.asOutParam());
6712 Assert(SUCCEEDED(rc));
6713 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6714 }
6715
6716 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6717 tr("No host PCI device %08x attached"),
6718 aHostAddress
6719 );
6720}
6721
6722HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6723{
6724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6725
6726 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6727
6728 size_t i = 0;
6729 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6730 it != mHWData->mPCIDeviceAssignments.end();
6731 ++i, ++it)
6732 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6733
6734 return S_OK;
6735}
6736
6737HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6738{
6739 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6740
6741 return S_OK;
6742}
6743
6744HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6745{
6746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6747
6748 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6749
6750 return S_OK;
6751}
6752
6753HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6754{
6755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6756 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6757 if (SUCCEEDED(hrc))
6758 {
6759 hrc = mHWData.backupEx();
6760 if (SUCCEEDED(hrc))
6761 {
6762 i_setModified(IsModified_MachineData);
6763 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6764 }
6765 }
6766 return hrc;
6767}
6768
6769HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6770{
6771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6772 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6773 return S_OK;
6774}
6775
6776HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6777{
6778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6779 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6780 if (SUCCEEDED(hrc))
6781 {
6782 hrc = mHWData.backupEx();
6783 if (SUCCEEDED(hrc))
6784 {
6785 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6786 if (SUCCEEDED(hrc))
6787 i_setModified(IsModified_MachineData);
6788 }
6789 }
6790 return hrc;
6791}
6792
6793HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6794{
6795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6796
6797 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6798
6799 return S_OK;
6800}
6801
6802HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6803{
6804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6806 if (SUCCEEDED(hrc))
6807 {
6808 hrc = mHWData.backupEx();
6809 if (SUCCEEDED(hrc))
6810 {
6811 i_setModified(IsModified_MachineData);
6812 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6813 }
6814 }
6815 return hrc;
6816}
6817
6818HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6819{
6820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6821
6822 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6823
6824 return S_OK;
6825}
6826
6827HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6828{
6829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6830
6831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6832 if ( SUCCEEDED(hrc)
6833 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6834 {
6835 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6836 int vrc;
6837
6838 if (aAutostartEnabled)
6839 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6840 else
6841 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6842
6843 if (RT_SUCCESS(vrc))
6844 {
6845 hrc = mHWData.backupEx();
6846 if (SUCCEEDED(hrc))
6847 {
6848 i_setModified(IsModified_MachineData);
6849 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6850 }
6851 }
6852 else if (vrc == VERR_NOT_SUPPORTED)
6853 hrc = setError(VBOX_E_NOT_SUPPORTED,
6854 tr("The VM autostart feature is not supported on this platform"));
6855 else if (vrc == VERR_PATH_NOT_FOUND)
6856 hrc = setError(E_FAIL,
6857 tr("The path to the autostart database is not set"));
6858 else
6859 hrc = setError(E_UNEXPECTED,
6860 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6861 aAutostartEnabled ? "Adding" : "Removing",
6862 mUserData->s.strName.c_str(), vrc);
6863 }
6864 return hrc;
6865}
6866
6867HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6868{
6869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6870
6871 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6872
6873 return S_OK;
6874}
6875
6876HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6877{
6878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6879 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6880 if (SUCCEEDED(hrc))
6881 {
6882 hrc = mHWData.backupEx();
6883 if (SUCCEEDED(hrc))
6884 {
6885 i_setModified(IsModified_MachineData);
6886 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6887 }
6888 }
6889 return hrc;
6890}
6891
6892HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6893{
6894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6895
6896 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6897
6898 return S_OK;
6899}
6900
6901HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6902{
6903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6904 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6905 if ( SUCCEEDED(hrc)
6906 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6907 {
6908 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6909 int vrc;
6910
6911 if (aAutostopType != AutostopType_Disabled)
6912 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6913 else
6914 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6915
6916 if (RT_SUCCESS(vrc))
6917 {
6918 hrc = mHWData.backupEx();
6919 if (SUCCEEDED(hrc))
6920 {
6921 i_setModified(IsModified_MachineData);
6922 mHWData->mAutostart.enmAutostopType = aAutostopType;
6923 }
6924 }
6925 else if (vrc == VERR_NOT_SUPPORTED)
6926 hrc = setError(VBOX_E_NOT_SUPPORTED,
6927 tr("The VM autostop feature is not supported on this platform"));
6928 else if (vrc == VERR_PATH_NOT_FOUND)
6929 hrc = setError(E_FAIL,
6930 tr("The path to the autostart database is not set"));
6931 else
6932 hrc = setError(E_UNEXPECTED,
6933 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6934 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6935 mUserData->s.strName.c_str(), vrc);
6936 }
6937 return hrc;
6938}
6939
6940HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6941{
6942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6943
6944 aDefaultFrontend = mHWData->mDefaultFrontend;
6945
6946 return S_OK;
6947}
6948
6949HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6950{
6951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6952 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6953 if (SUCCEEDED(hrc))
6954 {
6955 hrc = mHWData.backupEx();
6956 if (SUCCEEDED(hrc))
6957 {
6958 i_setModified(IsModified_MachineData);
6959 mHWData->mDefaultFrontend = aDefaultFrontend;
6960 }
6961 }
6962 return hrc;
6963}
6964
6965HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6966{
6967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6968 size_t cbIcon = mUserData->mIcon.size();
6969 aIcon.resize(cbIcon);
6970 if (cbIcon)
6971 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6972 return S_OK;
6973}
6974
6975HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6976{
6977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6978 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6979 if (SUCCEEDED(hrc))
6980 {
6981 i_setModified(IsModified_MachineData);
6982 mUserData.backup();
6983 size_t cbIcon = aIcon.size();
6984 mUserData->mIcon.resize(cbIcon);
6985 if (cbIcon)
6986 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6987 }
6988 return hrc;
6989}
6990
6991HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6992{
6993#ifdef VBOX_WITH_USB
6994 *aUSBProxyAvailable = true;
6995#else
6996 *aUSBProxyAvailable = false;
6997#endif
6998 return S_OK;
6999}
7000
7001HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7002 ComPtr<IProgress> &aProgress)
7003{
7004 ComObjPtr<Progress> pP;
7005 Progress *ppP = pP;
7006 IProgress *iP = static_cast<IProgress *>(ppP);
7007 IProgress **pProgress = &iP;
7008
7009 IMachine *pTarget = aTarget;
7010
7011 /* Convert the options. */
7012 RTCList<CloneOptions_T> optList;
7013 if (aOptions.size())
7014 for (size_t i = 0; i < aOptions.size(); ++i)
7015 optList.append(aOptions[i]);
7016
7017 if (optList.contains(CloneOptions_Link))
7018 {
7019 if (!i_isSnapshotMachine())
7020 return setError(E_INVALIDARG,
7021 tr("Linked clone can only be created from a snapshot"));
7022 if (aMode != CloneMode_MachineState)
7023 return setError(E_INVALIDARG,
7024 tr("Linked clone can only be created for a single machine state"));
7025 }
7026 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7027
7028 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7029
7030 HRESULT rc = pWorker->start(pProgress);
7031
7032 pP = static_cast<Progress *>(*pProgress);
7033 pP.queryInterfaceTo(aProgress.asOutParam());
7034
7035 return rc;
7036
7037}
7038
7039// public methods for internal purposes
7040/////////////////////////////////////////////////////////////////////////////
7041
7042/**
7043 * Adds the given IsModified_* flag to the dirty flags of the machine.
7044 * This must be called either during i_loadSettings or under the machine write lock.
7045 * @param fl
7046 */
7047void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7048{
7049 mData->flModifications |= fl;
7050 if (fAllowStateModification && i_isStateModificationAllowed())
7051 mData->mCurrentStateModified = true;
7052}
7053
7054/**
7055 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7056 * care of the write locking.
7057 *
7058 * @param fModifications The flag to add.
7059 */
7060void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7061{
7062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7063 i_setModified(fModification, fAllowStateModification);
7064}
7065
7066/**
7067 * Saves the registry entry of this machine to the given configuration node.
7068 *
7069 * @param aEntryNode Node to save the registry entry to.
7070 *
7071 * @note locks this object for reading.
7072 */
7073HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7074{
7075 AutoLimitedCaller autoCaller(this);
7076 AssertComRCReturnRC(autoCaller.rc());
7077
7078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7079
7080 data.uuid = mData->mUuid;
7081 data.strSettingsFile = mData->m_strConfigFile;
7082
7083 return S_OK;
7084}
7085
7086/**
7087 * Calculates the absolute path of the given path taking the directory of the
7088 * machine settings file as the current directory.
7089 *
7090 * @param aPath Path to calculate the absolute path for.
7091 * @param aResult Where to put the result (used only on success, can be the
7092 * same Utf8Str instance as passed in @a aPath).
7093 * @return IPRT result.
7094 *
7095 * @note Locks this object for reading.
7096 */
7097int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7098{
7099 AutoCaller autoCaller(this);
7100 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7101
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103
7104 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7105
7106 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7107
7108 strSettingsDir.stripFilename();
7109 char folder[RTPATH_MAX];
7110 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7111 if (RT_SUCCESS(vrc))
7112 aResult = folder;
7113
7114 return vrc;
7115}
7116
7117/**
7118 * Copies strSource to strTarget, making it relative to the machine folder
7119 * if it is a subdirectory thereof, or simply copying it otherwise.
7120 *
7121 * @param strSource Path to evaluate and copy.
7122 * @param strTarget Buffer to receive target path.
7123 *
7124 * @note Locks this object for reading.
7125 */
7126void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7127 Utf8Str &strTarget)
7128{
7129 AutoCaller autoCaller(this);
7130 AssertComRCReturn(autoCaller.rc(), (void)0);
7131
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133
7134 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7135 // use strTarget as a temporary buffer to hold the machine settings dir
7136 strTarget = mData->m_strConfigFileFull;
7137 strTarget.stripFilename();
7138 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7139 {
7140 // is relative: then append what's left
7141 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7142 // for empty paths (only possible for subdirs) use "." to avoid
7143 // triggering default settings for not present config attributes.
7144 if (strTarget.isEmpty())
7145 strTarget = ".";
7146 }
7147 else
7148 // is not relative: then overwrite
7149 strTarget = strSource;
7150}
7151
7152/**
7153 * Returns the full path to the machine's log folder in the
7154 * \a aLogFolder argument.
7155 */
7156void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7157{
7158 AutoCaller autoCaller(this);
7159 AssertComRCReturnVoid(autoCaller.rc());
7160
7161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7162
7163 char szTmp[RTPATH_MAX];
7164 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7165 if (RT_SUCCESS(vrc))
7166 {
7167 if (szTmp[0] && !mUserData.isNull())
7168 {
7169 char szTmp2[RTPATH_MAX];
7170 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7171 if (RT_SUCCESS(vrc))
7172 aLogFolder = BstrFmt("%s%c%s",
7173 szTmp2,
7174 RTPATH_DELIMITER,
7175 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7176 }
7177 else
7178 vrc = VERR_PATH_IS_RELATIVE;
7179 }
7180
7181 if (RT_FAILURE(vrc))
7182 {
7183 // fallback if VBOX_USER_LOGHOME is not set or invalid
7184 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7185 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7186 aLogFolder.append(RTPATH_DELIMITER);
7187 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7188 }
7189}
7190
7191/**
7192 * Returns the full path to the machine's log file for an given index.
7193 */
7194Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7195 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7196{
7197 Utf8Str logFolder;
7198 getLogFolder(logFolder);
7199 Assert(logFolder.length());
7200 Utf8Str log;
7201 if (idx == 0)
7202 log = Utf8StrFmt("%s%cVBox.log",
7203 logFolder.c_str(), RTPATH_DELIMITER);
7204 else
7205 log = Utf8StrFmt("%s%cVBox.log.%d",
7206 logFolder.c_str(), RTPATH_DELIMITER, idx);
7207 return log;
7208}
7209
7210/**
7211 * Returns the full path to the machine's (hardened) startup log file.
7212 */
7213Utf8Str Machine::i_getStartupLogFilename(void)
7214{
7215 Utf8Str strFilename;
7216 getLogFolder(strFilename);
7217 Assert(strFilename.length());
7218 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7219 return strFilename;
7220}
7221
7222
7223/**
7224 * Composes a unique saved state filename based on the current system time. The filename is
7225 * granular to the second so this will work so long as no more than one snapshot is taken on
7226 * a machine per second.
7227 *
7228 * Before version 4.1, we used this formula for saved state files:
7229 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7230 * which no longer works because saved state files can now be shared between the saved state of the
7231 * "saved" machine and an online snapshot, and the following would cause problems:
7232 * 1) save machine
7233 * 2) create online snapshot from that machine state --> reusing saved state file
7234 * 3) save machine again --> filename would be reused, breaking the online snapshot
7235 *
7236 * So instead we now use a timestamp.
7237 *
7238 * @param str
7239 */
7240
7241void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7242{
7243 AutoCaller autoCaller(this);
7244 AssertComRCReturnVoid(autoCaller.rc());
7245
7246 {
7247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7248 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7249 }
7250
7251 RTTIMESPEC ts;
7252 RTTimeNow(&ts);
7253 RTTIME time;
7254 RTTimeExplode(&time, &ts);
7255
7256 strStateFilePath += RTPATH_DELIMITER;
7257 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7258 time.i32Year, time.u8Month, time.u8MonthDay,
7259 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7260}
7261
7262/**
7263 * Returns the full path to the default video capture file.
7264 */
7265void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7266{
7267 AutoCaller autoCaller(this);
7268 AssertComRCReturnVoid(autoCaller.rc());
7269
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271
7272 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7273 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7274 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7275}
7276
7277/**
7278 * Returns whether at least one USB controller is present for the VM.
7279 */
7280bool Machine::i_isUSBControllerPresent()
7281{
7282 AutoCaller autoCaller(this);
7283 AssertComRCReturn(autoCaller.rc(), false);
7284
7285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7286
7287 return (mUSBControllers->size() > 0);
7288}
7289
7290/**
7291 * @note Locks this object for writing, calls the client process
7292 * (inside the lock).
7293 */
7294HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7295 const Utf8Str &strFrontend,
7296 const Utf8Str &strEnvironment,
7297 ProgressProxy *aProgress)
7298{
7299 LogFlowThisFuncEnter();
7300
7301 AssertReturn(aControl, E_FAIL);
7302 AssertReturn(aProgress, E_FAIL);
7303 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7304
7305 AutoCaller autoCaller(this);
7306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7307
7308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 if (!mData->mRegistered)
7311 return setError(E_UNEXPECTED,
7312 tr("The machine '%s' is not registered"),
7313 mUserData->s.strName.c_str());
7314
7315 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7316
7317 if ( mData->mSession.mState == SessionState_Locked
7318 || mData->mSession.mState == SessionState_Spawning
7319 || mData->mSession.mState == SessionState_Unlocking)
7320 return setError(VBOX_E_INVALID_OBJECT_STATE,
7321 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7322 mUserData->s.strName.c_str());
7323
7324 /* may not be busy */
7325 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7326
7327 /* get the path to the executable */
7328 char szPath[RTPATH_MAX];
7329 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7330 size_t cchBufLeft = strlen(szPath);
7331 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7332 szPath[cchBufLeft] = 0;
7333 char *pszNamePart = szPath + cchBufLeft;
7334 cchBufLeft = sizeof(szPath) - cchBufLeft;
7335
7336 int vrc = VINF_SUCCESS;
7337 RTPROCESS pid = NIL_RTPROCESS;
7338
7339 RTENV env = RTENV_DEFAULT;
7340
7341 if (!strEnvironment.isEmpty())
7342 {
7343 char *newEnvStr = NULL;
7344
7345 do
7346 {
7347 /* clone the current environment */
7348 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7349 AssertRCBreakStmt(vrc2, vrc = vrc2);
7350
7351 newEnvStr = RTStrDup(strEnvironment.c_str());
7352 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7353
7354 /* put new variables to the environment
7355 * (ignore empty variable names here since RTEnv API
7356 * intentionally doesn't do that) */
7357 char *var = newEnvStr;
7358 for (char *p = newEnvStr; *p; ++p)
7359 {
7360 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7361 {
7362 *p = '\0';
7363 if (*var)
7364 {
7365 char *val = strchr(var, '=');
7366 if (val)
7367 {
7368 *val++ = '\0';
7369 vrc2 = RTEnvSetEx(env, var, val);
7370 }
7371 else
7372 vrc2 = RTEnvUnsetEx(env, var);
7373 if (RT_FAILURE(vrc2))
7374 break;
7375 }
7376 var = p + 1;
7377 }
7378 }
7379 if (RT_SUCCESS(vrc2) && *var)
7380 vrc2 = RTEnvPutEx(env, var);
7381
7382 AssertRCBreakStmt(vrc2, vrc = vrc2);
7383 }
7384 while (0);
7385
7386 if (newEnvStr != NULL)
7387 RTStrFree(newEnvStr);
7388 }
7389
7390 /* Hardened startup logging */
7391#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7392 Utf8Str strSupStartLogArg("--sup-startup-log=");
7393 {
7394 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7395 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7396 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7397 {
7398 Utf8Str strStartupLogDir = strStartupLogFile;
7399 strStartupLogDir.stripFilename();
7400 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7401 file without stripping the file. */
7402 }
7403 strSupStartLogArg.append(strStartupLogFile);
7404 }
7405 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7406#else
7407 const char *pszSupStartupLogArg = NULL;
7408#endif
7409
7410
7411#ifdef VBOX_WITH_QTGUI
7412 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7413 {
7414# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7415 /* Modify the base path so that we don't need to use ".." below. */
7416 RTPathStripTrailingSlash(szPath);
7417 RTPathStripFilename(szPath);
7418 cchBufLeft = strlen(szPath);
7419 pszNamePart = szPath + cchBufLeft;
7420 cchBufLeft = sizeof(szPath) - cchBufLeft;
7421
7422# define OSX_APP_NAME "VirtualBoxVM"
7423# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7424
7425 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7426 if ( strAppOverride.contains(".")
7427 || strAppOverride.contains("/")
7428 || strAppOverride.contains("\\")
7429 || strAppOverride.contains(":"))
7430 strAppOverride.setNull();
7431 Utf8Str strAppPath;
7432 if (!strAppOverride.isEmpty())
7433 {
7434 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7435 Utf8Str strFullPath(szPath);
7436 strFullPath.append(strAppPath);
7437 /* there is a race, but people using this deserve the failure */
7438 if (!RTFileExists(strFullPath.c_str()))
7439 strAppOverride.setNull();
7440 }
7441 if (strAppOverride.isEmpty())
7442 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7443 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7444 strcpy(pszNamePart, strAppPath.c_str());
7445# else
7446 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7447 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7448 strcpy(pszNamePart, s_szVirtualBox_exe);
7449# endif
7450
7451 Utf8Str idStr = mData->mUuid.toString();
7452 const char *apszArgs[] =
7453 {
7454 szPath,
7455 "--comment", mUserData->s.strName.c_str(),
7456 "--startvm", idStr.c_str(),
7457 "--no-startvm-errormsgbox",
7458 pszSupStartupLogArg,
7459 NULL
7460 };
7461 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7462 }
7463#else /* !VBOX_WITH_QTGUI */
7464 if (0)
7465 ;
7466#endif /* VBOX_WITH_QTGUI */
7467
7468 else
7469
7470#ifdef VBOX_WITH_VBOXSDL
7471 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7472 {
7473 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7474 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7475 strcpy(pszNamePart, s_szVBoxSDL_exe);
7476
7477 Utf8Str idStr = mData->mUuid.toString();
7478 const char *apszArgs[] =
7479 {
7480 szPath,
7481 "--comment", mUserData->s.strName.c_str(),
7482 "--startvm", idStr.c_str(),
7483 pszSupStartupLogArg,
7484 NULL
7485 };
7486 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7487 }
7488#else /* !VBOX_WITH_VBOXSDL */
7489 if (0)
7490 ;
7491#endif /* !VBOX_WITH_VBOXSDL */
7492
7493 else
7494
7495#ifdef VBOX_WITH_HEADLESS
7496 if ( strFrontend == "headless"
7497 || strFrontend == "capture"
7498 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7499 )
7500 {
7501 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7502 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7503 * and a VM works even if the server has not been installed.
7504 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7505 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7506 * differently in 4.0 and 3.x.
7507 */
7508 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7509 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7510 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7511
7512 Utf8Str idStr = mData->mUuid.toString();
7513 const char *apszArgs[] =
7514 {
7515 szPath,
7516 "--comment", mUserData->s.strName.c_str(),
7517 "--startvm", idStr.c_str(),
7518 "--vrde", "config",
7519 0, /* For "--capture". */
7520 0, /* For "--sup-startup-log". */
7521 0
7522 };
7523 unsigned iArg = 7;
7524 if (strFrontend == "capture")
7525 apszArgs[iArg++] = "--capture";
7526 apszArgs[iArg++] = pszSupStartupLogArg;
7527
7528# ifdef RT_OS_WINDOWS
7529 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7530# else
7531 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7532# endif
7533 }
7534#else /* !VBOX_WITH_HEADLESS */
7535 if (0)
7536 ;
7537#endif /* !VBOX_WITH_HEADLESS */
7538 else
7539 {
7540 RTEnvDestroy(env);
7541 return setError(E_INVALIDARG,
7542 tr("Invalid frontend name: '%s'"),
7543 strFrontend.c_str());
7544 }
7545
7546 RTEnvDestroy(env);
7547
7548 if (RT_FAILURE(vrc))
7549 return setError(VBOX_E_IPRT_ERROR,
7550 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7551 mUserData->s.strName.c_str(), vrc);
7552
7553 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7554
7555 /*
7556 * Note that we don't release the lock here before calling the client,
7557 * because it doesn't need to call us back if called with a NULL argument.
7558 * Releasing the lock here is dangerous because we didn't prepare the
7559 * launch data yet, but the client we've just started may happen to be
7560 * too fast and call LockMachine() that will fail (because of PID, etc.),
7561 * so that the Machine will never get out of the Spawning session state.
7562 */
7563
7564 /* inform the session that it will be a remote one */
7565 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7566#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7567 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7568#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7569 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7570#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7571 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7572
7573 if (FAILED(rc))
7574 {
7575 /* restore the session state */
7576 mData->mSession.mState = SessionState_Unlocked;
7577 alock.release();
7578 mParent->i_addProcessToReap(pid);
7579 /* The failure may occur w/o any error info (from RPC), so provide one */
7580 return setError(VBOX_E_VM_ERROR,
7581 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7582 }
7583
7584 /* attach launch data to the machine */
7585 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7586 mData->mSession.mRemoteControls.push_back(aControl);
7587 mData->mSession.mProgress = aProgress;
7588 mData->mSession.mPID = pid;
7589 mData->mSession.mState = SessionState_Spawning;
7590 mData->mSession.mType = strFrontend;
7591
7592 alock.release();
7593 mParent->i_addProcessToReap(pid);
7594
7595 LogFlowThisFuncLeave();
7596 return S_OK;
7597}
7598
7599/**
7600 * Returns @c true if the given session machine instance has an open direct
7601 * session (and optionally also for direct sessions which are closing) and
7602 * returns the session control machine instance if so.
7603 *
7604 * Note that when the method returns @c false, the arguments remain unchanged.
7605 *
7606 * @param aMachine Session machine object.
7607 * @param aControl Direct session control object (optional).
7608 * @param aAllowClosing If true then additionally a session which is currently
7609 * being closed will also be allowed.
7610 *
7611 * @note locks this object for reading.
7612 */
7613bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7614 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7615 bool aAllowClosing /*= false*/)
7616{
7617 AutoLimitedCaller autoCaller(this);
7618 AssertComRCReturn(autoCaller.rc(), false);
7619
7620 /* just return false for inaccessible machines */
7621 if (getObjectState().getState() != ObjectState::Ready)
7622 return false;
7623
7624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7625
7626 if ( mData->mSession.mState == SessionState_Locked
7627 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7628 )
7629 {
7630 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7631
7632 aMachine = mData->mSession.mMachine;
7633
7634 if (aControl != NULL)
7635 *aControl = mData->mSession.mDirectControl;
7636
7637 return true;
7638 }
7639
7640 return false;
7641}
7642
7643/**
7644 * Returns @c true if the given machine has an spawning direct session.
7645 *
7646 * @note locks this object for reading.
7647 */
7648bool Machine::i_isSessionSpawning()
7649{
7650 AutoLimitedCaller autoCaller(this);
7651 AssertComRCReturn(autoCaller.rc(), false);
7652
7653 /* just return false for inaccessible machines */
7654 if (getObjectState().getState() != ObjectState::Ready)
7655 return false;
7656
7657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7658
7659 if (mData->mSession.mState == SessionState_Spawning)
7660 return true;
7661
7662 return false;
7663}
7664
7665/**
7666 * Called from the client watcher thread to check for unexpected client process
7667 * death during Session_Spawning state (e.g. before it successfully opened a
7668 * direct session).
7669 *
7670 * On Win32 and on OS/2, this method is called only when we've got the
7671 * direct client's process termination notification, so it always returns @c
7672 * true.
7673 *
7674 * On other platforms, this method returns @c true if the client process is
7675 * terminated and @c false if it's still alive.
7676 *
7677 * @note Locks this object for writing.
7678 */
7679bool Machine::i_checkForSpawnFailure()
7680{
7681 AutoCaller autoCaller(this);
7682 if (!autoCaller.isOk())
7683 {
7684 /* nothing to do */
7685 LogFlowThisFunc(("Already uninitialized!\n"));
7686 return true;
7687 }
7688
7689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7690
7691 if (mData->mSession.mState != SessionState_Spawning)
7692 {
7693 /* nothing to do */
7694 LogFlowThisFunc(("Not spawning any more!\n"));
7695 return true;
7696 }
7697
7698 HRESULT rc = S_OK;
7699
7700 /* PID not yet initialized, skip check. */
7701 if (mData->mSession.mPID == NIL_RTPROCESS)
7702 return false;
7703
7704 RTPROCSTATUS status;
7705 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7706
7707 if (vrc != VERR_PROCESS_RUNNING)
7708 {
7709 Utf8Str strExtraInfo;
7710
7711#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7712 /* If the startup logfile exists and is of non-zero length, tell the
7713 user to look there for more details to encourage them to attach it
7714 when reporting startup issues. */
7715 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7716 uint64_t cbStartupLogFile = 0;
7717 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7718 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7719 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7720#endif
7721
7722 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7723 rc = setError(E_FAIL,
7724 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7725 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7726 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7727 rc = setError(E_FAIL,
7728 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7729 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7730 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7731 rc = setError(E_FAIL,
7732 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7733 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7734 else
7735 rc = setError(E_FAIL,
7736 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7737 i_getName().c_str(), vrc, strExtraInfo.c_str());
7738 }
7739
7740 if (FAILED(rc))
7741 {
7742 /* Close the remote session, remove the remote control from the list
7743 * and reset session state to Closed (@note keep the code in sync with
7744 * the relevant part in LockMachine()). */
7745
7746 Assert(mData->mSession.mRemoteControls.size() == 1);
7747 if (mData->mSession.mRemoteControls.size() == 1)
7748 {
7749 ErrorInfoKeeper eik;
7750 mData->mSession.mRemoteControls.front()->Uninitialize();
7751 }
7752
7753 mData->mSession.mRemoteControls.clear();
7754 mData->mSession.mState = SessionState_Unlocked;
7755
7756 /* finalize the progress after setting the state */
7757 if (!mData->mSession.mProgress.isNull())
7758 {
7759 mData->mSession.mProgress->notifyComplete(rc);
7760 mData->mSession.mProgress.setNull();
7761 }
7762
7763 mData->mSession.mPID = NIL_RTPROCESS;
7764
7765 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7766 return true;
7767 }
7768
7769 return false;
7770}
7771
7772/**
7773 * Checks whether the machine can be registered. If so, commits and saves
7774 * all settings.
7775 *
7776 * @note Must be called from mParent's write lock. Locks this object and
7777 * children for writing.
7778 */
7779HRESULT Machine::i_prepareRegister()
7780{
7781 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7782
7783 AutoLimitedCaller autoCaller(this);
7784 AssertComRCReturnRC(autoCaller.rc());
7785
7786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7787
7788 /* wait for state dependents to drop to zero */
7789 i_ensureNoStateDependencies();
7790
7791 if (!mData->mAccessible)
7792 return setError(VBOX_E_INVALID_OBJECT_STATE,
7793 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7794 mUserData->s.strName.c_str(),
7795 mData->mUuid.toString().c_str());
7796
7797 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7798
7799 if (mData->mRegistered)
7800 return setError(VBOX_E_INVALID_OBJECT_STATE,
7801 tr("The machine '%s' with UUID {%s} is already registered"),
7802 mUserData->s.strName.c_str(),
7803 mData->mUuid.toString().c_str());
7804
7805 HRESULT rc = S_OK;
7806
7807 // Ensure the settings are saved. If we are going to be registered and
7808 // no config file exists yet, create it by calling i_saveSettings() too.
7809 if ( (mData->flModifications)
7810 || (!mData->pMachineConfigFile->fileExists())
7811 )
7812 {
7813 rc = i_saveSettings(NULL);
7814 // no need to check whether VirtualBox.xml needs saving too since
7815 // we can't have a machine XML file rename pending
7816 if (FAILED(rc)) return rc;
7817 }
7818
7819 /* more config checking goes here */
7820
7821 if (SUCCEEDED(rc))
7822 {
7823 /* we may have had implicit modifications we want to fix on success */
7824 i_commit();
7825
7826 mData->mRegistered = true;
7827 }
7828 else
7829 {
7830 /* we may have had implicit modifications we want to cancel on failure*/
7831 i_rollback(false /* aNotify */);
7832 }
7833
7834 return rc;
7835}
7836
7837/**
7838 * Increases the number of objects dependent on the machine state or on the
7839 * registered state. Guarantees that these two states will not change at least
7840 * until #releaseStateDependency() is called.
7841 *
7842 * Depending on the @a aDepType value, additional state checks may be made.
7843 * These checks will set extended error info on failure. See
7844 * #checkStateDependency() for more info.
7845 *
7846 * If this method returns a failure, the dependency is not added and the caller
7847 * is not allowed to rely on any particular machine state or registration state
7848 * value and may return the failed result code to the upper level.
7849 *
7850 * @param aDepType Dependency type to add.
7851 * @param aState Current machine state (NULL if not interested).
7852 * @param aRegistered Current registered state (NULL if not interested).
7853 *
7854 * @note Locks this object for writing.
7855 */
7856HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7857 MachineState_T *aState /* = NULL */,
7858 BOOL *aRegistered /* = NULL */)
7859{
7860 AutoCaller autoCaller(this);
7861 AssertComRCReturnRC(autoCaller.rc());
7862
7863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7864
7865 HRESULT rc = i_checkStateDependency(aDepType);
7866 if (FAILED(rc)) return rc;
7867
7868 {
7869 if (mData->mMachineStateChangePending != 0)
7870 {
7871 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7872 * drop to zero so don't add more. It may make sense to wait a bit
7873 * and retry before reporting an error (since the pending state
7874 * transition should be really quick) but let's just assert for
7875 * now to see if it ever happens on practice. */
7876
7877 AssertFailed();
7878
7879 return setError(E_ACCESSDENIED,
7880 tr("Machine state change is in progress. Please retry the operation later."));
7881 }
7882
7883 ++mData->mMachineStateDeps;
7884 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7885 }
7886
7887 if (aState)
7888 *aState = mData->mMachineState;
7889 if (aRegistered)
7890 *aRegistered = mData->mRegistered;
7891
7892 return S_OK;
7893}
7894
7895/**
7896 * Decreases the number of objects dependent on the machine state.
7897 * Must always complete the #addStateDependency() call after the state
7898 * dependency is no more necessary.
7899 */
7900void Machine::i_releaseStateDependency()
7901{
7902 AutoCaller autoCaller(this);
7903 AssertComRCReturnVoid(autoCaller.rc());
7904
7905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7906
7907 /* releaseStateDependency() w/o addStateDependency()? */
7908 AssertReturnVoid(mData->mMachineStateDeps != 0);
7909 -- mData->mMachineStateDeps;
7910
7911 if (mData->mMachineStateDeps == 0)
7912 {
7913 /* inform i_ensureNoStateDependencies() that there are no more deps */
7914 if (mData->mMachineStateChangePending != 0)
7915 {
7916 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7917 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7918 }
7919 }
7920}
7921
7922Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7923{
7924 /* start with nothing found */
7925 Utf8Str strResult("");
7926
7927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7928
7929 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7930 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7931 // found:
7932 strResult = it->second; // source is a Utf8Str
7933
7934 return strResult;
7935}
7936
7937// protected methods
7938/////////////////////////////////////////////////////////////////////////////
7939
7940/**
7941 * Performs machine state checks based on the @a aDepType value. If a check
7942 * fails, this method will set extended error info, otherwise it will return
7943 * S_OK. It is supposed, that on failure, the caller will immediately return
7944 * the return value of this method to the upper level.
7945 *
7946 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7947 *
7948 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7949 * current state of this machine object allows to change settings of the
7950 * machine (i.e. the machine is not registered, or registered but not running
7951 * and not saved). It is useful to call this method from Machine setters
7952 * before performing any change.
7953 *
7954 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7955 * as for MutableStateDep except that if the machine is saved, S_OK is also
7956 * returned. This is useful in setters which allow changing machine
7957 * properties when it is in the saved state.
7958 *
7959 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7960 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7961 * Aborted).
7962 *
7963 * @param aDepType Dependency type to check.
7964 *
7965 * @note Non Machine based classes should use #addStateDependency() and
7966 * #releaseStateDependency() methods or the smart AutoStateDependency
7967 * template.
7968 *
7969 * @note This method must be called from under this object's read or write
7970 * lock.
7971 */
7972HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7973{
7974 switch (aDepType)
7975 {
7976 case AnyStateDep:
7977 {
7978 break;
7979 }
7980 case MutableStateDep:
7981 {
7982 if ( mData->mRegistered
7983 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7984 Paused should actually be included here... (Live Migration) */
7985 || ( mData->mMachineState != MachineState_Paused
7986 && mData->mMachineState != MachineState_Running
7987 && mData->mMachineState != MachineState_Aborted
7988 && mData->mMachineState != MachineState_Teleported
7989 && mData->mMachineState != MachineState_PoweredOff
7990 )
7991 )
7992 )
7993 return setError(VBOX_E_INVALID_VM_STATE,
7994 tr("The machine is not mutable (state is %s)"),
7995 Global::stringifyMachineState(mData->mMachineState));
7996 break;
7997 }
7998 case MutableOrSavedStateDep:
7999 {
8000 if ( mData->mRegistered
8001 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8002 Paused should actually be included here... (Live Migration) */
8003 || ( mData->mMachineState != MachineState_Paused
8004 && mData->mMachineState != MachineState_Running
8005 && mData->mMachineState != MachineState_Aborted
8006 && mData->mMachineState != MachineState_Teleported
8007 && mData->mMachineState != MachineState_Saved
8008 && mData->mMachineState != MachineState_PoweredOff
8009 )
8010 )
8011 )
8012 return setError(VBOX_E_INVALID_VM_STATE,
8013 tr("The machine is not mutable (state is %s)"),
8014 Global::stringifyMachineState(mData->mMachineState));
8015 break;
8016 }
8017 case OfflineStateDep:
8018 {
8019 if ( mData->mRegistered
8020 && ( !i_isSessionMachine()
8021 || ( mData->mMachineState != MachineState_PoweredOff
8022 && mData->mMachineState != MachineState_Saved
8023 && mData->mMachineState != MachineState_Aborted
8024 && mData->mMachineState != MachineState_Teleported
8025 )
8026 )
8027 )
8028 return setError(VBOX_E_INVALID_VM_STATE,
8029 tr("The machine is not offline (state is %s)"),
8030 Global::stringifyMachineState(mData->mMachineState));
8031 break;
8032 }
8033 }
8034
8035 return S_OK;
8036}
8037
8038/**
8039 * Helper to initialize all associated child objects and allocate data
8040 * structures.
8041 *
8042 * This method must be called as a part of the object's initialization procedure
8043 * (usually done in the #init() method).
8044 *
8045 * @note Must be called only from #init() or from #registeredInit().
8046 */
8047HRESULT Machine::initDataAndChildObjects()
8048{
8049 AutoCaller autoCaller(this);
8050 AssertComRCReturnRC(autoCaller.rc());
8051 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8052 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8053
8054 AssertReturn(!mData->mAccessible, E_FAIL);
8055
8056 /* allocate data structures */
8057 mSSData.allocate();
8058 mUserData.allocate();
8059 mHWData.allocate();
8060 mMediaData.allocate();
8061 mStorageControllers.allocate();
8062 mUSBControllers.allocate();
8063
8064 /* initialize mOSTypeId */
8065 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8066
8067 /* create associated BIOS settings object */
8068 unconst(mBIOSSettings).createObject();
8069 mBIOSSettings->init(this);
8070
8071 /* create an associated VRDE object (default is disabled) */
8072 unconst(mVRDEServer).createObject();
8073 mVRDEServer->init(this);
8074
8075 /* create associated serial port objects */
8076 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8077 {
8078 unconst(mSerialPorts[slot]).createObject();
8079 mSerialPorts[slot]->init(this, slot);
8080 }
8081
8082 /* create associated parallel port objects */
8083 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8084 {
8085 unconst(mParallelPorts[slot]).createObject();
8086 mParallelPorts[slot]->init(this, slot);
8087 }
8088
8089 /* create the audio adapter object (always present, default is disabled) */
8090 unconst(mAudioAdapter).createObject();
8091 mAudioAdapter->init(this);
8092
8093 /* create the USB device filters object (always present) */
8094 unconst(mUSBDeviceFilters).createObject();
8095 mUSBDeviceFilters->init(this);
8096
8097 /* create associated network adapter objects */
8098 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8099 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8100 {
8101 unconst(mNetworkAdapters[slot]).createObject();
8102 mNetworkAdapters[slot]->init(this, slot);
8103 }
8104
8105 /* create the bandwidth control */
8106 unconst(mBandwidthControl).createObject();
8107 mBandwidthControl->init(this);
8108
8109 return S_OK;
8110}
8111
8112/**
8113 * Helper to uninitialize all associated child objects and to free all data
8114 * structures.
8115 *
8116 * This method must be called as a part of the object's uninitialization
8117 * procedure (usually done in the #uninit() method).
8118 *
8119 * @note Must be called only from #uninit() or from #registeredInit().
8120 */
8121void Machine::uninitDataAndChildObjects()
8122{
8123 AutoCaller autoCaller(this);
8124 AssertComRCReturnVoid(autoCaller.rc());
8125 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8126 || getObjectState().getState() == ObjectState::Limited);
8127
8128 /* tell all our other child objects we've been uninitialized */
8129 if (mBandwidthControl)
8130 {
8131 mBandwidthControl->uninit();
8132 unconst(mBandwidthControl).setNull();
8133 }
8134
8135 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8136 {
8137 if (mNetworkAdapters[slot])
8138 {
8139 mNetworkAdapters[slot]->uninit();
8140 unconst(mNetworkAdapters[slot]).setNull();
8141 }
8142 }
8143
8144 if (mUSBDeviceFilters)
8145 {
8146 mUSBDeviceFilters->uninit();
8147 unconst(mUSBDeviceFilters).setNull();
8148 }
8149
8150 if (mAudioAdapter)
8151 {
8152 mAudioAdapter->uninit();
8153 unconst(mAudioAdapter).setNull();
8154 }
8155
8156 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8157 {
8158 if (mParallelPorts[slot])
8159 {
8160 mParallelPorts[slot]->uninit();
8161 unconst(mParallelPorts[slot]).setNull();
8162 }
8163 }
8164
8165 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8166 {
8167 if (mSerialPorts[slot])
8168 {
8169 mSerialPorts[slot]->uninit();
8170 unconst(mSerialPorts[slot]).setNull();
8171 }
8172 }
8173
8174 if (mVRDEServer)
8175 {
8176 mVRDEServer->uninit();
8177 unconst(mVRDEServer).setNull();
8178 }
8179
8180 if (mBIOSSettings)
8181 {
8182 mBIOSSettings->uninit();
8183 unconst(mBIOSSettings).setNull();
8184 }
8185
8186 /* Deassociate media (only when a real Machine or a SnapshotMachine
8187 * instance is uninitialized; SessionMachine instances refer to real
8188 * Machine media). This is necessary for a clean re-initialization of
8189 * the VM after successfully re-checking the accessibility state. Note
8190 * that in case of normal Machine or SnapshotMachine uninitialization (as
8191 * a result of unregistering or deleting the snapshot), outdated media
8192 * attachments will already be uninitialized and deleted, so this
8193 * code will not affect them. */
8194 if ( !!mMediaData
8195 && (!i_isSessionMachine())
8196 )
8197 {
8198 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8199 it != mMediaData->mAttachments.end();
8200 ++it)
8201 {
8202 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8203 if (pMedium.isNull())
8204 continue;
8205 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8206 AssertComRC(rc);
8207 }
8208 }
8209
8210 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8211 {
8212 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8213 if (mData->mFirstSnapshot)
8214 {
8215 // snapshots tree is protected by machine write lock; strictly
8216 // this isn't necessary here since we're deleting the entire
8217 // machine, but otherwise we assert in Snapshot::uninit()
8218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8219 mData->mFirstSnapshot->uninit();
8220 mData->mFirstSnapshot.setNull();
8221 }
8222
8223 mData->mCurrentSnapshot.setNull();
8224 }
8225
8226 /* free data structures (the essential mData structure is not freed here
8227 * since it may be still in use) */
8228 mMediaData.free();
8229 mStorageControllers.free();
8230 mUSBControllers.free();
8231 mHWData.free();
8232 mUserData.free();
8233 mSSData.free();
8234}
8235
8236/**
8237 * Returns a pointer to the Machine object for this machine that acts like a
8238 * parent for complex machine data objects such as shared folders, etc.
8239 *
8240 * For primary Machine objects and for SnapshotMachine objects, returns this
8241 * object's pointer itself. For SessionMachine objects, returns the peer
8242 * (primary) machine pointer.
8243 */
8244Machine* Machine::i_getMachine()
8245{
8246 if (i_isSessionMachine())
8247 return (Machine*)mPeer;
8248 return this;
8249}
8250
8251/**
8252 * Makes sure that there are no machine state dependents. If necessary, waits
8253 * for the number of dependents to drop to zero.
8254 *
8255 * Make sure this method is called from under this object's write lock to
8256 * guarantee that no new dependents may be added when this method returns
8257 * control to the caller.
8258 *
8259 * @note Locks this object for writing. The lock will be released while waiting
8260 * (if necessary).
8261 *
8262 * @warning To be used only in methods that change the machine state!
8263 */
8264void Machine::i_ensureNoStateDependencies()
8265{
8266 AssertReturnVoid(isWriteLockOnCurrentThread());
8267
8268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8269
8270 /* Wait for all state dependents if necessary */
8271 if (mData->mMachineStateDeps != 0)
8272 {
8273 /* lazy semaphore creation */
8274 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8275 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8276
8277 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8278 mData->mMachineStateDeps));
8279
8280 ++mData->mMachineStateChangePending;
8281
8282 /* reset the semaphore before waiting, the last dependent will signal
8283 * it */
8284 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8285
8286 alock.release();
8287
8288 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8289
8290 alock.acquire();
8291
8292 -- mData->mMachineStateChangePending;
8293 }
8294}
8295
8296/**
8297 * Changes the machine state and informs callbacks.
8298 *
8299 * This method is not intended to fail so it either returns S_OK or asserts (and
8300 * returns a failure).
8301 *
8302 * @note Locks this object for writing.
8303 */
8304HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8305{
8306 LogFlowThisFuncEnter();
8307 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8308
8309 AutoCaller autoCaller(this);
8310 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8311
8312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8313
8314 /* wait for state dependents to drop to zero */
8315 i_ensureNoStateDependencies();
8316
8317 if (mData->mMachineState != aMachineState)
8318 {
8319 mData->mMachineState = aMachineState;
8320
8321 RTTimeNow(&mData->mLastStateChange);
8322
8323 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8324 }
8325
8326 LogFlowThisFuncLeave();
8327 return S_OK;
8328}
8329
8330/**
8331 * Searches for a shared folder with the given logical name
8332 * in the collection of shared folders.
8333 *
8334 * @param aName logical name of the shared folder
8335 * @param aSharedFolder where to return the found object
8336 * @param aSetError whether to set the error info if the folder is
8337 * not found
8338 * @return
8339 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8340 *
8341 * @note
8342 * must be called from under the object's lock!
8343 */
8344HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8345 ComObjPtr<SharedFolder> &aSharedFolder,
8346 bool aSetError /* = false */)
8347{
8348 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8349 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8350 it != mHWData->mSharedFolders.end();
8351 ++it)
8352 {
8353 SharedFolder *pSF = *it;
8354 AutoCaller autoCaller(pSF);
8355 if (pSF->i_getName() == aName)
8356 {
8357 aSharedFolder = pSF;
8358 rc = S_OK;
8359 break;
8360 }
8361 }
8362
8363 if (aSetError && FAILED(rc))
8364 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8365
8366 return rc;
8367}
8368
8369/**
8370 * Initializes all machine instance data from the given settings structures
8371 * from XML. The exception is the machine UUID which needs special handling
8372 * depending on the caller's use case, so the caller needs to set that herself.
8373 *
8374 * This gets called in several contexts during machine initialization:
8375 *
8376 * -- When machine XML exists on disk already and needs to be loaded into memory,
8377 * for example, from registeredInit() to load all registered machines on
8378 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8379 * attached to the machine should be part of some media registry already.
8380 *
8381 * -- During OVF import, when a machine config has been constructed from an
8382 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8383 * ensure that the media listed as attachments in the config (which have
8384 * been imported from the OVF) receive the correct registry ID.
8385 *
8386 * -- During VM cloning.
8387 *
8388 * @param config Machine settings from XML.
8389 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8390 * for each attached medium in the config.
8391 * @return
8392 */
8393HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8394 const Guid *puuidRegistry)
8395{
8396 // copy name, description, OS type, teleporter, UTC etc.
8397 mUserData->s = config.machineUserData;
8398
8399 // Decode the Icon overide data from config userdata and set onto Machine.
8400 #define DECODE_STR_MAX _1M
8401 const char* pszStr = config.machineUserData.ovIcon.c_str();
8402 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8403 if (cbOut > DECODE_STR_MAX)
8404 return setError(E_FAIL,
8405 tr("Icon Data too long.'%d' > '%d'"),
8406 cbOut,
8407 DECODE_STR_MAX);
8408 mUserData->mIcon.resize(cbOut);
8409 int vrc = VINF_SUCCESS;
8410 if (cbOut)
8411 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8412 if (RT_FAILURE(vrc))
8413 {
8414 mUserData->mIcon.resize(0);
8415 return setError(E_FAIL,
8416 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8417 pszStr,
8418 vrc);
8419 }
8420
8421 // look up the object by Id to check it is valid
8422 ComPtr<IGuestOSType> guestOSType;
8423 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8424 guestOSType.asOutParam());
8425 if (FAILED(rc)) return rc;
8426
8427 // stateFile (optional)
8428 if (config.strStateFile.isEmpty())
8429 mSSData->strStateFilePath.setNull();
8430 else
8431 {
8432 Utf8Str stateFilePathFull(config.strStateFile);
8433 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8434 if (RT_FAILURE(vrc))
8435 return setError(E_FAIL,
8436 tr("Invalid saved state file path '%s' (%Rrc)"),
8437 config.strStateFile.c_str(),
8438 vrc);
8439 mSSData->strStateFilePath = stateFilePathFull;
8440 }
8441
8442 // snapshot folder needs special processing so set it again
8443 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8444 if (FAILED(rc)) return rc;
8445
8446 /* Copy the extra data items (Not in any case config is already the same as
8447 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8448 * make sure the extra data map is copied). */
8449 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8450
8451 /* currentStateModified (optional, default is true) */
8452 mData->mCurrentStateModified = config.fCurrentStateModified;
8453
8454 mData->mLastStateChange = config.timeLastStateChange;
8455
8456 /*
8457 * note: all mUserData members must be assigned prior this point because
8458 * we need to commit changes in order to let mUserData be shared by all
8459 * snapshot machine instances.
8460 */
8461 mUserData.commitCopy();
8462
8463 // machine registry, if present (must be loaded before snapshots)
8464 if (config.canHaveOwnMediaRegistry())
8465 {
8466 // determine machine folder
8467 Utf8Str strMachineFolder = i_getSettingsFileFull();
8468 strMachineFolder.stripFilename();
8469 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8470 config.mediaRegistry,
8471 strMachineFolder);
8472 if (FAILED(rc)) return rc;
8473 }
8474
8475 /* Snapshot node (optional) */
8476 size_t cRootSnapshots;
8477 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8478 {
8479 // there must be only one root snapshot
8480 Assert(cRootSnapshots == 1);
8481
8482 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8483
8484 rc = i_loadSnapshot(snap,
8485 config.uuidCurrentSnapshot,
8486 NULL); // no parent == first snapshot
8487 if (FAILED(rc)) return rc;
8488 }
8489
8490 // hardware data
8491 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8492 if (FAILED(rc)) return rc;
8493
8494 // load storage controllers
8495 rc = i_loadStorageControllers(config.storageMachine,
8496 puuidRegistry,
8497 NULL /* puuidSnapshot */);
8498 if (FAILED(rc)) return rc;
8499
8500 /*
8501 * NOTE: the assignment below must be the last thing to do,
8502 * otherwise it will be not possible to change the settings
8503 * somewhere in the code above because all setters will be
8504 * blocked by i_checkStateDependency(MutableStateDep).
8505 */
8506
8507 /* set the machine state to Aborted or Saved when appropriate */
8508 if (config.fAborted)
8509 {
8510 mSSData->strStateFilePath.setNull();
8511
8512 /* no need to use i_setMachineState() during init() */
8513 mData->mMachineState = MachineState_Aborted;
8514 }
8515 else if (!mSSData->strStateFilePath.isEmpty())
8516 {
8517 /* no need to use i_setMachineState() during init() */
8518 mData->mMachineState = MachineState_Saved;
8519 }
8520
8521 // after loading settings, we are no longer different from the XML on disk
8522 mData->flModifications = 0;
8523
8524 return S_OK;
8525}
8526
8527/**
8528 * Recursively loads all snapshots starting from the given.
8529 *
8530 * @param aNode <Snapshot> node.
8531 * @param aCurSnapshotId Current snapshot ID from the settings file.
8532 * @param aParentSnapshot Parent snapshot.
8533 */
8534HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8535 const Guid &aCurSnapshotId,
8536 Snapshot *aParentSnapshot)
8537{
8538 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8539 AssertReturn(!i_isSessionMachine(), E_FAIL);
8540
8541 HRESULT rc = S_OK;
8542
8543 Utf8Str strStateFile;
8544 if (!data.strStateFile.isEmpty())
8545 {
8546 /* optional */
8547 strStateFile = data.strStateFile;
8548 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8549 if (RT_FAILURE(vrc))
8550 return setError(E_FAIL,
8551 tr("Invalid saved state file path '%s' (%Rrc)"),
8552 strStateFile.c_str(),
8553 vrc);
8554 }
8555
8556 /* create a snapshot machine object */
8557 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8558 pSnapshotMachine.createObject();
8559 rc = pSnapshotMachine->initFromSettings(this,
8560 data.hardware,
8561 &data.debugging,
8562 &data.autostart,
8563 data.storage,
8564 data.uuid.ref(),
8565 strStateFile);
8566 if (FAILED(rc)) return rc;
8567
8568 /* create a snapshot object */
8569 ComObjPtr<Snapshot> pSnapshot;
8570 pSnapshot.createObject();
8571 /* initialize the snapshot */
8572 rc = pSnapshot->init(mParent, // VirtualBox object
8573 data.uuid,
8574 data.strName,
8575 data.strDescription,
8576 data.timestamp,
8577 pSnapshotMachine,
8578 aParentSnapshot);
8579 if (FAILED(rc)) return rc;
8580
8581 /* memorize the first snapshot if necessary */
8582 if (!mData->mFirstSnapshot)
8583 mData->mFirstSnapshot = pSnapshot;
8584
8585 /* memorize the current snapshot when appropriate */
8586 if ( !mData->mCurrentSnapshot
8587 && pSnapshot->i_getId() == aCurSnapshotId
8588 )
8589 mData->mCurrentSnapshot = pSnapshot;
8590
8591 // now create the children
8592 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8593 it != data.llChildSnapshots.end();
8594 ++it)
8595 {
8596 const settings::Snapshot &childData = *it;
8597 // recurse
8598 rc = i_loadSnapshot(childData,
8599 aCurSnapshotId,
8600 pSnapshot); // parent = the one we created above
8601 if (FAILED(rc)) return rc;
8602 }
8603
8604 return rc;
8605}
8606
8607/**
8608 * Loads settings into mHWData.
8609 *
8610 * @param data Reference to the hardware settings.
8611 * @param pDbg Pointer to the debugging settings.
8612 * @param pAutostart Pointer to the autostart settings.
8613 */
8614HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8615 const settings::Autostart *pAutostart)
8616{
8617 AssertReturn(!i_isSessionMachine(), E_FAIL);
8618
8619 HRESULT rc = S_OK;
8620
8621 try
8622 {
8623 /* The hardware version attribute (optional). */
8624 mHWData->mHWVersion = data.strVersion;
8625 mHWData->mHardwareUUID = data.uuid;
8626
8627 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8628 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8629 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8630 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8631 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8632 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8633 mHWData->mPAEEnabled = data.fPAE;
8634 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8635 mHWData->mLongMode = data.enmLongMode;
8636 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8637 mHWData->mCPUCount = data.cCPUs;
8638 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8639 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8640
8641 // cpu
8642 if (mHWData->mCPUHotPlugEnabled)
8643 {
8644 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8645 it != data.llCpus.end();
8646 ++it)
8647 {
8648 const settings::Cpu &cpu = *it;
8649
8650 mHWData->mCPUAttached[cpu.ulId] = true;
8651 }
8652 }
8653
8654 // cpuid leafs
8655 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8656 it != data.llCpuIdLeafs.end();
8657 ++it)
8658 {
8659 const settings::CpuIdLeaf &leaf = *it;
8660
8661 switch (leaf.ulId)
8662 {
8663 case 0x0:
8664 case 0x1:
8665 case 0x2:
8666 case 0x3:
8667 case 0x4:
8668 case 0x5:
8669 case 0x6:
8670 case 0x7:
8671 case 0x8:
8672 case 0x9:
8673 case 0xA:
8674 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8675 break;
8676
8677 case 0x80000000:
8678 case 0x80000001:
8679 case 0x80000002:
8680 case 0x80000003:
8681 case 0x80000004:
8682 case 0x80000005:
8683 case 0x80000006:
8684 case 0x80000007:
8685 case 0x80000008:
8686 case 0x80000009:
8687 case 0x8000000A:
8688 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8689 break;
8690
8691 default:
8692 /* just ignore */
8693 break;
8694 }
8695 }
8696
8697 mHWData->mMemorySize = data.ulMemorySizeMB;
8698 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8699
8700 // boot order
8701 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8702 {
8703 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8704 if (it == data.mapBootOrder.end())
8705 mHWData->mBootOrder[i] = DeviceType_Null;
8706 else
8707 mHWData->mBootOrder[i] = it->second;
8708 }
8709
8710 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8711 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8712 mHWData->mMonitorCount = data.cMonitors;
8713 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8714 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8715 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8716 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8717 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8718 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8719 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8720 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8721 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8722 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8723 if (!data.strVideoCaptureFile.isEmpty())
8724 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8725 else
8726 mHWData->mVideoCaptureFile.setNull();
8727 mHWData->mFirmwareType = data.firmwareType;
8728 mHWData->mPointingHIDType = data.pointingHIDType;
8729 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8730 mHWData->mChipsetType = data.chipsetType;
8731 mHWData->mParavirtProvider = data.paravirtProvider;
8732 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8733 mHWData->mHPETEnabled = data.fHPETEnabled;
8734
8735 /* VRDEServer */
8736 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8737 if (FAILED(rc)) return rc;
8738
8739 /* BIOS */
8740 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8741 if (FAILED(rc)) return rc;
8742
8743 // Bandwidth control (must come before network adapters)
8744 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8745 if (FAILED(rc)) return rc;
8746
8747 /* Shared folders */
8748 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8749 it != data.usbSettings.llUSBControllers.end();
8750 ++it)
8751 {
8752 const settings::USBController &settingsCtrl = *it;
8753 ComObjPtr<USBController> newCtrl;
8754
8755 newCtrl.createObject();
8756 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8757 mUSBControllers->push_back(newCtrl);
8758 }
8759
8760 /* USB device filters */
8761 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8762 if (FAILED(rc)) return rc;
8763
8764 // network adapters
8765 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8766 size_t oldCount = mNetworkAdapters.size();
8767 if (newCount > oldCount)
8768 {
8769 mNetworkAdapters.resize(newCount);
8770 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8771 {
8772 unconst(mNetworkAdapters[slot]).createObject();
8773 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8774 }
8775 }
8776 else if (newCount < oldCount)
8777 mNetworkAdapters.resize(newCount);
8778 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8779 it != data.llNetworkAdapters.end();
8780 ++it)
8781 {
8782 const settings::NetworkAdapter &nic = *it;
8783
8784 /* slot unicity is guaranteed by XML Schema */
8785 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8786 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8787 if (FAILED(rc)) return rc;
8788 }
8789
8790 // serial ports
8791 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8792 it != data.llSerialPorts.end();
8793 ++it)
8794 {
8795 const settings::SerialPort &s = *it;
8796
8797 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8798 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8799 if (FAILED(rc)) return rc;
8800 }
8801
8802 // parallel ports (optional)
8803 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8804 it != data.llParallelPorts.end();
8805 ++it)
8806 {
8807 const settings::ParallelPort &p = *it;
8808
8809 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8810 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8811 if (FAILED(rc)) return rc;
8812 }
8813
8814 /* AudioAdapter */
8815 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8816 if (FAILED(rc)) return rc;
8817
8818 /* Shared folders */
8819 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8820 it != data.llSharedFolders.end();
8821 ++it)
8822 {
8823 const settings::SharedFolder &sf = *it;
8824
8825 ComObjPtr<SharedFolder> sharedFolder;
8826 /* Check for double entries. Not allowed! */
8827 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8828 if (SUCCEEDED(rc))
8829 return setError(VBOX_E_OBJECT_IN_USE,
8830 tr("Shared folder named '%s' already exists"),
8831 sf.strName.c_str());
8832
8833 /* Create the new shared folder. Don't break on error. This will be
8834 * reported when the machine starts. */
8835 sharedFolder.createObject();
8836 rc = sharedFolder->init(i_getMachine(),
8837 sf.strName,
8838 sf.strHostPath,
8839 RT_BOOL(sf.fWritable),
8840 RT_BOOL(sf.fAutoMount),
8841 false /* fFailOnError */);
8842 if (FAILED(rc)) return rc;
8843 mHWData->mSharedFolders.push_back(sharedFolder);
8844 }
8845
8846 // Clipboard
8847 mHWData->mClipboardMode = data.clipboardMode;
8848
8849 // drag'n'drop
8850 mHWData->mDnDMode = data.dndMode;
8851
8852 // guest settings
8853 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8854
8855 // IO settings
8856 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8857 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8858
8859 // Host PCI devices
8860 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8861 it != data.pciAttachments.end();
8862 ++it)
8863 {
8864 const settings::HostPCIDeviceAttachment &hpda = *it;
8865 ComObjPtr<PCIDeviceAttachment> pda;
8866
8867 pda.createObject();
8868 pda->i_loadSettings(this, hpda);
8869 mHWData->mPCIDeviceAssignments.push_back(pda);
8870 }
8871
8872 /*
8873 * (The following isn't really real hardware, but it lives in HWData
8874 * for reasons of convenience.)
8875 */
8876
8877#ifdef VBOX_WITH_GUEST_PROPS
8878 /* Guest properties (optional) */
8879 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8880 it != data.llGuestProperties.end();
8881 ++it)
8882 {
8883 const settings::GuestProperty &prop = *it;
8884 uint32_t fFlags = guestProp::NILFLAG;
8885 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8886 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8887 mHWData->mGuestProperties[prop.strName] = property;
8888 }
8889
8890 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8891#endif /* VBOX_WITH_GUEST_PROPS defined */
8892
8893 rc = i_loadDebugging(pDbg);
8894 if (FAILED(rc))
8895 return rc;
8896
8897 mHWData->mAutostart = *pAutostart;
8898
8899 /* default frontend */
8900 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8901 }
8902 catch(std::bad_alloc &)
8903 {
8904 return E_OUTOFMEMORY;
8905 }
8906
8907 AssertComRC(rc);
8908 return rc;
8909}
8910
8911/**
8912 * Called from Machine::loadHardware() to load the debugging settings of the
8913 * machine.
8914 *
8915 * @param pDbg Pointer to the settings.
8916 */
8917HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8918{
8919 mHWData->mDebugging = *pDbg;
8920 /* no more processing currently required, this will probably change. */
8921 return S_OK;
8922}
8923
8924/**
8925 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8926 *
8927 * @param data
8928 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8929 * @param puuidSnapshot
8930 * @return
8931 */
8932HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8933 const Guid *puuidRegistry,
8934 const Guid *puuidSnapshot)
8935{
8936 AssertReturn(!i_isSessionMachine(), E_FAIL);
8937
8938 HRESULT rc = S_OK;
8939
8940 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8941 it != data.llStorageControllers.end();
8942 ++it)
8943 {
8944 const settings::StorageController &ctlData = *it;
8945
8946 ComObjPtr<StorageController> pCtl;
8947 /* Try to find one with the name first. */
8948 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8949 if (SUCCEEDED(rc))
8950 return setError(VBOX_E_OBJECT_IN_USE,
8951 tr("Storage controller named '%s' already exists"),
8952 ctlData.strName.c_str());
8953
8954 pCtl.createObject();
8955 rc = pCtl->init(this,
8956 ctlData.strName,
8957 ctlData.storageBus,
8958 ctlData.ulInstance,
8959 ctlData.fBootable);
8960 if (FAILED(rc)) return rc;
8961
8962 mStorageControllers->push_back(pCtl);
8963
8964 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8965 if (FAILED(rc)) return rc;
8966
8967 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8968 if (FAILED(rc)) return rc;
8969
8970 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8971 if (FAILED(rc)) return rc;
8972
8973 /* Set IDE emulation settings (only for AHCI controller). */
8974 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8975 {
8976 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8977 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8978 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8979 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8980 )
8981 return rc;
8982 }
8983
8984 /* Load the attached devices now. */
8985 rc = i_loadStorageDevices(pCtl,
8986 ctlData,
8987 puuidRegistry,
8988 puuidSnapshot);
8989 if (FAILED(rc)) return rc;
8990 }
8991
8992 return S_OK;
8993}
8994
8995/**
8996 * Called from i_loadStorageControllers for a controller's devices.
8997 *
8998 * @param aStorageController
8999 * @param data
9000 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9001 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9002 * @return
9003 */
9004HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9005 const settings::StorageController &data,
9006 const Guid *puuidRegistry,
9007 const Guid *puuidSnapshot)
9008{
9009 HRESULT rc = S_OK;
9010
9011 /* paranoia: detect duplicate attachments */
9012 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9013 it != data.llAttachedDevices.end();
9014 ++it)
9015 {
9016 const settings::AttachedDevice &ad = *it;
9017
9018 for (settings::AttachedDevicesList::const_iterator it2 = it;
9019 it2 != data.llAttachedDevices.end();
9020 ++it2)
9021 {
9022 if (it == it2)
9023 continue;
9024
9025 const settings::AttachedDevice &ad2 = *it2;
9026
9027 if ( ad.lPort == ad2.lPort
9028 && ad.lDevice == ad2.lDevice)
9029 {
9030 return setError(E_FAIL,
9031 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9032 aStorageController->i_getName().c_str(),
9033 ad.lPort,
9034 ad.lDevice,
9035 mUserData->s.strName.c_str());
9036 }
9037 }
9038 }
9039
9040 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9041 it != data.llAttachedDevices.end();
9042 ++it)
9043 {
9044 const settings::AttachedDevice &dev = *it;
9045 ComObjPtr<Medium> medium;
9046
9047 switch (dev.deviceType)
9048 {
9049 case DeviceType_Floppy:
9050 case DeviceType_DVD:
9051 if (dev.strHostDriveSrc.isNotEmpty())
9052 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9053 false /* fRefresh */, medium);
9054 else
9055 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9056 dev.uuid,
9057 false /* fRefresh */,
9058 false /* aSetError */,
9059 medium);
9060 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9061 // This is not an error. The host drive or UUID might have vanished, so just go
9062 // ahead without this removeable medium attachment
9063 rc = S_OK;
9064 break;
9065
9066 case DeviceType_HardDisk:
9067 {
9068 /* find a hard disk by UUID */
9069 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9070 if (FAILED(rc))
9071 {
9072 if (i_isSnapshotMachine())
9073 {
9074 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9075 // so the user knows that the bad disk is in a snapshot somewhere
9076 com::ErrorInfo info;
9077 return setError(E_FAIL,
9078 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9079 puuidSnapshot->raw(),
9080 info.getText().raw());
9081 }
9082 else
9083 return rc;
9084 }
9085
9086 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9087
9088 if (medium->i_getType() == MediumType_Immutable)
9089 {
9090 if (i_isSnapshotMachine())
9091 return setError(E_FAIL,
9092 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9093 "of the virtual machine '%s' ('%s')"),
9094 medium->i_getLocationFull().c_str(),
9095 dev.uuid.raw(),
9096 puuidSnapshot->raw(),
9097 mUserData->s.strName.c_str(),
9098 mData->m_strConfigFileFull.c_str());
9099
9100 return setError(E_FAIL,
9101 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9102 medium->i_getLocationFull().c_str(),
9103 dev.uuid.raw(),
9104 mUserData->s.strName.c_str(),
9105 mData->m_strConfigFileFull.c_str());
9106 }
9107
9108 if (medium->i_getType() == MediumType_MultiAttach)
9109 {
9110 if (i_isSnapshotMachine())
9111 return setError(E_FAIL,
9112 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9113 "of the virtual machine '%s' ('%s')"),
9114 medium->i_getLocationFull().c_str(),
9115 dev.uuid.raw(),
9116 puuidSnapshot->raw(),
9117 mUserData->s.strName.c_str(),
9118 mData->m_strConfigFileFull.c_str());
9119
9120 return setError(E_FAIL,
9121 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9122 medium->i_getLocationFull().c_str(),
9123 dev.uuid.raw(),
9124 mUserData->s.strName.c_str(),
9125 mData->m_strConfigFileFull.c_str());
9126 }
9127
9128 if ( !i_isSnapshotMachine()
9129 && medium->i_getChildren().size() != 0
9130 )
9131 return setError(E_FAIL,
9132 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9133 "because it has %d differencing child hard disks"),
9134 medium->i_getLocationFull().c_str(),
9135 dev.uuid.raw(),
9136 mUserData->s.strName.c_str(),
9137 mData->m_strConfigFileFull.c_str(),
9138 medium->i_getChildren().size());
9139
9140 if (i_findAttachment(mMediaData->mAttachments,
9141 medium))
9142 return setError(E_FAIL,
9143 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9144 medium->i_getLocationFull().c_str(),
9145 dev.uuid.raw(),
9146 mUserData->s.strName.c_str(),
9147 mData->m_strConfigFileFull.c_str());
9148
9149 break;
9150 }
9151
9152 default:
9153 return setError(E_FAIL,
9154 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9155 medium->i_getLocationFull().c_str(),
9156 mUserData->s.strName.c_str(),
9157 mData->m_strConfigFileFull.c_str());
9158 }
9159
9160 if (FAILED(rc))
9161 break;
9162
9163 /* Bandwidth groups are loaded at this point. */
9164 ComObjPtr<BandwidthGroup> pBwGroup;
9165
9166 if (!dev.strBwGroup.isEmpty())
9167 {
9168 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9169 if (FAILED(rc))
9170 return setError(E_FAIL,
9171 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9172 medium->i_getLocationFull().c_str(),
9173 dev.strBwGroup.c_str(),
9174 mUserData->s.strName.c_str(),
9175 mData->m_strConfigFileFull.c_str());
9176 pBwGroup->i_reference();
9177 }
9178
9179 const Bstr controllerName = aStorageController->i_getName();
9180 ComObjPtr<MediumAttachment> pAttachment;
9181 pAttachment.createObject();
9182 rc = pAttachment->init(this,
9183 medium,
9184 controllerName,
9185 dev.lPort,
9186 dev.lDevice,
9187 dev.deviceType,
9188 false,
9189 dev.fPassThrough,
9190 dev.fTempEject,
9191 dev.fNonRotational,
9192 dev.fDiscard,
9193 dev.fHotPluggable,
9194 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9195 if (FAILED(rc)) break;
9196
9197 /* associate the medium with this machine and snapshot */
9198 if (!medium.isNull())
9199 {
9200 AutoCaller medCaller(medium);
9201 if (FAILED(medCaller.rc())) return medCaller.rc();
9202 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9203
9204 if (i_isSnapshotMachine())
9205 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9206 else
9207 rc = medium->i_addBackReference(mData->mUuid);
9208 /* If the medium->addBackReference fails it sets an appropriate
9209 * error message, so no need to do any guesswork here. */
9210
9211 if (puuidRegistry)
9212 // caller wants registry ID to be set on all attached media (OVF import case)
9213 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9214 }
9215
9216 if (FAILED(rc))
9217 break;
9218
9219 /* back up mMediaData to let registeredInit() properly rollback on failure
9220 * (= limited accessibility) */
9221 i_setModified(IsModified_Storage);
9222 mMediaData.backup();
9223 mMediaData->mAttachments.push_back(pAttachment);
9224 }
9225
9226 return rc;
9227}
9228
9229/**
9230 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9231 *
9232 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9233 * @param aSnapshot where to return the found snapshot
9234 * @param aSetError true to set extended error info on failure
9235 */
9236HRESULT Machine::i_findSnapshotById(const Guid &aId,
9237 ComObjPtr<Snapshot> &aSnapshot,
9238 bool aSetError /* = false */)
9239{
9240 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9241
9242 if (!mData->mFirstSnapshot)
9243 {
9244 if (aSetError)
9245 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9246 return E_FAIL;
9247 }
9248
9249 if (aId.isZero())
9250 aSnapshot = mData->mFirstSnapshot;
9251 else
9252 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9253
9254 if (!aSnapshot)
9255 {
9256 if (aSetError)
9257 return setError(E_FAIL,
9258 tr("Could not find a snapshot with UUID {%s}"),
9259 aId.toString().c_str());
9260 return E_FAIL;
9261 }
9262
9263 return S_OK;
9264}
9265
9266/**
9267 * Returns the snapshot with the given name or fails of no such snapshot.
9268 *
9269 * @param aName snapshot name to find
9270 * @param aSnapshot where to return the found snapshot
9271 * @param aSetError true to set extended error info on failure
9272 */
9273HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9274 ComObjPtr<Snapshot> &aSnapshot,
9275 bool aSetError /* = false */)
9276{
9277 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9278
9279 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9280
9281 if (!mData->mFirstSnapshot)
9282 {
9283 if (aSetError)
9284 return setError(VBOX_E_OBJECT_NOT_FOUND,
9285 tr("This machine does not have any snapshots"));
9286 return VBOX_E_OBJECT_NOT_FOUND;
9287 }
9288
9289 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9290
9291 if (!aSnapshot)
9292 {
9293 if (aSetError)
9294 return setError(VBOX_E_OBJECT_NOT_FOUND,
9295 tr("Could not find a snapshot named '%s'"), strName.c_str());
9296 return VBOX_E_OBJECT_NOT_FOUND;
9297 }
9298
9299 return S_OK;
9300}
9301
9302/**
9303 * Returns a storage controller object with the given name.
9304 *
9305 * @param aName storage controller name to find
9306 * @param aStorageController where to return the found storage controller
9307 * @param aSetError true to set extended error info on failure
9308 */
9309HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9310 ComObjPtr<StorageController> &aStorageController,
9311 bool aSetError /* = false */)
9312{
9313 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9314
9315 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9316 it != mStorageControllers->end();
9317 ++it)
9318 {
9319 if ((*it)->i_getName() == aName)
9320 {
9321 aStorageController = (*it);
9322 return S_OK;
9323 }
9324 }
9325
9326 if (aSetError)
9327 return setError(VBOX_E_OBJECT_NOT_FOUND,
9328 tr("Could not find a storage controller named '%s'"),
9329 aName.c_str());
9330 return VBOX_E_OBJECT_NOT_FOUND;
9331}
9332
9333/**
9334 * Returns a USB controller object with the given name.
9335 *
9336 * @param aName USB controller name to find
9337 * @param aUSBController where to return the found USB controller
9338 * @param aSetError true to set extended error info on failure
9339 */
9340HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9341 ComObjPtr<USBController> &aUSBController,
9342 bool aSetError /* = false */)
9343{
9344 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9345
9346 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9347 it != mUSBControllers->end();
9348 ++it)
9349 {
9350 if ((*it)->i_getName() == aName)
9351 {
9352 aUSBController = (*it);
9353 return S_OK;
9354 }
9355 }
9356
9357 if (aSetError)
9358 return setError(VBOX_E_OBJECT_NOT_FOUND,
9359 tr("Could not find a storage controller named '%s'"),
9360 aName.c_str());
9361 return VBOX_E_OBJECT_NOT_FOUND;
9362}
9363
9364/**
9365 * Returns the number of USB controller instance of the given type.
9366 *
9367 * @param enmType USB controller type.
9368 */
9369ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9370{
9371 ULONG cCtrls = 0;
9372
9373 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9374 it != mUSBControllers->end();
9375 ++it)
9376 {
9377 if ((*it)->i_getControllerType() == enmType)
9378 cCtrls++;
9379 }
9380
9381 return cCtrls;
9382}
9383
9384HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9385 MediaData::AttachmentList &atts)
9386{
9387 AutoCaller autoCaller(this);
9388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9389
9390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9391
9392 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9393 it != mMediaData->mAttachments.end();
9394 ++it)
9395 {
9396 const ComObjPtr<MediumAttachment> &pAtt = *it;
9397 // should never happen, but deal with NULL pointers in the list.
9398 AssertStmt(!pAtt.isNull(), continue);
9399
9400 // getControllerName() needs caller+read lock
9401 AutoCaller autoAttCaller(pAtt);
9402 if (FAILED(autoAttCaller.rc()))
9403 {
9404 atts.clear();
9405 return autoAttCaller.rc();
9406 }
9407 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9408
9409 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9410 atts.push_back(pAtt);
9411 }
9412
9413 return S_OK;
9414}
9415
9416
9417/**
9418 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9419 * file if the machine name was changed and about creating a new settings file
9420 * if this is a new machine.
9421 *
9422 * @note Must be never called directly but only from #saveSettings().
9423 */
9424HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9425{
9426 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9427
9428 HRESULT rc = S_OK;
9429
9430 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9431
9432 /// @todo need to handle primary group change, too
9433
9434 /* attempt to rename the settings file if machine name is changed */
9435 if ( mUserData->s.fNameSync
9436 && mUserData.isBackedUp()
9437 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9438 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9439 )
9440 {
9441 bool dirRenamed = false;
9442 bool fileRenamed = false;
9443
9444 Utf8Str configFile, newConfigFile;
9445 Utf8Str configFilePrev, newConfigFilePrev;
9446 Utf8Str configDir, newConfigDir;
9447
9448 do
9449 {
9450 int vrc = VINF_SUCCESS;
9451
9452 Utf8Str name = mUserData.backedUpData()->s.strName;
9453 Utf8Str newName = mUserData->s.strName;
9454 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9455 if (group == "/")
9456 group.setNull();
9457 Utf8Str newGroup = mUserData->s.llGroups.front();
9458 if (newGroup == "/")
9459 newGroup.setNull();
9460
9461 configFile = mData->m_strConfigFileFull;
9462
9463 /* first, rename the directory if it matches the group and machine name */
9464 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9465 group.c_str(), RTPATH_DELIMITER, name.c_str());
9466 /** @todo hack, make somehow use of ComposeMachineFilename */
9467 if (mUserData->s.fDirectoryIncludesUUID)
9468 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9469 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9470 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9471 /** @todo hack, make somehow use of ComposeMachineFilename */
9472 if (mUserData->s.fDirectoryIncludesUUID)
9473 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9474 configDir = configFile;
9475 configDir.stripFilename();
9476 newConfigDir = configDir;
9477 if ( configDir.length() >= groupPlusName.length()
9478 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9479 groupPlusName.c_str()))
9480 {
9481 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9482 Utf8Str newConfigBaseDir(newConfigDir);
9483 newConfigDir.append(newGroupPlusName);
9484 /* consistency: use \ if appropriate on the platform */
9485 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9486 /* new dir and old dir cannot be equal here because of 'if'
9487 * above and because name != newName */
9488 Assert(configDir != newConfigDir);
9489 if (!fSettingsFileIsNew)
9490 {
9491 /* perform real rename only if the machine is not new */
9492 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9493 if ( vrc == VERR_FILE_NOT_FOUND
9494 || vrc == VERR_PATH_NOT_FOUND)
9495 {
9496 /* create the parent directory, then retry renaming */
9497 Utf8Str parent(newConfigDir);
9498 parent.stripFilename();
9499 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9500 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9501 }
9502 if (RT_FAILURE(vrc))
9503 {
9504 rc = setError(E_FAIL,
9505 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9506 configDir.c_str(),
9507 newConfigDir.c_str(),
9508 vrc);
9509 break;
9510 }
9511 /* delete subdirectories which are no longer needed */
9512 Utf8Str dir(configDir);
9513 dir.stripFilename();
9514 while (dir != newConfigBaseDir && dir != ".")
9515 {
9516 vrc = RTDirRemove(dir.c_str());
9517 if (RT_FAILURE(vrc))
9518 break;
9519 dir.stripFilename();
9520 }
9521 dirRenamed = true;
9522 }
9523 }
9524
9525 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9526 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9527
9528 /* then try to rename the settings file itself */
9529 if (newConfigFile != configFile)
9530 {
9531 /* get the path to old settings file in renamed directory */
9532 configFile = Utf8StrFmt("%s%c%s",
9533 newConfigDir.c_str(),
9534 RTPATH_DELIMITER,
9535 RTPathFilename(configFile.c_str()));
9536 if (!fSettingsFileIsNew)
9537 {
9538 /* perform real rename only if the machine is not new */
9539 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9540 if (RT_FAILURE(vrc))
9541 {
9542 rc = setError(E_FAIL,
9543 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9544 configFile.c_str(),
9545 newConfigFile.c_str(),
9546 vrc);
9547 break;
9548 }
9549 fileRenamed = true;
9550 configFilePrev = configFile;
9551 configFilePrev += "-prev";
9552 newConfigFilePrev = newConfigFile;
9553 newConfigFilePrev += "-prev";
9554 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9555 }
9556 }
9557
9558 // update m_strConfigFileFull amd mConfigFile
9559 mData->m_strConfigFileFull = newConfigFile;
9560 // compute the relative path too
9561 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9562
9563 // store the old and new so that VirtualBox::i_saveSettings() can update
9564 // the media registry
9565 if ( mData->mRegistered
9566 && (configDir != newConfigDir || configFile != newConfigFile))
9567 {
9568 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9569
9570 if (pfNeedsGlobalSaveSettings)
9571 *pfNeedsGlobalSaveSettings = true;
9572 }
9573
9574 // in the saved state file path, replace the old directory with the new directory
9575 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9576 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9577
9578 // and do the same thing for the saved state file paths of all the online snapshots
9579 if (mData->mFirstSnapshot)
9580 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9581 newConfigDir.c_str());
9582 }
9583 while (0);
9584
9585 if (FAILED(rc))
9586 {
9587 /* silently try to rename everything back */
9588 if (fileRenamed)
9589 {
9590 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9591 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9592 }
9593 if (dirRenamed)
9594 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9595 }
9596
9597 if (FAILED(rc)) return rc;
9598 }
9599
9600 if (fSettingsFileIsNew)
9601 {
9602 /* create a virgin config file */
9603 int vrc = VINF_SUCCESS;
9604
9605 /* ensure the settings directory exists */
9606 Utf8Str path(mData->m_strConfigFileFull);
9607 path.stripFilename();
9608 if (!RTDirExists(path.c_str()))
9609 {
9610 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9611 if (RT_FAILURE(vrc))
9612 {
9613 return setError(E_FAIL,
9614 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9615 path.c_str(),
9616 vrc);
9617 }
9618 }
9619
9620 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9621 path = Utf8Str(mData->m_strConfigFileFull);
9622 RTFILE f = NIL_RTFILE;
9623 vrc = RTFileOpen(&f, path.c_str(),
9624 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9625 if (RT_FAILURE(vrc))
9626 return setError(E_FAIL,
9627 tr("Could not create the settings file '%s' (%Rrc)"),
9628 path.c_str(),
9629 vrc);
9630 RTFileClose(f);
9631 }
9632
9633 return rc;
9634}
9635
9636/**
9637 * Saves and commits machine data, user data and hardware data.
9638 *
9639 * Note that on failure, the data remains uncommitted.
9640 *
9641 * @a aFlags may combine the following flags:
9642 *
9643 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9644 * Used when saving settings after an operation that makes them 100%
9645 * correspond to the settings from the current snapshot.
9646 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9647 * #isReallyModified() returns false. This is necessary for cases when we
9648 * change machine data directly, not through the backup()/commit() mechanism.
9649 * - SaveS_Force: settings will be saved without doing a deep compare of the
9650 * settings structures. This is used when this is called because snapshots
9651 * have changed to avoid the overhead of the deep compare.
9652 *
9653 * @note Must be called from under this object's write lock. Locks children for
9654 * writing.
9655 *
9656 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9657 * initialized to false and that will be set to true by this function if
9658 * the caller must invoke VirtualBox::i_saveSettings() because the global
9659 * settings have changed. This will happen if a machine rename has been
9660 * saved and the global machine and media registries will therefore need
9661 * updating.
9662 */
9663HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9664 int aFlags /*= 0*/)
9665{
9666 LogFlowThisFuncEnter();
9667
9668 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9669
9670 /* make sure child objects are unable to modify the settings while we are
9671 * saving them */
9672 i_ensureNoStateDependencies();
9673
9674 AssertReturn(!i_isSnapshotMachine(),
9675 E_FAIL);
9676
9677 HRESULT rc = S_OK;
9678 bool fNeedsWrite = false;
9679
9680 /* First, prepare to save settings. It will care about renaming the
9681 * settings directory and file if the machine name was changed and about
9682 * creating a new settings file if this is a new machine. */
9683 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9684 if (FAILED(rc)) return rc;
9685
9686 // keep a pointer to the current settings structures
9687 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9688 settings::MachineConfigFile *pNewConfig = NULL;
9689
9690 try
9691 {
9692 // make a fresh one to have everyone write stuff into
9693 pNewConfig = new settings::MachineConfigFile(NULL);
9694 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9695
9696 // now go and copy all the settings data from COM to the settings structures
9697 // (this calles i_saveSettings() on all the COM objects in the machine)
9698 i_copyMachineDataToSettings(*pNewConfig);
9699
9700 if (aFlags & SaveS_ResetCurStateModified)
9701 {
9702 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9703 mData->mCurrentStateModified = FALSE;
9704 fNeedsWrite = true; // always, no need to compare
9705 }
9706 else if (aFlags & SaveS_Force)
9707 {
9708 fNeedsWrite = true; // always, no need to compare
9709 }
9710 else
9711 {
9712 if (!mData->mCurrentStateModified)
9713 {
9714 // do a deep compare of the settings that we just saved with the settings
9715 // previously stored in the config file; this invokes MachineConfigFile::operator==
9716 // which does a deep compare of all the settings, which is expensive but less expensive
9717 // than writing out XML in vain
9718 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9719
9720 // could still be modified if any settings changed
9721 mData->mCurrentStateModified = fAnySettingsChanged;
9722
9723 fNeedsWrite = fAnySettingsChanged;
9724 }
9725 else
9726 fNeedsWrite = true;
9727 }
9728
9729 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9730
9731 if (fNeedsWrite)
9732 // now spit it all out!
9733 pNewConfig->write(mData->m_strConfigFileFull);
9734
9735 mData->pMachineConfigFile = pNewConfig;
9736 delete pOldConfig;
9737 i_commit();
9738
9739 // after saving settings, we are no longer different from the XML on disk
9740 mData->flModifications = 0;
9741 }
9742 catch (HRESULT err)
9743 {
9744 // we assume that error info is set by the thrower
9745 rc = err;
9746
9747 // restore old config
9748 delete pNewConfig;
9749 mData->pMachineConfigFile = pOldConfig;
9750 }
9751 catch (...)
9752 {
9753 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9754 }
9755
9756 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9757 {
9758 /* Fire the data change event, even on failure (since we've already
9759 * committed all data). This is done only for SessionMachines because
9760 * mutable Machine instances are always not registered (i.e. private
9761 * to the client process that creates them) and thus don't need to
9762 * inform callbacks. */
9763 if (i_isSessionMachine())
9764 mParent->i_onMachineDataChange(mData->mUuid);
9765 }
9766
9767 LogFlowThisFunc(("rc=%08X\n", rc));
9768 LogFlowThisFuncLeave();
9769 return rc;
9770}
9771
9772/**
9773 * Implementation for saving the machine settings into the given
9774 * settings::MachineConfigFile instance. This copies machine extradata
9775 * from the previous machine config file in the instance data, if any.
9776 *
9777 * This gets called from two locations:
9778 *
9779 * -- Machine::i_saveSettings(), during the regular XML writing;
9780 *
9781 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9782 * exported to OVF and we write the VirtualBox proprietary XML
9783 * into a <vbox:Machine> tag.
9784 *
9785 * This routine fills all the fields in there, including snapshots, *except*
9786 * for the following:
9787 *
9788 * -- fCurrentStateModified. There is some special logic associated with that.
9789 *
9790 * The caller can then call MachineConfigFile::write() or do something else
9791 * with it.
9792 *
9793 * Caller must hold the machine lock!
9794 *
9795 * This throws XML errors and HRESULT, so the caller must have a catch block!
9796 */
9797void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9798{
9799 // deep copy extradata
9800 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9801
9802 config.uuid = mData->mUuid;
9803
9804 // copy name, description, OS type, teleport, UTC etc.
9805 config.machineUserData = mUserData->s;
9806
9807 // Encode the Icon Override data from Machine and store on config userdata.
9808 std::vector<BYTE> iconByte;
9809 getIcon(iconByte);
9810 ssize_t cbData = iconByte.size();
9811 if (cbData > 0)
9812 {
9813 ssize_t cchOut = RTBase64EncodedLength(cbData);
9814 Utf8Str strIconData;
9815 strIconData.reserve(cchOut+1);
9816 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9817 strIconData.mutableRaw(), strIconData.capacity(),
9818 NULL);
9819 if (RT_FAILURE(vrc))
9820 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9821 strIconData.jolt();
9822 config.machineUserData.ovIcon = strIconData;
9823 }
9824 else
9825 config.machineUserData.ovIcon.setNull();
9826
9827 if ( mData->mMachineState == MachineState_Saved
9828 || mData->mMachineState == MachineState_Restoring
9829 // when deleting a snapshot we may or may not have a saved state in the current state,
9830 // so let's not assert here please
9831 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9832 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9833 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9834 && (!mSSData->strStateFilePath.isEmpty())
9835 )
9836 )
9837 {
9838 Assert(!mSSData->strStateFilePath.isEmpty());
9839 /* try to make the file name relative to the settings file dir */
9840 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9841 }
9842 else
9843 {
9844 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9845 config.strStateFile.setNull();
9846 }
9847
9848 if (mData->mCurrentSnapshot)
9849 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9850 else
9851 config.uuidCurrentSnapshot.clear();
9852
9853 config.timeLastStateChange = mData->mLastStateChange;
9854 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9855 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9856
9857 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9858 if (FAILED(rc)) throw rc;
9859
9860 rc = i_saveStorageControllers(config.storageMachine);
9861 if (FAILED(rc)) throw rc;
9862
9863 // save machine's media registry if this is VirtualBox 4.0 or later
9864 if (config.canHaveOwnMediaRegistry())
9865 {
9866 // determine machine folder
9867 Utf8Str strMachineFolder = i_getSettingsFileFull();
9868 strMachineFolder.stripFilename();
9869 mParent->i_saveMediaRegistry(config.mediaRegistry,
9870 i_getId(), // only media with registry ID == machine UUID
9871 strMachineFolder);
9872 // this throws HRESULT
9873 }
9874
9875 // save snapshots
9876 rc = i_saveAllSnapshots(config);
9877 if (FAILED(rc)) throw rc;
9878}
9879
9880/**
9881 * Saves all snapshots of the machine into the given machine config file. Called
9882 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9883 * @param config
9884 * @return
9885 */
9886HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9887{
9888 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9889
9890 HRESULT rc = S_OK;
9891
9892 try
9893 {
9894 config.llFirstSnapshot.clear();
9895
9896 if (mData->mFirstSnapshot)
9897 {
9898 settings::Snapshot snapNew;
9899 config.llFirstSnapshot.push_back(snapNew);
9900
9901 // get reference to the fresh copy of the snapshot on the list and
9902 // work on that copy directly to avoid excessive copying later
9903 settings::Snapshot &snap = config.llFirstSnapshot.front();
9904
9905 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9906 if (FAILED(rc)) throw rc;
9907 }
9908
9909// if (mType == IsSessionMachine)
9910// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9911
9912 }
9913 catch (HRESULT err)
9914 {
9915 /* we assume that error info is set by the thrower */
9916 rc = err;
9917 }
9918 catch (...)
9919 {
9920 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9921 }
9922
9923 return rc;
9924}
9925
9926/**
9927 * Saves the VM hardware configuration. It is assumed that the
9928 * given node is empty.
9929 *
9930 * @param data Reference to the settings object for the hardware config.
9931 * @param pDbg Pointer to the settings object for the debugging config
9932 * which happens to live in mHWData.
9933 * @param pAutostart Pointer to the settings object for the autostart config
9934 * which happens to live in mHWData.
9935 */
9936HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9937 settings::Autostart *pAutostart)
9938{
9939 HRESULT rc = S_OK;
9940
9941 try
9942 {
9943 /* The hardware version attribute (optional).
9944 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9945 if ( mHWData->mHWVersion == "1"
9946 && mSSData->strStateFilePath.isEmpty()
9947 )
9948 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9949 other point needs to be found where this can be done. */
9950
9951 data.strVersion = mHWData->mHWVersion;
9952 data.uuid = mHWData->mHardwareUUID;
9953
9954 // CPU
9955 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9956 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9957 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9958 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9959 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9960 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9961 data.fPAE = !!mHWData->mPAEEnabled;
9962 data.enmLongMode = mHWData->mLongMode;
9963 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9964 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9965
9966 /* Standard and Extended CPUID leafs. */
9967 data.llCpuIdLeafs.clear();
9968 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9969 {
9970 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9971 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9972 }
9973 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9974 {
9975 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9976 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9977 }
9978
9979 data.cCPUs = mHWData->mCPUCount;
9980 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9981 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9982
9983 data.llCpus.clear();
9984 if (data.fCpuHotPlug)
9985 {
9986 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9987 {
9988 if (mHWData->mCPUAttached[idx])
9989 {
9990 settings::Cpu cpu;
9991 cpu.ulId = idx;
9992 data.llCpus.push_back(cpu);
9993 }
9994 }
9995 }
9996
9997 // memory
9998 data.ulMemorySizeMB = mHWData->mMemorySize;
9999 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10000
10001 // firmware
10002 data.firmwareType = mHWData->mFirmwareType;
10003
10004 // HID
10005 data.pointingHIDType = mHWData->mPointingHIDType;
10006 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10007
10008 // chipset
10009 data.chipsetType = mHWData->mChipsetType;
10010
10011 // paravirt
10012 data.paravirtProvider = mHWData->mParavirtProvider;
10013
10014
10015 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10016
10017 // HPET
10018 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10019
10020 // boot order
10021 data.mapBootOrder.clear();
10022 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10023 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10024
10025 // display
10026 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10027 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10028 data.cMonitors = mHWData->mMonitorCount;
10029 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10030 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10031 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10032 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10033 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10034 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10035 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10036 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10037 {
10038 if (mHWData->maVideoCaptureScreens[i])
10039 ASMBitSet(&data.u64VideoCaptureScreens, i);
10040 else
10041 ASMBitClear(&data.u64VideoCaptureScreens, i);
10042 }
10043 /* store relative video capture file if possible */
10044 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10045
10046 /* VRDEServer settings (optional) */
10047 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10048 if (FAILED(rc)) throw rc;
10049
10050 /* BIOS (required) */
10051 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10052 if (FAILED(rc)) throw rc;
10053
10054 /* USB Controller (required) */
10055 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10056 {
10057 ComObjPtr<USBController> ctrl = *it;
10058 settings::USBController settingsCtrl;
10059
10060 settingsCtrl.strName = ctrl->i_getName();
10061 settingsCtrl.enmType = ctrl->i_getControllerType();
10062
10063 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10064 }
10065
10066 /* USB device filters (required) */
10067 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10068 if (FAILED(rc)) throw rc;
10069
10070 /* Network adapters (required) */
10071 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10072 data.llNetworkAdapters.clear();
10073 /* Write out only the nominal number of network adapters for this
10074 * chipset type. Since Machine::commit() hasn't been called there
10075 * may be extra NIC settings in the vector. */
10076 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10077 {
10078 settings::NetworkAdapter nic;
10079 nic.ulSlot = (uint32_t)slot;
10080 /* paranoia check... must not be NULL, but must not crash either. */
10081 if (mNetworkAdapters[slot])
10082 {
10083 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10084 if (FAILED(rc)) throw rc;
10085
10086 data.llNetworkAdapters.push_back(nic);
10087 }
10088 }
10089
10090 /* Serial ports */
10091 data.llSerialPorts.clear();
10092 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10093 {
10094 settings::SerialPort s;
10095 s.ulSlot = slot;
10096 rc = mSerialPorts[slot]->i_saveSettings(s);
10097 if (FAILED(rc)) return rc;
10098
10099 data.llSerialPorts.push_back(s);
10100 }
10101
10102 /* Parallel ports */
10103 data.llParallelPorts.clear();
10104 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10105 {
10106 settings::ParallelPort p;
10107 p.ulSlot = slot;
10108 rc = mParallelPorts[slot]->i_saveSettings(p);
10109 if (FAILED(rc)) return rc;
10110
10111 data.llParallelPorts.push_back(p);
10112 }
10113
10114 /* Audio adapter */
10115 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10116 if (FAILED(rc)) return rc;
10117
10118 /* Shared folders */
10119 data.llSharedFolders.clear();
10120 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10121 it != mHWData->mSharedFolders.end();
10122 ++it)
10123 {
10124 SharedFolder *pSF = *it;
10125 AutoCaller sfCaller(pSF);
10126 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10127 settings::SharedFolder sf;
10128 sf.strName = pSF->i_getName();
10129 sf.strHostPath = pSF->i_getHostPath();
10130 sf.fWritable = !!pSF->i_isWritable();
10131 sf.fAutoMount = !!pSF->i_isAutoMounted();
10132
10133 data.llSharedFolders.push_back(sf);
10134 }
10135
10136 // clipboard
10137 data.clipboardMode = mHWData->mClipboardMode;
10138
10139 // drag'n'drop
10140 data.dndMode = mHWData->mDnDMode;
10141
10142 /* Guest */
10143 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10144
10145 // IO settings
10146 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10147 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10148
10149 /* BandwidthControl (required) */
10150 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10151 if (FAILED(rc)) throw rc;
10152
10153 /* Host PCI devices */
10154 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10155 it != mHWData->mPCIDeviceAssignments.end();
10156 ++it)
10157 {
10158 ComObjPtr<PCIDeviceAttachment> pda = *it;
10159 settings::HostPCIDeviceAttachment hpda;
10160
10161 rc = pda->i_saveSettings(hpda);
10162 if (FAILED(rc)) throw rc;
10163
10164 data.pciAttachments.push_back(hpda);
10165 }
10166
10167
10168 // guest properties
10169 data.llGuestProperties.clear();
10170#ifdef VBOX_WITH_GUEST_PROPS
10171 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10172 it != mHWData->mGuestProperties.end();
10173 ++it)
10174 {
10175 HWData::GuestProperty property = it->second;
10176
10177 /* Remove transient guest properties at shutdown unless we
10178 * are saving state */
10179 if ( ( mData->mMachineState == MachineState_PoweredOff
10180 || mData->mMachineState == MachineState_Aborted
10181 || mData->mMachineState == MachineState_Teleported)
10182 && ( property.mFlags & guestProp::TRANSIENT
10183 || property.mFlags & guestProp::TRANSRESET))
10184 continue;
10185 settings::GuestProperty prop;
10186 prop.strName = it->first;
10187 prop.strValue = property.strValue;
10188 prop.timestamp = property.mTimestamp;
10189 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10190 guestProp::writeFlags(property.mFlags, szFlags);
10191 prop.strFlags = szFlags;
10192
10193 data.llGuestProperties.push_back(prop);
10194 }
10195
10196 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10197 /* I presume this doesn't require a backup(). */
10198 mData->mGuestPropertiesModified = FALSE;
10199#endif /* VBOX_WITH_GUEST_PROPS defined */
10200
10201 *pDbg = mHWData->mDebugging;
10202 *pAutostart = mHWData->mAutostart;
10203
10204 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10205 }
10206 catch(std::bad_alloc &)
10207 {
10208 return E_OUTOFMEMORY;
10209 }
10210
10211 AssertComRC(rc);
10212 return rc;
10213}
10214
10215/**
10216 * Saves the storage controller configuration.
10217 *
10218 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10219 */
10220HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10221{
10222 data.llStorageControllers.clear();
10223
10224 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10225 it != mStorageControllers->end();
10226 ++it)
10227 {
10228 HRESULT rc;
10229 ComObjPtr<StorageController> pCtl = *it;
10230
10231 settings::StorageController ctl;
10232 ctl.strName = pCtl->i_getName();
10233 ctl.controllerType = pCtl->i_getControllerType();
10234 ctl.storageBus = pCtl->i_getStorageBus();
10235 ctl.ulInstance = pCtl->i_getInstance();
10236 ctl.fBootable = pCtl->i_getBootable();
10237
10238 /* Save the port count. */
10239 ULONG portCount;
10240 rc = pCtl->COMGETTER(PortCount)(&portCount);
10241 ComAssertComRCRet(rc, rc);
10242 ctl.ulPortCount = portCount;
10243
10244 /* Save fUseHostIOCache */
10245 BOOL fUseHostIOCache;
10246 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10247 ComAssertComRCRet(rc, rc);
10248 ctl.fUseHostIOCache = !!fUseHostIOCache;
10249
10250 /* Save IDE emulation settings. */
10251 if (ctl.controllerType == StorageControllerType_IntelAhci)
10252 {
10253 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10254 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10255 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10256 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10257 )
10258 ComAssertComRCRet(rc, rc);
10259 }
10260
10261 /* save the devices now. */
10262 rc = i_saveStorageDevices(pCtl, ctl);
10263 ComAssertComRCRet(rc, rc);
10264
10265 data.llStorageControllers.push_back(ctl);
10266 }
10267
10268 return S_OK;
10269}
10270
10271/**
10272 * Saves the hard disk configuration.
10273 */
10274HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10275 settings::StorageController &data)
10276{
10277 MediaData::AttachmentList atts;
10278
10279 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10280 if (FAILED(rc)) return rc;
10281
10282 data.llAttachedDevices.clear();
10283 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10284 it != atts.end();
10285 ++it)
10286 {
10287 settings::AttachedDevice dev;
10288 IMediumAttachment *iA = *it;
10289 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10290 Medium *pMedium = pAttach->i_getMedium();
10291
10292 dev.deviceType = pAttach->i_getType();
10293 dev.lPort = pAttach->i_getPort();
10294 dev.lDevice = pAttach->i_getDevice();
10295 dev.fPassThrough = pAttach->i_getPassthrough();
10296 dev.fHotPluggable = pAttach->i_getHotPluggable();
10297 if (pMedium)
10298 {
10299 if (pMedium->i_isHostDrive())
10300 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10301 else
10302 dev.uuid = pMedium->i_getId();
10303 dev.fTempEject = pAttach->i_getTempEject();
10304 dev.fNonRotational = pAttach->i_getNonRotational();
10305 dev.fDiscard = pAttach->i_getDiscard();
10306 }
10307
10308 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10309
10310 data.llAttachedDevices.push_back(dev);
10311 }
10312
10313 return S_OK;
10314}
10315
10316/**
10317 * Saves machine state settings as defined by aFlags
10318 * (SaveSTS_* values).
10319 *
10320 * @param aFlags Combination of SaveSTS_* flags.
10321 *
10322 * @note Locks objects for writing.
10323 */
10324HRESULT Machine::i_saveStateSettings(int aFlags)
10325{
10326 if (aFlags == 0)
10327 return S_OK;
10328
10329 AutoCaller autoCaller(this);
10330 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10331
10332 /* This object's write lock is also necessary to serialize file access
10333 * (prevent concurrent reads and writes) */
10334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10335
10336 HRESULT rc = S_OK;
10337
10338 Assert(mData->pMachineConfigFile);
10339
10340 try
10341 {
10342 if (aFlags & SaveSTS_CurStateModified)
10343 mData->pMachineConfigFile->fCurrentStateModified = true;
10344
10345 if (aFlags & SaveSTS_StateFilePath)
10346 {
10347 if (!mSSData->strStateFilePath.isEmpty())
10348 /* try to make the file name relative to the settings file dir */
10349 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10350 else
10351 mData->pMachineConfigFile->strStateFile.setNull();
10352 }
10353
10354 if (aFlags & SaveSTS_StateTimeStamp)
10355 {
10356 Assert( mData->mMachineState != MachineState_Aborted
10357 || mSSData->strStateFilePath.isEmpty());
10358
10359 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10360
10361 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10362//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10363 }
10364
10365 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10366 }
10367 catch (...)
10368 {
10369 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10370 }
10371
10372 return rc;
10373}
10374
10375/**
10376 * Ensures that the given medium is added to a media registry. If this machine
10377 * was created with 4.0 or later, then the machine registry is used. Otherwise
10378 * the global VirtualBox media registry is used.
10379 *
10380 * Caller must NOT hold machine lock, media tree or any medium locks!
10381 *
10382 * @param pMedium
10383 */
10384void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10385{
10386 /* Paranoia checks: do not hold machine or media tree locks. */
10387 AssertReturnVoid(!isWriteLockOnCurrentThread());
10388 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10389
10390 ComObjPtr<Medium> pBase;
10391 {
10392 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10393 pBase = pMedium->i_getBase();
10394 }
10395
10396 /* Paranoia checks: do not hold medium locks. */
10397 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10398 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10399
10400 // decide which medium registry to use now that the medium is attached:
10401 Guid uuid;
10402 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10403 // machine XML is VirtualBox 4.0 or higher:
10404 uuid = i_getId(); // machine UUID
10405 else
10406 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10407
10408 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10409 mParent->i_markRegistryModified(uuid);
10410
10411 /* For more complex hard disk structures it can happen that the base
10412 * medium isn't yet associated with any medium registry. Do that now. */
10413 if (pMedium != pBase)
10414 {
10415 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10416 mParent->i_markRegistryModified(uuid);
10417 }
10418}
10419
10420/**
10421 * Creates differencing hard disks for all normal hard disks attached to this
10422 * machine and a new set of attachments to refer to created disks.
10423 *
10424 * Used when taking a snapshot or when deleting the current state. Gets called
10425 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10426 *
10427 * This method assumes that mMediaData contains the original hard disk attachments
10428 * it needs to create diffs for. On success, these attachments will be replaced
10429 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10430 * called to delete created diffs which will also rollback mMediaData and restore
10431 * whatever was backed up before calling this method.
10432 *
10433 * Attachments with non-normal hard disks are left as is.
10434 *
10435 * If @a aOnline is @c false then the original hard disks that require implicit
10436 * diffs will be locked for reading. Otherwise it is assumed that they are
10437 * already locked for writing (when the VM was started). Note that in the latter
10438 * case it is responsibility of the caller to lock the newly created diffs for
10439 * writing if this method succeeds.
10440 *
10441 * @param aProgress Progress object to run (must contain at least as
10442 * many operations left as the number of hard disks
10443 * attached).
10444 * @param aOnline Whether the VM was online prior to this operation.
10445 *
10446 * @note The progress object is not marked as completed, neither on success nor
10447 * on failure. This is a responsibility of the caller.
10448 *
10449 * @note Locks this object and the media tree for writing.
10450 */
10451HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10452 ULONG aWeight,
10453 bool aOnline)
10454{
10455 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10456
10457 AutoCaller autoCaller(this);
10458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10459
10460 AutoMultiWriteLock2 alock(this->lockHandle(),
10461 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10462
10463 /* must be in a protective state because we release the lock below */
10464 AssertReturn( mData->mMachineState == MachineState_Saving
10465 || mData->mMachineState == MachineState_LiveSnapshotting
10466 || mData->mMachineState == MachineState_RestoringSnapshot
10467 || mData->mMachineState == MachineState_DeletingSnapshot
10468 , E_FAIL);
10469
10470 HRESULT rc = S_OK;
10471
10472 // use appropriate locked media map (online or offline)
10473 MediumLockListMap lockedMediaOffline;
10474 MediumLockListMap *lockedMediaMap;
10475 if (aOnline)
10476 lockedMediaMap = &mData->mSession.mLockedMedia;
10477 else
10478 lockedMediaMap = &lockedMediaOffline;
10479
10480 try
10481 {
10482 if (!aOnline)
10483 {
10484 /* lock all attached hard disks early to detect "in use"
10485 * situations before creating actual diffs */
10486 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10487 it != mMediaData->mAttachments.end();
10488 ++it)
10489 {
10490 MediumAttachment* pAtt = *it;
10491 if (pAtt->i_getType() == DeviceType_HardDisk)
10492 {
10493 Medium* pMedium = pAtt->i_getMedium();
10494 Assert(pMedium);
10495
10496 MediumLockList *pMediumLockList(new MediumLockList());
10497 alock.release();
10498 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10499 false /* fMediumLockWrite */,
10500 NULL,
10501 *pMediumLockList);
10502 alock.acquire();
10503 if (FAILED(rc))
10504 {
10505 delete pMediumLockList;
10506 throw rc;
10507 }
10508 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10509 if (FAILED(rc))
10510 {
10511 throw setError(rc,
10512 tr("Collecting locking information for all attached media failed"));
10513 }
10514 }
10515 }
10516
10517 /* Now lock all media. If this fails, nothing is locked. */
10518 alock.release();
10519 rc = lockedMediaMap->Lock();
10520 alock.acquire();
10521 if (FAILED(rc))
10522 {
10523 throw setError(rc,
10524 tr("Locking of attached media failed"));
10525 }
10526 }
10527
10528 /* remember the current list (note that we don't use backup() since
10529 * mMediaData may be already backed up) */
10530 MediaData::AttachmentList atts = mMediaData->mAttachments;
10531
10532 /* start from scratch */
10533 mMediaData->mAttachments.clear();
10534
10535 /* go through remembered attachments and create diffs for normal hard
10536 * disks and attach them */
10537 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10538 it != atts.end();
10539 ++it)
10540 {
10541 MediumAttachment* pAtt = *it;
10542
10543 DeviceType_T devType = pAtt->i_getType();
10544 Medium* pMedium = pAtt->i_getMedium();
10545
10546 if ( devType != DeviceType_HardDisk
10547 || pMedium == NULL
10548 || pMedium->i_getType() != MediumType_Normal)
10549 {
10550 /* copy the attachment as is */
10551
10552 /** @todo the progress object created in Console::TakeSnaphot
10553 * only expects operations for hard disks. Later other
10554 * device types need to show up in the progress as well. */
10555 if (devType == DeviceType_HardDisk)
10556 {
10557 if (pMedium == NULL)
10558 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10559 aWeight); // weight
10560 else
10561 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10562 pMedium->i_getBase()->i_getName().c_str()).raw(),
10563 aWeight); // weight
10564 }
10565
10566 mMediaData->mAttachments.push_back(pAtt);
10567 continue;
10568 }
10569
10570 /* need a diff */
10571 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10572 pMedium->i_getBase()->i_getName().c_str()).raw(),
10573 aWeight); // weight
10574
10575 Utf8Str strFullSnapshotFolder;
10576 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10577
10578 ComObjPtr<Medium> diff;
10579 diff.createObject();
10580 // store the diff in the same registry as the parent
10581 // (this cannot fail here because we can't create implicit diffs for
10582 // unregistered images)
10583 Guid uuidRegistryParent;
10584 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10585 Assert(fInRegistry); NOREF(fInRegistry);
10586 rc = diff->init(mParent,
10587 pMedium->i_getPreferredDiffFormat(),
10588 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10589 uuidRegistryParent,
10590 DeviceType_HardDisk);
10591 if (FAILED(rc)) throw rc;
10592
10593 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10594 * the push_back? Looks like we're going to release medium with the
10595 * wrong kind of lock (general issue with if we fail anywhere at all)
10596 * and an orphaned VDI in the snapshots folder. */
10597
10598 /* update the appropriate lock list */
10599 MediumLockList *pMediumLockList;
10600 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10601 AssertComRCThrowRC(rc);
10602 if (aOnline)
10603 {
10604 alock.release();
10605 /* The currently attached medium will be read-only, change
10606 * the lock type to read. */
10607 rc = pMediumLockList->Update(pMedium, false);
10608 alock.acquire();
10609 AssertComRCThrowRC(rc);
10610 }
10611
10612 /* release the locks before the potentially lengthy operation */
10613 alock.release();
10614 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10615 pMediumLockList,
10616 NULL /* aProgress */,
10617 true /* aWait */);
10618 alock.acquire();
10619 if (FAILED(rc)) throw rc;
10620
10621 /* actual lock list update is done in Medium::commitMedia */
10622
10623 rc = diff->i_addBackReference(mData->mUuid);
10624 AssertComRCThrowRC(rc);
10625
10626 /* add a new attachment */
10627 ComObjPtr<MediumAttachment> attachment;
10628 attachment.createObject();
10629 rc = attachment->init(this,
10630 diff,
10631 pAtt->i_getControllerName(),
10632 pAtt->i_getPort(),
10633 pAtt->i_getDevice(),
10634 DeviceType_HardDisk,
10635 true /* aImplicit */,
10636 false /* aPassthrough */,
10637 false /* aTempEject */,
10638 pAtt->i_getNonRotational(),
10639 pAtt->i_getDiscard(),
10640 pAtt->i_getHotPluggable(),
10641 pAtt->i_getBandwidthGroup());
10642 if (FAILED(rc)) throw rc;
10643
10644 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10645 AssertComRCThrowRC(rc);
10646 mMediaData->mAttachments.push_back(attachment);
10647 }
10648 }
10649 catch (HRESULT aRC) { rc = aRC; }
10650
10651 /* unlock all hard disks we locked when there is no VM */
10652 if (!aOnline)
10653 {
10654 ErrorInfoKeeper eik;
10655
10656 HRESULT rc1 = lockedMediaMap->Clear();
10657 AssertComRC(rc1);
10658 }
10659
10660 return rc;
10661}
10662
10663/**
10664 * Deletes implicit differencing hard disks created either by
10665 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10666 *
10667 * Note that to delete hard disks created by #AttachDevice() this method is
10668 * called from #fixupMedia() when the changes are rolled back.
10669 *
10670 * @note Locks this object and the media tree for writing.
10671 */
10672HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10673{
10674 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10675
10676 AutoCaller autoCaller(this);
10677 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10678
10679 AutoMultiWriteLock2 alock(this->lockHandle(),
10680 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10681
10682 /* We absolutely must have backed up state. */
10683 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10684
10685 /* Check if there are any implicitly created diff images. */
10686 bool fImplicitDiffs = false;
10687 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10688 it != mMediaData->mAttachments.end();
10689 ++it)
10690 {
10691 const ComObjPtr<MediumAttachment> &pAtt = *it;
10692 if (pAtt->i_isImplicit())
10693 {
10694 fImplicitDiffs = true;
10695 break;
10696 }
10697 }
10698 /* If there is nothing to do, leave early. This saves lots of image locking
10699 * effort. It also avoids a MachineStateChanged event without real reason.
10700 * This is important e.g. when loading a VM config, because there should be
10701 * no events. Otherwise API clients can become thoroughly confused for
10702 * inaccessible VMs (the code for loading VM configs uses this method for
10703 * cleanup if the config makes no sense), as they take such events as an
10704 * indication that the VM is alive, and they would force the VM config to
10705 * be reread, leading to an endless loop. */
10706 if (!fImplicitDiffs)
10707 return S_OK;
10708
10709 HRESULT rc = S_OK;
10710 MachineState_T oldState = mData->mMachineState;
10711
10712 /* will release the lock before the potentially lengthy operation,
10713 * so protect with the special state (unless already protected) */
10714 if ( oldState != MachineState_Saving
10715 && oldState != MachineState_LiveSnapshotting
10716 && oldState != MachineState_RestoringSnapshot
10717 && oldState != MachineState_DeletingSnapshot
10718 && oldState != MachineState_DeletingSnapshotOnline
10719 && oldState != MachineState_DeletingSnapshotPaused
10720 )
10721 i_setMachineState(MachineState_SettingUp);
10722
10723 // use appropriate locked media map (online or offline)
10724 MediumLockListMap lockedMediaOffline;
10725 MediumLockListMap *lockedMediaMap;
10726 if (aOnline)
10727 lockedMediaMap = &mData->mSession.mLockedMedia;
10728 else
10729 lockedMediaMap = &lockedMediaOffline;
10730
10731 try
10732 {
10733 if (!aOnline)
10734 {
10735 /* lock all attached hard disks early to detect "in use"
10736 * situations before deleting actual diffs */
10737 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10738 it != mMediaData->mAttachments.end();
10739 ++it)
10740 {
10741 MediumAttachment* pAtt = *it;
10742 if (pAtt->i_getType() == DeviceType_HardDisk)
10743 {
10744 Medium* pMedium = pAtt->i_getMedium();
10745 Assert(pMedium);
10746
10747 MediumLockList *pMediumLockList(new MediumLockList());
10748 alock.release();
10749 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10750 false /* fMediumLockWrite */,
10751 NULL,
10752 *pMediumLockList);
10753 alock.acquire();
10754
10755 if (FAILED(rc))
10756 {
10757 delete pMediumLockList;
10758 throw rc;
10759 }
10760
10761 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10762 if (FAILED(rc))
10763 throw rc;
10764 }
10765 }
10766
10767 if (FAILED(rc))
10768 throw rc;
10769 } // end of offline
10770
10771 /* Lock lists are now up to date and include implicitly created media */
10772
10773 /* Go through remembered attachments and delete all implicitly created
10774 * diffs and fix up the attachment information */
10775 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10776 MediaData::AttachmentList implicitAtts;
10777 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10778 it != mMediaData->mAttachments.end();
10779 ++it)
10780 {
10781 ComObjPtr<MediumAttachment> pAtt = *it;
10782 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10783 if (pMedium.isNull())
10784 continue;
10785
10786 // Implicit attachments go on the list for deletion and back references are removed.
10787 if (pAtt->i_isImplicit())
10788 {
10789 /* Deassociate and mark for deletion */
10790 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10791 rc = pMedium->i_removeBackReference(mData->mUuid);
10792 if (FAILED(rc))
10793 throw rc;
10794 implicitAtts.push_back(pAtt);
10795 continue;
10796 }
10797
10798 /* Was this medium attached before? */
10799 if (!i_findAttachment(oldAtts, pMedium))
10800 {
10801 /* no: de-associate */
10802 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10803 rc = pMedium->i_removeBackReference(mData->mUuid);
10804 if (FAILED(rc))
10805 throw rc;
10806 continue;
10807 }
10808 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10809 }
10810
10811 /* If there are implicit attachments to delete, throw away the lock
10812 * map contents (which will unlock all media) since the medium
10813 * attachments will be rolled back. Below we need to completely
10814 * recreate the lock map anyway since it is infinitely complex to
10815 * do this incrementally (would need reconstructing each attachment
10816 * change, which would be extremely hairy). */
10817 if (implicitAtts.size() != 0)
10818 {
10819 ErrorInfoKeeper eik;
10820
10821 HRESULT rc1 = lockedMediaMap->Clear();
10822 AssertComRC(rc1);
10823 }
10824
10825 /* rollback hard disk changes */
10826 mMediaData.rollback();
10827
10828 MultiResult mrc(S_OK);
10829
10830 // Delete unused implicit diffs.
10831 if (implicitAtts.size() != 0)
10832 {
10833 alock.release();
10834
10835 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10836 {
10837 // Remove medium associated with this attachment.
10838 ComObjPtr<MediumAttachment> pAtt = *it;
10839 Assert(pAtt);
10840 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10841 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10842 Assert(pMedium);
10843
10844 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10845 // continue on delete failure, just collect error messages
10846 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10847 pMedium->i_getLocationFull().c_str() ));
10848 mrc = rc;
10849 }
10850
10851 alock.acquire();
10852
10853 /* if there is a VM recreate media lock map as mentioned above,
10854 * otherwise it is a waste of time and we leave things unlocked */
10855 if (aOnline)
10856 {
10857 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10858 /* must never be NULL, but better safe than sorry */
10859 if (!pMachine.isNull())
10860 {
10861 alock.release();
10862 rc = mData->mSession.mMachine->i_lockMedia();
10863 alock.acquire();
10864 if (FAILED(rc))
10865 throw rc;
10866 }
10867 }
10868 }
10869 }
10870 catch (HRESULT aRC) {rc = aRC;}
10871
10872 if (mData->mMachineState == MachineState_SettingUp)
10873 i_setMachineState(oldState);
10874
10875 /* unlock all hard disks we locked when there is no VM */
10876 if (!aOnline)
10877 {
10878 ErrorInfoKeeper eik;
10879
10880 HRESULT rc1 = lockedMediaMap->Clear();
10881 AssertComRC(rc1);
10882 }
10883
10884 return rc;
10885}
10886
10887
10888/**
10889 * Looks through the given list of media attachments for one with the given parameters
10890 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10891 * can be searched as well if needed.
10892 *
10893 * @param list
10894 * @param aControllerName
10895 * @param aControllerPort
10896 * @param aDevice
10897 * @return
10898 */
10899MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10900 IN_BSTR aControllerName,
10901 LONG aControllerPort,
10902 LONG aDevice)
10903{
10904 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10905 {
10906 MediumAttachment *pAttach = *it;
10907 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10908 return pAttach;
10909 }
10910
10911 return NULL;
10912}
10913
10914/**
10915 * Looks through the given list of media attachments for one with the given parameters
10916 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10917 * can be searched as well if needed.
10918 *
10919 * @param list
10920 * @param aControllerName
10921 * @param aControllerPort
10922 * @param aDevice
10923 * @return
10924 */
10925MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10926 ComObjPtr<Medium> pMedium)
10927{
10928 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10929 {
10930 MediumAttachment *pAttach = *it;
10931 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10932 if (pMediumThis == pMedium)
10933 return pAttach;
10934 }
10935
10936 return NULL;
10937}
10938
10939/**
10940 * Looks through the given list of media attachments for one with the given parameters
10941 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10942 * can be searched as well if needed.
10943 *
10944 * @param list
10945 * @param aControllerName
10946 * @param aControllerPort
10947 * @param aDevice
10948 * @return
10949 */
10950MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10951 Guid &id)
10952{
10953 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10954 {
10955 MediumAttachment *pAttach = *it;
10956 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10957 if (pMediumThis->i_getId() == id)
10958 return pAttach;
10959 }
10960
10961 return NULL;
10962}
10963
10964/**
10965 * Main implementation for Machine::DetachDevice. This also gets called
10966 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10967 *
10968 * @param pAttach Medium attachment to detach.
10969 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10970 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10971 * SnapshotMachine, and this must be its snapshot.
10972 * @return
10973 */
10974HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10975 AutoWriteLock &writeLock,
10976 Snapshot *pSnapshot)
10977{
10978 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10979 DeviceType_T mediumType = pAttach->i_getType();
10980
10981 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10982
10983 if (pAttach->i_isImplicit())
10984 {
10985 /* attempt to implicitly delete the implicitly created diff */
10986
10987 /// @todo move the implicit flag from MediumAttachment to Medium
10988 /// and forbid any hard disk operation when it is implicit. Or maybe
10989 /// a special media state for it to make it even more simple.
10990
10991 Assert(mMediaData.isBackedUp());
10992
10993 /* will release the lock before the potentially lengthy operation, so
10994 * protect with the special state */
10995 MachineState_T oldState = mData->mMachineState;
10996 i_setMachineState(MachineState_SettingUp);
10997
10998 writeLock.release();
10999
11000 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11001 true /*aWait*/);
11002
11003 writeLock.acquire();
11004
11005 i_setMachineState(oldState);
11006
11007 if (FAILED(rc)) return rc;
11008 }
11009
11010 i_setModified(IsModified_Storage);
11011 mMediaData.backup();
11012 mMediaData->mAttachments.remove(pAttach);
11013
11014 if (!oldmedium.isNull())
11015 {
11016 // if this is from a snapshot, do not defer detachment to commitMedia()
11017 if (pSnapshot)
11018 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11019 // else if non-hard disk media, do not defer detachment to commitMedia() either
11020 else if (mediumType != DeviceType_HardDisk)
11021 oldmedium->i_removeBackReference(mData->mUuid);
11022 }
11023
11024 return S_OK;
11025}
11026
11027/**
11028 * Goes thru all media of the given list and
11029 *
11030 * 1) calls i_detachDevice() on each of them for this machine and
11031 * 2) adds all Medium objects found in the process to the given list,
11032 * depending on cleanupMode.
11033 *
11034 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11035 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11036 * media to the list.
11037 *
11038 * This gets called from Machine::Unregister, both for the actual Machine and
11039 * the SnapshotMachine objects that might be found in the snapshots.
11040 *
11041 * Requires caller and locking. The machine lock must be passed in because it
11042 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11043 *
11044 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11045 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11046 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11047 * Full, then all media get added;
11048 * otherwise no media get added.
11049 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11050 * @return
11051 */
11052HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11053 Snapshot *pSnapshot,
11054 CleanupMode_T cleanupMode,
11055 MediaList &llMedia)
11056{
11057 Assert(isWriteLockOnCurrentThread());
11058
11059 HRESULT rc;
11060
11061 // make a temporary list because i_detachDevice invalidates iterators into
11062 // mMediaData->mAttachments
11063 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11064
11065 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11066 {
11067 ComObjPtr<MediumAttachment> &pAttach = *it;
11068 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11069
11070 if (!pMedium.isNull())
11071 {
11072 AutoCaller mac(pMedium);
11073 if (FAILED(mac.rc())) return mac.rc();
11074 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11075 DeviceType_T devType = pMedium->i_getDeviceType();
11076 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11077 && devType == DeviceType_HardDisk)
11078 || (cleanupMode == CleanupMode_Full)
11079 )
11080 {
11081 llMedia.push_back(pMedium);
11082 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11083 /*
11084 * Search for medias which are not attached to any machine, but
11085 * in the chain to an attached disk. Mediums are only consided
11086 * if they are:
11087 * - have only one child
11088 * - no references to any machines
11089 * - are of normal medium type
11090 */
11091 while (!pParent.isNull())
11092 {
11093 AutoCaller mac1(pParent);
11094 if (FAILED(mac1.rc())) return mac1.rc();
11095 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11096 if (pParent->i_getChildren().size() == 1)
11097 {
11098 if ( pParent->i_getMachineBackRefCount() == 0
11099 && pParent->i_getType() == MediumType_Normal
11100 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11101 llMedia.push_back(pParent);
11102 }
11103 else
11104 break;
11105 pParent = pParent->i_getParent();
11106 }
11107 }
11108 }
11109
11110 // real machine: then we need to use the proper method
11111 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11112
11113 if (FAILED(rc))
11114 return rc;
11115 }
11116
11117 return S_OK;
11118}
11119
11120/**
11121 * Perform deferred hard disk detachments.
11122 *
11123 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11124 * backed up).
11125 *
11126 * If @a aOnline is @c true then this method will also unlock the old hard disks
11127 * for which the new implicit diffs were created and will lock these new diffs for
11128 * writing.
11129 *
11130 * @param aOnline Whether the VM was online prior to this operation.
11131 *
11132 * @note Locks this object for writing!
11133 */
11134void Machine::i_commitMedia(bool aOnline /*= false*/)
11135{
11136 AutoCaller autoCaller(this);
11137 AssertComRCReturnVoid(autoCaller.rc());
11138
11139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11140
11141 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11142
11143 HRESULT rc = S_OK;
11144
11145 /* no attach/detach operations -- nothing to do */
11146 if (!mMediaData.isBackedUp())
11147 return;
11148
11149 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11150 bool fMediaNeedsLocking = false;
11151
11152 /* enumerate new attachments */
11153 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11154 it != mMediaData->mAttachments.end();
11155 ++it)
11156 {
11157 MediumAttachment *pAttach = *it;
11158
11159 pAttach->i_commit();
11160
11161 Medium* pMedium = pAttach->i_getMedium();
11162 bool fImplicit = pAttach->i_isImplicit();
11163
11164 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11165 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11166 fImplicit));
11167
11168 /** @todo convert all this Machine-based voodoo to MediumAttachment
11169 * based commit logic. */
11170 if (fImplicit)
11171 {
11172 /* convert implicit attachment to normal */
11173 pAttach->i_setImplicit(false);
11174
11175 if ( aOnline
11176 && pMedium
11177 && pAttach->i_getType() == DeviceType_HardDisk
11178 )
11179 {
11180 ComObjPtr<Medium> parent = pMedium->i_getParent();
11181 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11182
11183 /* update the appropriate lock list */
11184 MediumLockList *pMediumLockList;
11185 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11186 AssertComRC(rc);
11187 if (pMediumLockList)
11188 {
11189 /* unlock if there's a need to change the locking */
11190 if (!fMediaNeedsLocking)
11191 {
11192 rc = mData->mSession.mLockedMedia.Unlock();
11193 AssertComRC(rc);
11194 fMediaNeedsLocking = true;
11195 }
11196 rc = pMediumLockList->Update(parent, false);
11197 AssertComRC(rc);
11198 rc = pMediumLockList->Append(pMedium, true);
11199 AssertComRC(rc);
11200 }
11201 }
11202
11203 continue;
11204 }
11205
11206 if (pMedium)
11207 {
11208 /* was this medium attached before? */
11209 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11210 {
11211 MediumAttachment *pOldAttach = *oldIt;
11212 if (pOldAttach->i_getMedium() == pMedium)
11213 {
11214 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11215
11216 /* yes: remove from old to avoid de-association */
11217 oldAtts.erase(oldIt);
11218 break;
11219 }
11220 }
11221 }
11222 }
11223
11224 /* enumerate remaining old attachments and de-associate from the
11225 * current machine state */
11226 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11227 {
11228 MediumAttachment *pAttach = *it;
11229 Medium* pMedium = pAttach->i_getMedium();
11230
11231 /* Detach only hard disks, since DVD/floppy media is detached
11232 * instantly in MountMedium. */
11233 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11234 {
11235 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11236
11237 /* now de-associate from the current machine state */
11238 rc = pMedium->i_removeBackReference(mData->mUuid);
11239 AssertComRC(rc);
11240
11241 if (aOnline)
11242 {
11243 /* unlock since medium is not used anymore */
11244 MediumLockList *pMediumLockList;
11245 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11246 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11247 {
11248 /* this happens for online snapshots, there the attachment
11249 * is changing, but only to a diff image created under
11250 * the old one, so there is no separate lock list */
11251 Assert(!pMediumLockList);
11252 }
11253 else
11254 {
11255 AssertComRC(rc);
11256 if (pMediumLockList)
11257 {
11258 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11259 AssertComRC(rc);
11260 }
11261 }
11262 }
11263 }
11264 }
11265
11266 /* take media locks again so that the locking state is consistent */
11267 if (fMediaNeedsLocking)
11268 {
11269 Assert(aOnline);
11270 rc = mData->mSession.mLockedMedia.Lock();
11271 AssertComRC(rc);
11272 }
11273
11274 /* commit the hard disk changes */
11275 mMediaData.commit();
11276
11277 if (i_isSessionMachine())
11278 {
11279 /*
11280 * Update the parent machine to point to the new owner.
11281 * This is necessary because the stored parent will point to the
11282 * session machine otherwise and cause crashes or errors later
11283 * when the session machine gets invalid.
11284 */
11285 /** @todo Change the MediumAttachment class to behave like any other
11286 * class in this regard by creating peer MediumAttachment
11287 * objects for session machines and share the data with the peer
11288 * machine.
11289 */
11290 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11291 it != mMediaData->mAttachments.end();
11292 ++it)
11293 (*it)->i_updateParentMachine(mPeer);
11294
11295 /* attach new data to the primary machine and reshare it */
11296 mPeer->mMediaData.attach(mMediaData);
11297 }
11298
11299 return;
11300}
11301
11302/**
11303 * Perform deferred deletion of implicitly created diffs.
11304 *
11305 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11306 * backed up).
11307 *
11308 * @note Locks this object for writing!
11309 */
11310void Machine::i_rollbackMedia()
11311{
11312 AutoCaller autoCaller(this);
11313 AssertComRCReturnVoid(autoCaller.rc());
11314
11315 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11316 LogFlowThisFunc(("Entering rollbackMedia\n"));
11317
11318 HRESULT rc = S_OK;
11319
11320 /* no attach/detach operations -- nothing to do */
11321 if (!mMediaData.isBackedUp())
11322 return;
11323
11324 /* enumerate new attachments */
11325 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11326 it != mMediaData->mAttachments.end();
11327 ++it)
11328 {
11329 MediumAttachment *pAttach = *it;
11330 /* Fix up the backrefs for DVD/floppy media. */
11331 if (pAttach->i_getType() != DeviceType_HardDisk)
11332 {
11333 Medium* pMedium = pAttach->i_getMedium();
11334 if (pMedium)
11335 {
11336 rc = pMedium->i_removeBackReference(mData->mUuid);
11337 AssertComRC(rc);
11338 }
11339 }
11340
11341 (*it)->i_rollback();
11342
11343 pAttach = *it;
11344 /* Fix up the backrefs for DVD/floppy media. */
11345 if (pAttach->i_getType() != DeviceType_HardDisk)
11346 {
11347 Medium* pMedium = pAttach->i_getMedium();
11348 if (pMedium)
11349 {
11350 rc = pMedium->i_addBackReference(mData->mUuid);
11351 AssertComRC(rc);
11352 }
11353 }
11354 }
11355
11356 /** @todo convert all this Machine-based voodoo to MediumAttachment
11357 * based rollback logic. */
11358 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11359
11360 return;
11361}
11362
11363/**
11364 * Returns true if the settings file is located in the directory named exactly
11365 * as the machine; this means, among other things, that the machine directory
11366 * should be auto-renamed.
11367 *
11368 * @param aSettingsDir if not NULL, the full machine settings file directory
11369 * name will be assigned there.
11370 *
11371 * @note Doesn't lock anything.
11372 * @note Not thread safe (must be called from this object's lock).
11373 */
11374bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11375{
11376 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11377 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11378 if (aSettingsDir)
11379 *aSettingsDir = strMachineDirName;
11380 strMachineDirName.stripPath(); // vmname
11381 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11382 strConfigFileOnly.stripPath() // vmname.vbox
11383 .stripSuffix(); // vmname
11384 /** @todo hack, make somehow use of ComposeMachineFilename */
11385 if (mUserData->s.fDirectoryIncludesUUID)
11386 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11387
11388 AssertReturn(!strMachineDirName.isEmpty(), false);
11389 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11390
11391 return strMachineDirName == strConfigFileOnly;
11392}
11393
11394/**
11395 * Discards all changes to machine settings.
11396 *
11397 * @param aNotify Whether to notify the direct session about changes or not.
11398 *
11399 * @note Locks objects for writing!
11400 */
11401void Machine::i_rollback(bool aNotify)
11402{
11403 AutoCaller autoCaller(this);
11404 AssertComRCReturn(autoCaller.rc(), (void)0);
11405
11406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11407
11408 if (!mStorageControllers.isNull())
11409 {
11410 if (mStorageControllers.isBackedUp())
11411 {
11412 /* unitialize all new devices (absent in the backed up list). */
11413 StorageControllerList::const_iterator it = mStorageControllers->begin();
11414 StorageControllerList *backedList = mStorageControllers.backedUpData();
11415 while (it != mStorageControllers->end())
11416 {
11417 if ( std::find(backedList->begin(), backedList->end(), *it)
11418 == backedList->end()
11419 )
11420 {
11421 (*it)->uninit();
11422 }
11423 ++it;
11424 }
11425
11426 /* restore the list */
11427 mStorageControllers.rollback();
11428 }
11429
11430 /* rollback any changes to devices after restoring the list */
11431 if (mData->flModifications & IsModified_Storage)
11432 {
11433 StorageControllerList::const_iterator it = mStorageControllers->begin();
11434 while (it != mStorageControllers->end())
11435 {
11436 (*it)->i_rollback();
11437 ++it;
11438 }
11439 }
11440 }
11441
11442 if (!mUSBControllers.isNull())
11443 {
11444 if (mUSBControllers.isBackedUp())
11445 {
11446 /* unitialize all new devices (absent in the backed up list). */
11447 USBControllerList::const_iterator it = mUSBControllers->begin();
11448 USBControllerList *backedList = mUSBControllers.backedUpData();
11449 while (it != mUSBControllers->end())
11450 {
11451 if ( std::find(backedList->begin(), backedList->end(), *it)
11452 == backedList->end()
11453 )
11454 {
11455 (*it)->uninit();
11456 }
11457 ++it;
11458 }
11459
11460 /* restore the list */
11461 mUSBControllers.rollback();
11462 }
11463
11464 /* rollback any changes to devices after restoring the list */
11465 if (mData->flModifications & IsModified_USB)
11466 {
11467 USBControllerList::const_iterator it = mUSBControllers->begin();
11468 while (it != mUSBControllers->end())
11469 {
11470 (*it)->i_rollback();
11471 ++it;
11472 }
11473 }
11474 }
11475
11476 mUserData.rollback();
11477
11478 mHWData.rollback();
11479
11480 if (mData->flModifications & IsModified_Storage)
11481 i_rollbackMedia();
11482
11483 if (mBIOSSettings)
11484 mBIOSSettings->i_rollback();
11485
11486 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11487 mVRDEServer->i_rollback();
11488
11489 if (mAudioAdapter)
11490 mAudioAdapter->i_rollback();
11491
11492 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11493 mUSBDeviceFilters->i_rollback();
11494
11495 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11496 mBandwidthControl->i_rollback();
11497
11498 if (!mHWData.isNull())
11499 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11500 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11501 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11502 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11503
11504 if (mData->flModifications & IsModified_NetworkAdapters)
11505 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11506 if ( mNetworkAdapters[slot]
11507 && mNetworkAdapters[slot]->i_isModified())
11508 {
11509 mNetworkAdapters[slot]->i_rollback();
11510 networkAdapters[slot] = mNetworkAdapters[slot];
11511 }
11512
11513 if (mData->flModifications & IsModified_SerialPorts)
11514 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11515 if ( mSerialPorts[slot]
11516 && mSerialPorts[slot]->i_isModified())
11517 {
11518 mSerialPorts[slot]->i_rollback();
11519 serialPorts[slot] = mSerialPorts[slot];
11520 }
11521
11522 if (mData->flModifications & IsModified_ParallelPorts)
11523 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11524 if ( mParallelPorts[slot]
11525 && mParallelPorts[slot]->i_isModified())
11526 {
11527 mParallelPorts[slot]->i_rollback();
11528 parallelPorts[slot] = mParallelPorts[slot];
11529 }
11530
11531 if (aNotify)
11532 {
11533 /* inform the direct session about changes */
11534
11535 ComObjPtr<Machine> that = this;
11536 uint32_t flModifications = mData->flModifications;
11537 alock.release();
11538
11539 if (flModifications & IsModified_SharedFolders)
11540 that->i_onSharedFolderChange();
11541
11542 if (flModifications & IsModified_VRDEServer)
11543 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11544 if (flModifications & IsModified_USB)
11545 that->i_onUSBControllerChange();
11546
11547 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11548 if (networkAdapters[slot])
11549 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11550 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11551 if (serialPorts[slot])
11552 that->i_onSerialPortChange(serialPorts[slot]);
11553 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11554 if (parallelPorts[slot])
11555 that->i_onParallelPortChange(parallelPorts[slot]);
11556
11557 if (flModifications & IsModified_Storage)
11558 that->i_onStorageControllerChange();
11559
11560#if 0
11561 if (flModifications & IsModified_BandwidthControl)
11562 that->onBandwidthControlChange();
11563#endif
11564 }
11565}
11566
11567/**
11568 * Commits all the changes to machine settings.
11569 *
11570 * Note that this operation is supposed to never fail.
11571 *
11572 * @note Locks this object and children for writing.
11573 */
11574void Machine::i_commit()
11575{
11576 AutoCaller autoCaller(this);
11577 AssertComRCReturnVoid(autoCaller.rc());
11578
11579 AutoCaller peerCaller(mPeer);
11580 AssertComRCReturnVoid(peerCaller.rc());
11581
11582 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11583
11584 /*
11585 * use safe commit to ensure Snapshot machines (that share mUserData)
11586 * will still refer to a valid memory location
11587 */
11588 mUserData.commitCopy();
11589
11590 mHWData.commit();
11591
11592 if (mMediaData.isBackedUp())
11593 i_commitMedia(Global::IsOnline(mData->mMachineState));
11594
11595 mBIOSSettings->i_commit();
11596 mVRDEServer->i_commit();
11597 mAudioAdapter->i_commit();
11598 mUSBDeviceFilters->i_commit();
11599 mBandwidthControl->i_commit();
11600
11601 /* Since mNetworkAdapters is a list which might have been changed (resized)
11602 * without using the Backupable<> template we need to handle the copying
11603 * of the list entries manually, including the creation of peers for the
11604 * new objects. */
11605 bool commitNetworkAdapters = false;
11606 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11607 if (mPeer)
11608 {
11609 /* commit everything, even the ones which will go away */
11610 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11611 mNetworkAdapters[slot]->i_commit();
11612 /* copy over the new entries, creating a peer and uninit the original */
11613 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11614 for (size_t slot = 0; slot < newSize; slot++)
11615 {
11616 /* look if this adapter has a peer device */
11617 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11618 if (!peer)
11619 {
11620 /* no peer means the adapter is a newly created one;
11621 * create a peer owning data this data share it with */
11622 peer.createObject();
11623 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11624 }
11625 mPeer->mNetworkAdapters[slot] = peer;
11626 }
11627 /* uninit any no longer needed network adapters */
11628 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11629 mNetworkAdapters[slot]->uninit();
11630 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11631 {
11632 if (mPeer->mNetworkAdapters[slot])
11633 mPeer->mNetworkAdapters[slot]->uninit();
11634 }
11635 /* Keep the original network adapter count until this point, so that
11636 * discarding a chipset type change will not lose settings. */
11637 mNetworkAdapters.resize(newSize);
11638 mPeer->mNetworkAdapters.resize(newSize);
11639 }
11640 else
11641 {
11642 /* we have no peer (our parent is the newly created machine);
11643 * just commit changes to the network adapters */
11644 commitNetworkAdapters = true;
11645 }
11646 if (commitNetworkAdapters)
11647 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11648 mNetworkAdapters[slot]->i_commit();
11649
11650 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11651 mSerialPorts[slot]->i_commit();
11652 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11653 mParallelPorts[slot]->i_commit();
11654
11655 bool commitStorageControllers = false;
11656
11657 if (mStorageControllers.isBackedUp())
11658 {
11659 mStorageControllers.commit();
11660
11661 if (mPeer)
11662 {
11663 /* Commit all changes to new controllers (this will reshare data with
11664 * peers for those who have peers) */
11665 StorageControllerList *newList = new StorageControllerList();
11666 StorageControllerList::const_iterator it = mStorageControllers->begin();
11667 while (it != mStorageControllers->end())
11668 {
11669 (*it)->i_commit();
11670
11671 /* look if this controller has a peer device */
11672 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11673 if (!peer)
11674 {
11675 /* no peer means the device is a newly created one;
11676 * create a peer owning data this device share it with */
11677 peer.createObject();
11678 peer->init(mPeer, *it, true /* aReshare */);
11679 }
11680 else
11681 {
11682 /* remove peer from the old list */
11683 mPeer->mStorageControllers->remove(peer);
11684 }
11685 /* and add it to the new list */
11686 newList->push_back(peer);
11687
11688 ++it;
11689 }
11690
11691 /* uninit old peer's controllers that are left */
11692 it = mPeer->mStorageControllers->begin();
11693 while (it != mPeer->mStorageControllers->end())
11694 {
11695 (*it)->uninit();
11696 ++it;
11697 }
11698
11699 /* attach new list of controllers to our peer */
11700 mPeer->mStorageControllers.attach(newList);
11701 }
11702 else
11703 {
11704 /* we have no peer (our parent is the newly created machine);
11705 * just commit changes to devices */
11706 commitStorageControllers = true;
11707 }
11708 }
11709 else
11710 {
11711 /* the list of controllers itself is not changed,
11712 * just commit changes to controllers themselves */
11713 commitStorageControllers = true;
11714 }
11715
11716 if (commitStorageControllers)
11717 {
11718 StorageControllerList::const_iterator it = mStorageControllers->begin();
11719 while (it != mStorageControllers->end())
11720 {
11721 (*it)->i_commit();
11722 ++it;
11723 }
11724 }
11725
11726 bool commitUSBControllers = false;
11727
11728 if (mUSBControllers.isBackedUp())
11729 {
11730 mUSBControllers.commit();
11731
11732 if (mPeer)
11733 {
11734 /* Commit all changes to new controllers (this will reshare data with
11735 * peers for those who have peers) */
11736 USBControllerList *newList = new USBControllerList();
11737 USBControllerList::const_iterator it = mUSBControllers->begin();
11738 while (it != mUSBControllers->end())
11739 {
11740 (*it)->i_commit();
11741
11742 /* look if this controller has a peer device */
11743 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11744 if (!peer)
11745 {
11746 /* no peer means the device is a newly created one;
11747 * create a peer owning data this device share it with */
11748 peer.createObject();
11749 peer->init(mPeer, *it, true /* aReshare */);
11750 }
11751 else
11752 {
11753 /* remove peer from the old list */
11754 mPeer->mUSBControllers->remove(peer);
11755 }
11756 /* and add it to the new list */
11757 newList->push_back(peer);
11758
11759 ++it;
11760 }
11761
11762 /* uninit old peer's controllers that are left */
11763 it = mPeer->mUSBControllers->begin();
11764 while (it != mPeer->mUSBControllers->end())
11765 {
11766 (*it)->uninit();
11767 ++it;
11768 }
11769
11770 /* attach new list of controllers to our peer */
11771 mPeer->mUSBControllers.attach(newList);
11772 }
11773 else
11774 {
11775 /* we have no peer (our parent is the newly created machine);
11776 * just commit changes to devices */
11777 commitUSBControllers = true;
11778 }
11779 }
11780 else
11781 {
11782 /* the list of controllers itself is not changed,
11783 * just commit changes to controllers themselves */
11784 commitUSBControllers = true;
11785 }
11786
11787 if (commitUSBControllers)
11788 {
11789 USBControllerList::const_iterator it = mUSBControllers->begin();
11790 while (it != mUSBControllers->end())
11791 {
11792 (*it)->i_commit();
11793 ++it;
11794 }
11795 }
11796
11797 if (i_isSessionMachine())
11798 {
11799 /* attach new data to the primary machine and reshare it */
11800 mPeer->mUserData.attach(mUserData);
11801 mPeer->mHWData.attach(mHWData);
11802 /* mMediaData is reshared by fixupMedia */
11803 // mPeer->mMediaData.attach(mMediaData);
11804 Assert(mPeer->mMediaData.data() == mMediaData.data());
11805 }
11806}
11807
11808/**
11809 * Copies all the hardware data from the given machine.
11810 *
11811 * Currently, only called when the VM is being restored from a snapshot. In
11812 * particular, this implies that the VM is not running during this method's
11813 * call.
11814 *
11815 * @note This method must be called from under this object's lock.
11816 *
11817 * @note This method doesn't call #commit(), so all data remains backed up and
11818 * unsaved.
11819 */
11820void Machine::i_copyFrom(Machine *aThat)
11821{
11822 AssertReturnVoid(!i_isSnapshotMachine());
11823 AssertReturnVoid(aThat->i_isSnapshotMachine());
11824
11825 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11826
11827 mHWData.assignCopy(aThat->mHWData);
11828
11829 // create copies of all shared folders (mHWData after attaching a copy
11830 // contains just references to original objects)
11831 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11832 it != mHWData->mSharedFolders.end();
11833 ++it)
11834 {
11835 ComObjPtr<SharedFolder> folder;
11836 folder.createObject();
11837 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11838 AssertComRC(rc);
11839 *it = folder;
11840 }
11841
11842 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11843 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11844 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11845 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11846 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11847
11848 /* create private copies of all controllers */
11849 mStorageControllers.backup();
11850 mStorageControllers->clear();
11851 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11852 it != aThat->mStorageControllers->end();
11853 ++it)
11854 {
11855 ComObjPtr<StorageController> ctrl;
11856 ctrl.createObject();
11857 ctrl->initCopy(this, *it);
11858 mStorageControllers->push_back(ctrl);
11859 }
11860
11861 /* create private copies of all USB controllers */
11862 mUSBControllers.backup();
11863 mUSBControllers->clear();
11864 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11865 it != aThat->mUSBControllers->end();
11866 ++it)
11867 {
11868 ComObjPtr<USBController> ctrl;
11869 ctrl.createObject();
11870 ctrl->initCopy(this, *it);
11871 mUSBControllers->push_back(ctrl);
11872 }
11873
11874 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11875 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11876 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11877 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11878 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11879 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11880 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11881}
11882
11883/**
11884 * Returns whether the given storage controller is hotplug capable.
11885 *
11886 * @returns true if the controller supports hotplugging
11887 * false otherwise.
11888 * @param enmCtrlType The controller type to check for.
11889 */
11890bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11891{
11892 ComPtr<ISystemProperties> systemProperties;
11893 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11894 if (FAILED(rc))
11895 return false;
11896
11897 BOOL aHotplugCapable = FALSE;
11898 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11899
11900 return RT_BOOL(aHotplugCapable);
11901}
11902
11903#ifdef VBOX_WITH_RESOURCE_USAGE_API
11904
11905void Machine::i_getDiskList(MediaList &list)
11906{
11907 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11908 it != mMediaData->mAttachments.end();
11909 ++it)
11910 {
11911 MediumAttachment* pAttach = *it;
11912 /* just in case */
11913 AssertStmt(pAttach, continue);
11914
11915 AutoCaller localAutoCallerA(pAttach);
11916 if (FAILED(localAutoCallerA.rc())) continue;
11917
11918 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11919
11920 if (pAttach->i_getType() == DeviceType_HardDisk)
11921 list.push_back(pAttach->i_getMedium());
11922 }
11923}
11924
11925void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11926{
11927 AssertReturnVoid(isWriteLockOnCurrentThread());
11928 AssertPtrReturnVoid(aCollector);
11929
11930 pm::CollectorHAL *hal = aCollector->getHAL();
11931 /* Create sub metrics */
11932 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11933 "Percentage of processor time spent in user mode by the VM process.");
11934 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11935 "Percentage of processor time spent in kernel mode by the VM process.");
11936 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11937 "Size of resident portion of VM process in memory.");
11938 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11939 "Actual size of all VM disks combined.");
11940 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11941 "Network receive rate.");
11942 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11943 "Network transmit rate.");
11944 /* Create and register base metrics */
11945 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11946 cpuLoadUser, cpuLoadKernel);
11947 aCollector->registerBaseMetric(cpuLoad);
11948 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11949 ramUsageUsed);
11950 aCollector->registerBaseMetric(ramUsage);
11951 MediaList disks;
11952 i_getDiskList(disks);
11953 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11954 diskUsageUsed);
11955 aCollector->registerBaseMetric(diskUsage);
11956
11957 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11958 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11959 new pm::AggregateAvg()));
11960 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11961 new pm::AggregateMin()));
11962 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11963 new pm::AggregateMax()));
11964 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11965 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11966 new pm::AggregateAvg()));
11967 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11968 new pm::AggregateMin()));
11969 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11970 new pm::AggregateMax()));
11971
11972 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11973 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11974 new pm::AggregateAvg()));
11975 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11976 new pm::AggregateMin()));
11977 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11978 new pm::AggregateMax()));
11979
11980 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11981 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11982 new pm::AggregateAvg()));
11983 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11984 new pm::AggregateMin()));
11985 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11986 new pm::AggregateMax()));
11987
11988
11989 /* Guest metrics collector */
11990 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11991 aCollector->registerGuest(mCollectorGuest);
11992 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11993 this, __PRETTY_FUNCTION__, mCollectorGuest));
11994
11995 /* Create sub metrics */
11996 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11997 "Percentage of processor time spent in user mode as seen by the guest.");
11998 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11999 "Percentage of processor time spent in kernel mode as seen by the guest.");
12000 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12001 "Percentage of processor time spent idling as seen by the guest.");
12002
12003 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12004 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12005 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12006 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12007 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12008 pm::SubMetric *guestMemCache = new pm::SubMetric(
12009 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12010
12011 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12012 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12013
12014 /* Create and register base metrics */
12015 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12016 machineNetRx, machineNetTx);
12017 aCollector->registerBaseMetric(machineNetRate);
12018
12019 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12020 guestLoadUser, guestLoadKernel, guestLoadIdle);
12021 aCollector->registerBaseMetric(guestCpuLoad);
12022
12023 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12024 guestMemTotal, guestMemFree,
12025 guestMemBalloon, guestMemShared,
12026 guestMemCache, guestPagedTotal);
12027 aCollector->registerBaseMetric(guestCpuMem);
12028
12029 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12030 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12031 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12032 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12033
12034 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12035 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12036 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12037 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12038
12039 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12040 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12041 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12042 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12043
12044 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12045 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12046 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12047 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12048
12049 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12050 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12051 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12052 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12053
12054 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12055 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12056 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12058
12059 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12060 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12061 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12063
12064 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12066 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12068
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12073
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12078
12079 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12082 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12083}
12084
12085void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12086{
12087 AssertReturnVoid(isWriteLockOnCurrentThread());
12088
12089 if (aCollector)
12090 {
12091 aCollector->unregisterMetricsFor(aMachine);
12092 aCollector->unregisterBaseMetricsFor(aMachine);
12093 }
12094}
12095
12096#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12097
12098
12099////////////////////////////////////////////////////////////////////////////////
12100
12101DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12102
12103HRESULT SessionMachine::FinalConstruct()
12104{
12105 LogFlowThisFunc(("\n"));
12106
12107 mClientToken = NULL;
12108
12109 return BaseFinalConstruct();
12110}
12111
12112void SessionMachine::FinalRelease()
12113{
12114 LogFlowThisFunc(("\n"));
12115
12116 Assert(!mClientToken);
12117 /* paranoia, should not hang around any more */
12118 if (mClientToken)
12119 {
12120 delete mClientToken;
12121 mClientToken = NULL;
12122 }
12123
12124 uninit(Uninit::Unexpected);
12125
12126 BaseFinalRelease();
12127}
12128
12129/**
12130 * @note Must be called only by Machine::LockMachine() from its own write lock.
12131 */
12132HRESULT SessionMachine::init(Machine *aMachine)
12133{
12134 LogFlowThisFuncEnter();
12135 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12136
12137 AssertReturn(aMachine, E_INVALIDARG);
12138
12139 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12140
12141 /* Enclose the state transition NotReady->InInit->Ready */
12142 AutoInitSpan autoInitSpan(this);
12143 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12144
12145 HRESULT rc = S_OK;
12146
12147 /* create the machine client token */
12148 try
12149 {
12150 mClientToken = new ClientToken(aMachine, this);
12151 if (!mClientToken->isReady())
12152 {
12153 delete mClientToken;
12154 mClientToken = NULL;
12155 rc = E_FAIL;
12156 }
12157 }
12158 catch (std::bad_alloc &)
12159 {
12160 rc = E_OUTOFMEMORY;
12161 }
12162 if (FAILED(rc))
12163 return rc;
12164
12165 /* memorize the peer Machine */
12166 unconst(mPeer) = aMachine;
12167 /* share the parent pointer */
12168 unconst(mParent) = aMachine->mParent;
12169
12170 /* take the pointers to data to share */
12171 mData.share(aMachine->mData);
12172 mSSData.share(aMachine->mSSData);
12173
12174 mUserData.share(aMachine->mUserData);
12175 mHWData.share(aMachine->mHWData);
12176 mMediaData.share(aMachine->mMediaData);
12177
12178 mStorageControllers.allocate();
12179 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12180 it != aMachine->mStorageControllers->end();
12181 ++it)
12182 {
12183 ComObjPtr<StorageController> ctl;
12184 ctl.createObject();
12185 ctl->init(this, *it);
12186 mStorageControllers->push_back(ctl);
12187 }
12188
12189 mUSBControllers.allocate();
12190 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12191 it != aMachine->mUSBControllers->end();
12192 ++it)
12193 {
12194 ComObjPtr<USBController> ctl;
12195 ctl.createObject();
12196 ctl->init(this, *it);
12197 mUSBControllers->push_back(ctl);
12198 }
12199
12200 unconst(mBIOSSettings).createObject();
12201 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12202 /* create another VRDEServer object that will be mutable */
12203 unconst(mVRDEServer).createObject();
12204 mVRDEServer->init(this, aMachine->mVRDEServer);
12205 /* create another audio adapter object that will be mutable */
12206 unconst(mAudioAdapter).createObject();
12207 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12208 /* create a list of serial ports that will be mutable */
12209 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12210 {
12211 unconst(mSerialPorts[slot]).createObject();
12212 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12213 }
12214 /* create a list of parallel ports that will be mutable */
12215 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12216 {
12217 unconst(mParallelPorts[slot]).createObject();
12218 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12219 }
12220
12221 /* create another USB device filters object that will be mutable */
12222 unconst(mUSBDeviceFilters).createObject();
12223 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12224
12225 /* create a list of network adapters that will be mutable */
12226 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12227 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12228 {
12229 unconst(mNetworkAdapters[slot]).createObject();
12230 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12231 }
12232
12233 /* create another bandwidth control object that will be mutable */
12234 unconst(mBandwidthControl).createObject();
12235 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12236
12237 /* default is to delete saved state on Saved -> PoweredOff transition */
12238 mRemoveSavedState = true;
12239
12240 /* Confirm a successful initialization when it's the case */
12241 autoInitSpan.setSucceeded();
12242
12243 miNATNetworksStarted = 0;
12244
12245 LogFlowThisFuncLeave();
12246 return rc;
12247}
12248
12249/**
12250 * Uninitializes this session object. If the reason is other than
12251 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12252 * or the client watcher code.
12253 *
12254 * @param aReason uninitialization reason
12255 *
12256 * @note Locks mParent + this object for writing.
12257 */
12258void SessionMachine::uninit(Uninit::Reason aReason)
12259{
12260 LogFlowThisFuncEnter();
12261 LogFlowThisFunc(("reason=%d\n", aReason));
12262
12263 /*
12264 * Strongly reference ourselves to prevent this object deletion after
12265 * mData->mSession.mMachine.setNull() below (which can release the last
12266 * reference and call the destructor). Important: this must be done before
12267 * accessing any members (and before AutoUninitSpan that does it as well).
12268 * This self reference will be released as the very last step on return.
12269 */
12270 ComObjPtr<SessionMachine> selfRef = this;
12271
12272 /* Enclose the state transition Ready->InUninit->NotReady */
12273 AutoUninitSpan autoUninitSpan(this);
12274 if (autoUninitSpan.uninitDone())
12275 {
12276 LogFlowThisFunc(("Already uninitialized\n"));
12277 LogFlowThisFuncLeave();
12278 return;
12279 }
12280
12281 if (autoUninitSpan.initFailed())
12282 {
12283 /* We've been called by init() because it's failed. It's not really
12284 * necessary (nor it's safe) to perform the regular uninit sequence
12285 * below, the following is enough.
12286 */
12287 LogFlowThisFunc(("Initialization failed.\n"));
12288 /* destroy the machine client token */
12289 if (mClientToken)
12290 {
12291 delete mClientToken;
12292 mClientToken = NULL;
12293 }
12294 uninitDataAndChildObjects();
12295 mData.free();
12296 unconst(mParent) = NULL;
12297 unconst(mPeer) = NULL;
12298 LogFlowThisFuncLeave();
12299 return;
12300 }
12301
12302 MachineState_T lastState;
12303 {
12304 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12305 lastState = mData->mMachineState;
12306 }
12307 NOREF(lastState);
12308
12309#ifdef VBOX_WITH_USB
12310 // release all captured USB devices, but do this before requesting the locks below
12311 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12312 {
12313 /* Console::captureUSBDevices() is called in the VM process only after
12314 * setting the machine state to Starting or Restoring.
12315 * Console::detachAllUSBDevices() will be called upon successful
12316 * termination. So, we need to release USB devices only if there was
12317 * an abnormal termination of a running VM.
12318 *
12319 * This is identical to SessionMachine::DetachAllUSBDevices except
12320 * for the aAbnormal argument. */
12321 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12322 AssertComRC(rc);
12323 NOREF(rc);
12324
12325 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12326 if (service)
12327 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12328 }
12329#endif /* VBOX_WITH_USB */
12330
12331 // we need to lock this object in uninit() because the lock is shared
12332 // with mPeer (as well as data we modify below). mParent lock is needed
12333 // by several calls to it, and USB needs host lock.
12334 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12335
12336#ifdef VBOX_WITH_RESOURCE_USAGE_API
12337 /*
12338 * It is safe to call Machine::i_unregisterMetrics() here because
12339 * PerformanceCollector::samplerCallback no longer accesses guest methods
12340 * holding the lock.
12341 */
12342 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12343 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12344 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12345 this, __PRETTY_FUNCTION__, mCollectorGuest));
12346 if (mCollectorGuest)
12347 {
12348 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12349 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12350 mCollectorGuest = NULL;
12351 }
12352#endif
12353
12354 if (aReason == Uninit::Abnormal)
12355 {
12356 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12357 Global::IsOnlineOrTransient(lastState)));
12358
12359 /* reset the state to Aborted */
12360 if (mData->mMachineState != MachineState_Aborted)
12361 i_setMachineState(MachineState_Aborted);
12362 }
12363
12364 // any machine settings modified?
12365 if (mData->flModifications)
12366 {
12367 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12368 i_rollback(false /* aNotify */);
12369 }
12370
12371 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12372 || !mConsoleTaskData.mSnapshot);
12373 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12374 {
12375 LogWarningThisFunc(("canceling failed save state request!\n"));
12376 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12377 }
12378 else if (!mConsoleTaskData.mSnapshot.isNull())
12379 {
12380 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12381
12382 /* delete all differencing hard disks created (this will also attach
12383 * their parents back by rolling back mMediaData) */
12384 i_rollbackMedia();
12385
12386 // delete the saved state file (it might have been already created)
12387 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12388 // think it's still in use
12389 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12390 mConsoleTaskData.mSnapshot->uninit();
12391 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12392 }
12393
12394 mData->mSession.mPID = NIL_RTPROCESS;
12395
12396 if (aReason == Uninit::Unexpected)
12397 {
12398 /* Uninitialization didn't come from #checkForDeath(), so tell the
12399 * client watcher thread to update the set of machines that have open
12400 * sessions. */
12401 mParent->i_updateClientWatcher();
12402 }
12403
12404 /* uninitialize all remote controls */
12405 if (mData->mSession.mRemoteControls.size())
12406 {
12407 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12408 mData->mSession.mRemoteControls.size()));
12409
12410 Data::Session::RemoteControlList::iterator it =
12411 mData->mSession.mRemoteControls.begin();
12412 while (it != mData->mSession.mRemoteControls.end())
12413 {
12414 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12415 HRESULT rc = (*it)->Uninitialize();
12416 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12417 if (FAILED(rc))
12418 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12419 ++it;
12420 }
12421 mData->mSession.mRemoteControls.clear();
12422 }
12423
12424 /* Remove all references to the NAT network service. The service will stop
12425 * if all references (also from other VMs) are removed. */
12426 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12427 {
12428 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12429 {
12430 NetworkAttachmentType_T type;
12431 HRESULT hrc;
12432
12433 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12434 if ( SUCCEEDED(hrc)
12435 && type == NetworkAttachmentType_NATNetwork)
12436 {
12437 Bstr name;
12438 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12439 if (SUCCEEDED(hrc))
12440 {
12441 multilock.release();
12442 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12443 mUserData->s.strName.c_str(), name.raw()));
12444 mParent->i_natNetworkRefDec(name.raw());
12445 multilock.acquire();
12446 }
12447 }
12448 }
12449 }
12450
12451 /*
12452 * An expected uninitialization can come only from #checkForDeath().
12453 * Otherwise it means that something's gone really wrong (for example,
12454 * the Session implementation has released the VirtualBox reference
12455 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12456 * etc). However, it's also possible, that the client releases the IPC
12457 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12458 * but the VirtualBox release event comes first to the server process.
12459 * This case is practically possible, so we should not assert on an
12460 * unexpected uninit, just log a warning.
12461 */
12462
12463 if ((aReason == Uninit::Unexpected))
12464 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12465
12466 if (aReason != Uninit::Normal)
12467 {
12468 mData->mSession.mDirectControl.setNull();
12469 }
12470 else
12471 {
12472 /* this must be null here (see #OnSessionEnd()) */
12473 Assert(mData->mSession.mDirectControl.isNull());
12474 Assert(mData->mSession.mState == SessionState_Unlocking);
12475 Assert(!mData->mSession.mProgress.isNull());
12476 }
12477 if (mData->mSession.mProgress)
12478 {
12479 if (aReason == Uninit::Normal)
12480 mData->mSession.mProgress->i_notifyComplete(S_OK);
12481 else
12482 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12483 COM_IIDOF(ISession),
12484 getComponentName(),
12485 tr("The VM session was aborted"));
12486 mData->mSession.mProgress.setNull();
12487 }
12488
12489 /* remove the association between the peer machine and this session machine */
12490 Assert( (SessionMachine*)mData->mSession.mMachine == this
12491 || aReason == Uninit::Unexpected);
12492
12493 /* reset the rest of session data */
12494 mData->mSession.mMachine.setNull();
12495 mData->mSession.mState = SessionState_Unlocked;
12496 mData->mSession.mType.setNull();
12497
12498 /* destroy the machine client token before leaving the exclusive lock */
12499 if (mClientToken)
12500 {
12501 delete mClientToken;
12502 mClientToken = NULL;
12503 }
12504
12505 /* fire an event */
12506 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12507
12508 uninitDataAndChildObjects();
12509
12510 /* free the essential data structure last */
12511 mData.free();
12512
12513 /* release the exclusive lock before setting the below two to NULL */
12514 multilock.release();
12515
12516 unconst(mParent) = NULL;
12517 unconst(mPeer) = NULL;
12518
12519 LogFlowThisFuncLeave();
12520}
12521
12522// util::Lockable interface
12523////////////////////////////////////////////////////////////////////////////////
12524
12525/**
12526 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12527 * with the primary Machine instance (mPeer).
12528 */
12529RWLockHandle *SessionMachine::lockHandle() const
12530{
12531 AssertReturn(mPeer != NULL, NULL);
12532 return mPeer->lockHandle();
12533}
12534
12535// IInternalMachineControl methods
12536////////////////////////////////////////////////////////////////////////////////
12537
12538/**
12539 * Passes collected guest statistics to performance collector object
12540 */
12541HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12542 ULONG aCpuKernel, ULONG aCpuIdle,
12543 ULONG aMemTotal, ULONG aMemFree,
12544 ULONG aMemBalloon, ULONG aMemShared,
12545 ULONG aMemCache, ULONG aPageTotal,
12546 ULONG aAllocVMM, ULONG aFreeVMM,
12547 ULONG aBalloonedVMM, ULONG aSharedVMM,
12548 ULONG aVmNetRx, ULONG aVmNetTx)
12549{
12550#ifdef VBOX_WITH_RESOURCE_USAGE_API
12551 if (mCollectorGuest)
12552 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12553 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12554 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12555 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12556
12557 return S_OK;
12558#else
12559 NOREF(aValidStats);
12560 NOREF(aCpuUser);
12561 NOREF(aCpuKernel);
12562 NOREF(aCpuIdle);
12563 NOREF(aMemTotal);
12564 NOREF(aMemFree);
12565 NOREF(aMemBalloon);
12566 NOREF(aMemShared);
12567 NOREF(aMemCache);
12568 NOREF(aPageTotal);
12569 NOREF(aAllocVMM);
12570 NOREF(aFreeVMM);
12571 NOREF(aBalloonedVMM);
12572 NOREF(aSharedVMM);
12573 NOREF(aVmNetRx);
12574 NOREF(aVmNetTx);
12575 return E_NOTIMPL;
12576#endif
12577}
12578
12579/**
12580 * @note Locks this object for writing.
12581 */
12582HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12583{
12584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12585
12586 mRemoveSavedState = RT_BOOL(aRemove);
12587
12588 return S_OK;
12589}
12590
12591/**
12592 * @note Locks the same as #i_setMachineState() does.
12593 */
12594HRESULT SessionMachine::updateState(MachineState_T aState)
12595{
12596 return i_setMachineState(aState);
12597}
12598
12599/**
12600 * @note Locks this object for writing.
12601 */
12602HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12603{
12604 IProgress* pProgress(aProgress);
12605
12606 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12607
12608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12609
12610 if (mData->mSession.mState != SessionState_Locked)
12611 return VBOX_E_INVALID_OBJECT_STATE;
12612
12613 if (!mData->mSession.mProgress.isNull())
12614 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12615
12616 /* If we didn't reference the NAT network service yet, add a reference to
12617 * force a start */
12618 if (miNATNetworksStarted < 1)
12619 {
12620 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12621 {
12622 NetworkAttachmentType_T type;
12623 HRESULT hrc;
12624 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12625 if ( SUCCEEDED(hrc)
12626 && type == NetworkAttachmentType_NATNetwork)
12627 {
12628 Bstr name;
12629 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12630 if (SUCCEEDED(hrc))
12631 {
12632 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12633 mUserData->s.strName.c_str(), name.raw()));
12634 mPeer->lockHandle()->unlockWrite();
12635 mParent->i_natNetworkRefInc(name.raw());
12636#ifdef RT_LOCK_STRICT
12637 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12638#else
12639 mPeer->lockHandle()->lockWrite();
12640#endif
12641 }
12642 }
12643 }
12644 miNATNetworksStarted++;
12645 }
12646
12647 LogFlowThisFunc(("returns S_OK.\n"));
12648 return S_OK;
12649}
12650
12651/**
12652 * @note Locks this object for writing.
12653 */
12654HRESULT SessionMachine::endPowerUp(LONG aResult)
12655{
12656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12657
12658 if (mData->mSession.mState != SessionState_Locked)
12659 return VBOX_E_INVALID_OBJECT_STATE;
12660
12661 /* Finalize the LaunchVMProcess progress object. */
12662 if (mData->mSession.mProgress)
12663 {
12664 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12665 mData->mSession.mProgress.setNull();
12666 }
12667
12668 if (SUCCEEDED((HRESULT)aResult))
12669 {
12670#ifdef VBOX_WITH_RESOURCE_USAGE_API
12671 /* The VM has been powered up successfully, so it makes sense
12672 * now to offer the performance metrics for a running machine
12673 * object. Doing it earlier wouldn't be safe. */
12674 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12675 mData->mSession.mPID);
12676#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12677 }
12678
12679 return S_OK;
12680}
12681
12682/**
12683 * @note Locks this object for writing.
12684 */
12685HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12686{
12687 LogFlowThisFuncEnter();
12688
12689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12690
12691 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12692 E_FAIL);
12693
12694 /* create a progress object to track operation completion */
12695 ComObjPtr<Progress> pProgress;
12696 pProgress.createObject();
12697 pProgress->init(i_getVirtualBox(),
12698 static_cast<IMachine *>(this) /* aInitiator */,
12699 Bstr(tr("Stopping the virtual machine")).raw(),
12700 FALSE /* aCancelable */);
12701
12702 /* fill in the console task data */
12703 mConsoleTaskData.mLastState = mData->mMachineState;
12704 mConsoleTaskData.mProgress = pProgress;
12705
12706 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12707 i_setMachineState(MachineState_Stopping);
12708
12709 pProgress.queryInterfaceTo(aProgress.asOutParam());
12710
12711 return S_OK;
12712}
12713
12714/**
12715 * @note Locks this object for writing.
12716 */
12717HRESULT SessionMachine::endPoweringDown(LONG aResult,
12718 const com::Utf8Str &aErrMsg)
12719{
12720 LogFlowThisFuncEnter();
12721
12722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12723
12724 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12725 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12726 && mConsoleTaskData.mLastState != MachineState_Null,
12727 E_FAIL);
12728
12729 /*
12730 * On failure, set the state to the state we had when BeginPoweringDown()
12731 * was called (this is expected by Console::PowerDown() and the associated
12732 * task). On success the VM process already changed the state to
12733 * MachineState_PoweredOff, so no need to do anything.
12734 */
12735 if (FAILED(aResult))
12736 i_setMachineState(mConsoleTaskData.mLastState);
12737
12738 /* notify the progress object about operation completion */
12739 Assert(mConsoleTaskData.mProgress);
12740 if (SUCCEEDED(aResult))
12741 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12742 else
12743 {
12744 if (aErrMsg.length())
12745 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12746 COM_IIDOF(ISession),
12747 getComponentName(),
12748 aErrMsg.c_str());
12749 else
12750 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12751 }
12752
12753 /* clear out the temporary saved state data */
12754 mConsoleTaskData.mLastState = MachineState_Null;
12755 mConsoleTaskData.mProgress.setNull();
12756
12757 LogFlowThisFuncLeave();
12758 return S_OK;
12759}
12760
12761
12762/**
12763 * Goes through the USB filters of the given machine to see if the given
12764 * device matches any filter or not.
12765 *
12766 * @note Locks the same as USBController::hasMatchingFilter() does.
12767 */
12768HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12769 BOOL *aMatched,
12770 ULONG *aMaskedInterfaces)
12771{
12772 LogFlowThisFunc(("\n"));
12773
12774#ifdef VBOX_WITH_USB
12775 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12776#else
12777 NOREF(aDevice);
12778 NOREF(aMaskedInterfaces);
12779 *aMatched = FALSE;
12780#endif
12781
12782 return S_OK;
12783}
12784
12785/**
12786 * @note Locks the same as Host::captureUSBDevice() does.
12787 */
12788HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12789{
12790 LogFlowThisFunc(("\n"));
12791
12792#ifdef VBOX_WITH_USB
12793 /* if captureDeviceForVM() fails, it must have set extended error info */
12794 clearError();
12795 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12796 if (FAILED(rc)) return rc;
12797
12798 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12799 AssertReturn(service, E_FAIL);
12800 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12801#else
12802 NOREF(aId);
12803 return E_NOTIMPL;
12804#endif
12805}
12806
12807/**
12808 * @note Locks the same as Host::detachUSBDevice() does.
12809 */
12810HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12811 BOOL aDone)
12812{
12813 LogFlowThisFunc(("\n"));
12814
12815#ifdef VBOX_WITH_USB
12816 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12817 AssertReturn(service, E_FAIL);
12818 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12819#else
12820 NOREF(aId);
12821 NOREF(aDone);
12822 return E_NOTIMPL;
12823#endif
12824}
12825
12826/**
12827 * Inserts all machine filters to the USB proxy service and then calls
12828 * Host::autoCaptureUSBDevices().
12829 *
12830 * Called by Console from the VM process upon VM startup.
12831 *
12832 * @note Locks what called methods lock.
12833 */
12834HRESULT SessionMachine::autoCaptureUSBDevices()
12835{
12836 LogFlowThisFunc(("\n"));
12837
12838#ifdef VBOX_WITH_USB
12839 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12840 AssertComRC(rc);
12841 NOREF(rc);
12842
12843 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12844 AssertReturn(service, E_FAIL);
12845 return service->autoCaptureDevicesForVM(this);
12846#else
12847 return S_OK;
12848#endif
12849}
12850
12851/**
12852 * Removes all machine filters from the USB proxy service and then calls
12853 * Host::detachAllUSBDevices().
12854 *
12855 * Called by Console from the VM process upon normal VM termination or by
12856 * SessionMachine::uninit() upon abnormal VM termination (from under the
12857 * Machine/SessionMachine lock).
12858 *
12859 * @note Locks what called methods lock.
12860 */
12861HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12862{
12863 LogFlowThisFunc(("\n"));
12864
12865#ifdef VBOX_WITH_USB
12866 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12867 AssertComRC(rc);
12868 NOREF(rc);
12869
12870 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12871 AssertReturn(service, E_FAIL);
12872 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12873#else
12874 NOREF(aDone);
12875 return S_OK;
12876#endif
12877}
12878
12879/**
12880 * @note Locks this object for writing.
12881 */
12882HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12883 ComPtr<IProgress> &aProgress)
12884{
12885 LogFlowThisFuncEnter();
12886
12887 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12888 /*
12889 * We don't assert below because it might happen that a non-direct session
12890 * informs us it is closed right after we've been uninitialized -- it's ok.
12891 */
12892
12893 /* get IInternalSessionControl interface */
12894 ComPtr<IInternalSessionControl> control(aSession);
12895
12896 ComAssertRet(!control.isNull(), E_INVALIDARG);
12897
12898 /* Creating a Progress object requires the VirtualBox lock, and
12899 * thus locking it here is required by the lock order rules. */
12900 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12901
12902 if (control == mData->mSession.mDirectControl)
12903 {
12904 /* The direct session is being normally closed by the client process
12905 * ----------------------------------------------------------------- */
12906
12907 /* go to the closing state (essential for all open*Session() calls and
12908 * for #checkForDeath()) */
12909 Assert(mData->mSession.mState == SessionState_Locked);
12910 mData->mSession.mState = SessionState_Unlocking;
12911
12912 /* set direct control to NULL to release the remote instance */
12913 mData->mSession.mDirectControl.setNull();
12914 LogFlowThisFunc(("Direct control is set to NULL\n"));
12915
12916 if (mData->mSession.mProgress)
12917 {
12918 /* finalize the progress, someone might wait if a frontend
12919 * closes the session before powering on the VM. */
12920 mData->mSession.mProgress->notifyComplete(E_FAIL,
12921 COM_IIDOF(ISession),
12922 getComponentName(),
12923 tr("The VM session was closed before any attempt to power it on"));
12924 mData->mSession.mProgress.setNull();
12925 }
12926
12927 /* Create the progress object the client will use to wait until
12928 * #checkForDeath() is called to uninitialize this session object after
12929 * it releases the IPC semaphore.
12930 * Note! Because we're "reusing" mProgress here, this must be a proxy
12931 * object just like for LaunchVMProcess. */
12932 Assert(mData->mSession.mProgress.isNull());
12933 ComObjPtr<ProgressProxy> progress;
12934 progress.createObject();
12935 ComPtr<IUnknown> pPeer(mPeer);
12936 progress->init(mParent, pPeer,
12937 Bstr(tr("Closing session")).raw(),
12938 FALSE /* aCancelable */);
12939 progress.queryInterfaceTo(aProgress.asOutParam());
12940 mData->mSession.mProgress = progress;
12941 }
12942 else
12943 {
12944 /* the remote session is being normally closed */
12945 Data::Session::RemoteControlList::iterator it =
12946 mData->mSession.mRemoteControls.begin();
12947 while (it != mData->mSession.mRemoteControls.end())
12948 {
12949 if (control == *it)
12950 break;
12951 ++it;
12952 }
12953 BOOL found = it != mData->mSession.mRemoteControls.end();
12954 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12955 E_INVALIDARG);
12956 // This MUST be erase(it), not remove(*it) as the latter triggers a
12957 // very nasty use after free due to the place where the value "lives".
12958 mData->mSession.mRemoteControls.erase(it);
12959 }
12960
12961 /* signal the client watcher thread, because the client is going away */
12962 mParent->i_updateClientWatcher();
12963
12964 LogFlowThisFuncLeave();
12965 return S_OK;
12966}
12967
12968/**
12969 * @note Locks this object for writing.
12970 */
12971HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12972 com::Utf8Str &aStateFilePath)
12973{
12974 LogFlowThisFuncEnter();
12975
12976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12977
12978 AssertReturn( mData->mMachineState == MachineState_Paused
12979 && mConsoleTaskData.mLastState == MachineState_Null
12980 && mConsoleTaskData.strStateFilePath.isEmpty(),
12981 E_FAIL);
12982
12983 /* create a progress object to track operation completion */
12984 ComObjPtr<Progress> pProgress;
12985 pProgress.createObject();
12986 pProgress->init(i_getVirtualBox(),
12987 static_cast<IMachine *>(this) /* aInitiator */,
12988 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12989 FALSE /* aCancelable */);
12990
12991 /* stateFilePath is null when the machine is not running */
12992 if (mData->mMachineState == MachineState_Paused)
12993 i_composeSavedStateFilename(aStateFilePath);
12994
12995 /* fill in the console task data */
12996 mConsoleTaskData.mLastState = mData->mMachineState;
12997 mConsoleTaskData.strStateFilePath = aStateFilePath;
12998 mConsoleTaskData.mProgress = pProgress;
12999
13000 /* set the state to Saving (this is expected by Console::SaveState()) */
13001 i_setMachineState(MachineState_Saving);
13002
13003 pProgress.queryInterfaceTo(aProgress.asOutParam());
13004
13005 return S_OK;
13006}
13007
13008/**
13009 * @note Locks mParent + this object for writing.
13010 */
13011HRESULT SessionMachine::endSavingState(LONG aResult,
13012 const com::Utf8Str &aErrMsg)
13013{
13014 LogFlowThisFunc(("\n"));
13015
13016 /* endSavingState() need mParent lock */
13017 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13018
13019 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13020 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13021 && mConsoleTaskData.mLastState != MachineState_Null
13022 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13023 E_FAIL);
13024
13025 /*
13026 * On failure, set the state to the state we had when BeginSavingState()
13027 * was called (this is expected by Console::SaveState() and the associated
13028 * task). On success the VM process already changed the state to
13029 * MachineState_Saved, so no need to do anything.
13030 */
13031 if (FAILED(aResult))
13032 i_setMachineState(mConsoleTaskData.mLastState);
13033
13034 return i_endSavingState(aResult, aErrMsg);
13035}
13036
13037/**
13038 * @note Locks this object for writing.
13039 */
13040HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13041{
13042 LogFlowThisFunc(("\n"));
13043
13044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13045
13046 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13047 || mData->mMachineState == MachineState_Teleported
13048 || mData->mMachineState == MachineState_Aborted
13049 , E_FAIL); /** @todo setError. */
13050
13051 com::Utf8Str stateFilePathFull;
13052 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13053 if (RT_FAILURE(vrc))
13054 return setError(VBOX_E_FILE_ERROR,
13055 tr("Invalid saved state file path '%s' (%Rrc)"),
13056 aSavedStateFile.c_str(),
13057 vrc);
13058
13059 mSSData->strStateFilePath = stateFilePathFull;
13060
13061 /* The below i_setMachineState() will detect the state transition and will
13062 * update the settings file */
13063
13064 return i_setMachineState(MachineState_Saved);
13065}
13066
13067HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13068 std::vector<com::Utf8Str> &aValues,
13069 std::vector<LONG64> &aTimestamps,
13070 std::vector<com::Utf8Str> &aFlags)
13071{
13072 LogFlowThisFunc(("\n"));
13073
13074#ifdef VBOX_WITH_GUEST_PROPS
13075 using namespace guestProp;
13076
13077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13078
13079 size_t cEntries = mHWData->mGuestProperties.size();
13080 aNames.resize(cEntries);
13081 aValues.resize(cEntries);
13082 aTimestamps.resize(cEntries);
13083 aFlags.resize(cEntries);
13084
13085 size_t i = 0;
13086 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13087 it != mHWData->mGuestProperties.end();
13088 ++it, ++i)
13089 {
13090 char szFlags[MAX_FLAGS_LEN + 1];
13091 aNames[i] = it->first;
13092 aValues[i] = it->second.strValue;
13093 aTimestamps[i] = it->second.mTimestamp;
13094
13095 /* If it is NULL, keep it NULL. */
13096 if (it->second.mFlags)
13097 {
13098 writeFlags(it->second.mFlags, szFlags);
13099 aFlags[i] = szFlags;
13100 }
13101 else
13102 aFlags[i] = "";
13103 }
13104 return S_OK;
13105#else
13106 ReturnComNotImplemented();
13107#endif
13108}
13109
13110HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13111 const com::Utf8Str &aValue,
13112 LONG64 aTimestamp,
13113 const com::Utf8Str &aFlags)
13114{
13115 LogFlowThisFunc(("\n"));
13116
13117#ifdef VBOX_WITH_GUEST_PROPS
13118 using namespace guestProp;
13119
13120 try
13121 {
13122 /*
13123 * Convert input up front.
13124 */
13125 uint32_t fFlags = NILFLAG;
13126 if (aFlags.length())
13127 {
13128 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13129 AssertRCReturn(vrc, E_INVALIDARG);
13130 }
13131
13132 /*
13133 * Now grab the object lock, validate the state and do the update.
13134 */
13135
13136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13137
13138 switch (mData->mMachineState)
13139 {
13140 case MachineState_Paused:
13141 case MachineState_Running:
13142 case MachineState_Teleporting:
13143 case MachineState_TeleportingPausedVM:
13144 case MachineState_LiveSnapshotting:
13145 case MachineState_DeletingSnapshotOnline:
13146 case MachineState_DeletingSnapshotPaused:
13147 case MachineState_Saving:
13148 case MachineState_Stopping:
13149 break;
13150
13151 default:
13152 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13153 VBOX_E_INVALID_VM_STATE);
13154 }
13155
13156 i_setModified(IsModified_MachineData);
13157 mHWData.backup();
13158
13159 bool fDelete = !aValue.length();
13160 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13161 if (it != mHWData->mGuestProperties.end())
13162 {
13163 if (!fDelete)
13164 {
13165 it->second.strValue = aValue;
13166 it->second.mTimestamp = aTimestamp;
13167 it->second.mFlags = fFlags;
13168 }
13169 else
13170 mHWData->mGuestProperties.erase(it);
13171
13172 mData->mGuestPropertiesModified = TRUE;
13173 }
13174 else if (!fDelete)
13175 {
13176 HWData::GuestProperty prop;
13177 prop.strValue = aValue;
13178 prop.mTimestamp = aTimestamp;
13179 prop.mFlags = fFlags;
13180
13181 mHWData->mGuestProperties[aName] = prop;
13182 mData->mGuestPropertiesModified = TRUE;
13183 }
13184
13185 /*
13186 * Send a callback notification if appropriate
13187 */
13188 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13189 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13190 RTSTR_MAX,
13191 aName.c_str(),
13192 RTSTR_MAX, NULL)
13193 )
13194 {
13195 alock.release();
13196
13197 mParent->i_onGuestPropertyChange(mData->mUuid,
13198 Bstr(aName).raw(),
13199 Bstr(aValue).raw(),
13200 Bstr(aFlags).raw());
13201 }
13202 }
13203 catch (...)
13204 {
13205 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13206 }
13207 return S_OK;
13208#else
13209 ReturnComNotImplemented();
13210#endif
13211}
13212
13213
13214HRESULT SessionMachine::lockMedia()
13215{
13216 AutoMultiWriteLock2 alock(this->lockHandle(),
13217 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13218
13219 AssertReturn( mData->mMachineState == MachineState_Starting
13220 || mData->mMachineState == MachineState_Restoring
13221 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13222
13223 clearError();
13224 alock.release();
13225 return i_lockMedia();
13226}
13227
13228HRESULT SessionMachine::unlockMedia()
13229{
13230 HRESULT hrc = i_unlockMedia();
13231 return hrc;
13232}
13233
13234HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13235 ComPtr<IMediumAttachment> &aNewAttachment)
13236{
13237 // request the host lock first, since might be calling Host methods for getting host drives;
13238 // next, protect the media tree all the while we're in here, as well as our member variables
13239 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13240 this->lockHandle(),
13241 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13242
13243 IMediumAttachment *iAttach = aAttachment;
13244 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13245
13246 Bstr ctrlName;
13247 LONG lPort;
13248 LONG lDevice;
13249 bool fTempEject;
13250 {
13251 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13252
13253 /* Need to query the details first, as the IMediumAttachment reference
13254 * might be to the original settings, which we are going to change. */
13255 ctrlName = pAttach->i_getControllerName();
13256 lPort = pAttach->i_getPort();
13257 lDevice = pAttach->i_getDevice();
13258 fTempEject = pAttach->i_getTempEject();
13259 }
13260
13261 if (!fTempEject)
13262 {
13263 /* Remember previously mounted medium. The medium before taking the
13264 * backup is not necessarily the same thing. */
13265 ComObjPtr<Medium> oldmedium;
13266 oldmedium = pAttach->i_getMedium();
13267
13268 i_setModified(IsModified_Storage);
13269 mMediaData.backup();
13270
13271 // The backup operation makes the pAttach reference point to the
13272 // old settings. Re-get the correct reference.
13273 pAttach = i_findAttachment(mMediaData->mAttachments,
13274 ctrlName.raw(),
13275 lPort,
13276 lDevice);
13277
13278 {
13279 AutoCaller autoAttachCaller(this);
13280 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13281
13282 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13283 if (!oldmedium.isNull())
13284 oldmedium->i_removeBackReference(mData->mUuid);
13285
13286 pAttach->i_updateMedium(NULL);
13287 pAttach->i_updateEjected();
13288 }
13289
13290 i_setModified(IsModified_Storage);
13291 }
13292 else
13293 {
13294 {
13295 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13296 pAttach->i_updateEjected();
13297 }
13298 }
13299
13300 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13301
13302 return S_OK;
13303}
13304
13305// public methods only for internal purposes
13306/////////////////////////////////////////////////////////////////////////////
13307
13308#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13309/**
13310 * Called from the client watcher thread to check for expected or unexpected
13311 * death of the client process that has a direct session to this machine.
13312 *
13313 * On Win32 and on OS/2, this method is called only when we've got the
13314 * mutex (i.e. the client has either died or terminated normally) so it always
13315 * returns @c true (the client is terminated, the session machine is
13316 * uninitialized).
13317 *
13318 * On other platforms, the method returns @c true if the client process has
13319 * terminated normally or abnormally and the session machine was uninitialized,
13320 * and @c false if the client process is still alive.
13321 *
13322 * @note Locks this object for writing.
13323 */
13324bool SessionMachine::i_checkForDeath()
13325{
13326 Uninit::Reason reason;
13327 bool terminated = false;
13328
13329 /* Enclose autoCaller with a block because calling uninit() from under it
13330 * will deadlock. */
13331 {
13332 AutoCaller autoCaller(this);
13333 if (!autoCaller.isOk())
13334 {
13335 /* return true if not ready, to cause the client watcher to exclude
13336 * the corresponding session from watching */
13337 LogFlowThisFunc(("Already uninitialized!\n"));
13338 return true;
13339 }
13340
13341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13342
13343 /* Determine the reason of death: if the session state is Closing here,
13344 * everything is fine. Otherwise it means that the client did not call
13345 * OnSessionEnd() before it released the IPC semaphore. This may happen
13346 * either because the client process has abnormally terminated, or
13347 * because it simply forgot to call ISession::Close() before exiting. We
13348 * threat the latter also as an abnormal termination (see
13349 * Session::uninit() for details). */
13350 reason = mData->mSession.mState == SessionState_Unlocking ?
13351 Uninit::Normal :
13352 Uninit::Abnormal;
13353
13354 if (mClientToken)
13355 terminated = mClientToken->release();
13356 } /* AutoCaller block */
13357
13358 if (terminated)
13359 uninit(reason);
13360
13361 return terminated;
13362}
13363
13364void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13365{
13366 LogFlowThisFunc(("\n"));
13367
13368 strTokenId.setNull();
13369
13370 AutoCaller autoCaller(this);
13371 AssertComRCReturnVoid(autoCaller.rc());
13372
13373 Assert(mClientToken);
13374 if (mClientToken)
13375 mClientToken->getId(strTokenId);
13376}
13377#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13378IToken *SessionMachine::i_getToken()
13379{
13380 LogFlowThisFunc(("\n"));
13381
13382 AutoCaller autoCaller(this);
13383 AssertComRCReturn(autoCaller.rc(), NULL);
13384
13385 Assert(mClientToken);
13386 if (mClientToken)
13387 return mClientToken->getToken();
13388 else
13389 return NULL;
13390}
13391#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13392
13393Machine::ClientToken *SessionMachine::i_getClientToken()
13394{
13395 LogFlowThisFunc(("\n"));
13396
13397 AutoCaller autoCaller(this);
13398 AssertComRCReturn(autoCaller.rc(), NULL);
13399
13400 return mClientToken;
13401}
13402
13403
13404/**
13405 * @note Locks this object for reading.
13406 */
13407HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13408{
13409 LogFlowThisFunc(("\n"));
13410
13411 AutoCaller autoCaller(this);
13412 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13413
13414 ComPtr<IInternalSessionControl> directControl;
13415 {
13416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13417 directControl = mData->mSession.mDirectControl;
13418 }
13419
13420 /* ignore notifications sent after #OnSessionEnd() is called */
13421 if (!directControl)
13422 return S_OK;
13423
13424 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13425}
13426
13427/**
13428 * @note Locks this object for reading.
13429 */
13430HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13431 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13432 IN_BSTR aGuestIp, LONG aGuestPort)
13433{
13434 LogFlowThisFunc(("\n"));
13435
13436 AutoCaller autoCaller(this);
13437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13438
13439 ComPtr<IInternalSessionControl> directControl;
13440 {
13441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13442 directControl = mData->mSession.mDirectControl;
13443 }
13444
13445 /* ignore notifications sent after #OnSessionEnd() is called */
13446 if (!directControl)
13447 return S_OK;
13448 /*
13449 * instead acting like callback we ask IVirtualBox deliver corresponding event
13450 */
13451
13452 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13453 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13454 return S_OK;
13455}
13456
13457/**
13458 * @note Locks this object for reading.
13459 */
13460HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13461{
13462 LogFlowThisFunc(("\n"));
13463
13464 AutoCaller autoCaller(this);
13465 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13466
13467 ComPtr<IInternalSessionControl> directControl;
13468 {
13469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13470 directControl = mData->mSession.mDirectControl;
13471 }
13472
13473 /* ignore notifications sent after #OnSessionEnd() is called */
13474 if (!directControl)
13475 return S_OK;
13476
13477 return directControl->OnSerialPortChange(serialPort);
13478}
13479
13480/**
13481 * @note Locks this object for reading.
13482 */
13483HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13484{
13485 LogFlowThisFunc(("\n"));
13486
13487 AutoCaller autoCaller(this);
13488 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13489
13490 ComPtr<IInternalSessionControl> directControl;
13491 {
13492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13493 directControl = mData->mSession.mDirectControl;
13494 }
13495
13496 /* ignore notifications sent after #OnSessionEnd() is called */
13497 if (!directControl)
13498 return S_OK;
13499
13500 return directControl->OnParallelPortChange(parallelPort);
13501}
13502
13503/**
13504 * @note Locks this object for reading.
13505 */
13506HRESULT SessionMachine::i_onStorageControllerChange()
13507{
13508 LogFlowThisFunc(("\n"));
13509
13510 AutoCaller autoCaller(this);
13511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13512
13513 ComPtr<IInternalSessionControl> directControl;
13514 {
13515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13516 directControl = mData->mSession.mDirectControl;
13517 }
13518
13519 /* ignore notifications sent after #OnSessionEnd() is called */
13520 if (!directControl)
13521 return S_OK;
13522
13523 return directControl->OnStorageControllerChange();
13524}
13525
13526/**
13527 * @note Locks this object for reading.
13528 */
13529HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13530{
13531 LogFlowThisFunc(("\n"));
13532
13533 AutoCaller autoCaller(this);
13534 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13535
13536 ComPtr<IInternalSessionControl> directControl;
13537 {
13538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13539 directControl = mData->mSession.mDirectControl;
13540 }
13541
13542 /* ignore notifications sent after #OnSessionEnd() is called */
13543 if (!directControl)
13544 return S_OK;
13545
13546 return directControl->OnMediumChange(aAttachment, aForce);
13547}
13548
13549/**
13550 * @note Locks this object for reading.
13551 */
13552HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13553{
13554 LogFlowThisFunc(("\n"));
13555
13556 AutoCaller autoCaller(this);
13557 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13558
13559 ComPtr<IInternalSessionControl> directControl;
13560 {
13561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13562 directControl = mData->mSession.mDirectControl;
13563 }
13564
13565 /* ignore notifications sent after #OnSessionEnd() is called */
13566 if (!directControl)
13567 return S_OK;
13568
13569 return directControl->OnCPUChange(aCPU, aRemove);
13570}
13571
13572HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13573{
13574 LogFlowThisFunc(("\n"));
13575
13576 AutoCaller autoCaller(this);
13577 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13578
13579 ComPtr<IInternalSessionControl> directControl;
13580 {
13581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13582 directControl = mData->mSession.mDirectControl;
13583 }
13584
13585 /* ignore notifications sent after #OnSessionEnd() is called */
13586 if (!directControl)
13587 return S_OK;
13588
13589 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13590}
13591
13592/**
13593 * @note Locks this object for reading.
13594 */
13595HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13596{
13597 LogFlowThisFunc(("\n"));
13598
13599 AutoCaller autoCaller(this);
13600 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13601
13602 ComPtr<IInternalSessionControl> directControl;
13603 {
13604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13605 directControl = mData->mSession.mDirectControl;
13606 }
13607
13608 /* ignore notifications sent after #OnSessionEnd() is called */
13609 if (!directControl)
13610 return S_OK;
13611
13612 return directControl->OnVRDEServerChange(aRestart);
13613}
13614
13615/**
13616 * @note Locks this object for reading.
13617 */
13618HRESULT SessionMachine::i_onVideoCaptureChange()
13619{
13620 LogFlowThisFunc(("\n"));
13621
13622 AutoCaller autoCaller(this);
13623 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13624
13625 ComPtr<IInternalSessionControl> directControl;
13626 {
13627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13628 directControl = mData->mSession.mDirectControl;
13629 }
13630
13631 /* ignore notifications sent after #OnSessionEnd() is called */
13632 if (!directControl)
13633 return S_OK;
13634
13635 return directControl->OnVideoCaptureChange();
13636}
13637
13638/**
13639 * @note Locks this object for reading.
13640 */
13641HRESULT SessionMachine::i_onUSBControllerChange()
13642{
13643 LogFlowThisFunc(("\n"));
13644
13645 AutoCaller autoCaller(this);
13646 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13647
13648 ComPtr<IInternalSessionControl> directControl;
13649 {
13650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13651 directControl = mData->mSession.mDirectControl;
13652 }
13653
13654 /* ignore notifications sent after #OnSessionEnd() is called */
13655 if (!directControl)
13656 return S_OK;
13657
13658 return directControl->OnUSBControllerChange();
13659}
13660
13661/**
13662 * @note Locks this object for reading.
13663 */
13664HRESULT SessionMachine::i_onSharedFolderChange()
13665{
13666 LogFlowThisFunc(("\n"));
13667
13668 AutoCaller autoCaller(this);
13669 AssertComRCReturnRC(autoCaller.rc());
13670
13671 ComPtr<IInternalSessionControl> directControl;
13672 {
13673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13674 directControl = mData->mSession.mDirectControl;
13675 }
13676
13677 /* ignore notifications sent after #OnSessionEnd() is called */
13678 if (!directControl)
13679 return S_OK;
13680
13681 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13682}
13683
13684/**
13685 * @note Locks this object for reading.
13686 */
13687HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13688{
13689 LogFlowThisFunc(("\n"));
13690
13691 AutoCaller autoCaller(this);
13692 AssertComRCReturnRC(autoCaller.rc());
13693
13694 ComPtr<IInternalSessionControl> directControl;
13695 {
13696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13697 directControl = mData->mSession.mDirectControl;
13698 }
13699
13700 /* ignore notifications sent after #OnSessionEnd() is called */
13701 if (!directControl)
13702 return S_OK;
13703
13704 return directControl->OnClipboardModeChange(aClipboardMode);
13705}
13706
13707/**
13708 * @note Locks this object for reading.
13709 */
13710HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13711{
13712 LogFlowThisFunc(("\n"));
13713
13714 AutoCaller autoCaller(this);
13715 AssertComRCReturnRC(autoCaller.rc());
13716
13717 ComPtr<IInternalSessionControl> directControl;
13718 {
13719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13720 directControl = mData->mSession.mDirectControl;
13721 }
13722
13723 /* ignore notifications sent after #OnSessionEnd() is called */
13724 if (!directControl)
13725 return S_OK;
13726
13727 return directControl->OnDnDModeChange(aDnDMode);
13728}
13729
13730/**
13731 * @note Locks this object for reading.
13732 */
13733HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13734{
13735 LogFlowThisFunc(("\n"));
13736
13737 AutoCaller autoCaller(this);
13738 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13739
13740 ComPtr<IInternalSessionControl> directControl;
13741 {
13742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13743 directControl = mData->mSession.mDirectControl;
13744 }
13745
13746 /* ignore notifications sent after #OnSessionEnd() is called */
13747 if (!directControl)
13748 return S_OK;
13749
13750 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13751}
13752
13753/**
13754 * @note Locks this object for reading.
13755 */
13756HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13757{
13758 LogFlowThisFunc(("\n"));
13759
13760 AutoCaller autoCaller(this);
13761 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13762
13763 ComPtr<IInternalSessionControl> directControl;
13764 {
13765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13766 directControl = mData->mSession.mDirectControl;
13767 }
13768
13769 /* ignore notifications sent after #OnSessionEnd() is called */
13770 if (!directControl)
13771 return S_OK;
13772
13773 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13774}
13775
13776/**
13777 * Returns @c true if this machine's USB controller reports it has a matching
13778 * filter for the given USB device and @c false otherwise.
13779 *
13780 * @note locks this object for reading.
13781 */
13782bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13783{
13784 AutoCaller autoCaller(this);
13785 /* silently return if not ready -- this method may be called after the
13786 * direct machine session has been called */
13787 if (!autoCaller.isOk())
13788 return false;
13789
13790#ifdef VBOX_WITH_USB
13791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13792
13793 switch (mData->mMachineState)
13794 {
13795 case MachineState_Starting:
13796 case MachineState_Restoring:
13797 case MachineState_TeleportingIn:
13798 case MachineState_Paused:
13799 case MachineState_Running:
13800 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13801 * elsewhere... */
13802 alock.release();
13803 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13804 default: break;
13805 }
13806#else
13807 NOREF(aDevice);
13808 NOREF(aMaskedIfs);
13809#endif
13810 return false;
13811}
13812
13813/**
13814 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13815 */
13816HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13817 IVirtualBoxErrorInfo *aError,
13818 ULONG aMaskedIfs,
13819 const com::Utf8Str &aCaptureFilename)
13820{
13821 LogFlowThisFunc(("\n"));
13822
13823 AutoCaller autoCaller(this);
13824
13825 /* This notification may happen after the machine object has been
13826 * uninitialized (the session was closed), so don't assert. */
13827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13828
13829 ComPtr<IInternalSessionControl> directControl;
13830 {
13831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13832 directControl = mData->mSession.mDirectControl;
13833 }
13834
13835 /* fail on notifications sent after #OnSessionEnd() is called, it is
13836 * expected by the caller */
13837 if (!directControl)
13838 return E_FAIL;
13839
13840 /* No locks should be held at this point. */
13841 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13842 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13843
13844 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13845}
13846
13847/**
13848 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13849 */
13850HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13851 IVirtualBoxErrorInfo *aError)
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856
13857 /* This notification may happen after the machine object has been
13858 * uninitialized (the session was closed), so don't assert. */
13859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13860
13861 ComPtr<IInternalSessionControl> directControl;
13862 {
13863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13864 directControl = mData->mSession.mDirectControl;
13865 }
13866
13867 /* fail on notifications sent after #OnSessionEnd() is called, it is
13868 * expected by the caller */
13869 if (!directControl)
13870 return E_FAIL;
13871
13872 /* No locks should be held at this point. */
13873 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13874 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13875
13876 return directControl->OnUSBDeviceDetach(aId, aError);
13877}
13878
13879// protected methods
13880/////////////////////////////////////////////////////////////////////////////
13881
13882/**
13883 * Helper method to finalize saving the state.
13884 *
13885 * @note Must be called from under this object's lock.
13886 *
13887 * @param aRc S_OK if the snapshot has been taken successfully
13888 * @param aErrMsg human readable error message for failure
13889 *
13890 * @note Locks mParent + this objects for writing.
13891 */
13892HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13893{
13894 LogFlowThisFuncEnter();
13895
13896 AutoCaller autoCaller(this);
13897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13898
13899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13900
13901 HRESULT rc = S_OK;
13902
13903 if (SUCCEEDED(aRc))
13904 {
13905 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13906
13907 /* save all VM settings */
13908 rc = i_saveSettings(NULL);
13909 // no need to check whether VirtualBox.xml needs saving also since
13910 // we can't have a name change pending at this point
13911 }
13912 else
13913 {
13914 // delete the saved state file (it might have been already created);
13915 // we need not check whether this is shared with a snapshot here because
13916 // we certainly created this saved state file here anew
13917 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13918 }
13919
13920 /* notify the progress object about operation completion */
13921 Assert(mConsoleTaskData.mProgress);
13922 if (SUCCEEDED(aRc))
13923 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13924 else
13925 {
13926 if (aErrMsg.length())
13927 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13928 COM_IIDOF(ISession),
13929 getComponentName(),
13930 aErrMsg.c_str());
13931 else
13932 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13933 }
13934
13935 /* clear out the temporary saved state data */
13936 mConsoleTaskData.mLastState = MachineState_Null;
13937 mConsoleTaskData.strStateFilePath.setNull();
13938 mConsoleTaskData.mProgress.setNull();
13939
13940 LogFlowThisFuncLeave();
13941 return rc;
13942}
13943
13944/**
13945 * Deletes the given file if it is no longer in use by either the current machine state
13946 * (if the machine is "saved") or any of the machine's snapshots.
13947 *
13948 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13949 * but is different for each SnapshotMachine. When calling this, the order of calling this
13950 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13951 * is therefore critical. I know, it's all rather messy.
13952 *
13953 * @param strStateFile
13954 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13955 * the test for whether the saved state file is in use.
13956 */
13957void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13958 Snapshot *pSnapshotToIgnore)
13959{
13960 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13961 if ( (strStateFile.isNotEmpty())
13962 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13963 )
13964 // ... and it must also not be shared with other snapshots
13965 if ( !mData->mFirstSnapshot
13966 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13967 // this checks the SnapshotMachine's state file paths
13968 )
13969 RTFileDelete(strStateFile.c_str());
13970}
13971
13972/**
13973 * Locks the attached media.
13974 *
13975 * All attached hard disks are locked for writing and DVD/floppy are locked for
13976 * reading. Parents of attached hard disks (if any) are locked for reading.
13977 *
13978 * This method also performs accessibility check of all media it locks: if some
13979 * media is inaccessible, the method will return a failure and a bunch of
13980 * extended error info objects per each inaccessible medium.
13981 *
13982 * Note that this method is atomic: if it returns a success, all media are
13983 * locked as described above; on failure no media is locked at all (all
13984 * succeeded individual locks will be undone).
13985 *
13986 * The caller is responsible for doing the necessary state sanity checks.
13987 *
13988 * The locks made by this method must be undone by calling #unlockMedia() when
13989 * no more needed.
13990 */
13991HRESULT SessionMachine::i_lockMedia()
13992{
13993 AutoCaller autoCaller(this);
13994 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13995
13996 AutoMultiWriteLock2 alock(this->lockHandle(),
13997 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13998
13999 /* bail out if trying to lock things with already set up locking */
14000 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14001
14002 MultiResult mrc(S_OK);
14003
14004 /* Collect locking information for all medium objects attached to the VM. */
14005 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14006 it != mMediaData->mAttachments.end();
14007 ++it)
14008 {
14009 MediumAttachment* pAtt = *it;
14010 DeviceType_T devType = pAtt->i_getType();
14011 Medium *pMedium = pAtt->i_getMedium();
14012
14013 MediumLockList *pMediumLockList(new MediumLockList());
14014 // There can be attachments without a medium (floppy/dvd), and thus
14015 // it's impossible to create a medium lock list. It still makes sense
14016 // to have the empty medium lock list in the map in case a medium is
14017 // attached later.
14018 if (pMedium != NULL)
14019 {
14020 MediumType_T mediumType = pMedium->i_getType();
14021 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14022 || mediumType == MediumType_Shareable;
14023 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14024
14025 alock.release();
14026 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14027 !fIsReadOnlyLock /* fMediumLockWrite */,
14028 NULL,
14029 *pMediumLockList);
14030 alock.acquire();
14031 if (FAILED(mrc))
14032 {
14033 delete pMediumLockList;
14034 mData->mSession.mLockedMedia.Clear();
14035 break;
14036 }
14037 }
14038
14039 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14040 if (FAILED(rc))
14041 {
14042 mData->mSession.mLockedMedia.Clear();
14043 mrc = setError(rc,
14044 tr("Collecting locking information for all attached media failed"));
14045 break;
14046 }
14047 }
14048
14049 if (SUCCEEDED(mrc))
14050 {
14051 /* Now lock all media. If this fails, nothing is locked. */
14052 alock.release();
14053 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14054 alock.acquire();
14055 if (FAILED(rc))
14056 {
14057 mrc = setError(rc,
14058 tr("Locking of attached media failed"));
14059 }
14060 }
14061
14062 return mrc;
14063}
14064
14065/**
14066 * Undoes the locks made by by #lockMedia().
14067 */
14068HRESULT SessionMachine::i_unlockMedia()
14069{
14070 AutoCaller autoCaller(this);
14071 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14072
14073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14074
14075 /* we may be holding important error info on the current thread;
14076 * preserve it */
14077 ErrorInfoKeeper eik;
14078
14079 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14080 AssertComRC(rc);
14081 return rc;
14082}
14083
14084/**
14085 * Helper to change the machine state (reimplementation).
14086 *
14087 * @note Locks this object for writing.
14088 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14089 * it can cause crashes in random places due to unexpectedly committing
14090 * the current settings. The caller is responsible for that. The call
14091 * to saveStateSettings is fine, because this method does not commit.
14092 */
14093HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14094{
14095 LogFlowThisFuncEnter();
14096 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14100
14101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14102
14103 MachineState_T oldMachineState = mData->mMachineState;
14104
14105 AssertMsgReturn(oldMachineState != aMachineState,
14106 ("oldMachineState=%s, aMachineState=%s\n",
14107 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14108 E_FAIL);
14109
14110 HRESULT rc = S_OK;
14111
14112 int stsFlags = 0;
14113 bool deleteSavedState = false;
14114
14115 /* detect some state transitions */
14116
14117 if ( ( oldMachineState == MachineState_Saved
14118 && aMachineState == MachineState_Restoring)
14119 || ( ( oldMachineState == MachineState_PoweredOff
14120 || oldMachineState == MachineState_Teleported
14121 || oldMachineState == MachineState_Aborted
14122 )
14123 && ( aMachineState == MachineState_TeleportingIn
14124 || aMachineState == MachineState_Starting
14125 )
14126 )
14127 )
14128 {
14129 /* The EMT thread is about to start */
14130
14131 /* Nothing to do here for now... */
14132
14133 /// @todo NEWMEDIA don't let mDVDDrive and other children
14134 /// change anything when in the Starting/Restoring state
14135 }
14136 else if ( ( oldMachineState == MachineState_Running
14137 || oldMachineState == MachineState_Paused
14138 || oldMachineState == MachineState_Teleporting
14139 || oldMachineState == MachineState_LiveSnapshotting
14140 || oldMachineState == MachineState_Stuck
14141 || oldMachineState == MachineState_Starting
14142 || oldMachineState == MachineState_Stopping
14143 || oldMachineState == MachineState_Saving
14144 || oldMachineState == MachineState_Restoring
14145 || oldMachineState == MachineState_TeleportingPausedVM
14146 || oldMachineState == MachineState_TeleportingIn
14147 )
14148 && ( aMachineState == MachineState_PoweredOff
14149 || aMachineState == MachineState_Saved
14150 || aMachineState == MachineState_Teleported
14151 || aMachineState == MachineState_Aborted
14152 )
14153 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14154 * snapshot */
14155 && ( mConsoleTaskData.mSnapshot.isNull()
14156 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14157 )
14158 )
14159 {
14160 /* The EMT thread has just stopped, unlock attached media. Note that as
14161 * opposed to locking that is done from Console, we do unlocking here
14162 * because the VM process may have aborted before having a chance to
14163 * properly unlock all media it locked. */
14164
14165 unlockMedia();
14166 }
14167
14168 if (oldMachineState == MachineState_Restoring)
14169 {
14170 if (aMachineState != MachineState_Saved)
14171 {
14172 /*
14173 * delete the saved state file once the machine has finished
14174 * restoring from it (note that Console sets the state from
14175 * Restoring to Saved if the VM couldn't restore successfully,
14176 * to give the user an ability to fix an error and retry --
14177 * we keep the saved state file in this case)
14178 */
14179 deleteSavedState = true;
14180 }
14181 }
14182 else if ( oldMachineState == MachineState_Saved
14183 && ( aMachineState == MachineState_PoweredOff
14184 || aMachineState == MachineState_Aborted
14185 || aMachineState == MachineState_Teleported
14186 )
14187 )
14188 {
14189 /*
14190 * delete the saved state after Console::ForgetSavedState() is called
14191 * or if the VM process (owning a direct VM session) crashed while the
14192 * VM was Saved
14193 */
14194
14195 /// @todo (dmik)
14196 // Not sure that deleting the saved state file just because of the
14197 // client death before it attempted to restore the VM is a good
14198 // thing. But when it crashes we need to go to the Aborted state
14199 // which cannot have the saved state file associated... The only
14200 // way to fix this is to make the Aborted condition not a VM state
14201 // but a bool flag: i.e., when a crash occurs, set it to true and
14202 // change the state to PoweredOff or Saved depending on the
14203 // saved state presence.
14204
14205 deleteSavedState = true;
14206 mData->mCurrentStateModified = TRUE;
14207 stsFlags |= SaveSTS_CurStateModified;
14208 }
14209
14210 if ( aMachineState == MachineState_Starting
14211 || aMachineState == MachineState_Restoring
14212 || aMachineState == MachineState_TeleportingIn
14213 )
14214 {
14215 /* set the current state modified flag to indicate that the current
14216 * state is no more identical to the state in the
14217 * current snapshot */
14218 if (!mData->mCurrentSnapshot.isNull())
14219 {
14220 mData->mCurrentStateModified = TRUE;
14221 stsFlags |= SaveSTS_CurStateModified;
14222 }
14223 }
14224
14225 if (deleteSavedState)
14226 {
14227 if (mRemoveSavedState)
14228 {
14229 Assert(!mSSData->strStateFilePath.isEmpty());
14230
14231 // it is safe to delete the saved state file if ...
14232 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14233 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14234 // ... none of the snapshots share the saved state file
14235 )
14236 RTFileDelete(mSSData->strStateFilePath.c_str());
14237 }
14238
14239 mSSData->strStateFilePath.setNull();
14240 stsFlags |= SaveSTS_StateFilePath;
14241 }
14242
14243 /* redirect to the underlying peer machine */
14244 mPeer->i_setMachineState(aMachineState);
14245
14246 if ( aMachineState == MachineState_PoweredOff
14247 || aMachineState == MachineState_Teleported
14248 || aMachineState == MachineState_Aborted
14249 || aMachineState == MachineState_Saved)
14250 {
14251 /* the machine has stopped execution
14252 * (or the saved state file was adopted) */
14253 stsFlags |= SaveSTS_StateTimeStamp;
14254 }
14255
14256 if ( ( oldMachineState == MachineState_PoweredOff
14257 || oldMachineState == MachineState_Aborted
14258 || oldMachineState == MachineState_Teleported
14259 )
14260 && aMachineState == MachineState_Saved)
14261 {
14262 /* the saved state file was adopted */
14263 Assert(!mSSData->strStateFilePath.isEmpty());
14264 stsFlags |= SaveSTS_StateFilePath;
14265 }
14266
14267#ifdef VBOX_WITH_GUEST_PROPS
14268 if ( aMachineState == MachineState_PoweredOff
14269 || aMachineState == MachineState_Aborted
14270 || aMachineState == MachineState_Teleported)
14271 {
14272 /* Make sure any transient guest properties get removed from the
14273 * property store on shutdown. */
14274 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14275
14276 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14277 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14278 while (it != llGuestProperties.end())
14279 {
14280 const settings::GuestProperty &prop = *it;
14281 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14282 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14283 {
14284 it = llGuestProperties.erase(it);
14285 fNeedsSaving = true;
14286 }
14287 else
14288 {
14289 ++it;
14290 }
14291 }
14292
14293 if (fNeedsSaving)
14294 {
14295 mData->mCurrentStateModified = TRUE;
14296 stsFlags |= SaveSTS_CurStateModified;
14297 }
14298 }
14299#endif /* VBOX_WITH_GUEST_PROPS */
14300
14301 rc = i_saveStateSettings(stsFlags);
14302
14303 if ( ( oldMachineState != MachineState_PoweredOff
14304 && oldMachineState != MachineState_Aborted
14305 && oldMachineState != MachineState_Teleported
14306 )
14307 && ( aMachineState == MachineState_PoweredOff
14308 || aMachineState == MachineState_Aborted
14309 || aMachineState == MachineState_Teleported
14310 )
14311 )
14312 {
14313 /* we've been shut down for any reason */
14314 /* no special action so far */
14315 }
14316
14317 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14318 LogFlowThisFuncLeave();
14319 return rc;
14320}
14321
14322/**
14323 * Sends the current machine state value to the VM process.
14324 *
14325 * @note Locks this object for reading, then calls a client process.
14326 */
14327HRESULT SessionMachine::i_updateMachineStateOnClient()
14328{
14329 AutoCaller autoCaller(this);
14330 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14331
14332 ComPtr<IInternalSessionControl> directControl;
14333 {
14334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14335 AssertReturn(!!mData, E_FAIL);
14336 directControl = mData->mSession.mDirectControl;
14337
14338 /* directControl may be already set to NULL here in #OnSessionEnd()
14339 * called too early by the direct session process while there is still
14340 * some operation (like deleting the snapshot) in progress. The client
14341 * process in this case is waiting inside Session::close() for the
14342 * "end session" process object to complete, while #uninit() called by
14343 * #checkForDeath() on the Watcher thread is waiting for the pending
14344 * operation to complete. For now, we accept this inconsistent behavior
14345 * and simply do nothing here. */
14346
14347 if (mData->mSession.mState == SessionState_Unlocking)
14348 return S_OK;
14349
14350 AssertReturn(!directControl.isNull(), E_FAIL);
14351 }
14352
14353 return directControl->UpdateMachineState(mData->mMachineState);
14354}
14355
14356HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14357{
14358 NOREF(aRemove);
14359 ReturnComNotImplemented();
14360}
14361
14362HRESULT Machine::updateState(MachineState_T aState)
14363{
14364 NOREF(aState);
14365 ReturnComNotImplemented();
14366}
14367
14368HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14369{
14370 NOREF(aProgress);
14371 ReturnComNotImplemented();
14372}
14373
14374HRESULT Machine::endPowerUp(LONG aResult)
14375{
14376 NOREF(aResult);
14377 ReturnComNotImplemented();
14378}
14379
14380HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14381{
14382 NOREF(aProgress);
14383 ReturnComNotImplemented();
14384}
14385
14386HRESULT Machine::endPoweringDown(LONG aResult,
14387 const com::Utf8Str &aErrMsg)
14388{
14389 NOREF(aResult);
14390 NOREF(aErrMsg);
14391 ReturnComNotImplemented();
14392}
14393
14394HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14395 BOOL *aMatched,
14396 ULONG *aMaskedInterfaces)
14397{
14398 NOREF(aDevice);
14399 NOREF(aMatched);
14400 NOREF(aMaskedInterfaces);
14401 ReturnComNotImplemented();
14402
14403}
14404
14405HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14406{
14407 NOREF(aId); NOREF(aCaptureFilename);
14408 ReturnComNotImplemented();
14409}
14410
14411HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14412 BOOL aDone)
14413{
14414 NOREF(aId);
14415 NOREF(aDone);
14416 ReturnComNotImplemented();
14417}
14418
14419HRESULT Machine::autoCaptureUSBDevices()
14420{
14421 ReturnComNotImplemented();
14422}
14423
14424HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14425{
14426 NOREF(aDone);
14427 ReturnComNotImplemented();
14428}
14429
14430HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14431 ComPtr<IProgress> &aProgress)
14432{
14433 NOREF(aSession);
14434 NOREF(aProgress);
14435 ReturnComNotImplemented();
14436}
14437
14438HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14439 com::Utf8Str &aStateFilePath)
14440{
14441 NOREF(aProgress);
14442 NOREF(aStateFilePath);
14443 ReturnComNotImplemented();
14444}
14445
14446HRESULT Machine::endSavingState(LONG aResult,
14447 const com::Utf8Str &aErrMsg)
14448{
14449 NOREF(aResult);
14450 NOREF(aErrMsg);
14451 ReturnComNotImplemented();
14452}
14453
14454HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14455{
14456 NOREF(aSavedStateFile);
14457 ReturnComNotImplemented();
14458}
14459
14460HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14461 const com::Utf8Str &aName,
14462 const com::Utf8Str &aDescription,
14463 const ComPtr<IProgress> &aConsoleProgress,
14464 BOOL aFTakingSnapshotOnline,
14465 com::Utf8Str &aStateFilePath)
14466{
14467 NOREF(aInitiator);
14468 NOREF(aName);
14469 NOREF(aDescription);
14470 NOREF(aConsoleProgress);
14471 NOREF(aFTakingSnapshotOnline);
14472 NOREF(aStateFilePath);
14473 ReturnComNotImplemented();
14474}
14475
14476HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14477{
14478 NOREF(aSuccess);
14479 ReturnComNotImplemented();
14480}
14481
14482HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14483 const com::Guid &aStartId,
14484 const com::Guid &aEndId,
14485 BOOL aDeleteAllChildren,
14486 MachineState_T *aMachineState,
14487 ComPtr<IProgress> &aProgress)
14488{
14489 NOREF(aInitiator);
14490 NOREF(aStartId);
14491 NOREF(aEndId);
14492 NOREF(aDeleteAllChildren);
14493 NOREF(aMachineState);
14494 NOREF(aProgress);
14495 ReturnComNotImplemented();
14496}
14497
14498HRESULT Machine::finishOnlineMergeMedium()
14499{
14500 ReturnComNotImplemented();
14501}
14502
14503HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14504 const ComPtr<ISnapshot> &aSnapshot,
14505 MachineState_T *aMachineState,
14506 ComPtr<IProgress> &aProgress)
14507{
14508 NOREF(aInitiator);
14509 NOREF(aSnapshot);
14510 NOREF(aMachineState);
14511 NOREF(aProgress);
14512 ReturnComNotImplemented();
14513}
14514
14515HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14516 std::vector<com::Utf8Str> &aValues,
14517 std::vector<LONG64> &aTimestamps,
14518 std::vector<com::Utf8Str> &aFlags)
14519{
14520 NOREF(aNames);
14521 NOREF(aValues);
14522 NOREF(aTimestamps);
14523 NOREF(aFlags);
14524 ReturnComNotImplemented();
14525}
14526
14527HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14528 const com::Utf8Str &aValue,
14529 LONG64 aTimestamp,
14530 const com::Utf8Str &aFlags)
14531{
14532 NOREF(aName);
14533 NOREF(aValue);
14534 NOREF(aTimestamp);
14535 NOREF(aFlags);
14536 ReturnComNotImplemented();
14537}
14538
14539HRESULT Machine::lockMedia()
14540{
14541 ReturnComNotImplemented();
14542}
14543
14544HRESULT Machine::unlockMedia()
14545{
14546 ReturnComNotImplemented();
14547}
14548
14549HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14550 ComPtr<IMediumAttachment> &aNewAttachment)
14551{
14552 NOREF(aAttachment);
14553 NOREF(aNewAttachment);
14554 ReturnComNotImplemented();
14555}
14556
14557HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14558 ULONG aCpuUser,
14559 ULONG aCpuKernel,
14560 ULONG aCpuIdle,
14561 ULONG aMemTotal,
14562 ULONG aMemFree,
14563 ULONG aMemBalloon,
14564 ULONG aMemShared,
14565 ULONG aMemCache,
14566 ULONG aPagedTotal,
14567 ULONG aMemAllocTotal,
14568 ULONG aMemFreeTotal,
14569 ULONG aMemBalloonTotal,
14570 ULONG aMemSharedTotal,
14571 ULONG aVmNetRx,
14572 ULONG aVmNetTx)
14573{
14574 NOREF(aValidStats);
14575 NOREF(aCpuUser);
14576 NOREF(aCpuKernel);
14577 NOREF(aCpuIdle);
14578 NOREF(aMemTotal);
14579 NOREF(aMemFree);
14580 NOREF(aMemBalloon);
14581 NOREF(aMemShared);
14582 NOREF(aMemCache);
14583 NOREF(aPagedTotal);
14584 NOREF(aMemAllocTotal);
14585 NOREF(aMemFreeTotal);
14586 NOREF(aMemBalloonTotal);
14587 NOREF(aMemSharedTotal);
14588 NOREF(aVmNetRx);
14589 NOREF(aVmNetTx);
14590 ReturnComNotImplemented();
14591}
Note: See TracBrowser for help on using the repository browser.

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